Coche RC Autonomo basado en Rasberry Pi y Arduino


Este proyecto desarrollado por   Zeng Wang  constituye  una versión reducida de un  sistema de auto-conducción ( eso si usando un coche de RC) con una Raspberry Pi 2 Model B – Placa base (ARM Quad-Core 900 MHz, 1 GB RAM, 4 x USB, HDMI, RJ-45)
, un Arduino ,y  un ordenador exterior con software de código abierto.

El sistema utiliza tanto una Raspberry Pi con la cámara incorporada  oficial  como un sensor ultrasónico como entradas, enviando la informacion a un ordenador  que recibe el video  y se encarga del procesamiento en tiempo real del video capturado por la raspberry Pi mediante  reconocimiento de objetos (señal de stop y semáforos) y medición de distancia  ordenando las señales correspondientes  de la dirección mediante una placa Arduino para el control del coche de RC.

Dos programas ejecutándose en la Raspberry Pi permiten gestionar la información que recibe y la transmiten junto al vídeo de baja resolución (320 x 240) que capta la cámara a otro ordenador vía WiFi .A su vez el mando que controla el coche teledirigido efectivamente sigue dirigiéndolo, pero lo hace a través de las órdenes que le pasa ese ordenador vía USB mediante una placa Arduino.

El código fuente esta disponible en git hub 

El resultado, como puede verse en el vídeo, es espectacular .

 

 

 

El objetivo del autor Wang en efecto era modificar un coche RC  para que fuese capaz de  manejar tres tareas: auto-conducción en la pista, la señal de detención  ,la detección semáforo, y evitar colisiones frontales.

El sistema construido  consta de tres subsistemas:

  • Unidad de entrada (cámara, el sensor de ultrasonidos)
  • Unidad de procesamiento (ordenador) y
  • Unidad de control del coche RC.

Unidad de entrada

Una placa  Raspberry Pi 2 Model B – Placa base (ARM Quad-Core 900 MHz, 1 GB RAM, 4 x USB, HDMI, RJ-45) al que se conecta un módulo de cámara pi y un sensor ultrasónico-HC SR04 se utiliza para recoger datos de entrada. Dos programas de cliente se ejecutan en Raspberry Pi para el streaming de vídeo en color y datos de los sensores ultrasónicos a la computadora a través de conexión Wi-Fi local.Con el fin de conseguir una baja latencia de streaming de vídeo, el vídeo se escala hasta QVGA (320 × 240) resolución.

Unidad de procesamiento

La unidad de procesamiento (ordenador) se encarga de múltiples tareas: recibir datos de Raspberry Pi, el entrenamiento de la red neural red y la predicción (dirección), la detección de objetos (señal de stop y semáforos), medición de distancia (visión monocular), y el envío de instrucciones via Arduino a través de conexión USB .

Servidor TCP
Un programa de servidor TCP multihilo se ejecuta en el ordenador para recibir cuadros de imagen por streaming y datos ultrasónicos de la Raspberry Pi. Los cuadros de imagen se convierten a escala de grises y se descodifican en matrices numpy.

Red Neuronal
Una ventaja de usar la red neuronal es que una vez que la red ha aprendido, sólo necesita cargar parámetros formados después, y por tanto, la predicción puede ser muy rápida. Sólo la mitad inferior de la imagen de entrada se utiliza para fines de aprendizaje y de predicción. Hay 38.400 (320 × 120) nodos en la capa de entrada y 32 nodos en la capa oculta. El número de nodos en la capa oculta se elige bastante arbitrario.

Hay cuatro nodos en la capa de salida, donde cada nodo corresponde a las instrucciones de control de dirección: izquierda, derecha, adelante y atrás respectivamente (aunque inversa no se utiliza  Wang la ha incluido en la capa de salida).

mlp_half_32-2

A continuación se muestra el proceso de recolección de datos de entrenamiento.Primero cada cuadro se recorta y se convierte en una matriz numpy. Entonces la imagen de tren está emparejado con la etiqueta de tren (entrada humana). Por último, todos los datos y etiquetas de imagen emparejados se guardan en un archivo NPZ. La red neuronal está entrenada en el uso de OpenCV  usando el  método de propagación de vuelta. Una vez que la formación se lleva a cabo, los pesos se guardan en un archivo xml. Para generar predicciones, la misma red neuronal se construye y se carga con el archivo xml capacitado.

collect_train_data

Detección de Objetos
Para este proyecto Wang adaptó el enfoque basado en la forma utilizando clasificadores en cascada Haar basados ​​en características para la detección de objetos. Desde cada objeto se requiere su propio clasificador y sigue el mismo proceso de formación y detección, asi que en este proyecto sólo se centra en señal de stop y la detección del semáforo.

OpenCV proporciona un entrenador, así como detector. Las muestras positivas (contener objeto de destino) fueron adquiridos utilizando un smartphone , y se recortan para que solo el objeto deseado sea visible. Las muestras negativas (sin objeto de destino), por otro lado, se recogieron al azar. En particular, para el semáforo  muestras positivas contienen igual número de semáforos en rojo que semáforos en verde. El mismo conjunto de datos de muestras negativas se utilizan tanto para la señal de stop como para  la formación del semáforo. A continuación se muestran algunas muestras positivas y negativas utilizadas en este proyecto.

pos_neg_samples

sample_tabel

Para reconocer diferentes estados del semáforo (rojo, verde), se necesita un poco de procesamiento de imágenes más allá de la detección.

El diagrama de flujo a continuación resume el proceso de reconocimiento semáforo.

brightest_spot

En primer lugar, entrenado el clasificador en  cascada se utiliza éste para detectar un semáforo. El cuadro delimitador es considerada como una región de interés (ROI). En segundo lugar, la falta de definición de Gauss se aplica dentro de la ROI para reducir ruidos. En tercer lugar, encontrar el punto más brillante en el retorno de la inversión. Por último, los estados rojos o verdes se determinan basándose simplemente en la posición del punto más brillante en el retorno de la inversión.

Distancia de medida
Raspberry Pi sólo puede soportar un módulo de cámara de un pi. El uso de dos cámaras web USB traerá peso extra para el coche RC y también parece poco práctico.Por lo tanto, se elige el método visión monocular.

Este proyecto adaptó un modelo de geometría de detectar la distancia a un objeto utilizando el método de la visión monocular propuesto por Chu, Ji, Guo, Li y Wang (2004).

distante

P es un punto en el objeto de destino; d es la distancia desde el centro óptico hasta el punto P. En base a la geometría de la relación anterior, la fórmula (1) muestra cómo calcular la distancia d. En la fórmula (1), f es la longitud focal de la cámara; ∂ es el ángulo de inclinación de la cámara; h es la altura del centro óptico; (x0, y0) se refiere al punto de plano de la imagen y el eje óptico intersección; (x, y) se refiere a la proyección del punto P en el plano de la imagen. Supongamos O1 (u0, v0) es la cámara de coordenadas del punto de eje óptico y el plano de la imagen de intersección, también supongamos que la dimensión física de un píxel correspondiente al eje X y el eje Y en el plano de la imagen son dx y dy. Entonces:
distance_measure
v es la cámara de coordenadas en el eje y, y se puede devolver en el proceso de detección de objetos. Todos los demás parámetros son parámetros intrínsecos de la cámara que se pueden recuperar de la matriz de la cámara.

OpenCV proporciona funciones para calibración de la cámara. Matriz de la cámara para la cámara de 5MP pi se devuelve después de la calibración. Idealmente, a_x y a_y tienen el mismo valor. Varianza de estos dos valores se traducirá en píxeles no cuadrados en la imagen. La siguiente matriz indica que la lente de longitud focal fija en cámara pi ofrece un razonablemente buen resultado en el manejo de la distorsión de aspecto. Aquí es un artículo interesante discutir la distancia focal de la cámara pi con la acción de la lente y su equivalente de cámara de 35mm.
pi_cam_matrixLa matriz devuelve valores en píxeles y h se mide en centímetros. Mediante la aplicación de la fórmula (3), la distancia física d se calcula en centímetros.

Unidad RC Car Control

El coche RC utilizada en este proyecto tiene un controlador tipo de interruptor de encendido / apagado. Cuando se pulsa un botón, la resistencia entre el contacto pasador y el suelo chip de referencia es cero. Por lo tanto, una placa Arduino se utiliza para simular las acciones sobre el pulsador. Cuatro pines de Arduino se eligen para conectar cuatro clavijas de chips en el controlador, que corresponde a adelante, atrás, izquierda y derecha, respectivamente, acciones. Pines de Arduino envian señales baja indicando a tierra los pines de chips del controlador; Por otro lado el envío de señal de alta indica la resistencia entre los pines de chips y suelo permanecen sin cambios. El Arduino se conecta al ordenador a través de USB. Las salidas de computadora comandos para Arduino utilizando interfaz serie, y luego el Arduino lee los comandos y escribe señales de bajo o alto, simulando acciones de botón de prensa para conducir el coche RC.

Resultados

La predicción de las muestras de prueba devuelve una precisión de 85% en comparación con la precisión de 96% de que las muestras de entrenamiento devoluciones. En la situación de conducción real, predicciones se generan cerca de 10 veces por segundo (velocidad de transmisión de aproximadamente 10 fotogramas / s).

Características Haar por naturaleza son la rotación sensible. En este proyecto, sin embargo, la rotación no es una preocupación ya que tanto la señal de stop y el semáforo son objetos fijos, que es también un caso general en el entorno del mundo real.

drive_test01drive_test02

Para distancia aspecto medición, el sensor de ultrasonidos sólo se utiliza para determinar la distancia a un obstáculo delante del coche RC y proporciona resultados exactos cuando se toma la detección de ángulo adecuado y estado de la superficie en las consideraciones. Por otro lado, la cámara Pi ofrece «suficientemente buenos» resultados de la medición. De hecho, siempre y cuando sabemos que el número correspondiente a la distancia real, sabemos cuándo dejar el coche RC. Los resultados experimentales de detección a distancia usando la cámara pi se muestran a continuación:
camera_measure2

En este proyecto, la exactitud de la medición de la distancia utilizando el enfoque de visión monocular podría estar influenciada por los siguientes factores: (1) errores en la medición de los valores reales, (2) objeto de delimitación variaciones de caja en el proceso de detección, (3) errores en el proceso de calibración de la cámara, (4) relación no lineal entre la distancia y la cámara de coordenadas: la mayor distancia del, más rápido cambio de la cámara de coordenadas, por lo tanto cuanto mayor sea el error.

En general, el coche de RC podría navegar con éxito en la pista con la capacidad de evitar la colisión frontal, y responder a la señal de stop y semáforos en consecuencia.

Segun Wang ,el proceso de entrenamiento fue relativamente sencillo debido a la sencillez del modelo de carretera utilizada, y a eso se le sumaba la detección de objetos gestionada por la plataforma OpenCV (Open Source Computer Vision) que entre otras cosas también permitía calibrar la cámara.

 

Es sin duda  un proyecto apasionante y sorprendente el que ha realizado  Zheng Wang  demostrando  una vez hasta donde  puede llegar  la capacidad de procesamiento  actual  presentes en dispositivos tan reducidos y modestos como lo constituye la gama  de la Raspberry Pi 2 Model B – Placa base (ARM Quad-Core 900 MHz, 1 GB RAM, 4 x USB, HDMI, RJ-45)

Mas  información en la web del autor

Raspberry Pi Cero conectada a pantalla de información


Raspberry Pi Zero es un nuevo dispositivo que entra fuerte en el terreno de los dispositivos conectados y a la Internet de las Cosas. Gracias a su reducido tamaño y consumo podemos darle un montón de posibilidades en casa, como por ejemplo la automatización de determinadas tareas o simplemente mostrar información en un espacio ínfimo. Especialmente si la complementamos con unpequeño display OLED como éste, comercializado por Adafruit.

Frederic Vandenbosch ha creado un interesante proyecto con la Raspberry Pi Zero, usando un  display de Adafruit anteriormente mencionado y un par de pulsadores soldados en los terminales GPIO de la Raspberry Pi.

Para conectar la Zero a internet, Frederic ha utilizado  una extensión wifi de la que ya hablamos en este blog   y que permite agregar conectividad Wi-Fi a la menor de las Raspberry Pi soldándolo directamente a la placa. Como podéis ver en el vídeo que acompaña la imagen, los dos pulsadores son programables y en los ejemplos propuestos permiten navegar por una serie de opciones, como por ejemplo mostrar la dirección IP o forzar la reconexión de la Raspberry Pi Zero a internet.

 

 

Hardware

El hardware consiste en los siguientes componentes:

  • Raspberry Pi Cero
  • Adafruit 128 × 64 SSD1306 OLED
  • Edimax wifi dongle
  • 2 grandes pulsadores

El cableado del OLED y botones es bastante sencillo, como se ilustra a continuación:

adafrut

El dongle wifi se conecta de la misma manera como lo hice anteriormente con el hub USB:

  • Pi Zero PP1 a dongle 5V
  • Pi Cero PP6 al dongle GND
  • Pi Cero PP22 para dongle D +
  • Pi Cero PP23 para dongle D-

Por último, para mantener todo en su lugar, he diseñado una caja sencilla lo suficientemente grande para encajar todo. Un panel trasero se atornilla en el lugar para mantener todos los componentes en el interior, dejando al descubierto el puerto microUSB entrada de energía en el lado.

 

Un poco de cinta kapton evita contactos expuesto a tocarse y mantiene el cableado en su lugar. El dongle wifi se coloca en la parte superior para una mejor conectividad. Su LED azul brillante brilla a través del recinto, dando una clara indicación de su estado y la actividad.

Los archivos para el recinto de impresión 3D se pueden encontrar en Thingiverse: http://www.thingiverse.com/thing:1193350

Software

Por el lado del software del proyecto, se necesita la creación de la tarjeta microSD con la última imagen Raspbian Jessie disponible.Arrancando  desde el combo hub Pi Cero / USB con el teclado y wifi dongle conectado  porque el puerto USB de este proyecto Pi Cero se ha cableado a un dongle wifi y un teclado ya no se puede conectar .

Se configura el wifi añadiendo el SSID y la frase de contraseña correcta en el archivo / etc / network / interfaces. Después de probar la conectividad wifi, poner la tarjeta microSD de vuelta en el Pi correcta. Con la conectividad de red, es posible iniciar sesión con SSH y trabajar en el guión para mostrar la información deseada.

Uso de la Biblioteca de Python Adafruit OLED SSD1306 y algo de código Python personalizada, en el ejemplo se programan  tres pantallas diferentes:

  • Hora y fecha
  • Configuración de la red
  • Los medios sociales suscriptores / seguidores

El botón izquierdo a través de las diferentes pantallas, mientras que el botón derecho desencadena una acción personalizada por pantalla.

En el caso de la visualización de la hora y la fecha, el botón, cambia entre las 12h y las 24h representación. Para la configuración de red, obliga al wifi de volver a conectar mediante un descenso de la interfaz y obligando de nuevo. Por último, para evitar el tráfico excesivo, información de medios sociales solamente se recupera cada cinco minutos, al pulsar el botón de fuerza a la recuperación de la información.

Por supuesto, esto es sólo un subconjunto de lo que podría ser mostrado. Podría recuperar la información del tiempo, el correo electrónico, últimos tweets, etc … Usted también podría tener que desplazarse por pantallas diferentes sin necesidad de pulsar un botón. Todo es posible.

El código actual se puede encontrar a continuación. Está lejos de ser perfecto, pero hace el trabajo.

Por último, para conseguir que se inicie automáticamente al arrancar el sistema, cree un script lanzador (por ejemplo, «launcher.sh«) que contiene la ruta de acceso a la secuencia de comandos, así:

  #! / bin / sh
 cd / home / pi
 sudo python info_display.py y

Y por último, añadir el cronjob con el comando «sudo crontab -e«:

  reboot sh /home/pi/launcher.sh

Cada vez que el Pi se inicia, se pondrá en marcha el script.

#!/usr/bin/env python
import time
import Adafruit_SSD1306
import RPi.GPIO as GPIO
import Image
import ImageFont
import ImageDraw
import os
def display_time():
# Collect current time and date
if(time_format):
current_time = time.strftime(«%I:%M«)
else:
current_time = time.strftime(«%H:%M«)
current_date = time.strftime(«%d/%m/%Y«)
# Clear image buffer by drawing a black filled box
draw.rectangle((0,0,width,height), outline=0, fill=0)
# Set font type and size
font = ImageFont.truetype(Minecraftia.ttf, 35)
# Position time
x_pos = (disp.width/2)/2)
y_pos = 2 + (disp.height48)/2 (35/2)
# Draw time
draw.text((x_pos, y_pos), current_time, font=font, fill=255)
# Set font type and size
font = ImageFont.truetype(Minecraftia.ttf, 8)
# Position date
x_pos = (disp.width/2)(string_width(font,current_date)/2)
y_pos = disp.height10
# Draw date
draw.text((x_pos, y_pos), current_date, font=font, fill=255)
# Draw the image buffer
disp.image(image)
disp.display()
def display_social():
# Collect social media subscribers/followers/… by parsing webpages
twitter = os.popen(«curl https://twitter.com/f_vdbosch?lang=en | grep ‘data-nav=followers‘ | grep -o ‘[0-9]\+’«).read()
youtube = os.popen(«curl https://www.youtube.com/c/FrederickVandenbosch | grep -o ‘[0-9]\+ subscribers’ | grep -o ‘[0-9]\+’«).read()
facebook = «0«
instagram = os.popen(«curl https://www.instagram.com/f_vdbosch/ | grep -o ‘followed_by:{count:[0-9]\+}’ | grep -o ‘[0-9]\+’«).read()
googleplus = «0«
# Put data in lists that can be iterated over
channels = [«YouTube«, «Twitter«, «Facebook«, «Instagram«, «Google+«]
subscribers = [youtube, twitter, facebook, instagram, googleplus]
# Clear image buffer by drawing a black filled box
draw.rectangle((0,0,width,height), outline=0, fill=0)
# Set font type and size
font = ImageFont.truetype(Minecraftia.ttf, 8)
# Iterate over lists
for i in range(0, 5):
# Position channel name
x_pos = 2
y_pos = 2 + (((disp.height4)/5)*i)
# Draw channel name
draw.text((x_pos, y_pos), channels[i], font=font, fill=255)
# Position subcribers/followers/…
x_pos = disp.width 2 string_width(font, subscribers[i])
y_pos = 2 + (((disp.height4)/5)*i)
# Draw subcribers/followers/…
draw.text((x_pos, y_pos), subscribers[i], font=font, fill=255)
# Draw the image buffer
disp.image(image)
disp.display()
def display_network():
# Collect network information by parsing command line outputs
ipaddress = os.popen(«ifconfig wlan0 | grep ‘inet addr’ | awk -F: ‘{print $2}’ | awk ‘{print $1}’«).read()
netmask = os.popen(«ifconfig wlan0 | grep ‘Mask’ | awk -F: ‘{print $4}’«).read()
gateway = os.popen(«route -n | grep ‘^0.0.0.0’ | awk ‘{print $2}’«).read()
ssid = os.popen(«iwconfig wlan0 | grep ‘ESSID’ | awk ‘{print $4}’ | awk -F\\ ‘{print $2}’«).read()
# Clear image buffer by drawing a black filled box
draw.rectangle((0,0,width,height), outline=0, fill=0)
# Set font type and size
font = ImageFont.truetype(Minecraftia.ttf, 12)
# Position SSID
x_pos = 2
y_pos = 2
# Draw SSID
draw.text((x_pos, y_pos), ssid, font=font, fill=255)
# Set font type and size
font = ImageFont.truetype(Minecraftia.ttf, 8)
# Position IP
y_pos += 12 + 10
# Draw IP
draw.text((x_pos, y_pos), «IP: «+ipaddress, font=font, fill=255)
# Position NM
y_pos += 10
# Draw NM
draw.text((x_pos, y_pos), «NM: «+netmask, font=font, fill=255)
# Position GW
y_pos += 10
# Draw GW
draw.text((x_pos, y_pos), «GW: «+gateway, font=font, fill=255)
# Draw the image buffer
disp.image(image)
disp.display()
def display_custom(text):
# Clear image buffer by drawing a black filled box
draw.rectangle((0,0,width,height), outline=0, fill=0)
# Set font type and size
font = ImageFont.truetype(Minecraftia.ttf, 8)
# Position SSID
x_pos = (width/2) (string_width(font,text)/2)
y_pos = (height/2) (8/2)
# Draw SSID
draw.text((x_pos, y_pos), text, font=font, fill=255)
# Draw the image buffer
disp.image(image)
disp.display()
def string_width(fontType,string):
string_width = 0
for i, c in enumerate(string):
char_width, char_height = draw.textsize(c, font=fontType)
string_width += char_width
return string_width
# Set up GPIO with internal pull-up
GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# 128×64 display with hardware I2C
disp = Adafruit_SSD1306.SSD1306_128_64(rst=24)
# Initialize library
disp.begin()
# Get display width and height
width = disp.width
height = disp.height
# Clear display
disp.clear()
disp.display()
# Create image buffer with mode ‘1’ for 1-bit color
image = Image.new(1, (width, height))
# Load default font
font = ImageFont.load_default()
# Create drawing object
draw = ImageDraw.Draw(image)
prev_millis = 0
prev_social = 0
display = 0
time_format = True
while True:
millis = int(round(time.time() * 1000))
# Software debouncing
if((millis prev_millis) > 250):
# Cycle through different displays
if(not GPIO.input(12)):
display += 1
if(display > 2):
display = 0
prev_millis = int(round(time.time() * 1000))
# Trigger action based on current display
elif(not GPIO.input(16)):
if(display == 0):
# Toggle between 12/24h format
time_format = not time_format
time.sleep(0.01)
elif(display == 1):
# Reconnect to network
display_custom(«reconnecting wifi …«)
os.popen(«sudo ifdown wlan0; sleep 5; sudo ifup –force wlan0«)
time.sleep(0.01)
elif(display == 2):
# Refresh social media now
display_custom(«fetching data …«)
display_social()
time.sleep(0.01)
prev_millis = int(round(time.time() * 1000))
if(display == 0):
display_time()
prev_social = 0
elif(display == 1):
display_network()
prev_social = 0
elif(display == 2):
# Only fetch social media data every 5 minutes when active
if((millis prev_social) > 300000):
display_custom(«fetching data …«)
display_social()
prev_social = millis
time.sleep(0.1)

 

El uso de una pantalla OLED Adafruit, dos pulsadores, un dongle wifi y un pi Cero, permite como vemos  una pantalla de información conectada a internet. La información podría ser cualquier cosa: fecha y hora, el clima, el estado de las redes sociales, etc … Los dos botones se utilizan para desplazarse por los datos y desencadenar ciertas acciones.
Sin duda el abanico de posibilidades es inmenso..