domingo, 31 de julio de 2011

PAROCA I - Plataforma Robótica de Carga: Chasis

El chasis para la Plataforma Robótica de Carga será integramente en aluminio y realizada 100% personalizada mediante elementos individuales. Estos deberán ser perforados, atornillados o machambrados para su uso, no obstante, nos aseguramos de la unicidad e individualidad del proyecto y de la Plataforma Robótica.


A tal efecto, se utilizarán los siguientes componentes:
  • 2x Ángulo de aluminio de 40mm x 40mm x 3mm x 500mm de largo (6 EUR/ud)
  • 4x Ángulo de aluminio de 40mm x 40mm x 3mm x 250mm de largo (4 EUR/ud)
  • 1x Plataforma antideslizante de aluminio de 500mm x 250mm x 3mm (9 EUR/ud)
  • 1x Placa aluminio de 100mm x 3mm x 250mm (4 EUR/ud)
Coste de los productos: 43 EUR, adquirido en Forward Metals.

En el siguiente conjunto de imágenes podemos ver la plataforma en sus elementos básicos (sin montar) y visto en comparativa a uno de los motores que utilizaré.


Visión inferior de como estará la distribución (los ángulos internos evidentemente no estarán así, sino formando una C para atornillar la base intermedia en donde se alojará la electrónica).


Visión inferior (sólo indicativa de una posible distribución).



Próximamente ...

En una nueva entrada en el blog, se describirá la distribución electrónica básica del PAROCA I, (señalización y motorización) que como adelanto mostraremos un boceto de estos en la imágen siguiente:



domingo, 24 de julio de 2011

PAROCA I - Plataforma Robótica de Carga: Driver + Motores

El primer elemento que necesitamos para la Plataforma Robótica de Carga (PAROCA I) es un driver para los motores DC que se van a utilizar en el prototipo, en este caso 4 motores DC brushes de 6V.

Motor Shield

Lo primero de todo para el driver de los motores es seleccionar el H-Bridge y que mejor que el conocidísimo L293D capaz de proporcionar 600mA (pico de 1.2 A) con protección térmica y operativo en un rango de tensión de 4.5V a 36V. Utilizaremos ya una solución de 4 H-Bridges existente en el mercado realizado por Adafruit.com, el MShield o Motor Shield.


Motor Gear
El motor escogido es el 75:1 Metal GearMotor 25Dx54L mm adquiridos en Pololu, capaz de proporcionar:

Gear ratio 75:1
Free-run speed @ 6V 75 rpm
Free-run current @ 6V 80 mA
Stall current @ 6V 2200 mA
Stall torque @ 6V 6.1Kg·cm


Montaje de los componentes
En la secuencia de imágenes siguiente se pueden ver los elementos que formarán parte del PAROCA I, en este caso:

1x Driver MShield
4x GearMotor 75:1
4x Ruedas 90x10mm
4x Mounting hubs
4x Soportes motor (brackets)
4x Cables JST para fácil conexión/desconexión


Componentes necesarios para montar el shield del motor compatible para Arduino.
Detalles del prototipo, alimentación por pila 9V recargable, Arduino UNO, Motor Shield y 1 Gearmotor.

Detalle de la soldadura de cable al motor protegida por envoltura tubo termoretráctil.
El conjunto de los componentes ya integrados.

Ejemplo del código fuente en Arduino
 #include <AFMotor.h>

// Motor DC en M1
AF_DCMotor motor(1);

void setup() {
Serial.begin(57600);
Serial.println("Iniciando motores...!");
// Configurando velocidad motor #1 (de 0 a 255)
motor.setSpeed(200);
motor.run(RELEASE); // PARAR
}

int i;

void loop() {
motor.run(FORWARD); // Motor: Avance
// motor.run(BACKWARD); // Motor: Retroceso
}

sábado, 16 de julio de 2011

PAROCA I - Plataforma Robótica de Carga: Kick off

Para mi nuevo proyecto de Plataforma Robótica de Carga (llamado desde ahora como proyecto PAROCA) empieza el desarrollo de los elementos que formarán parte del primer prototipo.

Así que: Kick off del proyecto !!

Anteriormente se ha escrito alguna entrada en el blog del uso del Horizonte Artificial para la detección del plano horizontal y su inclinación mediante un acelerometro ADXL335. El objetivo es emplearlo en lo que se llama Parallel manipulators (o Parallel robot) para el presente proyecto, de tal manera que se mantenga siempre la plataforma horizontal independientemente de la inclinación del suelo.

A tal efecto, y tras un estudio de las técnicas empleadas se describen dos vertientes ampliamente estudiadas:
  • Plataforma Stewart (basada en actuators lineales)
  • Delta robot (basado en actuators rotatorios)
Empezamos a mirar la mejor alternativa...

domingo, 10 de julio de 2011

Horizonte Artificial realizado en Processing vía Arduino

La presente entrada en el blog describe el prototipo creado para visualizar y gestionar los llamados "Ángulos de Euler" de aeronáutica/náutica -a fin de ser correctos deberían llamarse ángulos de Cardano. El objetivo, determinar la orientación de un objeto que puede rotar (cabeceo, guiñada y alabeo/balanceo) sobre los ejes intrínsecos.

La siguiente imagen muestra la interficie gráfica creada en Processing para visualizar en tiempo real la información que nos proviene de nuestra plataforma hardware (en este caso vía un acelerómetro de 3 ejes montada sobre una plataforma Arduino)


La primera prueba de horizonte artificial será en este caso sólo tratar y visualizar el ROLL (balanceo/alabeo)


Hardware
- Arduino UNO
- Acelerómetro ADXL335
- LED Tricolor
- Resistencias (1x8.2K, 2x100, 1x180)
- MiniPulsador
- Cables + miniplaca de prototipado


La siguiente imagen muestra el circuito completo de nuestro prototipo. El pulsador se utiliza para calibrar el acelerómetro de 3 ejes, es decir, cuando consideramos una posición estacionaria (sin movimiento) y totalmente horizontal, lo pulsamos y así establecemos el valor llamado zero-g, que sirve para compensar la fuerza gravitatoria según nuestra localización (en Barcelona es 9.803 m/s^2).


El led se enciende sólo durante el periodo de calibración.


En el programa almacenado en Arduino se utiliza el factor scale para fijar al máximo la gravedad a fin que sea considerada 1 gee = 9.8m/s^2.

Software
- Arduino 0021
- Processing 1.5


Se puede apreciar en la consola de la aplicación los grupos de 9 valores que se van recibiendo desde el Arduino, siendo estos:
  • los valores en mV de los ejes X,Y,Z
  • el valor XY respecto la posición de calibraje
  • el factor correctivo de la gravedad versus ADC
  • los milligee (la gravedad en miles)
  • el pitch
  • el roll
A continuación se facilita la primera versión del código fuente para ambos entornos, necesarios para que interactuen entre ellos.

Código fuente de Arduino
 /*  
Proyecto : Horizonte Artificial
Autor....: Ricard Forner (RFKsolutions)
Version..: 0.0.1
Fecha....: 10/07/2011

Código fuente: Arduino

Circuito
Pin 8: Lectura Pulsador Resistencia: 8.2 k
Pin 9: LED RGB (Azul) Resistencia: 100 ohm
Pin 10: LED RGB (Verde) Resistencia: 100 ohm
Pin 11: LED RGB (Rojo) Resistencia: 180 ohm

*/
class Accelerometer {
int p[3]; // pins ejes XYZ analog
int a[3]; // aceleracion (zero-based)
int b[3]; // aceleracion bias/calibracion
int g, t, r; // copia cache de calculos
int scale; // factor escala entre ADC y gravedad
int ledPin; // pin del led de calibracion

public:
Accelerometer(int pinX, int pinY, int pinZ, int pScale, int pLedPin) {
pinMode((p[0] = pinX), INPUT);
pinMode((p[1] = pinY), INPUT);
pinMode((p[2] = pinZ), INPUT);
for (int i = 0; i < 3; i++) {
b[i] = 512;
}
g = t = r = 0;
scale = pScale;
pinMode((ledPin = pLedPin), OUTPUT);
}

void update() {
for (int i = 0; i < 3; i++) {
a[i] = analogRead(p[i]) - b[i];
}
g = t = r = 0;
}

void calibrate() {
digitalWrite(ledPin, HIGH);
for (int i = 0; i < 3; i++) {
b[i] = analogRead(p[i]);
}
b[2] -= scale;
update();
digitalWrite(ledPin, LOW);
}

int milligee() {
if (g != 0) return g;
long squared = 0.0;
for (int i = 0; i < 3; i++) {
squared += (long)a[i] * (long)a[i];
}
g = squared * 1000 / (scale*scale);
return g;
}

int accel(int axis) {
if (axis < 0 || axis > 3) return 0;
return a[axis];
}

int roll() {
if (r != 0) return r;
r = (int)(atan2(a[0], a[2]) * 180. / M_PI);
return r;
}

int pitch() {
if (t != 0) return t;
t = (int)(acos(a[1] / (float)scale) * 180. / M_PI);
return t;
}

void toConsole() {
Serial.print("xV="); Serial.print(b[0]);
Serial.print("\tyV="); Serial.print(b[1]);
Serial.print("\tzV="); Serial.print(b[2]);
Serial.print("\tx="); Serial.print(a[0]);
Serial.print("\ty="); Serial.print(a[1]);
Serial.print("\tz="); Serial.print(a[2]);
Serial.print("\tmg="); Serial.print(milligee());
Serial.print("\tpitch="); Serial.print(pitch());
Serial.print("\troll="); Serial.print(roll());
Serial.println();
}

void toProcessing() {
Serial.print(0);
// XYX mV
Serial.print(";"); Serial.print(b[0]);
Serial.print(";"); Serial.print(b[1]);
Serial.print(";"); Serial.print(b[2]);
// XYX valores
Serial.print(";"); Serial.print(a[0]);
Serial.print(";"); Serial.print(a[1]);
Serial.print(";"); Serial.print(a[2]);
// mGe (1 gee = 9.8ms2)
Serial.print(";"); Serial.print(milligee());
// pitch
Serial.print(";"); Serial.print(pitch());
// roll
Serial.print(";"); Serial.print(roll());
Serial.println();
}

void loop() {
update();
}

};

const int sensorEjeX = 0;
const int sensorEjeY = 1;
const int sensorEjeZ = 2;
const int buttonPin = 8;
const int ledPinRed =11;
const int ledPinGreen =10;
const int ledPinBlue = 9;

int bucle = 0;
Accelerometer accel = Accelerometer(sensorEjeX, sensorEjeY, sensorEjeZ, 64, ledPinRed);

void establishContact() {
Serial.begin(38400);
Serial.println("Arduino conectado.");
}

void initButtons() {
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, LOW); // pulldown
}

void initLEDs() {
pinMode(ledPinRed, OUTPUT);
pinMode(ledPinGreen, OUTPUT);
pinMode(ledPinBlue, OUTPUT);
}

void setup(){
establishContact();
initButtons();
initLEDs();
}

void loop() {
delay(20);
accel.loop();

// Calibracion bajo peticion
if (HIGH == digitalRead(buttonPin)) { accel.calibrate(); }

// Enviamos solo 1 de cada 8 muestras
if (--bucle <= 0) { bucle = 8; accel.toProcessing(); }
}





Código fuente de Processing
 /**  
Proyecto : Horizonte Artificial
Autor....: Ricard Forner (RFKsolutions)
Version..: 0.0.1
Fecha....: 10/07/2011

Código fuente: Processing
*/

import processing.serial.*;

//
int appWidth = 400;
int appHeight = 480;
int appCenterX = appWidth/2;
int appCenterY = appHeight/2;
int diametro = 200;

// Puerto serie de comunicacion arduino
Serial port;

// valores procedentes de arduino
int sensorXmV;
int sensorYmV;
int sensorZmV;
int sensorX;
int sensorY;
int sensorZ;
int sensormg;
int sensorPitch;
int sensorRoll;

void setup() {
size(appWidth, appHeight);
smooth();
noStroke();
background(0);
PantallaTexto();
AbrirPuertoSerie();
}

void PantallaTexto() {
textMode(SCREEN);
text("Proyecto Horizonte Artificial", 30, 40);
text("by RFKsolutions", 30, 60);
text("v 0.0.1 - 10 julio de 2011", 30, 80);

text("Prueba: Visualización ROLL", appCenterX, appHeight-40);
text("Hardware: Arduino + ADXL335", appCenterX, appHeight-20);
}

void AbrirPuertoSerie() {
// Pintamos la lista de puertos disponibles
println(Serial.list());
// Puerto del arduino
port = new Serial(this, "COM17", 38400);
// no se leera la función serialEvent() hasta que aparezca un salto de linea
port.bufferUntil('\n');
}

void arduinoPuertoDisponible() {
while (port.available() > 0) {
serialEvent(port.read());
}
}

void serialEvent(int Serial) {
String inStr = port.readStringUntil('\n');

if (inStr!=null) {
inStr = trim(inStr);
// Se dividen los valores por el separador ";"
int[] sensores = int(split(inStr,";"));

if (sensores.length>=10) {
sensorXmV = sensores[1];
sensorYmV = sensores[2];
sensorZmV = sensores[3];
sensorX = sensores[4];
sensorY = sensores[5];
sensorZ = sensores[6];
sensormg = sensores[7];
sensorPitch = sensores[8];
sensorRoll = sensores[9];
}
print(sensorXmV); print("; ");
print(sensorYmV); print("; ");
print(sensorZmV); print("; ");
print(sensorX); print("; ");
print(sensorY); print("; ");
print(sensorZ); print("; ");
print(sensormg); print("; ");
print(sensorPitch); print("; ");
println(sensorRoll);
}
}

void draw() {
arduinoPuertoDisponible();

translate(appCenterX, appCenterY);

float lastAng = radians(sensorRoll);
int[] angs = {180, 180};
for (int i=0; i<angs.length; i++) {
// Horizonte: Zona
if (i==0) {
// Tierra
fill(208, 119, 0);
} else {
// Cielo
fill(108, 156, 255);
}
arc(0, 0, diametro, diametro, lastAng, lastAng+radians(angs[i]));
// Horizonte: Lineas
if (i==0) {
// Tierra lineas
fill(198, 198, 198);
for (int j=0; j<3; j++) {
rotate(lastAng);
rect(-((j%2==0)?40:20), +15+(j*30), (j%2==0)?80:40, 2);
rotate(-lastAng);
}
} else {
// Cielo lineas
fill(198, 198, 198);
for (int j=0; j<3; j++) {
rotate(PI+lastAng);
rect(-((j%2==0)?40:20), -15-(j*30), (j%2==0)?80:40, 2);
rotate(PI-lastAng);
}
}
lastAng += radians(angs[i]);
}

// Parrilla
fill(210,10,10);
rect((diametro/4), -2, (diametro/4), 4);
rect(-(diametro/2), -2, (diametro/4), 4);
}



Videos
Como resultado de lo publicado en el presente artículo, un par de videos de su funcionamiento.







jueves, 7 de julio de 2011

Servidor de 6Tb y creciendo...

El servidor de datos local basado en FreeNAS, que ampliamente se ha hablado en este blog, dispone en estos momentos de una capacidad total de 6Tb para datos con capacidades de crecimiento dada su escalabilidad tanto en hardware como por el propio sistema operativo NAS.

La configuración actual acutual es:

Placa base (Gigabyte GA-P31-ES3G-E)
4 puertos SATA
2 puertos IDE con adaptador a 2xCF
8 puertos USB2

Controladoras PCI
1x Sil3114 de 4 puertos SATA
1x Sil3124 de 4 puertos SATA (Port Multiplier y NCQ)

Sistema operativo
1x CF de 64Mb para el S.O. FreeNAS
1x CF de 8 Gb para scripts y ficheros intermedios

Datos
1x HD de 2Tb MDT Green
1x HD de 1Tb Seagate Green
1x HD de 750Gb MDT Green
1x HD de 750Gb WesternDigital Green
3x HD de 500Gb Seagate

El servidor sigue siendo ampliable mediante:
1 puerto eSata (externo)
1 puerto SATA/ PortMultiplier (interno)

Destacar que el consumo actual se situa entorno a los 65W.