jueves, 14 de febrero de 2013

Programación Orientada a Objetos y robótica

¿Por qué utilizar programación orientada a objetos?


[Artículo previo: Aprender a programar: una breve introducción]

Las facilidades de modularización, encapsulación, abstracción y automatización que ofrece la orientación a objetos, tanto en la conceptualización (análisis y diseño) como en la programación, son las mejores y más maduras que podemos encontrar.

Estas características son imprescindibles para crear el complejo software que necesita la robótica, aunque para la parte del software que se ha de ejecutar en un  microcontrolador la mejor solución suele ser simplemente C y programación estructurada. Pero para la robótica también es muy útil otro aspecto, una representación mucho más fácil de los elementos de la realidad que ha de manejar el software (actuadores, sensores, objetos del mundo real a percibir, acciones a realizar, ...). Más adelante produndizaremos un poco más.

Las herramientas necesarias para utilizarlas son también muy maduras y muchas de ellas gratuitas y/o libres:

- lenguajes de programación: "libres" como C++, y no tan "libres" como Java y C# (empresas como Oracle y Microsoft mantienen poder sobre ellos)Eclipse
- herramientas básicas como compiladores y entornos de edición y depuración de programas: los más libres, compilador GNU C++, entornos como Eclipse (no incluye compilador), no tan libre, como MonoDevelop o sólo gratuitos como Visual Studio Express que incluyen "compilador" (realmente no crea ejecutable para procesador, sino para máquina virtual, como Java).

¿En qué consiste?


Básicamente consiste en integrar en un único elemento, un objeto de una clase determinada,  todo lo necesario para realizar su trabajo,  tanto los datos a manejar como las operaciones que los manejan.

Un par de breves introducciones con mucha más información:

- Sencilla y clara presentación (pdf) de Martha Tello de Universidad Distrital Francisco José de Caldas (copia local por si falla el enlace)

- Clara y detallada presentación (pdf) de E.T.S.I. Telecomunicación Universidad de Málaga (copia local por si falla el enlace)

Libros gratuitos y libres:

- Programación orientada a objetos

- Divertido tutorial de C++

- Con este Curso de C# puedes aprender fácil y progresivamente C# (pdf)

Un par de ejemplos en C++ y C#


Los siguientes diagramas y código fuente forman parte del software que da vida a Hexawheels, que tiene una versión en C++ y otra en C#.

Diagrama general


Las clases HexaWheels y FuncionesBasicasHexaWheels, contienen el comportamiento, son la cúspide de la pirámide, ya que utilizan otras clases donde se realizan las comunicaciones, se reciben los datos de los sensores y se crean las secuencias de instrucciones para los servos.

[caption id="attachment_1012" align="aligncenter" width="272"]Diagrama clases AXControl CPP Diagrama clases AXControl CPP[/caption]

[youtube http://www.youtube.com/watch?v=Yhv43H5Omfc]

Ejemplo C++:


Declaración de clases métodos y funciones:

[sourcecode language="cpp"]
#ifndef HEXAWHEELS_H_
#define HEXAWHEELS_H_

#include "FuncionesBasicasHexaWheels.h"

namespace MiHexaWheels {

class HexaWheels : public FuncionesBasicasHexaWheels {
public:

HexaWheels();
virtual ~HexaWheels();

void andarEvitandoObstaculos();
void rodarEvitandoObstaculos();

protected:
int tiempoEspera;
int velocidad;
};

} /* namespace MiHexaWheels */
#endif /* HEXAWHEELS_H_ */
[/sourcecode]

Definición de clases, métodos y funciones ("el cuerpo"):

[sourcecode language="cpp"]
#include "HexaWheels.h"

namespace MiHexaWheels {

HexaWheels::HexaWheels() {
tiempoEspera = 500;
velocidad = 200;
}

HexaWheels::~HexaWheels() {
parar();
pausa(1000);
}

void HexaWheels::andarEvitandoObstaculos()
{
ponerPosicionEspera();
escaneadorFijoDMS.iniciar(1);

avanzarAndando();

// Es un bucle infinito
while (true)
{
// Si detecta un obstáculo maniobra para evitarlo
if (escaneadorFijoDMS.siObstaculoDetectado())
{
// retrocederTransversal(velocidad);
// y dejamos pasar un tiempo para que retroceda unos centímetros
//walker.
//pausa(tiempoEspera);
pararAndar();

situarPosicionRuedas(Circular);
pausa(tiempoEspera); // esperar o ir comprobando si aún se siguen moviendo los servos
girarIzquierda(velocidad * 2);
// y dejamos pasar un tiempo para que retroceda unos centímetros
pausa(tiempoEspera * 2);// esperar o ir comprobando si aún se siguen moviendo los servos
parar();
pausa(tiempoEspera); // esperar o ir comprobando si aún se siguen moviendo los servos

// seguimos avanzando
continuarAndando();
}
}
}

void HexaWheels::rodarEvitandoObstaculos()
{
situarPosicionRuedas(Transversal);

escaneadorGiratorioDMS.iniciar(3);

avanzarTransversal(velocidad);

// Es un bucle infinito
while (true)
{
// Si detecta un obstáculo maniobra para evitarlo
if (escaneadorGiratorioDMS.siObstaculoDetectado())
{
retrocederTransversal(velocidad);
// y dejamos pasar un tiempo para que retroceda unos centímetros
pausa(tiempoEspera);

girarIzquierda(velocidad * 2);
// y dejamos pasar un tiempo para que retroceda unos centímetros
pausa(tiempoEspera * 2);

// seguimos avanzando
avanzarTransversal(velocidad);
}
}
}

} /* namespace MiHexaWheels */
[/sourcecode]

Ejemplo C#:


[sourcecode language="csharp"]
namespace MiHexaWheels
{
// La clase Quadropodo hereda las funciones básicas de la clase FuncionesBasicasQuadropodo
class HexaWheels : FuncionesBasicasHexaWheels
{
protected int tiempoEspera = 500;
protected int velocidad = 200;

// De momento lo único que sabe hacer es pasear evitando obstáculos
public void rodarEvitandoObstaculos()
{
situarPosicionRuedas(PosicionRuedas.Transversal);

escaneoGiratorioDMS.iniciar();

avanzarTransversal(velocidad);

// Es un bucle infinito
while (true)
{
// Si detecta un obstáculo maniobra para evitarlo
if (escaneoGiratorioDMS.siObstaculoDetectado())
{
retrocederTransversal(velocidad);
// y dejamos pasar un tiempo para que retroceda unos centímetros
pausa(tiempoEspera);

girar(DireccionGiro.izquierda, velocidad * 2);
// y dejamos pasar un tiempo para que retroceda unos centímetros
pausa(tiempoEspera * 2);

// seguimos avanzando
avanzarTransversal(velocidad);
}
}
}

public void andarEvitandoObstaculos()
{
ponerPosicionEspera();
escaneoFijoDMS.iniciar();

avanzarAndando();

// Es un bucle infinito
while (true)
{
// Si detecta un obstáculo maniobra para evitarlo
if (escaneoFijoDMS.siObstaculoDetectado())
{
// retrocederTransversal(velocidad);
// y dejamos pasar un tiempo para que retroceda unos centímetros
//walker.
//pausa(tiempoEspera);
pararAndar();

situarPosicionRuedas(PosicionRuedas.Circular);
pausa(tiempoEspera); // esperar o ir comprobando si aún se siguen moviendo los servos
girar(DireccionGiro.izquierda , velocidad * 2);
// y dejamos pasar un tiempo para que retroceda unos centímetros
pausa(tiempoEspera * 2);// esperar o ir comprobando si aún se siguen moviendo los servos
parar();
pausa(tiempoEspera); // esperar o ir comprobando si aún se siguen moviendo los servos

// seguimos avanzando
continuarAndando();
}
}
}

// ¿qué más le podemos enseñar? ;)
}
}
[/sourcecode]

jueves, 31 de enero de 2013

Aprender a programar: una breve introducción

Este mes hace 20 años que me dedico profesionalmente al mundo del desarrollo de software, la mitad de ellos programando distintas aplicaciones en C++. Aunque desde 2006 no participo directamente en la parte de programación, me sigue fascinando tanto como el primer día (hacia junio de 1983) que escribí mi primer programa en un ZX-81 (increíble, tenía 1 KB de memoria y la CPU procesaba a 3,25Mhz).

[caption id="attachment_902" align="alignright" width="115"]Sinclair ZX81 Sinclair ZX81[/caption]

Tanto me fascina que en mi tiempo libre sigo ampliando conocimientos y habilidades en programación, realizando software para la creación de comportamientos para robots.

Esta fascinación me lleva también a compartir lo aprendido y ayudar a otras personas a que se animen a programar, especialmente robots, y de aquí todos estos artículos y mi participación en distintos foros de robótica.

Un poco de perspectiva: programación y desarrollo de software

Una parte fundamental del desarrollo de software es la programación, durante la cual se crea el código fuente con el que se generará el ejecutable a utilizar. Pero para crear este código fuente hemos de tener muy claro:

- qué ha de hacer y cómo sabemos que lo hace correctamente

- cómo vamos a estructurar este código fuente

- qué pruebas hacer para asegurarnos que el programa funciona correctamente

Sin obtener unas respuestas claras y reales a estas preguntas la programación se complicará tanto que en lugar de disfrutar programando nos parecerá un suplicio; probablemente sin conseguir que el programa funcione correctamente.

¿Qué necesito aprender para poder programar?

Igual que en un iceberg, la mayor parte no está a la vista.

1.- La parte obvia y más visible es aprender a utilizar:

  • un lenguaje de programación, como C, C++, C# o Java, por mencionar sólo los más conocidos,



  • las herramientas necesarias para crear un ejecutable con el programa escrito, como, por ejemplo, GNU C/C++ o Visual Studio de Microsoft


2.- Pero, realmente, mientras se aprende el lenguaje escogido es imprescindible aprender algunas cuestiones básicas y comunes a todos ellos:

  • cómo secuenciar las instrucciones a ejecutar (condiciones, bucles, etc.)



  • cómo guardar los datos (variables, objetos, arrays, listas, etc.)


3.- Y, donde empieza realmente la diversión, buscando el resultado correcto con sencillez, eficiencia y/o eficacia:

  • qué pasos ha de realizar el programa para que el resultado sea correcto (algoritmos)



  • cual es la mejor forma de estructurar el programa (arquitectura general del programa, diseño de clases y métodos o funciones)


Un ejemplo muy sencillo

[caption id="attachment_921" align="alignright" width="126"]Dynamixel AX12 Dynamixel AX12[/caption]

Ejemplo extraído de leyendo y moviendo Dynamixel AX-12 (I)  (Dynamixel son servomotores fabricados por Robotis)

Respecto el punto anterior 1:

  • El lenguaje utilizado es C



  • la herramienta utilizada para generar el ejecutable (un .hex para un microcontrolador en este caso) es Win AVR que utiliza, a su vez, GNU C.


[sourcecode language="c"]

int main(void)
{
init();

while(true) // repetiremos eternamente este bucle
{
int id=obtenerId(); // obtenemos el ID del AX-12 a utilizar
int posicion=obtenerPosicion(); // obtenemos la posición en la que situar el AX-12 seleccionado
dxl_write_word( id, P_GOAL_POSITION_L, posicion); // enviamos la orden al Dynamixel
}

return 0;
}
[/sourcecode]

Respecto el punto 2:

  • La secuencia principal es muy sencilla. Una repetición eterna ejecutando tres funciones: obtenerId, obtenerPosicion y dxl_write_word. Pero revisando qué hacen estas funciones veréis que a su vez incluyen, cada una de ellas, otras secuencias y llamadas a otras funciones. La estructura general es como un árbol, donde del tronco (main o parte principal) salen diversas ramas de las cuales pueden aparecer otras nuevas y así hasta varios niveles.



  • Los datos utilizados en este nivel principal son muy sencillos, guardaremos números enteros, uno con el identificador del servo a utilizar, id y otro con la  posición donde lo situaremos, posicion.


Veamos el contenido de obtenerId:

[sourcecode language="c"]
int obtenerId()
{
/*
Creamos una variable de un tamaño amplio, 256 bytes (posiciones de un carácter), aunque con 4
valdría, pero si se introdujeran más carácteres de los definidos provocaría un error.
*/
char cadena[256];

/*
Y otra variable de tipo entero, es recomendable asignar siempre un valor, en este caso
la inicializamos con el valor mínimo, 1
*/
int ax12Id=1;

// puts es muy similar a printf, muestra la cadena que recibe como parámetro por la pantalla
puts ("nVamos a mover un Dynamixel a voluntad. V02");

do
{// inicio del bucle
puts ("Introduce el ID del servo a mover, entre 1 y 18, ");
ax12Id=leerEnteroComoCadena(cadena); // llamamos a esta función; para entrar por pantalla el valor
// la admiración (!) es el operador lógico NOT. Repetiremos el bucle mientras el rango NO sea valido
}while(!enRangoValido(ax12Id, 1, 18));

// Mostramos el valor introducido
printf("ID del AX12: %in", ax12Id);

return ax12Id;
}
[/sourcecode]

Y, finalmente, respecto el punto 3:

  • como el ejemplo es muy sencillo el algoritmo utilizado también lo es:




    • inicializar (init)

    • eternamente hacer:

      • obtener datos del usuario (obtenerId y obtenerPosicion)

      • posicionar el servomotor con el identificador y posición introducidas (dxl_write_word)






  •  el programa está estructurado en distintas funciones que encapsulan el trabajo a realizar en pequeñas tareas, compartiendo información a través de los parámetros que reciben y el valor que retornan.



  • estas funciones están estructuradas en tres ficheros:

    • moviendoDynamixel.c

    • entrarDatos.c

    • myCM510.c


    ¿Por qué dividir en ficheros .c (módulos) y clases o funciones? Porque cuanto más modularizada y lógica sea la estructura más fácil es crearla, mantenerla y corregirla, pudiéndose reutilizar los elementos (módulos, clases, métodos y funciones) en distintos programas o partes de un mismo programa.

  • un detalle importante, la función init. Esta función está pensada para ser utilizada en muchos programas, no sólo en éste, ya que inicializa el controlador CM-510. Está incluida en el fichero myCM510.c , que contiene las funciones básicas necesarias para utilizar el controlador CM-510 (LEDs, teclas, zumbador,...)


Esquema del orden de la la ejecución de las funciones:

  1. main

    1. init

    2. <inicio bucle eterno>

    3. obtenerId

      1. leerEnteroComoCadena

      2. enRangoValido



    4. obtenerPosicion

      1. leerEnteroComoCadena

      2. enRangoValido



    5. dxl_write_word

    6. <fin bucle eterno>


     


Tutoriales más detallados

Robomind, programa gratuito, para uso personal, para empezar a programar de una forma muy fácil. Se puede utilizar con Lego Mindstorms.

[caption id="attachment_2279" align="aligncenter" width="150"]RoboMind RoboMind[/caption]

 

Introducción a la programación con C, libro/curso de la UOC (Universidad abierta de Cataluña) práctico y muy fácil de seguir.

La UPV /EHU tiene publicado este fantástico tutorial de programación (web. pdfs, zips,...)

Aprenda ANSI C como si estuviera en primero (pdf)

Curso de introducción a C de 13 páginas

Impresionante explicación sobre algoritmos (pdf)

Libros gratuitos y libres

Impresionante colección de libros gratuitos y libres, sobre programación y otros temas. Dos que me han parecido especialmente interesantes son:

- Introducción a la programación utilizando C, libro/curso de la UOC (Univ. Abierta de Cataluña)

- Programación orientada a objetos

- Práctica introducción a C++

- Divertido tutorial de C++

¿Y la programación Orientada a Objetos?

La programación orientada a objetos es un paso más para alcanzar una mejor concepción y estructuración del código fuente. Fundamentalmente consiste en encapsular en clases, y los objetos que se generan de ellas, agrupando datos y las operaciones que los manejan. Pero la riqueza que se puede alcanzar es mucho mayor de lo que puede parecer. Ver, por ejemplo, los famosos patrones de diseño

Artículo continuación: Programación orientada a objetos y robótica

[caption id="attachment_929" align="aligncenter" width="464"]Design patterns (patrones de diseño) Design patterns (patrones de diseño)[/caption]

lunes, 29 de octubre de 2012

Robotizando un coche teledirigido con Arduino

Esta es la primera entrega de una serie (espero) de ellas sobre cómo hackear un coche teledirigido y ponerle todos los sensores y cacharros que se nos ocurran.

Por supuesto, empezaremos por el principio: cómo hacer que el coche sea autónomo a través de un Arduino. El elegido en este caso es el Arduino Leonardo, pero vale  casi cualquier otro.

Materiales necesarios:

  • Un coche teledirigido baratillo, de los chinos mismo

  • Una placa Arduino

  • Cuatro resistencias (yo usé de 1K, pero valen de un valor +-50%)

  • Estaño

  • Funda termoretráctil

  • Varios cables de colores (a poder ser) de unos 20cm

  • Una batería de 9V


Herramientas necesarias:

  • Estañador

  • Destornillador

  • Pelacables (o una navaja, en su defecto)

  • Alicates de corte diagonal

  • Desoldador de estaño (no imprescindible, pero sí recomendable)


Pasos a seguir:

1. Desmonta el coche

Photobucket

Photobucket

Photobucket

2. Desatornilla la placa

Photobucket

3. Elimina el circuito integrado, ya que no lo necesitaremos para nada. Para ello tienes dos opciones:

a) Consigues calentar el estaño de sus patitas y lo sacas como Dios manda o...
b) Con los alicates le cortas las patitas por la parte de arriba y lo sacas con unas pinzas. Yo me he decidido por esta segunda opción. ;)

Photobucket

Éste es el aspecto que mostrará cuando las hayas cortado todas:

Photobucket

Photobucket

4. Prepara las resistencias

Lo que vamos a hacer ahora es proteger nuestra placa, por lo que vamos a coger las cuatro resistencias que tenemos (en mi caso de 1K, pero ya digo que el valor no es crítico, pueden ir desde 0,5K a 1,5K) y cuatro cables de colores de unos 10cm de largo, y los vamos a unir formando binomios resistencia-cable:

Photobucket

Cuando ya tengas las parejas hechas, consolida la unión con estaño.

Photobucket

Las cuatro parejas tendrán este aspecto:

Photobucket

Elimina el trozo sobrante con los alicates de corte.

5. Estudia el circuito integrado que desechaste

Fíjate en las letras impresas que tiene encima para poder Googlearlo. Si has comprado un coche en un chino o en algún sitio por el estilo, seguramente tendrás un integrado como este:

http://jumpjack.altervista.org/digitalrome/progetti/macchinina/TX-2C%28RX-2C%29AY.pdf

Lo que se trata es de buscar a qué corresponde cada pata del integrado para poder controlar nosotros a voluntad el coche. Fïjate que en este pdf aparecen tanto las especificaciones del transmisor (el que está en el mando), como del receptor (que es el que nos interesa). Los pines que estamos buscando son los correspondientes a:

  • Tierra (GND)

  • Derecha (RIGHT)

  • Izquierda (LEFT)

  • Marcha atrás (BACKWARD)

  • Hacia delante (FORWARD)


En mi caso, se trata de los pines 2, 6, 7, 10 y 11.

6. Introduce las parejas que preparaste y el quinto cable en los pines que has identificado

Lo que tienes que hacer ahora es, con ayuda del estañador y el desoldador, eliminar las patitas correspondientes a esos pines para poder introducir las resistencias por los agujeros. Procura limpiar bien la zona con el desoldador para que sea más fácil introducir la pata de la resistencia, aunque ya te digo que yo no tenía y no me costó excesivo trabajo dejar la zona presentable).

A continuación, introduce las cuatro resistencias por los pines correspondientes a las dos marchas y a los lados izquierdo y derecho de la siguiente manera:

Photobucket

Puedes observar que la resistencia queda hacia el lado de arriba, y cómo están limpios y preparados el resto de agujeros. Éste es el aspecto que tiene por debajo:

Photobucket

Una vez introduzcas las cuatro resistencias y el quinto cable que tenías apartado, pero aun no habías usado (es el que va en la patilla de GND, efectivamente), estaña los cinco pines y corta el sobrante tanto de las resistencias como del cable de tierra que te sobrará.

Photobucket

Photobucket

Éste es el aspecto que tendrá tu placa entonces:

Photobucket

7. Protegiendo tu placa

Lo que vas a hacer ahora es proteger esas parejas que hemos introducido con la funda termoretráctil. Para ello corta en varios trozos la funda de tal manera que cada trozo cubra totalmente la resistencia, el trozo estañado y un poco del cable. Unos 10-15 cm, vamos. Y como bien dice su nombre, aplícale calor para que se retraiga y se moldee según la resistencia y el cable. Puedes usar desde un mechero o una cerilla a un secador de pelo. Tú mismo. El objetivo es darle un mejor acabado a las conexiones eléctricas y dotar de protección mecánica y anti-abrasiva a los cables y demás componentes eléctricos.

Photobucket

Photobucket

Éste será el aspecto que tendrá finalmente tu placa.

8. Preparando el Arduino

Si no lo has hecho antes, ahora es el momento de anotar a qué color corresponde cada pin. Puedes hacer una tabla como la siguiente (la que resultó en mi caso):

PIN                  COLOR                  FUNCIÓN

2                               Verde                             GND

6                               Gris                                 Derecha

7                               Azul                                Izquierda

10                            Blanco                           Marcha atrás

11                            Amarillo                       Adelante

El código que vamos a preparar es muy sencillo, ya que únicamente se trata de verificar las cuatro funciones: las dos direcciones marcha atrás y adelante, y los dos lados izquierda y derecha. Se trata del siguiente:

[sourcecode language="c"]
/*
Probando las cuatro órdenes del coche
*/

int forward = 12;  // Forward pin
int reverse = 11;  // Reverse pin
int left = 10;     // Left pin
int right = 9;     // Right pin

void setup()       // Configuración de los pines como salidas
{
pinMode(forward, OUTPUT);
pinMode(reverse, OUTPUT);
pinMode(left, OUTPUT);
pinMode(right, OUTPUT);
}

void go_forward()
{
digitalWrite(forward,HIGH);  // Da la orden de adelante
digitalWrite(reverse,LOW);   // Apaga la orden de marcha atrás
}
void go_reverse()
{
digitalWrite(reverse,HIGH);  // Da la orden de marcha atrás
digitalWrite(forward,LOW);   // Apaga la orden de adelante
}
void stop_car()
{
digitalWrite(reverse,LOW);   // Apaga todas las órdenes
digitalWrite(forward,LOW);
digitalWrite(left,LOW);
digitalWrite(right,LOW);
}
void go_left()
{
digitalWrite(left,HIGH);     // Da la orden de izquierda
digitalWrite(right,LOW);     // Quita la orden de derecha
}
void go_right()
{
digitalWrite(right,HIGH);    // Da la orden de derecha
digitalWrite(left,LOW);      // Quita la orden de izquierda
}

void loop()
{

go_forward();
go_left();
delay(500);

stop_car();
delay(500);

go_reverse();
go_left();
delay(500);

stop_car();
delay(500);

go_forward();
go_right();
delay(500);

stop_car();
delay(500);

go_reverse();
go_right();
delay(500);

stop_car();
delay(500);

}
[/sourcecode]

9. Conectando y probando el Arduino

Y por fin llega el último paso: donde por fin pruebas si todo esto funciona. Elimina la antena del coche si no lo has hecho ya (obviamente, no nos hará falta) y atornilla la placa con los tornillos que quitaste en el paso 2.

Conecta los cables según los pines que has declarado en el programa (en mi caso del 9 al 12) y los colores que anotaste antes. El color de tierra lo llevarás al GND de la placa, que como ves en la foto, está junto a los otros cuatro cables (en mi caso, repito):

Photobucket

Para realizar las pruebas de código, o variaciones, te recomiendo que aun no alimentes la placa con una batería, de tal manera que tendrás que tener la placa accesible al USB. Puedes volver a montar el coche y sujetar la placa con una goma elástica, como en mi caso, o dejarlo ya desmontado para introducir la placa y la batería dentro. Ahí según tu elección.

10. Alimentando la placa

Tan sólo falta el toque final: hacer el coche totalmente autónomo. Con el código ya probado ya sabes que se mueve solo, así que sólo falta el tema de la alimentación. Para ello puedes usar una batería normal de 9V, como esta:

Photobucket

Puedes fijarla tanto en el propio chasis del coche (si tienes sitio), como en la carrocería (algo como esto):

Photobucket

La conexión la puedes realizar con un enchufe centro-positivo en el conector de alimentación de la placa, o con unos simples cables como los ya empleados, insertándolos en los pines GND y Vin del conector POWER de la placa. Según tengas el adaptador o no.

El resultado será algo como esto:

[youtube http://www.youtube.com/watch?v=ulftGvqm1LU]

Espero que te haya resultado divertido. Si tienes cualquier pregunta o duda, estamos aquí para ayudarte.

Un saludo y nos vemos en la siguiente entrega! :D

domingo, 22 de julio de 2012

Raspberry Pi and Pandaboard

Raspberry Pi and Pandaboard embedded programming and robotics

[caption id="attachment_707" align="alignleft" width="300"]Pandaboard and Raspberry Pi Pandaboard and Raspberry Pi[/caption]

In a few days [C# part ìt's already here, very soon the starting of the  C++ part] will start a tutorial about programming Pandaboard and Raspberry Pi, specially oriented towars robotics programming using the wonderful Robotis Bioloid kit.

It will try to explain clearly how to install the Raspbian and Ubuntu GNU Linux operating systems and the GNU C++ development environment.

The C++ robotics tutorial will start with a quick introduction to C++, examples using the AX-12 servos, communications using serial port and wireless connections, and, finally, the development of a 6 wheeled hexapod robot, that will be used to introduce advanced techniques like concurrent programming that the new C++ 11 bring to us.

[caption id="attachment_705" align="alignleft" width="300"]HexaWheels_01 HexaWheels_01[/caption]

By the way, Programming -- Principles and Practice Using C++ by Bjarn Stroustrup (the "father of C++) and C++ Concurreny in Action are two great books that cover wide and wisely all these subjects.

A video (excuse me for the poor quality, it willbe improved) with the first steps of the Raspberry Pi based robot

[youtube http://www.youtube.com/watch?v=Yhv43H5Omfc]

viernes, 11 de mayo de 2012

Programacion C con CM-510: leyendo y moviendo Dynamixel AX-12 (III y ultimo)

Programacion C con CM-510: leyendo y moviendo Dynamixel AX-12 (III y último)

Finalizaremos esta primera serie de programación del CM-510 en C moviendo un Dynamixel AX12 en función de los valores leídos con el sensor DMS.

[caption id="attachment_660" align="aligncenter" width="300" caption="DMS sensor"]DMS sensor[/caption]

La función "main" es así de sencilla:
[sourcecode language="c"]
int main(void)
{
int idAX12=4;
int puertoDelSensorDMS=3;

init();

// los dos ejemplos anterior refactorizados en una función

// ordenesDesdeTerminal();
// datosDesdeOtroAX12();

puts("Situaremos el AX12 en la posicion que nos indique el sensor DMS");
ordenesDesdeSensoresPorPasos(idAX12, puertoDelSensorDMS);

// una prueba cruda
//ordenesDesdeSensoresCrudo();

puts("The End");

return 0;
}
[/sourcecode]

Siendo "void ordenesDesdeSensoresPorPasos(int idAX12, int puertoDelSensor)" la función principal del ejemplo:

[sourcecode language="c"]
void ordenesDesdeSensoresPorPasos(int idAX12, int puertoDelSensor)
{
/*
Para evitar las habituales fluctuaciones que sufren los sensores
sólo tendremos en cuenta las diferencias superiores al siguiente valor
entre la anterior lectura y la actual
*/
int minimaDiferencia=25;
int diferencia=0; // la diferencia entre el valor anterior y el actual

int anteriorValor=0; // el anterior valor obtenido del sensor
int valor=0; // el de la lectura actual

while (true)
{
anteriorValor=valor; // guardamos el anterior valor leído del sensor

valor=leerSensor(puertoDelSensor); // leemos el actual

diferencia=anteriorValor-valor;
//printf("n%i", valor); // por si queremos ir viendo los valores que envía el sensor

_delay_ms(100); // una brevisima pausa

// diferencias menores que esta las consideramos fluctuaciones del sensor
if (abs(diferencia)>minimaDiferencia)
{
// utilizamos el valor leído del sensor como posición a situar el AX12
dxl_write_word( idAX12, P_GOAL_POSITION_L, valor);
}
}
}
[/sourcecode]

Utiliza la función leerSensor que tiene cierta dificultad, ya que utiliza la conversión analógico-digital de los microcontroladores ATMega. De momento no entraremos en los detalles, creo que será mejor explicarlo en una próxima serie que trate cuestiones más avanzadas. De todas formas, por si os despierta la curiosidad, os la incluyo a continuación; está basada en los ejemplos de Robotis:

[sourcecode language="c"]
int leerSensor(unsigned char idPuerto)
{
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); // ADC Enable, Clock 1/64div.

// printf( "nnIR example for CM-510nn" );

asignarPuerto(idPuerto);
//PORTA &= ~0x80;
//PORTA &= ~0x20;

//_delay_us(12); // Short Delay for rising sensor signal
_delay_us(24);
ADCSRA |= (1 << ADIF); // AD-Conversion Interrupt Flag Clear
ADCSRA |= (1 << ADSC); // AD-Conversion Start

while( !(ADCSRA & (1 << ADIF)) ); // Wait until AD-Conversion complete

PORTA = 0xFC; // IR-LED Off

//printf( "%drn", ADC); // Print Value on USART

//_delay_ms(50);
_delay_ms(esperaLeerSensorMS);

return ADC;
}
[/sourcecode]

Y aquí os podéis descargar el ejemplo completo.