Mostrando entradas con la etiqueta Talleres. Mostrar todas las entradas
Mostrando entradas con la etiqueta Talleres. Mostrar todas las entradas

jueves, 12 de diciembre de 2013

(III) ¿Cómo empezar a construir y programar robots?

Herramientas de programación de Arduino, Mindstorms y Bioloid


Tanto Lego Mindstorms como Bioloid de Robotis incluyen herramientas de programación gráfica (se utilizan iconos en lugar de únicamente escribir texto) y Arduino utiliza un editor de texto para los programas. Todos ellos permiten crear el ejecutable y enviarlo al controlador.

Entorno Arduino:

Además del entorno de programación donde editar los programas Arduino ofrece un conjunto de librerías que facilitan mucho la utilización de sensores, servos y otros dispositivos. Muchas de estas librerías para modelos de sensores concretos o servos  ya vienen incorporadas, otras se han de incluir manualmente.

En el editor se escribe el programa utilizando C o C++ (dos potentes, flexibles y muy utilizados lenguajes estándares, especialmente en robótica). El entorno de Arduino, tras unas pequeñas transformaciones, lo compila utilizando el compilador avr-gcc o avr-g++, generando el ejecutable que enviaremos al microcontrolador.

Todo lo que aprendemos de C y/o C++ nos será muy útil, pues se puede utilizar con todos los kits, robots, sistemas operativos y computadores. Y estaresmos, además, aprendiendo la forma de trabajar y herramientas habituales en programación.
ArduinoIDE

Lego Mindstorms NXT-G:

Es ideal para familiarizarse con Mindstorms y para pequeños programas, así como para las primeras pruebas que realicemos con un sensor u otro dispositivo nuevo.

Pero es exclusiva para Mindstorms, bastante fácil de utilizar para programas sencillos, pero, a medida que queramos desarrollar programas con un poco de complejidad, iremos descubriendo problemas y limitaciones (búsquedas en el programa, organizar modularmente, depuración de errores, guardar versiones y poderlas comparar)
NXT-G_EV3

Robotis Roboplus Tasks: (la ayuda, de momento, sólo está en inglés)

Se puede decir exactamente lo mismo que para Mindstorms NXT-G: Fácil de utilizar e ideal para empezar o para programas pequeños y pruebas, pero exclusiva para robots de (en este caso) Robotis. Adoleciendo de los mismos defectos, si queremos realizar programas con un mínimo de complejidad nos encontraremos con los mismos problemas.

Sólo añadiría que mi impresión es que está pensada para escribir pequeños programas que recojan los comandos mandados por el telemando y ejecuten las secuencias de movimiento creadas con Roboplus Motion (la otra utilidad básica de Robotis).
RoboplusTaks

¿A qué me refiero con un poco más de complejidad?


Retomando la propuesta de comportamiento del primer artículo de la serie podemos ver una progresión de menos a más complejidad, de forma progresiva.

El típico vehículo que hacemos que evite obstáculos mediante dos sensores de contacto (ultrasonidos o infrarojos), uno a la derecha y otro a la izquierda.

1.- El programa más sencillo simplemente evita los obstáculos girando unos pocos grados a la izquierda cuando detecte una "colisión" en el sensor derecho, y a la izquierda cuando la detecte en el sensor derecho.

2.- Intentar evitar el obstáculo con las mínimas maniobras posibles, dando el mínimo rodeo,  para seguir el rumbo previo.

Pero si el robot se nos acaba situando en un rincón o en un "callejón sin salida" (ha de girar 180 grados o salir marcha atrás) lo más probable es que nunca logre salir por sus propios medios de ahí, ya que irá de "rebotando" del lado izquierdo al derecho y viceversa hasta que se le agoten las baterías...

3.- Añadimos un poco de "inteligencia" y complejidad. En lugar de girar siempre unos determinados grados fijos vamos a hacer que cuando detecte cierta cantidad de colisiones seguidas (en determinada cantidad de milisegundos o segundos) aumente el número de grados que girará, para así acabar consiguiendo que salga del rincón o del "callejón".

4.- Ahora el problema es que tras salir del rincón o del "callejón" cuando detecte un obstáculo va a girar mucho más de lo necesario. Pues igual que se lo dimos, grados de más a girar, se lo quitamos; a medida que pasan determinados milisegundos o segundos sin detectar colisiones iremos disminuyendo los grados a girar hasta que lleguen al valor inicial.

Realmente sólo estamos utilizando dos sensores de contacto, medición del tiempo y contador de colisiones, pero ya no es ese sencillo programa inicial.

5.- Otro grado de complejidad, o probablemente más de un grado, sería añadir un sensor de luz que hiciera al robot tender hacia la fuente de luz más intensa, evitando los obtáculos del camino, claro.

A partir de este nivel de complejidad creo que utilizar la herramientas NXT-G o Roboplus Tasks requiere más esfuerzo y nos aparecerán más problemas que utilizando lenguajes estándares como Java, C o C++, así como sus herramientas asociadas.

Creo que una buena continuación serían estos artículos de introducción a la programación

martes, 10 de diciembre de 2013

(II) ¿Cómo empezar a construir y programar robots?

Resumen del anterior artículo


Antes de empezar con la programación resumamos las conclusiones del anterior artículo:

1.- Los conocimientos mínimos para empezar a construir un robot los tiene prácticamente todo el mundo o se pueden conseguir muy fácilmente:

+ electrónica o electricidad (aunque sólo sea saber distinguir voltaje e intensidad),

+mecánica (lo mínimo sería atornillar y destornillar o conectar piezas tipo Lego)

+informática (saber ejecutar un programa en un ordenador)

2.- Un kit basado en Arduino, Lego Mindstorms o Bioloid de Robotis es la forma más fácil, divertida y educativa de empezar.

¿Arduino, Mindstorms o Bioloid?


¿Por dónde queremos empezar y qué kit nos lo ofrece?

- Si queremos construir robots articulados que caminen con dos, cuatro o seis patas Bioloid es ideal, especialmente si además queremos centrarnos en programación.


- Si lo que queremos es construir distintos vehículos con ruedas o cadenas, utilizando distintos sensores para percibir el mundo, Lego Mindstorms es la mejor opción.


- Y si queremos centrarnos en la electrónica y/o programación sin gastar demasiado dinero Arduino o un kit basado en Arduino es nuestra mejor opción.

Empecemos por el kit más barato, kit Arduino. Porque, además, lo que aprenderemos con él nos servirá también para otros kits, especial, aunque no únicamente, respecto programación en C y C++.

Kit con base Arduino


[caption id="attachment_2229" align="alignleft" width="90"]Controlador Arduino Controlador Arduino[/caption]

Arduino, explicado de forma sencilla, es un controlador programable de sensores y motores, que

[caption id="attachment_2226" align="alignright" width="150"]Arduino robot Robot Arduino 66€[/caption]

podemos utilizar como "cerebro" de un robot. Podemos empezar consiguiendo un controlador Arduino y posteriormente el resto de elementos.

O bien, conseguir el controlador con un kit que incluya ya todos los elementos necesarios para construir un vehículo robot. Con el kit nos aseguramos que motores, sensores y piezas de la

[caption id="attachment_2242" align="alignleft" width="90"]Robot arduino 40€ Robot arduino 40€[/caption]

estructura encajarán y se conectarán correctamente, probablemente también nos resulte más barato. En cualquiera de ambos casos terminaremos programando el microcontrolador que incorpora Arduino. Pero Arduino no es sólo hardware, es también un conjunto de utilidades que nos facilitarán muchísimo el trabajo de programar. Y existe además una comunidad muy activa de usuarios donde podremos participar recibiendo y dando ayuda a otras personas.

Guía, ejemplos y manual


Existe bastante documentación en castellano, aquí la referencia a las principales sentencias y funciones y este fantástico manual en castellano creado por Ardumanía. Pero aún hay más:

- Conceptos básicos y ejemplos

- Guía de instalación y ejecución de primer ejemplo para Windows

Bien, ¿y cómo es en programa sencillo con el lenguaje de programación C?, así se hace parpaderar un LED:

[sourcecode language="c"]
/*
Parpadeo
Enciende durante un segundo un LED,y lo apaga durante otro segundo, así continuamente
 */

// Pin 13 está conectado a un LED en la mayoría de Arduinos
// asignamos en número de conector pin a la variable led:
int led = 13;

// la función setup se ejecuta una sola vez cuando pulsas reset
void setup() {
  // inicializ el conector pin digital como salida.
  pinMode(led, OUTPUT);
}

// la función loop (bucle) se ejecuta una vez tras otra contínuamente
void loop() {
  digitalWrite(led, HIGH);   // enciende LED (HIGH es nivel alto de voltaje)
  delay(1000);               // espera un segundo
  digitalWrite(led, LOW);    // apaga el LED bajando el voltaje; LOW, significa bajo
  delay(1000);               // espera un segundo
}

[/sourcecode]

¿Y C++? ¿Y las herramientas de programación que incluyen Robotis, RoboPlus Taks, y Lego Mindstorms, NXT-G? También, pero ya será en el siguiente artículo...

lunes, 25 de noviembre de 2013

¿Cómo empezar a construir y programar robots?

[caption id="attachment_2192" align="alignleft" width="213"]Honda Asimo Honda Asimo[/caption]

Introducción


Crear robots requiere una gran cantidad de conocimientos distintos y, dependiendo de lo sofisticado que sea el comportamiento o tareas que ha de realizar, puede ser realmente complejo y extremadamente difícil.

Los tres pilares fundamentales de la robótica son la electrónica, la mecánica y la informática; tiene otros, pero con estos tres ya podemos empezar a experimentar.

Además de conocimiento aplicable también necesitamos algunos elementos de los que partir, como motores (u otros actuadores), sensores, una computadora de pequeño tamaño, para evitar peso y consumo eléctrico, (habitualmente una SBC o microcontrolador) y una fuente de energía (batería para que sea autónomo) y algunas piezas que sustenten y mantengan unidos a todos estos elementos en sus movimientos y desplazamientos.

Estos elementos los podemos adquirir (algunos incluso fabricar)  por separado o, para facilitar la entrada a la róbótica, también los podemos adquirir de forma conjunta, como un kit.

Comparte en este foro tus dudas y comentarios

¿Qué necesito para empezar?


Sobre todo, ganas de disfrutar creando y aprendiendo.

* Adquirir unos mínimos y básicos conocimientos de:

+ electrónica o electricidad (aunque sólo sea saber distinguir voltaje e intensidad),

+mecánica (lo mínimo sería atornillar y destornillar o conectar piezas tipo Lego)

+informática (saber ejecutar un programa en un ordenador)

* Conseguir, al menos, microcontrolador (Arduino, por ejemplo), un par de motores, un sensor de distancias, unas pilas, cables y una estructura que lo soporte. El conjunto o kit básico.

Un kit, la mejor opción


Una clara ventaja de empezar con un kit es que puedes empezar YA y dedicarte a la parte fundamental (electrónica, mecánica o informática) que más te interese, porque las demás las tienes ya solucionadas; aunque en cualquier momento puedes ponerte a trabajar en cualquiera de las otras partes. Al comprar el kit, con todo el conjunto de elementos necesarios juntos, el precio además suele ser más barato.

Del más caro y complejo al más barato y sencillo

bioloid_bio_img01Bioloid: 18 potentes servomotores, 4 sensores. Ideal para humanoides, cuadrúpedos, hexápodos e incluso vehículos, y crearles software para que se comporten como seres vivos. Incluye software de programación Roboplus Tasks, también se puede programar en C, y de creación de movimientos Roboplus Motion.

Tiene un precio de unos 1000 euros. Sí, resulta algo caro, pero si dispones del dinero y de suficientes conocimientos de programación o ganas de aprender, creo que lo merece.

[caption id="attachment_76" align="alignleft" width="90"]NXT Acer Explorer con Acer NXT Acer Explorer con Acer[/caption]

Mindstorms: 2 servomotores, 1 motor, 4 sensores. Ideal para mecanismos y vehículos. Muy fácil de utilizar, pero también permite crear robots complejos. Incluye software de programacion NXT-G y, NXT es posible programarlo también en Java con Lejos y C/C++ con Osek, aún no disponible para EV3 o versiones muy preliminares.

Tiene un precio de unos 300 euros, aunque pueda parecer caro frente a otras opciones, su enorme potencial y flexibilidad en todos los aspectos (de construcción, programación y de ampliar su electrónica) lo hacen enormemente interesante.

 

[caption id="attachment_2226" align="alignleft" width="90"]Arduino robot Robot Arduino[/caption]

Vehículo basado en Arduino: 2 servomotores y los sensores con los que desees empezar. Es el más sencillo y barato, por unos 40-60 euros puedes tener lo mínimo. Ideal para profundizar en electrónica.

Se puede programar con el amigable entorno de programación Arduino.

Es el que tiene un precio más barato y que además lo puedes ir comprando por partes, a medida que vayas necesitándolas. No ofrece tanta flexibilidad como Mindstorms ni capacidad para crear robots articulados como Bioloid, pero realmente se puede aprender mucho más de lo que pueda parecer.

.

¿Y con menos dinero o sin dinero?

Por menos dinero puedes conseguir un microcontrolador tipo Arduino o sus clones, que cuestan poco más de 20 euros.

O, de forma totalmente gratuita, puedes empezar a aprender a programar en C o C++, que será muy útil para luego darle vida al robot.

¿Con qué robot empezar?


Un sencillo vehículo de dos ruedas tractoras y una libre es un buen punto de partida con el que empezar a divertirse aprendiendo.

A modo de sugerencia de un programa sencillo y cómo ir añadiéndole una mínima complejidad de comportamiento. Se puede hacer de muchísimas formas:

El típico vehículo que hacemos que evite obstáculos mediante dos sensores de contacto, uno a la derecha y otro a la izquierda.

1.- El programa más sencillo simplemente evita los obstáculos girando unos pocos grados a la izquierda cuando detecte una "colisión" en el sensor derecho, y a la izquierda cuando la detecte en el sensor derecho.

2.- Se intenta evitar el obstáculo con las mínimas maniobras posibles para seguir el rumbo establecido.

Pero si el robot se nos acaba situando en un rincón o en un "callejón sin salida" (ha de girar 180 grados o salir marcha atrás) lo más probable es que nunca logre salir por sus propios medios de ahí, ya que irá de "rebotando" del lado izquierdo al derecho y viceversa hasta que se le agoten las baterías...

3.- Añadimos un poco de "inteligencia" y complejidad. En lugar de girar siempre unos determinados grados fijos vamos a hacer que cuando detecte cierta cantidad de colisiones seguidas (en determinada cantidad de milisegundos o segundos) aumente el número de grados que girará, para así acabar consiguiendo que salga del rincón o del "callejón".

4.- Ahora el problema es que tras salir del rincón o del "callejón" cuando detecte un obstáculo va a girar mucho más de lo necesario. Pues igual que se lo dimos, grados de más a girar, se lo quitamos; a medida que pasan determinados milisegundos o segundos sin detectar colisiones iremos disminuyendo los grados a girar hasta que lleguen al valor inicial.

Realmente sólo estamos utilizando dos sensores de contacto, medición del tiempo y contador de colisiones, pero ya no es ese sencillo programa inicial.

Otro paso sería añadir un sensor de luz que hiciera al robot tender hacia la fuente de luz más intensa, evitando los obtáculos del camino, claro.

Y en el siguiente artículo...

Empezando a programar


Aprendiendo a programar con Bioloid, Mindstorms o robot basado en Arduino.

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]

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.

miércoles, 9 de mayo de 2012

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

Programación C con CM-510: leyendo y moviendo Dynamixel AX-12 (II)

En este programa utilizaremos un servo AX-12 para mover otro tal cual movamos el primero. Utilizaremos el ejemplo anterior con algunos cambios para incorporar esta nueva función, como habitualmente ocurre cuando se desarrolla software.

La función que realiza este control es void controlPorAX12(int parametroIdAX12Entrada, int parametroIdAX12Salida):

[sourcecode language="c"]
int esperaMientrasSeSitua(int idAX12)
{
int estado=COMM_TXSUCCESS;
boolean enMovimiento=true;

while (enMovimiento==true)
{
enMovimiento = dxl_read_byte(idAX12, P_MOVING);
estado = dxl_get_result();
if (estado!=COMM_RXSUCCESS)
break;
}

return estado;
}

void controlPorAX12(int parametroIdAX12Entrada, int parametroIdAX12Salida)
{
int posicion=512;

dxl_write_word( parametroIdAX12Entrada, P_GOAL_POSITION_L, posicion);
esperaMientrasSeSitua(parametroIdAX12Entrada);
dxl_write_byte (parametroIdAX12Entrada, P_TORQUE_ENABLE, 0); // quitar toque

dxl_write_word( parametroIdAX12Salida, P_GOAL_POSITION_L, posicion);
esperaMientrasSeSitua(parametroIdAX12Salida);

while(posicion>5 && posicion <1000)
{
posicion = dxl_read_word( parametroIdAX12Entrada, P_PRESENT_POSITION_L );
dxl_write_word( parametroIdAX12Salida, P_GOAL_POSITION_L, posicion );
}
printf ("nFin controlPorAX12 idAXEntrada:%i, posicion: %in", parametroIdAX12Entrada, posicion);
}
[/sourcecode]

Pero previamente hemos de entrar por RoboPlus Terminal el ID del servo AX-12 mando y del que queremos mover.

[sourcecode language="c"]
void datosDesdeOtroAX12()
{
int idAX12Entrada=0;
int idAX12Salida=0;

idAX12Entrada=obtenerId("de entrada");
if (idAX12Entrada!=0)
{
idAX12Salida=obtenerId("de salida");
if (idAX12Salida!=0)
{
printf("nFinaliza llevando el AX12 %i de entrada a su posición inicial (0) o final (1023)n", idAX12Entrada);
controlPorAX12(idAX12Entrada, idAX12Salida);
}
}
}
[/sourcecode]

La función principal (main) quedaría así:

[sourcecode language="c"]
int main(void)
{
init();

// ordenesDesdeTerminal(); el ejemplo anterior refactorizado en una función
datosDesdeOtroAX12();

puts("The End");

return 0;
}
[/sourcecode]

Aquí te puedes descargar el ejemplo completo

sábado, 5 de mayo de 2012

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

Programación C con CM-510: leyendo y moviendo Dynamixel AX-12 (I)

Como segunda entrega de este taller de programación de Bioloid vamos a realizar un programa que nos pedirá el identificador (ID) del AX-12+ a mover y la posición en la que lo situaremos.

Las explicaciones las he ido incluyendo en el código como comentarios, espero que los comentarios sean suficientes como para poderlo seguir, ya me diréis si es así.

El bucle principal es así de sencillo:

[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]

Unas breves explicaciones sobre printf: La función printf es mucho más potente de lo que parece a primera vista, admitiendo bastantes parámetros que nos permiten mostrar una gran cantidad de tipos de datos y formatos diferentes. En el siguiente ejemplo %iindica que en dicha posición del mensaje se incluirá un entero que le pasaremos como parámetro a continuación de la cadena "ID del AX12:". El carácter de control "n" indica que incluya un salto de línea.

Cada carácter de la cadena se almacena en un posición de memoria [I][D][ ][d][e][l]... siendo las cadenas un caso especial de vector o, en inglés, "array".

Descárgate aquí una presentación en PDF con explicaciones muy detalladas sobre cadenas en C, de la Universidad del País Vasco.

Las funciones obtenerId() y obtenerPosicion() son también bastante sencillas, ¿verdad?

[sourcecode language="c"]

/*
La siguiente función solicita la entrada de un valor para seleccionar el ID del Dynamixel al que
enviar las órdenes, Comprobando que el que esté entre 1 y 18
*/

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 ("nnVamos 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 se valido
}while(!enRangoValido(ax12Id, 1, 18));

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

return ax12Id;
}

// Repetiremos prácticamente el mismo código que la función anterior, ¿sería fácil crear una función reutilizable para ambos, verdad?
int obtenerPosicion()
{
char cadena[256];
int posicion=0;

do
{
puts ("Introduce un valor entre 0 y 1023 para situarlo en esa posicion");
posicion=leerEnteroComoCadena(cadena);
}while(!enRangoValido(posicion, 0, 1023));

printf("nPosicion: %in", posicion);

return posicion;
}
[/sourcecode]

Las funciones utilizadas para leer el texto de la consola son bastante interesantes,
ya que nos muestran la utilización de una cadena. También nos muestra como utilizar
los fichero .h (declaraciones) y .c (definiciones, el contenido de las funciones):

[sourcecode language="c"]
// Cuerpo (contenido) de las funciones de entrada de datos

/*
Recibimos una variable que tiene reservado espacio en memoria para almacenar
la cadena introducida
*/

void leerCadena(char parametroCadena[])
{
int i=0; // Utilizaremos esta variable como índice para ir almacenando los caracteres introducidos
do
{
parametroCadena[i]=getchar();  // almacenamos el carácter obtenido en la posición i de parametroCadena
putchar(parametroCadena[i]);   // lo mostramos por pantalla
if (parametroCadena[i]=='b')  // si se ha pulsado la tecla <Borrar> (justo encima de <Intro>)
i--;                       //    nos situamos en la posición anterior para escribir de nuevo sobre ella
else                           // si no
i++;                       //    escribiremos en la siguiente posición
}while(parametroCadena[i-1]!='n'); // mientras el último valor guardado NO sea INTRO. El símbolo ! representa el operador NOT
parametroCadena[i]=0;           // en la última posición de una cadena ha de estar el valor cero (NULO, null en Inglés)
}

/*
Leemos una cadena, la convertimos a entero y devolvemos el valor obtenido
*/
int leerEnteroComoCadena(char parametroCadena[])
{
leerCadena(parametroCadena);
return atoi(parametroCadena);
}
[/sourcecode]

El código fuente entero os lo podéis descargar desde aquí.

El lenguaje C, también C++, tiene algunas características muy interesantes, como la inclusión de código dependiendo de determinadas condiciones. Esto permite utilizar el mismo código fuente para distintos procesadores, simplemente cambiando uno o varios parámetros.

A modo de ejemplo este ZIP contiene un proyecto preparado para Dev-CPP con una versión de los ficheros fuente válida para PC y para CM-510; simplemente comentando o descomentando una línea del fichero "myCM510.h" (para utilizarlo con AVR Studio se ha de crear el correspondiente proyecto e incluir estos fuentes).

En lugar de mover el servo AX-12 muestra en pantalla el texto "dxl_write_word" con los parámetros recibidos. Podemos practicar C y realizar distintas pruebas en el PC sin conectar ningún servo.

[sourcecode language="c"]

// Si no está comentada la siguiente línea podremos compilar el programa para PC
// Si está comentada la siguiente línea podremos compilar el programa para CM-510
#define __PC_TEST_

[/sourcecode]

sábado, 28 de abril de 2012

Programacion C con CM-510: vectores (arrays), puertos y LEDs

Programanción C con Bioloid CM-510: "arrays", puertos y LEDs

Cadenas y Vectores ("Strings y Arrays)


Una de las formas más habituales y fáciles de manejar un conjunto de datos en C, y en muchos otros lenguajes como C++, Java y C#,  es mediante cadenas y vectores.

Cadenas

Una cadena consiste en un conjunto de caracteres, como las que utilizamos en el programa "holaMundo.c" del anterior artículo:

[sourcecode language="c"]

int main(void)
{
int ax12Id=6;

init();

printf("rn Un sencillo ejemplo");

...
}
[/sourcecode]

A la función printf le pasamos como parámetro la cadena "rn Un sencillo ejemplo"
printf("/r/n Un sencillo ejemplo");

En este caso no hemos utilizado ninguna variable para almacenarla, sino que se la hemos pasado directamente, como un literal. Si quisiéramos utilizar dicha cadena en distintas partes la hubiéramos almacenado en una variable, aunque también existen otras razones para utilizar no utilizar literales en el código, como agruparlas en un único punto para facilitar su localización o su traducción).
char titulo[]="Un sencillo ejemplo";

printf("rn %s", titulo);

Unas breves explicaciones sobre printf: La función printf es mucho más potente de lo que parece a primera vista, admitiendo bastantes parámetros que nos permiten mostrar una gran cantidad de tipos de datos y formatos diferentes. En este caso %s indica que en dicha posición del mensaje se incluirá una cadena que le pasaremos como parámetro a continuación, "titulo" en este ejemplo.

Cada carácter de la cadena se almacena en un posición de memoria [U][n][ ][s][e][n]... siendo las cadenas un caso especial de vector o, en inglés, "array".

Descárgate aquí una presentación en PDF con explicaciones muy detalladas sobre cadenas en C, de la Universidad del País Vasco.

Vectores

Un vector es probablemente la forma más sencilla de manejar o almacenar un conjunto de datos. En el ejemplo que viene a continuación utilizaremos el siguiente vector de enteros:

int pin[]={1,2,4,8,16,32,64};

Aunque quizás sea más claro si utilizamos la forma "completa", en lugar de la abreviada, de cargar datos:
int pin[7] //  creamos un vector con 7 posiciones de memoria para almacenar un entero (int)

int[0]=1; // vamos asignando cada valor... ¡Ojo, que el primero es la posición 0 (cero)!

int[1]=2;

int[2]=4;

int[3]=8;

int[4]=16;

int[5]=32;

int[6]=64;

Descárgate aquí una presentación en PDF con explicaciones muy detalladas sobre vectores en C, también de la Universidad del País Vasco.

Los puertos de los microcontroladores

Probablemente el elemento más característico de los microcontroladores son los puertos de Entrada/Salida E/S (Input/Output, I/O en inglés), con los cuales podemos enviar y recibir información para controlar distintos elementos electrónicos como sensores, actuadores y LEDs.

El controlador de Robotis CM-510 nos ofrece 6 conexiones donde podremos utilizar los puertos de entrada salida que el microcontrolador ATMega 2561 (página en inglés del fabricante ATMEL) incorpora.

[caption id="attachment_449" align="alignnone" width="300"]Controlador CM-510 Controlador CM-510[/caption]

Cada puerto se controla mediante tres registros (un registro es básicamente una zona de memoria):

  • DDRx: donde se indica si se enviarán o recibirán datos

  • PINx: aquí se reciben los datos

  • PORTx: y desde aquí se envían los datos al exterior del microcontrolador


Para manejar los puertos se han de utilizar los operadores de C a nivel de bits para activar y desactivar cada uno de los bits que representan los puntos de conexión (PIN) que componen los puertos. Estas operaciones utilizan los mismos operadores booleanos que las puertas lógicas y tablas de verdad

[caption id="attachment_461" align="alignnone" width="165"]tabla y puerta or tabla y puerta or[/caption]

[caption id="attachment_460" align="alignnone" width="167"]tabla y puerta and tabla y puerta and[/caption]

Mediante los puertos podremos controlar los LEDs y podremos comprobar si está pulsado alguno de los botones. Por ejemplo:

// El puerto se inicializa con los valores a 1, 1111 1111 ó 0x7F en hexadecimal. OJO 1 (uno) es LED apagado, 0 (cero) es encendido.

// Al ejecutar:
PORTC &= ~0x01; // el complementario de 0000 0001 es 1111 1110

/* la operación AND entre
1111 1111       el puerto C
1111 1110       y el valor complementario de 0000 0001
--—- —---
1111 1110       el resultado es que sólo el LED 1 estará encencido

Son operaciones sencillas pero en las cuales es fácil equivocarse. Las funciones son uno de los elementos fundamentales de C para encapsular los detalles y, una vez comprobado que funcionan perfectamente, “olvidarnos” de ellos:

[sourcecode language="c"]
int pin[]={1,2,4,8,16,32,64};

void encenderYapagarLEDs()
{
for (int i=0;i<=6;i++)
{
ledOn(pin[i]); //enciende LED
_delay_ms(500); // una pausa de medio segundo
ledOff(pin[i]); // apaga LED
}
}
[/sourcecode]

teniendo definido previamente, claro:

[sourcecode language="c"]
void ledOn(unsigned char ledId)
{
PORTC &= ~ledId;
}

void ledOff(unsigned char ledId)
{
PORTC |= ledId;
}
[/sourcecode]

Habitualmente en lugar de funciones se utiliza #define, pero creo que ahora es mucho más claro utilizar funciones. Por cierto, ¿por qué utilizo al array "int pin[]={1,2,4,8,16,32,64};"?, ¿de qué otras formas se puede hacer?

jueves, 26 de abril de 2012

Taller de programacion Bioloid: Primeros pasos en C

Taller de programación Bioloid: Primeros pasos en C

[This post is also in English]

Con este breve artículo comenzamos el taller de programación Bioloid con distintos lenguajes (C, C++ y C#) y en distintos entornos (ATMega, PC, SBC). Partiendo prácticamente de cero y hasta donde nos lleguen las fuerzas.

Los primeros pasos los daremos en C

C es un lenguaje sencillo, potente y extremadamente versátil con el que se desarrolla una gran cantidad de software para industrias tan diferentes como la del automóvil (enlace a traducción automática), el equipamiento médico o para la propia industria del software, desde Microsoft Office hasta sistemas operativos como Windows o Linux. (está en inglés pero se entiende fácilmente porque es una tabla de productos software bastante conocidos y lenguajes de programación utilizados).

Como va a ser un taller de programación muy práctico y dirigido a la programación de los servos Dynamixel de Robotis incluyo un enlace a un completo y popular libro Aprenda ANSI C como si estuviera en primero; si quieres una introducción más rápida descárgate esta presentación de 13 páginas, y aquí el documento con el estándar completo en inglés, pero ten en cuenta que es denso y no lo he encontrado en castellano.

{ Actualización:

He descubierto este estupendo y detallado curso de introducción a la programación con lenguaje C de la Universidad del País Vasco dirigido a personas que no tengan ningún conocimiento previo de programación.

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.

}

Uno de los programas más sencillos en C:

[sourcecode language="c"]
// Esta línea que empieza por dos barras inclinadas es un comentario

/*
Igual que éstas, que empiezan con una barra inclinada y un asterisco
y seguirá siendo un comentario que finaliza con otro asterisco y otra barra inclinada.

Los comentarios son muy útiles para realizar explicar qué vamos a hacer y,
especialmente, por qué lo hacemos así, ya que pasados unos meses no recordaremos los detalles.
*/

/*
Los includes nos sirven para anunciar al compilador que vamos a utilizar
funciones existentes en otros ficheros, como stdio.h, que en este ejempo
nos proporcionará la función printf para poder mostrar información en la pantalla.
(Esto no es exactamente así, pero ya lo veremos más adelante)
*/
#include

/*
Ésta es una de las formas de empezar un programa en C,
Creando la función principal (main) que todo programa en C necesita para empezar
*/
void main()

// El cuerpo o contenido de la función empieza con la siguiente llave
{

// ¿Adivinas qué hace la siguiente función?
printf ("Hola, Mundo");

// y, previsiblemente, la función acaba con esta otra llave
}
[/sourcecode]

Prueba a hacer algunas modificaciones en esta web, "output" es lo que mostraría el programa en la pantalla, "submit" significa enviar y simula la ejecución del programa. Si te equivocas te indicará los errores, también te puede mostrar "warnings", avisos.

Ahora realicemos el primer programa para el CM-510

Pero antes instalaremos el software necesario para programar el CM-510 (inglés). Si instalas WinAVR en "C:herramientasWinAVR-20100110" te podrás descargar un zip con todo preparado.

Cuando queramos volver a utilizar los programas RoboPlus Tasks, RoboPlus Motion y demás programas de Robotis deberemos cargar de nuevo el firmware de Robotis, restaurar firmware del CM-510 (inglés)

[sourcecode language="c"]
#include
#include "myCM510.h"

void ejecutarMovimiento1(int ax12Id)
{
dxl_write_word( ax12Id, P_GOAL_POSITION_L, 512);
}

void ejecutarMovimiento2(int ax12Id)
{
dxl_write_word( ax12Id, P_GOAL_POSITION_L, 600);
}

int main(void)
{
int ax12Id=6;

init();

printf("/r/n Un sencillo ejemplo");

printf("/r/n Realizar movimiento 1 con el AX-12 %i", ax12Id);
ejecutarMovimiento1(ax12Id);

printf("/r/n Pausa de medio segundo");
_delay_ms(500); // una pausa de medio segundo

printf("/r/n Pitido!");
buzzOn(100); // pitido

printf("/r/n Pausa de un segundo");
_delay_ms(1000); // una pausa de 1 segundo

printf("/r/n Realizar movimiento 2 con el AX-12 %i", ax12Id);
ejecutarMovimiento2(ax12Id);

printf("/r/n Fin");
}
[/sourcecode]

Los carácteres "/r/n" se utilizan para saltar a la siguiente línea en Windows.

Si has instalado el software necesario (WinAVR debe estar instalado en "C:herramientasWinAVR-20100110") y descomprimes el fichero TallerProgramacionBioloid_01.zip en el directorio raíz (C:) has de tener todo listo para poder modificar, compilar o simplemente cargar el ejecutable "hola_mundo.hex" en el CM-510. Has de ver algo similar a:

[caption id="attachment_381" align="alignnone" width="300"]01_Hola_Mundo_Salida_RoboPlus_Terminal 01_Hola_Mundo_Salida_RoboPlus_Terminal[/caption]

Algunas explicaciones
dxl_write_word( ax12Id, P_GOAL_POSITION_L, 600);

Es el comando incluído en las librerías de Robotis para CM-510 que nos permite enviar órdenes a un actuador Dynamixel de forma muy sencilla. Sólo le tenemos que indicar el ID del AX-12 a mover (en ax12Id), el código de la órden que el AX-12 ha de ejecutar, en este caso situarse en una posición determinada (P_GOAL_POSITION_L) y la posición en la que se ha de situar entre entre la 0 y la 1024 (600).

[caption id="attachment_416" align="alignnone" width="300"]dx_series_goal dx_series_goal[/caption]

Puntos principales:

Descomponer el programa en distintas partes

  • Al haber creado previamente la función init() en myCM510.h/myCM510.c nos permite incluirla en este programa simplificandolo mucho.

  • Además de simplificar la programación permite reutilizar el mismo código en distintos programas. Lo cual nos evita tener que repetir el mismo código muchas veces y, sobre todo, tener que corregir los fallos o mejorarlo sólo en un único sitio, no en todos los programas que se ha repetido. Más adelante veremos cómo organizar los directorios e incluso cómo crear librerías.

  • También nos permite encapsular los detalles de forma que cuando el programa empiece a crecer podamos manejarlos con facilidad sin que nos veamos desbordados.


Mostrar qué está ejecutando el procesador

  • Mediante la función printf podemos enviar a la pantalla texto que nos permite saber qué es lo que está haciendo el programa (printf lo envía al puerto serie y "RoboPlus Terminal" lee de éste y lo muestra por pantalla. Aprenderemos a hacerlo cuando empecemos a programar Bioloid desde el PC)


¿Se te ocurre una forma sencilla de evitar tener dos funciones tan parecidas como "void ejecutarMovimiento1(int ax12Id)" y "void ejecutarMovimiento2(int ax12Id)"?

martes, 22 de noviembre de 2011

CM-5: Creating a simple "Hello World" C program

This post try to explain how to compile, load in the CM-5 (transmit) and execute a C program (a servo version of the "Hello World" classic example).

What do you need?


(You can find here how to start programming CM-5 / CM-510)

Obviously, some Bioloid hardware:


- The CM-5


- The battery or power source


- The serial cable


- An AX-12


- A cable to connect the AX-12 with the ID 17 to the CM-5. You can change the ID in the program.



What should you do?


1. Download and install the software:


1.- Download and install the Robotis RoboPlus software.


2.- Download and install WinAVR


3.- Download a Hello World example for WinAVR.


The C “Hello World” servo program is based in the original Robotis example.


4.- You should create a folder for your CM-5 program, for example c:myprojectscm5helloworld Copy there the previously downloaded helloworld.zip and unzip it.



2. Compile and link the program


1. Execute WinAVR and open the unzipped project:


( Bioloid User's Guide.pdf could help you using WinAVR)




You should change the ID 17 of the servo for the ID of the servo you are using



2.- Compile and link. Make all:



3.- You should see “Errors: none”



4.- Transmit and execute the helloworld.hex to the CM-5


( Here you can find more info )



Well, if you only want to test the executable, you can download only the hex.


When loaded you should see an screen with two equal values as a correct "Checksum" check (in this example the value is 81, it's calculated from the source code). If the values are not equal there is an error in the transmission.


CM5HexLoaded


5.- Every time you press the red MODE button the servo should spin