Autor: Juan González Gómez (Obijuan)
Fecha: 18-Enero-2015
Licencia: Licencia Creative Commons BY-SA para el texto e imágenes. GPL v2 para el software
Este notebook es una adaptación de esta página de la wiki donde se comenzó a documentar la curva y su implementación en octave/Matlab
Además de ampliar la información sobre la curva, quiero aprovechar para aprender los ipython notebooks
La curva serpentinoide (o curva serpenoide, serpenoid curve en inglés) la descubrió el profesor Hirose en 1976 cuando realizaba su tesis doctoral en el Instituto de tecnología de Tokyo. Investigaba la biomecánica de las serpientes para su aplicación a la construcción de robots.
La curva serpentinoide es indispensable para el estudio y construcción de robots ápodos (gusanos y serpientes). El autor de este artículo la ha estudiado en su tesis doctoral para aplicarla a la locomoción de estos robots.
En este documento se describe la curva/onda serpentinoide, sus parámetros, sus propiedas y se presentan scripts en python para implementarla.
La curva serpentinoide es aquella cuya curvatura varía sinusoidalmente con la distancia a lo largo de la curva.
Su curvatura está dada por la ecuación:
$ K(s) = \frac{2\pi k}{l}\alpha\sin\left(\frac{2\pi k}{l}s\right) $ | Ecuación de curvatura de la serpentinoide | (**ec. 1**) |
donde:
** $l$ ** | Longitud total de la curva $ (l > 0) $ |
** $s$ ** | Es la variable. [Longitud del arco](http://es.wikipedia.org/wiki/Longitud_de_arco). Es la distancia a lo largo de la curva. $ s\in\left[0,l\right] $ |
** $ k $ ** | Número de ondulaciones. $ k > 0 $ |
** $ \alpha $ ** | Ángulo de serpenteo. $ \alpha\in\left[0,121\right] $ |
El ángulo de serpenteo $ \alpha $ es la pendiente de la curva en el punto $ s = 0 $ y determina la forma que tendrá la curva.
En la figura 1 se muestra la forma para ángulos de serpenteo de 0, 30, 60 y 90. Para $ \alpha=0 $ es una recta situada sobre el eje x, de longitud $ l $. Al aumentar $ \alpha $ la curva se eleva, ganando en altura pero reduciéndose en anchura.
En la figura 2 se muestra la curva serpentinoide para $ \alpha=121 $ grados, que es su valor máximo. A partir de ahí se producen colisiones entre los puntos de la curva.
|
**Figura 1**: Forma de la curva serpentinoide para diferentes valores del ángulo de serpenteo ($ \alpha $) |
|
**Figura 2**: Curva serpentinoide de ángulo de serpenteo ($ \alpha $) máximo. Se muestran dos ondulaciones ( $ k=2 $) |
En esta animación se puede ver cómo varía la forma de una curva serpentinoide de longitud fija $ l $ y 2 ondulaciones ($ k = 2 $), para diferentes ángulos de serpenteo. Cuando $ \alpha $ es cero, la curva serpentinoide es una recta de longitud $ l $ apoyada sobre el eje x
El parámetro $ k $ determina el número de ondulaciones de la curva serpentinoide.
En la figura 3 se han representado tres curvas con el mismo valor del ángulo de serpenteo ($ \alpha=70 $) y misma longitud $ l $, pero con diferentes valores de $ k $. Al aumentar k, aumenta el número de ondulaciones, disminuye la altura pero la anchura permanece constante
|
**Figura 3**: Forma una curva serpentinoide de longitud $ l $ para diferentes valores del parámetro $ k $, manteniendo fijo el valor de $ \alpha $ a 70 |
En la siguiente animación se muestra cómo varía la forma de una curva serpentinoide continua de longitud fija $ l=1 $ y ángulo de serpenteo $ \alpha = 70 $ con el parámetro $ k $. Se puede apreciar una propiedad muy importante: La anchura en el eje x NO varía con k
La longitud l de la curva determina su escala. En la figura 4 se muestra una curva serpentinoide con valores fijos del ángulo de serpenteo ($ \alpha $) y de las ondulaciones ($ k $) pero con distintas longitudes. La forma es la misma, pero escalada. Normalmente $ l $ es una constante, ya que los robots tienen longitud fija. Salvo que se trate de un robot modulare reconfigurable en cuyo caso puede aumentar por la adición de núevos módulos o disminuir por su separación
|
**Figura 4**: Forma de la curva serpentinoide con valores fijos del ángulo de serpenteo $ \alpha=70 $ y $ k=1 $ para diferentes valores de la longitud $ l $. La longitud determina la escala. |
Los puntos de la curva serpentinoide donde la curvatura es cero los calculamos igualando la ec. 1 a cero
$ K\left(s\right)=0 \Longrightarrow\ \frac{2\pi k}{l}\alpha\sin\left(\frac{2\pi k}{l}s\right) = 0$
Puesto que por definición, la longitud de la curva y el número de ondulaciones son distintos de cero ($ k > 0 $ y $ l > 0$) se tiene que:
$ \alpha\sin\left(\frac{2\pi k}{l}s\right) = 0 $
Y esta igualdad se cumple en dos casos:
Caso 1: $ \alpha = 0 $. Es el caso en el que la curva serpentinoide es una línea recta de longitud $ l $ apoyada sobre el eje x. Todos sus puntos tienen curvatura 0
Caso 2: $ \sin\left(\frac{2\pi k}{l}s\right) = 0 $ $ \Longrightarrow\ \frac{2\pi k}{l}s = \pi n$ con $ n = 0, 1, 2 ... \Longrightarrow\ s = n\frac{l}{2k}$ con $ n = 0, 1, 2 ... $
Los puntos de máxima curvatura los obtendremos calculando los máximos y mínimos de la ecuación de curvatura de la serpentinoide (ec. 1). Para ello igualamos a cero el valor absoluto de la derivada de la ecuación de curvatura:
$ | \frac{dK(s)}{ds} | = 0 \Longrightarrow\ | \frac{d \left( \frac{2\pi k}{l}\alpha\sin\left(\frac{2\pi k}{l}s\right) \right) }{ds} | = 0 \Longrightarrow\ \left( \frac{2\pi k}{l} \right)^2 \alpha \cos\left( \frac{2\pi k}{l}s \right) = 0 \Longrightarrow\ \alpha \cos\left( \frac{2\pi k}{l}s \right) = 0 $
El caso $ \alpha = 0 $ es una línea recta, así que, para el resto de curvas donde $ \alpha > 0 $ se cumple que:
$ \cos\left( \frac{2\pi k}{l}s \right) = 0 $
y eso implica que:
$ \frac{2\pi k}{l}s = \frac{\pi}{2} + n\pi $
Despejando s obtenemos la condición:
$ s = (2n + 1) \frac{l}{4k} $, con $ n = 0,1,2,3... $
Denotamos la pendiente del vector tangente a la curva por el punto s como
Por definición, la curvatura se define como el ritmo de cambio del vector unidad tangente a la curva, es decir:
$ K(s)=\frac{d\alpha_{s}}{ds} $
A partir de esa ecuación calculamos la pendiente de la curva mediante integración:
$ \alpha_{s}=\alpha+\int_{0}^{s}K(s)ds $
llegando a la expresión:
$ \alpha_{s}=\alpha\cos\left(\frac{2\pi k}{l}s\right) $ | Pendiente de la curva serpentinoide | **ec. 4** |
En la figura 6 se muestra el valor de $ \alpha_s $ sobre una curva serpentinoide. En los puntos de máxima curvatura $ \alpha_s = 0 $ y en los de curvatura nula $ |\alpha_s|=\alpha $
Las coordenadas cartesianas $ (x,y) $ de un punto $ s $ situado en la curva serpentinoide están dadas por las ecuaciones 5 y 6:
$ x\left(s\right)=\int_{0}^{s}\cos\left(\alpha\cos\left(\frac{2\pi k}{l}s\right)\right)ds $ | Coordenada x de la curva serpentinoide | **ec. 5** |
$ y\left(s\right)=\int_{0}^{s}\sin\left(\alpha\cos\left(\frac{2\pi k}{l}s\right)\right)ds $ | Coordenada y de la curva serpentinoide | **ec. 6** |
Estas integrales no tienen solución analítica, por lo que tienen que resolverse numéricamente
En ella aparece una curva serpeninoide y un punto genérico situado a una distancia $ s $. Sus coordenadas son $ x(s), y(s) $. La pendiente a la curva por el punto $ s $ es $ \alpha_s $. "Ampliando" el punto $ s $, vemos que está aproximado por un segmento de longitud $ ds $, que forma un ángulo $ \alpha_s $ con el eje x. Las proyecciones de este segmento son:
$$ dx=ds\ \cos\alpha_{s} $$ $$ dy=ds\ \sin\alpha_{s} $$
La abscisa del punto $ s $ se obtiene por tanto mediante la integración de los $ dx $ :
$ x\left(s\right)=\int_{0}^{s}dx= $
Aplicando la igualdad anterior para sustituir $ dx $ por $ ds $
$ = \int_{0}^{s}\cos\alpha_{s}ds = $
y finalmente se aplica la ec. 4:
$ =\int_{0}^{s}\cos\left(\alpha\cos\left(\frac{2\pi k}{l}s\right)\right)ds $
Llegamos al resultado final (ec. 5)
El razonamiento para obtener $ y(s) $ (ec. 6) es similar.
In [8]:
import numpy as np
import scipy.integrate as spi
import matplotlib.pylab as plt
%matplotlib inline
Estas son las funciones que al ser integradas nos darán las funciones paramétricas con las coordenadas x,y:
In [9]:
#-- Alpha parameters should be given in degrees
def calphas(s, alpha, l = 1, k = 1, phi = 0):
#-- Convert alpha and phi into radians
alpha_rad = alpha * np.pi / 180.
phi_rad = phi * np.pi / 180.
return np.cos(alpha_rad * np.cos(2 * np.pi * k * s / l + phi_rad))
def salphas(s, alpha, l = 1, k = 1, phi = 0):
#-- Convert alpha into radians
alpha_rad = alpha * np.pi / 180.
phi_rad = phi * np.pi / 180.
return np.sin(alpha_rad * np.cos(2 * np.pi * k * s / l + phi_rad))
Estas son las funciones parametricas que nos dan las coordenadas x,y a partir de s
In [10]:
#-- Scalar functions. Variable s is a real number
def serp_x_scalar (s, alpha, l = 1, k = 1, phi = 0):
return spi.quad(calphas, 0, s, args =(alpha, l, k, phi) )
def serp_y_scalar (s, alpha, l = 1, k = 1, phi = 0):
return spi.quad(salphas, 0, s, args =(alpha, l, k, phi) )
Y por último esta es la función que tiene las coordenadas de una curva serpentinoide. La variable s es un array con todos los valores
In [11]:
def serp (s, alpha = 45, l = 1, k = 1, phi = 0):
"""Serpenoid curve
Inputs:
- alpha : winding angle (in degrees)
- l : Curve length
- k : Number of undulations
S is the variable (it is an array). The x,y arrays are returned
"""
#-- Results
x = []
y = []
for i, vs in enumerate(s):
x.append(serp_x_scalar(vs, alpha, l, k, phi)[0])
y.append(serp_y_scalar(vs, alpha, l, k, phi)[0])
return x,y
Ejemplo de una curva serpentinoide con ángulo de serpenteo de 60 grados, longitud 1m y una única ondulación
In [12]:
#-- Obtener los puntos a lo largo de la curva
s = np.linspace(0, 1, 100)
#-- Obtener las coordenadas x,y
x, y = serp(s, alpha = 60, l = 1, k = 1)
#-- Dibujar la curva
plt.plot(x, y, linewidth = 3)
plt.grid()
plt.axis('equal')
plt.show()
In [15]:
from IPython.html import widgets # Widget definitions
from IPython.display import display # Used to display widgets in the notebook
In [31]:
widget_alpha = widgets.FloatSliderWidget()
widget_alpha.min = 0.0
widget_alpha.max = 121
widget_alpha.description = "Alpha"
widget_alpha.value = 60
widget_k = widgets.FloatSliderWidget()
widget_k.min = 0.1
widget_k.max = 20
widget_k.description = "k"
widget_k.value = 1
def draw_serp():
#-- Obtener los puntos a lo largo de la curva
s = np.linspace(0, 1, 100)
#-- Obtener las coordenadas x,y
x, y = serp(s, alpha = widget_alpha.value, l = 1, k = widget_k.value)
#-- Dibujar la curva
plt.plot(x, y, linewidth = 3)
plt.grid()
plt.axis('equal')
plt.show()
button_draw = widgets.ButtonWidget(description="Draw!")
def on_button_draw_clicked(b):
draw_serp()
button_draw.on_click(on_button_draw_clicked)
display(widget_alpha)
display(widget_k)
display(button_draw)
draw_serp()
In [13]: