La Ps3Eye es una gran camara para hacer tratamiento de imagenes, tiene una buena optica, una gran sensibilidad luminosa y mucha mas velocidad que una webcam normal, hasta 125 fps a 320x240 y 60 fps a 640x480.
En raspbian '2012-07-15-wheezy-raspbian' la cámara funciona directamente ya que el modulo esta incluido, el problema es que capturando con opencv desde python, por defecto la imagen esta a 320x240 y solo con opencv no es posible cambiar el framerate ni la mayoría de los parámetros aparte del tamaño de la imagen.
Al buscar información de cómo cambiar los parámetros de la cámara encuentras principalmente información sobre un modulo modificado que permite establecer el modo por defecto de la cámara, pero es necesario compilarlo para la RPi.
Mis conocimientos de linux son limitados, pero combinando las instrucciones de varios sitios ([1],[2]) conseguí hacerlo.. pero no es rápido, ni fácil, ... ni útil. Funciona, permite cambiar los parámetros de la cámara al cargar el modulo ... pero no en tiempo de ejecución.
Realmente todo ese lio no es necesario, el módulo que viene con raspbian permite configurar el framerate y los parametros comunes (brillo, ganacia, etc) al vuelo, sin recompilar nada ni complicarse especialmente la vida, incluso se puede hacer desde python sin abandonar opencv.
Tal vez porque no soy usuario habitual de linux no se me ocurrio antes, pero la forma es ir mas allá del modulo de la camara, directamente a v4l y desde allí modificar lo que queramos.
Instalando el paquete v4l-utils se instala una aplicacion en linea de comandos llamada v4l2-ctl que permite ver y modificar una gran cantidad de parámetros de los dispositivos v4l que haya disponibles, y podemos hacer esas mismas llamadas desde el interior de python para ajustar lo que queramos.
El estado actual del driver cargado lo podemos ver con
$ v4l2-ctl --all
y para ver las propiedades modificables
$ v4l2-ctl -l
para manejar estas propiedades desde python, por ejemplo el brillo ...
>>> import os
>>> os.system("v4l2-ctl --set-ctrl brightness=30")
y para ajustar el framerate ...
>>> os.system("v4l2-ctl --set-parm=60")
El cambio se aprecia después de varios frames, ~5, pero funciona estupendamente.
He podido hacer capturas a 320x240 ajustando a 30,40,50,60,75,100 y 125 fps sin problemas, pero a 640x480 solo funciona limpiamente con 15 y 30 fsp, en 40 fps la mitad de las veces da un timeout y con 50 y 60fps no se consigue ninguna imagen, solo timeouts.
Posiblemente 640x480@60fps es pedirle mucho a la RPi, que de todas formas no podría procesar nada a esa velocidad, pero con los otros modos va de sobra.
Por cierto, si se usa más de una cámara hay que especificar a quien se aplica el cambio
>>> os.system("v4l2-ctl -d0 --set-ctrl brightness=25") #para la camara 0
>>> os.system("v4l2-ctl -d1 --set-ctrl brightness=30") #para la camara 1
The Blinking Led
Pensamientos sobre robotica, electronica y cosas similares
Trasteando con la Raspberry Pi - I2C & SPI
Bueno, esta bastante claro que para ciertos usos de bajo nivel, la RPi no sirve, como leer señales analógicas o generar pulsos pwm.
Para este tipo de trabajos se necesitan dispositivos externos (o un microcontrolador) y una de las formas más comunes con las que estos dispositivos -y muchos sensores- se comunican es mediante comunicación serie sincrona -con la señal de reloj compartida- y los buses más habituales de este tipo son el SPI y el I2C que sí que están disponibles en la RPi.
-- Esto es a partir de la imagen 2012-07-15-wheezy-raspbian --
Para usar el I2C es necesario cargar dos módulos (ya vienen en raspbian, no hay que bajar nada)
$ sudo modprobe i2c-bcm2708
$ sudo modprobe i2c-dev
Para este tipo de trabajos se necesitan dispositivos externos (o un microcontrolador) y una de las formas más comunes con las que estos dispositivos -y muchos sensores- se comunican es mediante comunicación serie sincrona -con la señal de reloj compartida- y los buses más habituales de este tipo son el SPI y el I2C que sí que están disponibles en la RPi.
-- Esto es a partir de la imagen 2012-07-15-wheezy-raspbian --
Para usar el I2C es necesario cargar dos módulos (ya vienen en raspbian, no hay que bajar nada)
$ sudo modprobe i2c-bcm2708
$ sudo modprobe i2c-dev
Pero la primera vez hay que eliminar de la lista negra el primero, i2c-bcm2708, editando /etc/modprobe.d/raspi-blacklist.conf
Para cargar siempre el modulo al inicio, añadir 'i2c-dev' en /etc/modules
También es útil es útil instalar las i2ctools
$ sudo apt-get install i2c-tools
Para cargar siempre el modulo al inicio, añadir 'i2c-dev' en /etc/modules
También es útil es útil instalar las i2ctools
$ sudo apt-get install i2c-tools
Se pueden listar los dispositivos conectados al puerto i2C desde la linea de comandos mediante
$ sudo i2cdetect -y 0
$ sudo i2cdetect -y 0
Para usar el I2C con python hay que incluir el modulo python-smbus, disponible en el repositorio de raspbian, la documentacion aqui
La forma en la que lo he probado ha sido con el magnetometro hmc5883 conectado y alimentado directamente en el gpio con direccion 0x1e (se puede ver si esta disponible con el i2cdetect)
Es necesario ejecutar python como su o dar permisos con
$ sudo chmod a+rw /dev/i2c*
El código para hablar con el magnetometro es...
import smbus
i2c0=smbus.SMBus(0) #asocia el bus 0 de i2c - el del GPIO
------
Para usar el bus SPI es necasario actualizar el firmware de la rpi (el raspbian de base no monta los buses spi) con rpi-update -info aqui- y reiniciar.
-Se puede ver si los buses spi estan montados listando /dev/, tiene que aparecer los dispositivos spidev0.0 y spidev0.1
No lo he podido probar aun -no tengo nada con SPI por aquí ahora mismo-, pero según he podido leer basta con escribir y leer en /dev/spidev0.x para comunicarse.. parece que no es necesario nada especial....
La forma en la que lo he probado ha sido con el magnetometro hmc5883 conectado y alimentado directamente en el gpio con direccion 0x1e (se puede ver si esta disponible con el i2cdetect)
Es necesario ejecutar python como su o dar permisos con
$ sudo chmod a+rw /dev/i2c*
El código para hablar con el magnetometro es...
import smbus
i2c0=smbus.SMBus(0) #asocia el bus 0 de i2c - el del GPIO
i2c0.read_byte_data(0x1e,0x03) #lee el registro 0x03
i2c0.write_byte_data(0x1e,0x02,0) #escribe un 0 en 0x02------
Para usar el bus SPI es necasario actualizar el firmware de la rpi (el raspbian de base no monta los buses spi) con rpi-update -info aqui- y reiniciar.
-Se puede ver si los buses spi estan montados listando /dev/, tiene que aparecer los dispositivos spidev0.0 y spidev0.1
No lo he podido probar aun -no tengo nada con SPI por aquí ahora mismo-, pero según he podido leer basta con escribir y leer en /dev/spidev0.x para comunicarse.. parece que no es necesario nada especial....
Trasteando con la Raspberry Pi - GPIO
Si bien es cierto que la mayor parte del acceso a dispositivos tengo intención de hacerlo a través de un microcontrolador externo, no he podido resistir la tentación de usar los pines de uso general expuestos en la RPi ... este blog no se llama como se llama por nada, me gusta trastear :)
Creo que el uso de estos pines puede permitir a mucha gente, que no esta acostumbrada, a trastear a tan bajo nivel. Porque con un poco de electrónica simple -sin microcontroladores- se pueden manejar varios tipos de motores y otros actuadores que permitirían construir bastantes tipos de robots únicamente con la RPi como núcleo.
Bueno, siguiendo con el python, y gracias al módulo de GPIO que se puede encontrar aquí, manejar los pines de la RPi es, de nuevo, sumamente secillo
Creo que el uso de estos pines puede permitir a mucha gente, que no esta acostumbrada, a trastear a tan bajo nivel. Porque con un poco de electrónica simple -sin microcontroladores- se pueden manejar varios tipos de motores y otros actuadores que permitirían construir bastantes tipos de robots únicamente con la RPi como núcleo.
Bueno, siguiendo con el python, y gracias al módulo de GPIO que se puede encontrar aquí, manejar los pines de la RPi es, de nuevo, sumamente secillo
Una duda que tenía -de cara al uso de motores directamente desde la RPi- es la velocidad a la que se puede acceder a dicho puerto así que con un bucle sobre el código anterior he hecho la prueba, con un resultado de aprox 1700 accesos por segundo -tanto en lectura como en escritura- pero eso deja a la RPi con un uso de 98%.
La velocidad de acceso al puerto es algo justa, pero podría ser suficiente para generar pwm para un motor dc y bastante aceptable para motores paso a paso, eso sí, no quedará mucho tiempo para que el robot 'piense'. Posiblemente se pueda mejorar eso con un módulo para el kernel que use las interrupciones de bajo nivel ... pero de eso no tengo ni idea.
Trasteando con la Raspberry Pi - Video streaming básico
Al ir a montar un robot con cámaras, una de las cosas que quiero es ver las imágenes de la cámara del robot mientras estoy sentado en el salón con el portátil.
En las entradas anteriores se puede ver que ya esta resuelta la captura de imagen de las cámaras y el envió de datos por udp ... Tener envió de vídeo es cuestión de juntar esas dos piezas.
Una pequeña limitación del udp es que los paquetes son entidades individuales y el máximo tamaño de paquete son 64K. Así que cuando se quieren mandar más datos que eso, es necesario comprimir los datos o mandar varios paquetes y verificar -a mano- que todos llegan para juntaros de nuevo.
La imágenes de las cámaras son mayores de 64K, de modo que es necesario hacer algo con ellas para mandarlas.
La primera solución que he probado ha sido capturar imágenes a media resolución -320x240- y comprimirlas a jpg, de ese modo caben en un único paquete udp.
Aquí esta el cómo lo he hecho:
y en el otro lado...
Con este montaje alcanzo unas 6.5 imágenes por segundo enviadas por a RPi -y recibidas en el portátil- que no está mal y permite supervisar la actividad del robot, pero a coste de que la RPi esté al 98% de uso todo el rato.
Después de esta prueba, me quedó la duda de ver si merece la pena el comprimir la imagen frente al mandar varios paquetes de datos, porque la RPi tiene poco procesador y era posible que costase menos hacer varios envios que codificar la imagen.
La prueba no es más que una variación del código anterior añadiendo en el primer byte del mensaje el indice del envío y que se necesitan 4 envíos por cada imagen.
El resultado enviando la imagen a trozos es de 8.4 imágenes por segundo, casi 2 más que codificando la imagen, de modo que -al menos para esa resolución- merece más la pena enviar 4 veces más datos que codificarlos -y tiene la pequeña ventaja de que la imagen recibida no tiene las perdidas que se originan con la compresión-
Después de esta prueba, me quedó la duda de ver si merece la pena el comprimir la imagen frente al mandar varios paquetes de datos, porque la RPi tiene poco procesador y era posible que costase menos hacer varios envios que codificar la imagen.
La prueba no es más que una variación del código anterior añadiendo en el primer byte del mensaje el indice del envío y que se necesitan 4 envíos por cada imagen.
El resultado enviando la imagen a trozos es de 8.4 imágenes por segundo, casi 2 más que codificando la imagen, de modo que -al menos para esa resolución- merece más la pena enviar 4 veces más datos que codificarlos -y tiene la pequeña ventaja de que la imagen recibida no tiene las perdidas que se originan con la compresión-
Trasteando con la Raspberry Pi - Acceso remoto
Bien, tengo acceso a cámaras y al microcontrolador para la gestión de bajo nivel, con eso puedo hacer ya un robot autónomo.
Pero la experiencia me ha enseñado que para desarrollar un robot autónomo es necesario que sea accesible desde fuera para poder monitorizar los datos y , en general, supervisar al robot -no sea que le de por conquistar el mundo y empiece por mi casa-.
Como está dando buenos resultados sigo con python.
La mejor forma de supervisar un robot es que él te mande los datos a ti (y no al revés) y como con la RPi tengo wifi normal, mandaré los datos por red local.
Primera decisión ¿ TCP o UDP ?. TCP está bien cuando el cliente pide datos y el servidor se los sirve, en este caso quiero que el robot mande datos a mansalva para la supervisión, por tanto lo que quiero son datos más recientes.
La decisión es UDP, porque no necesito que haya un cliente, no tengo que pedir nada y los paquetes que me interesan son los recientes no los viejos, además, se consigue mejor ancho de banda al precio de que algún paquete se pierda -aunque siendo red local no creo que haya mucho problema con la perdida de paquetes-
De nuevo python me ha sorprendido gratamente, abrir un socket udp y ponerse a mandar datos es, de nuevo, sumamente sencillo como se puede ver en la página de la documentación.
y en el otro extremo de la linea..
Pero la experiencia me ha enseñado que para desarrollar un robot autónomo es necesario que sea accesible desde fuera para poder monitorizar los datos y , en general, supervisar al robot -no sea que le de por conquistar el mundo y empiece por mi casa-.
Como está dando buenos resultados sigo con python.
La mejor forma de supervisar un robot es que él te mande los datos a ti (y no al revés) y como con la RPi tengo wifi normal, mandaré los datos por red local.
Primera decisión ¿ TCP o UDP ?. TCP está bien cuando el cliente pide datos y el servidor se los sirve, en este caso quiero que el robot mande datos a mansalva para la supervisión, por tanto lo que quiero son datos más recientes.
La decisión es UDP, porque no necesito que haya un cliente, no tengo que pedir nada y los paquetes que me interesan son los recientes no los viejos, además, se consigue mejor ancho de banda al precio de que algún paquete se pierda -aunque siendo red local no creo que haya mucho problema con la perdida de paquetes-
De nuevo python me ha sorprendido gratamente, abrir un socket udp y ponerse a mandar datos es, de nuevo, sumamente sencillo como se puede ver en la página de la documentación.
y en el otro extremo de la linea..
Bueno, si se quieren mandar datos a mansalva hay que poner los envíos y las recepciones en un bucle, pero eso ya son detalles, al igual que si se abre otro puerto como cliente en la RPi y como servidor en el portátil se puede controlar el robot desde el portátil
Aquí he de añadir que para recibir los datos en el portail, y para mantener homogeneidad he instalado python en windows, lo que no ha supuesto ninguna dificultad. Es más, buscando el paquete de opencv para python en windows he podido comprobar que tb funciona -el usb aún no lo he probado-
Trasteando con la Raspberry Pi - Accediendo al USB
La RaspberryPi esta muy bien como sistema pequeño, versátil y con poco consumo, pero para maejar las señales de bajo nivel para motores, captura de datos analógicos, y alguna otra cosilla se necesita un microcontrolador -o algún otro dispositivo- externo.
Llevo años con la UsbLab, así que es natural que lo siguiente sea que la RPi hable con la UsbLab.
Al estar definida como dispositivo propio , para conectar el Pic a la RPi no ha hecho falta ningún módulo externo ni instalar nada.
Sigo probando con python, que parece bastante capaz y tiene un modulo para usb.
El modulo de usb -uno de ellos- se llama pyusb, el paquete que instala Debian es la versión 0.4, sin embargo en la página del proyecto esta la version 1.0, con notables diferencias y que siguiendo sus instrucciones esta tirado de montar.
Otro detalle, para poder tomar posesión del dispositivo usb es necesario tener los permisos correspondientes, eso se puede hace lanzando python como root o dándole al usuario normal los permisos tal como se detalla aqui
Superados estos pasos iniciales, ha resultado que hablar por usb es casi tan dificil como capturar imagenes.
He aquí el código necesario:
Y si puedo mandar un ping y recoger el pong, puedo mandar cualquier cosa, ya solo es cuestión de aprender algo mas de python.
Nota: el PIC18F4550 esta cargado con el stack de usb de microchip para dispositivo propio , no como dispositivo hid. La cadena de datos del ping es propia de mi código en la UsbLab, no un ping estándar del protocolo usb -si es que existe-
Trasteando con la Raspberry Pi - Captura de imágenes desde una cámara
De camino al robot, el segundo paso ha sido enganchar una webcam, ha resultado sorprendentemente fácil.
La ultima imagen del debian disponible en la página de la RPi incluye los drivers para usar Ps3Eye -que es una gran cámara para trastear en sistemas de visión- de modo que no ha hecho falta añadir nada.
Para la tesis he usado OpenCV y me apaño con él, es una forma fácil de capturar imágenes y procesarlas.
En general creo que un lenguaje de script es lo más comodo para trastear, pero meter matlab en la RPi me parece excesivo, por otro lado he tenido un mínimo contacto con python y me ha parecido bastante agradable.
La RPi esta orientada a la educación en incluye python, OpenCV tiene una api para python que se puede instalar sin complicación con el paquete python-opencv.
Después de la experiencia con la cámara, creo que python tiene las de ganar como entrono de trasteo en la RPi. He necesitado la apabullante cantidad de 4 lineas de código para capturar una imagen con la webcam y grabarla a disco:
Con ese código al capturar la cámara salen un par de mensajes de aviso, pero la captura sin problemas.
P.D.: En esta otra entrada hay información sobre como ajustar los parámetros de la cámara
Suscribirse a:
Entradas (Atom)