domingo, 17 de marzo de 2013

C. C++, C# robotics programming Workshop/tutorial with the cheapest robotic platform

[caption id="" align="alignleft" width="108"]My Smart Car construction My Smart Car construction[/caption]

[Update March 30, 2013: Added photo gallery]

I will start a C. C++, C# robotics programming Workshop/tutorial with the cheapest robotic platform I have found at dealextreme.com: (Arduino based) plus a Raspberry Pi (like this that use Bioloid as the hardware platform):

Ultrasonic Smart Car Kit

I have not still received the kit, but I will review the kit and start the workshop as soon as I receive it.

miércoles, 13 de marzo de 2013

Playing With Qt 5, C++ and Bioloid

I have been searching the best UI (libraries and UI creating tools) multiplatform framework for several years, and QT 5 is the best option I have found. Its libraries are easy to use, specially the slots (UI widgets connection to code) of QT5.  QT Creator is pretty easy to use and the UI designer is the free best tool I have tested. Really I'm learning to use it as creating these examples.

A very simple example:

[caption id="attachment_1508" align="aligncenter" width="287"]QT Bioloid Workshop QT Bioloid Workshop[/caption]

The code uses two classes from the AXControl_v2 library:

- Configuration, it loads the  parameters from the file HexaWheels.conf (currently in spanish):
Tiempo_Espera_ms=36 // wait time
Tiempo_Espera_Sensores_ms=60 // wait time for sensors
Nombre_Puerto_Serie=/dev/ttyUSB0 // serial port name
Baudios_Puerto_Serie=57600 // baud rate

- DynamixelCommunication, it offers the commands to control Dynamixel items

Connecting to the Dynamixel bus:

[sourcecode language="cpp"]
void MainWindow::on_B_Open_clicked()
{
pDynComm->open(); // open the connection using the parameters from the configuration file
setConnectionButtons(true); // enable and disable conveniently the buttons
}
[/sourcecode]

Getting and setting the AX12 Dynamixel position:

[sourcecode language="cpp"]
void MainWindow::on_B_AX12_1_GET_POS_clicked()
{
QString qStr;

int id=ui->SB_AX12_1_ID->text().toInt();
int position = pDynComm->readValue(id, MyCommunications::PresentPosition);
string str=std::to_string(position);

ui->E_AX12_1_POS->setText(qStr.fromStdString(str));
}

void MainWindow::on_B_AX12_1_SET_POS_clicked()
{
QString qStr=ui->SB_AX12_1_ID->text();
int id=Util::convertToInt(qStr.toStdString());

int position= ui->E_AX12_1_POS->text().toInt();
pDynComm->sendOrder(id, MyCommunications::GoalPosition,position);
}

[/sourcecode]

And this is the (unfinished) main window code:

[sourcecode language="cpp"]
#include

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "Configuration.h"
#include "DynamixelCommunication.h"
#include "Util.h"

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
pConf=new Configuration ("/home/jose/proyectos_svn_win/trunk/bioloid/Comun/CPP/AXControl_v2/src/HexaWheels.conf");
pDynComm=new DynamixelCommunication (pConf);
}

MainWindow::~MainWindow()
{
delete ui;

pDynComm->close();
delete pDynComm;
delete pConf;
}

void MainWindow::setConnectionButtons(bool onOff)
{
ui->B_Open->setEnabled(!onOff);
ui->B_Beep->setEnabled(onOff);
ui->B_Close->setEnabled(onOff);
}

void MainWindow::on_B_Open_clicked()
{
//QString output="Open it";
//QMessageBox::information(this, "Output", output, QMessageBox::Ok);

//dynComm.open("/dev/ttyUSB0",57600);
pDynComm->open();

setConnectionButtons(true);
}

void MainWindow::on_B_Beep_clicked()
{
pDynComm->sendOrder(200, MyCommunications::BeepCM510, 5);
}

void MainWindow::on_B_Close_clicked()
{
pDynComm->close();
setConnectionButtons(false);
QString output="Port closed!";
QMessageBox::information(this, "Output", output, QMessageBox::Ok);

}

void MainWindow::on_B_AX12_1_GET_POS_clicked()
{
QString qStr;

int id=ui->SB_AX12_1_ID->text().toInt();
int position = pDynComm->readValue(id, MyCommunications::PresentPosition);
string str=std::to_string(position);

ui->E_AX12_1_POS->setText(qStr.fromStdString(str));
}

void MainWindow::on_B_AX12_1_SET_POS_clicked()
{
QString qStr=ui->SB_AX12_1_ID->text();
int id=Util::convertToInt(qStr.toStdString());

int position= ui->E_AX12_1_POS->text().toInt();
pDynComm->sendOrder(id, MyCommunications::GoalPosition,position);
}

[/sourcecode]

But it's "growing":

[caption id="attachment_1546" align="aligncenter" width="285"]QT_Workbench QT_Workbench[/caption]

I have uploaded the source and binary of this work in progress to
https://www.box.com/s/mdsdeoem0gg4o2rapipj/s/mdsdeoem0gg4o2rapipj

viernes, 1 de marzo de 2013

Workshop: USB, serial and remote communications with C#

[Previous post: Workshop: Dynamixel communications with C#]

[caption id="attachment_1117" align="alignright" width="150"]Bioloid SerialPort2Dynamixel C# Bioloid SerialPort2Dynamixel C#[/caption]

SerialPort2Dynamixel


Encapsulating  the SerialPort .Net class offers an easy way to use the serial port and receive Dynamixel Zig messages with the Dynamixel protocol.

.

.

Collaborator classes:


[caption id="attachment_1134" align="alignright" width="150"]Bioloid SerialPort2Dynamixel C# Collaborators Bioloid SerialPort2Dynamixel C# Collaborators[/caption]

- The SerialPort .Net class.

- RCDataReader class, which unpack the Dynamixel Zigbee sequence offering the clean data received.

Operations:


The public interface that others classes will use offers principally these operations:

public void setRemoteControlMode(bool on), which sets on or off the reception of data

[sourcecode language="csharp"]

public void setRemoteControlMode(bool on)
{
if (on)
setReceiveDataMethod(rdDataReader.rawRemoteDataReceived);
else
setReceiveDataMethod(null);
}

[/sourcecode]

public void setReceiveDataMethod(remoteControlDataReceived rcDataReceived), that sets the method that will be called when serial port data is received.

And some basics serial port data operations:

[caption id="attachment_1112" align="alignright" width="131"]Raspberry Pi - USB2Dynamixel - CM510 Raspberry Pi - USB2Dynamixel - CM510[/caption]

public bool open(String com, int speed), to open the serial port which name is in the com parameter. Wireless communications and USB ports, as used by Zig or USB2Dynaniel, are also serial ports  (COM1, COM2, ... or /ttyUSB0, ttyUSB1).

public void close(), it will do nothing if the port is already closed.

public byte[] query(byte[] buffer, int pos, int wait), send (write) a query and gets (read) the result.

public void rawWrite(byte[] buffer, int pos), well... it will write whatever contains the buffer in the first pos positions

public byte[] rawRead() , read and returns the data received.

Notes:


To avoid concurrency problems all the operations that use the Dynamixel bus are protected with a Mutex object that avoids that two or more concurrent objects use SerialPort2Dynamixel simultaneously entering the same operation or using the same resources, like variables, objects or the Dynamixel bus.

[caption id="attachment_1620" align="aligncenter" width="300"]Xevel USB2AX Xevel USB2AX[/caption]

[caption id="attachment_1622" align="aligncenter" width="300"]USB2AX over USB2DYNAMIXEL USB2AX over USB2DYNAMIXEL[/caption]


RCDataReader


[caption id="attachment_1155" align="alignright" width="150"]Bioloid RCDataReader C# Bioloid RCDataReader C#[/caption]

[caption id="attachment_1128" align="alignleft" width="112"]Remote communications RemoteCommunications[/caption]

Its responsability is to receive the Dynamixel Zig packets and extract the data.

Collaborator class:


- The ZigSequence enum, with the Dynamixels protocols data sections

[caption id="attachment_1171" align="aligncenter" width="300"]RC-100 packet RC-100 packet[/caption]

Operations:


[caption id="attachment_1146" align="alignleft" width="105"]Robotis RC-100 remote controller values Robotis RC-100 remote controller values[/caption]

public void rawRemoteDataReceived(byte[] rcData), receives the Zigbee data.

public int getValue(), returns the last value received

miércoles, 27 de febrero de 2013

Workshop: Dynamixel communications with C#

[Next post: Workshop: USB, serial and remote communications with C#]

As I wrote in the previous post, I am not using Robotis Dynamixel SDK USB2Dynamixelbecause it only works with the  USB2Dynamixel, and I need that it also should work with the serial port and with zigbee or bluetooth (really all 4 use the serial connection). Also I want to query sensors connected to the CM-510.

[caption id="attachment_1053" align="alignright" width="150"]Zigbee device Zigbee[/caption]

Using the CM-510 and computer serial port (or USB to serial) connection you are free to use any wired or wireless device. Really there are a lot of possibilities.

We will start connecting to the Dynamixel bus and sending commands and queries. These classes do the work:

- DynamixelCommunication

- SerialPort2Dynamixel

- RCDataReader

But there are other classes that offer to them some additional services, like Configuration, Utils, Hex and several enumeration types.

I will use the Class-Responsability-Collaboration template to present the classes.

DynamixelCommunicationBioloid DynamixelCommunication class C#


The main responsibility of this class is sending commands and queries to any Dynamixel device, including the sensors, sound and other capabilities of the CM-510 controller.

Collaborator classes:


SerialPort2Dynamixel,  that offers operations to use the serial port encapsulating .Net SerialPort class

- Three enums for easy use and avoid errors, using an specific type is safer that using simple integers.

    public enum AXS1_IRSensor { Left, Center, Right, None };
    public enum AXS1_SoundNote { LA, LA_, SI, DO, DO_, RE }; //Only the first six 
    public enum DynamixelFunction, with all the Dynamixel protocols codes and some that I added for the CM-510.

- Configuration class, that reads a file where are stored basic configuration parameters. like:

        private static string ParameterSerialPortName
        private static string ParameterSerialPortBaudRate
        private static string ParameterWaitTime_ms
        private static string ParameterWaitTimeForSensors_ms

Bioloid communications C#



Operations:


The public operations are the interface that other classes will use, like:

- short readValue(int id, DynamixelFunction address), reads the value of any AX-12 parameter (or other Dynamixels)


- bool sendOrder(int id, DynamixelFunction address, int value), send commands, like position, speed or torque.


And the private that do internal work supporting the public interface, like:

static int getReadWordCommand(byte[] buffer, byte id, DynamixelFunction address), create the Dynamixel hexadecimal sequence (FF FF 0F 05 03 1E CB 01 FE)


- static short getQueryResult(byte[] res), once the query or command is sent it gets the result.


Let's see readValue and two other called functions:

[sourcecode language="csharp"]

public short readValue(int id, DynamixelFunction address)
{
mutex.WaitOne();
short position = -1;

try
{
int size = getReadWordCommand(buffer, (byte)id, address);
byte[] res = serialPort.query(buffer, size, WaitTimeReadSensor);

position = getQueryResult(res);
if (position < 0)
Debug.show("DynamixelCommunication.readValue", position);

}
catch (Exception e)
{
Debug.show("DynamixelCommunication.readValue", e.Message);
}

mutex.ReleaseMutex();

return position;
}

private static int getReadWordCommand(byte[] buffer, byte id, DynamixelFunction address)
{
//OXFF 0XFF ID LENGTH INSTRUCTION PARAMETER1 …PARAMETER N CHECK SUM
int pos = 0;

buffer[pos++] = 0xff;
buffer[pos++] = 0xff;
buffer[pos++] = id;

// bodyLength = 4
buffer[pos++] = 4;

//the instruction, read => 2
buffer[pos++] = 2;

// AX12 register
buffer[pos++] = (byte)address;

//bytes to read
buffer[pos++] = 2;

byte checksum = Utils.checkSumatory(buffer, pos);
buffer[pos++] = checksum;

return pos;
}

private static short getQueryResult(byte[] res)
{
short value = -1;

if (res != null)
{
int length = res.Length;
if (res != null && length > 5 && res[4] == 0)
{
byte l = 0;
byte h = res[5];
if (length > 6)
{
l = res[6];
}

value = Hex.fromHexHLConversionToShort(h, l);
}
}
return value;
}

[/sourcecode]

Notes:


To avoid concurrency problems all the operations that use the Dynamixel bus are protected with a Mutex object that avoids that two or more concurrent objects use DynamixelCommunication simultaneously entering the same operation or using the same resources, like variables, objects or the Dynamixel bus.

All the operations use the same buffer, but being protected with the Mutex object I think that is the better option, although in a previous version I used a very different approach where there were AX12 objects with their own buffer.

[Next post: Workshop: USB, serial and remote communications with C#]

domingo, 24 de febrero de 2013

Workshop: Programming a Bioloid robot workbench using C# and C++

[Next post: Dynamixel communications with C#]

It would be a workshop using C# .Net and C++ with Qt 5. The code presented here is used in this two different robots and boards, a HP 214 Ipaq with Windows Mobile and a Raspberry Pi, using the Robotis CM-510 as the servo and sensors controller:

[youtube http://www.youtube.com/watch?v=mvaMTdlb48E&w=281&h=210][youtube http://www.youtube.com/watch?v=Yhv43H5Omfc&w=281&h=210]

These will be the first steps, using C# and .Net , here the code and the exe for the Workbench UI:

Bioloid Workbench


Using this enhaced Toss Mode that adds some new functions.  Some of them:


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 (&amp;quot;nVamos a mover un Dynamixel a voluntad. V02&amp;quot;);

do
{// inicio del bucle
puts (&amp;quot;Introduce el ID del servo a mover, entre 1 y 18, &amp;quot;);
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(&amp;quot;ID del AX12: %in&amp;quot;, 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]