¿Quién no ha tenido alguna vez la necesidad de poder ver nuestra casa desde otro lugar (oficina, hotel, otra casa, etc), o poder hacer inspección de tuberías, respiraderos o sitios inaccesibles? La idea que planteo en este artículo es la de un coche teledirigido al que podemos manejar remotamente mediante wifi y que veamos por donde va.
Principalmente está compuesto de un coche RC, una placa arduino duemilanove (o UNO) con una motorshield y una raspbery pi modelo B con dos USB (un WIFI y una webcam) y el sistema operativo Raspbian.
El funcionamiento es sencillo: Por un lado con un programa en python que se ejecuta en la raspberry pi hacemos un servidor que reciba los comandos por tcp/ip, estos son enviados mediante el puerto serie de la raspberry pi al puerto serie del arduino, que será el encargado de manejar la motorshield para mover el conjunto. Por otro lado en la propia Raspberry Pi hacemos streaming de vídeo para que podamos ver a través de su webcam remotamente.
Todo está alimentado con una batería Lipo 7,4 V. (2S) de 1000 mAh. Por un lado está conectado directamente a arduino y por otro a un regulador UBEC para alimentar a la raspberry pi a través de los pines GPIO de 5v y GND.
La comunicación entre la rasbperry Pi y arduino se hace simplemente con un cable, ya que no es necesario ninguna protección al ser el pin TX de la raspberry pi el que se conecta al pin RX de arduino, y por tanto no hay riesgo con los voltajes diferentes (siempre y cuando no reprogrames arduino, si es así desconecta el cable de RX hasta haber terminado).
El programa de Arduino es bastante simple, sólo comprueba si existe un byte en el puerto serie y si lo hay lo interpreta para saber qué movimiento hay que hacer con los motores. Se parte del byte 0x30 (0 en ASCII) y se comprueban sus bits:
- Si el bit 0 está activado el coche va hacia delante.
- Si el bit 1 está activado el coche va hacia atrás.
- Si los bits anteriores están desactivados entonces el coche no se mueve.
- Si el bit 2 está activado gira las ruedas a la izquierda.
- Si el bit 3 está activado gira las ruedas a la derecha.
- Si los bits anteriores están desactivados entonces la dirección el coche permanece recta.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
int dirA = 12; // sentido int dirB = 13; // direccion int speedA = 10; // velocidad del motor int speedB = 11; // parada o encendido de la dirección int velocidad = 150; // PWM del motor de tracción // El robot va hacia atrás void atras() { digitalWrite (dirA, HIGH); } // El robot va hacia alante void alante() { digitalWrite (dirA, LOW); } // El robot tuerce a la izquierda void izquierda() { digitalWrite (dirB, LOW); } // El robot tuerce a la derecha void derecha() { digitalWrite (dirB, HIGH); } void setup() { // Configuración de las comunicaciones Serial.begin(115200); // Configuración de los pines de la motorshield pinMode (dirA, OUTPUT); pinMode (dirB, OUTPUT); pinMode (speedA, OUTPUT); pinMode (speedB, OUTPUT); digitalWrite(speedA, LOW); digitalWrite(speedB, LOW); } void loop() { while (Serial.available()) { int caracter = Serial.read(); caracter -= 0x30; if(caracter == 0) { digitalWrite(speedA, LOW); digitalWrite(speedB, LOW); } else { if(caracter & 1) { analogWrite(speedA, velocidad); alante(); } else if(caracter & 2) { analogWrite(speedA, velocidad); atras(); } else if((caracter & 3) == 0) { digitalWrite(speedA, LOW); } if(caracter & 4) { digitalWrite(speedB, HIGH); izquierda(); } else if(caracter & 8) { digitalWrite(speedB, HIGH); derecha(); } else if((caracter & 12) == 0) { digitalWrite(speedB, LOW); } } } } |
En el lado de la Raspberry Pi hay que instalar primero varias cosas:
1 2 |
sudo apt-get update sudo apt-get install gstreamer-tools gstreamer0.10-plugins-bad gstreamer0.10-plugins-good v4l-utils python-serial |
Editado 17/04/2014: Desactivar la consola serie que viene por defecto activada:
En el fichero /etc/inittab poner una almohadilla (#) al principio en la línea:
T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
En el fichero /boot/cmdline.txt sustituir la línea:
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
por
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
Finalmente hay que reiniciar la Raspberry Pi para que coja los cambios.
Después hay que dar permisos de lectura y escritura al puerto serie:
1 |
sudo chmod a+rw /dev/ttyAMA0 |
A continuación creamos un fichero llamado servidor.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import socket import serial ser=serial.Serial('/dev/ttyAMA0', 115200) host = '' port = 1976 backlog = 5 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((host,port)) s.listen(backlog) while 1: ser.write("0") print "se espera una conexion" client, address = s.accept() print "conectado " + address[0] while 1: data = client.recv(1) if data == '': break else: ser.write(data) |
Este programa python se encarga de crear un socket tcp/ip que escucha por el puerto 1976 , cuando alguien se conecta a este puerto entonces permanece a la espera de recibir los bytes que posteriormente enviará a Arduino a través del puerto serie.
Finalmente crearemos un fichero llamado webcam.sh con el siguiente contenido (cambiar 192.168.1.133 por la ip que tenga vuestra raspberry pi):
1 |
gst-launch -v v4l2src ! ffmpegcolorspace ! video/x-raw-yuv,width=320,height=240,framerate=\(fraction\)30/1 ! queue ! videorate ! video/x-raw-yuv,framerate=10/1 ! jpegenc ! multipartmux ! tcpserversink host=192.168.1.133 port=5000 sync=false |
Este programa shell arrancará un servidor de streaming con GStreamer. De momento GStreamer es el software que conozco que funciona más rápido enviando imágenes con un retardo de 2 segundos, ffmpeg tenía un retardo de hasta ¡¡¡ 10 segundos !!!. La idea la cogí de aquí.
Ahora que ya tenemos lo necesario en nuestra parte del coche, necesitamos configurar el router para redirigir los puertos 22, 1976 y 5000 hacía la ip que tenga la Raspberry Pi.
Desde el ordenador remoto necesitaremos tener el programa VLC para el recibir el streaming, un cliente de ssh (en windows el mejor es el putty) y Processing para enviar los comandos.
Los pasos para hacer que nuestro sistema de televigilancia son los siguientes:
Conectar mediante ssh a la raspberry pi y ejecutar el comando:
1 |
sh webcam.sh |
Abrir otra sesión de ssh y ejecutar el comando:
1 |
python servidor.py |
Arrancar VLC y abrir una ubicación de red poniendo la siguiente línea, con esto podremos ver lo que hay frente a la webcam (cambiar 192.168.1.133 por la ip que tenga vuestra raspberry pi):
1 |
tcp://192.168.1.133:5000 |
Arrancar el Processing con el siguiente programa y ejecutarlo (cambiar 192.168.1.133 por la ip que tenga vuestra raspberry pi):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
import processing.net.*; Client myClient; int valor; void setup() { size(200, 200); myClient = new Client(this, "192.168.1.133", 1976); valor = 0; } void draw() { } void keyReleased() { if (key == CODED) { if (keyCode == UP) { valor &= 0xFFFFFFFE; } else if (keyCode == DOWN) { valor &= 0xFFFFFFFD; } else if (keyCode == LEFT) { valor &= 0xFFFFFFFB; } else if (keyCode == RIGHT) { valor &= 0xFFFFFFF7; } myClient.write(valor + 0x30); } } void keyPressed() { if (key == CODED) { if (keyCode == UP) { valor |= 1; } else if (keyCode == DOWN) { valor |= 2; } else if (keyCode == LEFT) { valor |= 4; } else if (keyCode == RIGHT) { valor |= 8; } myClient.write(valor + 0x30); } } |
Si todo ha ido bien, ponemos la ventana de processing que nos ha abierto como activa cuando hemos lanzado el programa y pulsamos los botones de los cursores del teclado, momento en el cual veremos a través de la webcam (o en local si lo tenemos enfrente) cómo nos desplazamos.
Y finalmente un vídeo de cómo funciona: