beandeau
Robot caméra avec un Raspberry Pi

Le projet

Avec un Raspberry pi on peut facilement réaliser un serveur WEB. C'est une carte idéale pour réaliser un robot autonome commandé à distance par une interface graphique. De plus j'ai ajouté la caméra on board. Un logiciel comme VLC permet de récupérer les images sur n'importe quel ordinateur.

Pour alimenter le Raspberry j'ai utilisé des accus 1,2 V LR06 et un convertisseur 12V/5V. Le Raspberry ne pouvant pas commander directement des motoréducteurs j'ai utilisé une carte d'interface de commande de moteurs de fabrication personnelle. Toute autre carte de commande de moteurs peut convenir. Une carte de détection d'obstacle a aussi été ajoutée, elle peut être remplacée par un simple interrupteur à lame souple.

Quelques caractéristiques :

La caméra peut être orientée sur 2 axes à l'aide de 2 servomoteurs

Le robot avance, recule et tourne à droite ou à gauche

La vitesse de propulsion est réglable

La détection d'un obstacle arrête le déplacement du robot

L'électronique contrôle la tension des accumulateurs et avertit en cas de tension faible.

Le robot embarque un serveur WEB et communique en WIFI avec un autre ordinateur ou smartphone

Le schéma

 

Le convertisseur 12V / 5 V est un convertisseur DC/DC à faibles pertes pour automobile, pouvant délivrer 3 A en sortie. La tension d'entrée est contrôlée par le CAN MCP3008 relié au Raspberry par le port SPI. Le pont diviseur constitué d'une résistance de 10 Kohms et de 3,3 Kohms permet d'abaisser la tension en dessous du seuil des 3,3 V de l'alimentation du MCP3008.
Pour l'interfacage du CAN MCP3008 et du raspberry vous pouvez consulter la page sur ce site : Convertisseur Analogique Numérique pour Raspberry Pi.
Ce circuit est optionnel car il se contente d'indiquer à l'utilisateur le niveau des batteries.

La carte de détection d'obstacle facultative est équipée d'un capteur infrarouge. Elle met la sortie S au niveau +5V lorqu'un obstacle est détecté à une certaine distance réglable. La résistance d'1 Kohms et la diode zener protègent l'entrée GPIO 15 du raspberry d'une tension supérieure à 3,3 V.

Les 2 servomoteurs sont alimentés en 5 V et directement contrôlées par les sorties GPIO 17 et GPIO 18 du raspberry. le principe de fonctionnement logiciel est décrit sur la page : Commander un servomoteur avec le Raspberry Pi.

Les composants électroniques additionnels viennent se placer sur une carte de prototypage qui s'enfiche sur le connecteur 2 X 13 broches du Raspberry Pi. Cette carte est disponible dans E-Choppe d'Elektor avec la référence 120483. Une plaquette pastillée peut bien entendu remplacer cette carte de prototypage.

Le programme

Avant d'écrire un programme sous python, il faut :

- Charger la bibliothèque Rpi.GPIO.
Il faut d'abord charger l'archive et l'extraire.

$ sudo apt-get install python-dev
$ sudo wget http://pypi.python.org/packages/source/R/RPi.GPIO/RPi.GPIO-0.4.1a.tar.gz
$ tar -zxf RPi.GPIO-0.4.1a.tar.gz

Se placer dans le répertoire créé puis lancer l'installation avec python.

$ cd RPi.GPIO-0.4.1a
$ sudo python setup.py install

- Installer la bibliothèque Spidev, voir la page Convertisseur Analogique pour Raspberry PI

- Mettre le Raspberry en WIFI en plaçant un dongle sur le Port USB. J'utilise le dongle wifi PiHut, simple à mettre en oeuvre et peu coûteux. L'installation et l'utilisation sont décrites sur le site de PiHut.

- Se connecter à distance au Raspberry Pi (voir sur ces pages) , avec un PC. Utile pour lancer et modifier les programmes car il n'est pas simple d'avoir en permanence un écran et un clavier connectés au robot !

- Implémenter un serveur WEB. Après quelques essais avec plusieurs serveurs, j'ai choisi Webpyserver. Pour l'installation se reporter au site officiel Webpyserver.

Le serveur Web

Le programme qui tourne sous python à besoin d'une page HTML d'index, des dessins et une feuille de style CSS. La hiérarchie suivante doit être respectée :
Le contenu du dossier webpyserver logiquement placé dans /home/pi/web.py-0.37/webpyserver, contient le programme python robot2.py et 2 sous dossiers.
static -> pour y placer la page index.html
templates -> pour y placer les images et la feuille de style robot2.css

L'arborescence peut être créée avec les commandes

$ cd /
$mkdir webpyserver
$cd /webpyserver
$mkdir templates
$mkdir static
Les fichiers à charger
Dossier webpyserver webpy.zip
Le Programme

Il fonctionne sous python 2.7 et utilise un script Ajax pour renvoyer les données.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
import web
import spidev
import RPi.GPIO as GPIO
from RPIO import PWM
from web import form
import threading

# definit GPIO en sortie
GPIO.setmode(GPIO.BCM)
PWM.setup()
PWM.init_channel(0)

pins = [17,18,22,24,25,27,4]

for pin in pins:

GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, GPIO.LOW)


vitmotd = GPIO.PWM(4,500)
vitmotg = GPIO.PWM(27,500)
vset = 30

horizontal=150
vertical=150

GPIO.setup(15, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

def StopEvent(pin):

print("Detection obstacle")
vitmotd.stop()
vitmotg.stop()
GPIO.output(25,0)
GPIO.output(24,0)
Volt()

def Volt():

spiDevice = spidev.SpiDev()
channel = 0
spiDevice.open(0,0)
values = spiDevice.xfer2([1,(8+channel)<<4,0])
index = ((values[1]&3) << 8) + values[2]
print "Tension =", round(index*(13.2/1023),2),"V"
if index < 700 :

print "Batterie faible"
for x in range(4):

GPIO.output(22,1)
time.sleep(0.1)
GPIO.output(22,0)
time.sleep(0.1)


# definit la page de nom index pour le site web
urls = ('/','index')
dossier_web = web.template.render('templates/')

app = web.application(urls, globals())

# definit l' action à effectuer quand la page index est appelée
class index:

# utilise quand la page est demande
def GET(self):

return dossier_web.index()

# traite une requete ajax
def POST(self):

global vset
global vertical
global horizontal
userdata = web.input()
if hasattr(userdata,'vitesse'):

if userdata.vitesse == '0':

vset=30

elif userdata.vitesse == '1':

vset=45

elif userdata.vitesse == '2':

vset=55

elif userdata.vitesse == '3':

vset=70

elif userdata.vitesse == '4':

vset=85

elif userdata.vitesse == '5':

vset=100

return(userdata.vitesse)

elif hasattr(userdata,'direction'):

if userdata.direction == 'avant':

vitmotd.start(vset)
vitmotg.start(vset)
GPIO.output(25,1)
GPIO.output(24,1)

elif userdata.direction == 'gauche':

vitmotd.start(vset)
vitmotg.stop()
GPIO.output(25,1)
GPIO.output(24,0)

elif userdata.direction == 'droite':

vitmotd.stop()
vitmotg.start(vset)
GPIO.output(25,0)
GPIO.output(24,1)

elif userdata.direction == 'arriere':

vitmotd.start(vset)
vitmotg.start(vset)
GPIO.output(25,0)
GPIO.output(24,0)

elif userdata.direction == 'stop':

vitmotd.stop()
vitmotg.stop()
GPIO.output(25,0)
GPIO.output(24,0)
Volt()

elif hasattr(userdata,'orientation'):

if userdata.orientation=='btb':

vertical=vertical +2
if vertical > 180:

vertical = 180

elif userdata.orientation=='bth':

vertical=vertical - 2
if vertical < 120:

vertical = 120

elif userdata.orientation=='btd':

horizontal=horizontal - 2
if horizontal < 120:

horizontal = 120

elif userdata.orientation=='btg':

horizontal=horizontal + 2
if horizontal >180:

horizontal = 180

class ServoThread(threading.Thread):

def run(self) :

global vertical
global horizontal
while True :

PWM.add_channel_pulse(0, 17, 0, horizontal)
PWM.add_channel_pulse(0, 18 ,0, vertical)
time.sleep(0.05)

# programme

if __name__ == '__main__':
myServo= ServoThread()
myServo.start()
GPIO.add_event_detect(15, GPIO.RISING)
GPIO.add_event_callback(15,StopEvent,50)
app.run()

Commentaires :

Importation des bibliothèques et modules

 

 

 

 

Définition des broches et initialisation de la PWM

 

GPIO en sortie et au niveau bas

 

Attribution des vitesses moteur droit et gauche

 

Broche 15, niveau bas avec résistance de rappel

 

 

Fonction arrêt si obstacle

Fonction mesure de la tension de batterie

Port SPI pour le MCP3008

 

 

 

 

Définition des chemins pour index et templates

 

 

 

 

 

 

 

Traitement de la requête Ajax

 

 

Pour les 5 positions du curseur, les 5 PWM en %

 

 

 

 

 

 

 

 

 

 

 

Sens de rotation des moteurs et signaux PWM

 

 

 

 

 

 

 

 

 

 

 

 

 

Déplacement de la caméra avec les valeurs de 2 servomoteurs

 

 

 

 

 

 

 

 

 

 

 

 

 

Thread définissant les impulsions pour les servomoteurs

 

 

 

Branchement au programme d'interruption si un front montant est détecté sur la broche 15

La page d'index

Pour charger la page dans un navigateur, il faut connaître l'adresse IP du Raspberry Pi avec la commande ifconfig.

$ ifconfig
Dans la barre d'adresse du navigateur, on tape l'adresse IP suivi du port 8080 : par exemple 192.168.1.25:8080

 

La caméra

On trouvera tout sur l'installation de la camera sur le site de PiHut.

On utilise la commande raspivid pour une capture video en streaming et VLC pour la lecture. VLC va lire un flux Rtsp ( protocole de streaming en temps réel).
Le plus simple est de créer un fichier bash, donc exécutable par le Raspberry Pi. On lui donne le nom de camera.sh. On copie dans le fichier le texte suivant:

#!/bin/bash
echo "mise en route de la camera"
echo "VLC rtps://192.168.1.25:8554/"
raspivid -o - -t 0 -n -w 600 -h 400 -fps 12 | cvlc -vvv stream:///dev/stdin --sout '#rtp{sdp=rtsp://:8554/}' :demux=h264

Pour exécuter le fichier avec la console, on tape:

$./camera.sh

Les réglages peuvent être modifiés mais une image trop grande ou trop d'images par seconde risque de ralentir le fonctionnement. Le streaming fonctionne avec des tampons mémoires.

Les arguments :

"-o -" écriture du flux en sortie
"-t 0" pas de timeout
"-n" pas de flux sur la sortie HDMI (écran)
"cvlc" utilise VLC
"-sout" argument qui spécifie le flux rtsp et son Port
"-w" largeur de l'image: 64 to 1920
"-h" hauteur de l'image:  64 to 1080
"-fps" nombre d'images par seconde: 2 to 30
"demux=h264" format du fichier de sortie

Du côté de VLC, il suffit de choisir dans le menu fichier -> ouvrir un flux réseau et d'entrer l'adresse IP du Raspberry Pi précédée de rtsp:// et suivi du port :8554/

 

 

fleche_g.gif

Page Robotique

fleche_h.gif
Haut de page