Voiture robot autonome de suivi de visage avec Arduino Nano et OpenCV
Composants et fournitures
| Composant | Quantité |
|---|---|
| Creator Ci20 (carte embarquée Linux) | 1 |
| Double pont en H L298 (SparkFun) | 1 |
| Batterie Li-Ion 1000 mAh | 1 |
| Servomoteurs Tower Pro MG996R | 2 |
| Caméra (générique, ex: vieille webcam récupérée) | 1 |
| Kit base robot GoPiGo (Dexter Industries) | 1 |
| Arduino Nano R3 | 1 |
| Capteur ultrasons HC-SR04 | 1 |
Applications et services en ligne
- Arduino IDE
- OpenCV (bibliothèque vision par ordinateur)
À propos de ce projet
Un robot qui suit votre visage : si vous vous éloignez, il vient vers vous !
Prérequis logiciels (sur Ci20, Debian 8)
- OpenCV (
sudo apt-get install libopencv-dev python-opencv– version 2.4.9.1, fonctionne bien) - Python + PySerial (
sudo apt-get install python python-serial) - Correction permissions port série :
sudo chown ci20:ci20 /dev/ttyUSB0(USB Arduino)
sudo chown ci20:ci20 /dev/ttyS1(port natif Ci20)
Matériel utilisé (partiellement récupéré)
- Vieille webcam d'ordinateur portable
- Module ultrasons HC-SR04
- 2 × Batteries 3,7 V 4000 mAh (récupérées)
- 2 × Servos
- Convertisseur DC-DC
- Arduino Nano
- Kit base robot GoPiGo
- 2 × L298N
Lancement
Placez haarcascade_frontalface_alt.xml dans le même dossier que le script Python. Puis :
sudo python facetrackingcar.py 0
(Remplacez 0 par l'index de votre caméra si plusieurs sources vidéo).
Pour gagner en performance, l'image capturée n'est pas affichée. Pour la voir, décommentez :
// cv.ShowImage("result", img)
// cv.NamedWindow("result", 1)
// cv.DestroyWindow("result")
Code
faceser.py (Python – OpenCV suivi visage + envoi commandes série)
#!/usr/bin/python
""" Démonstration détection visage Haar + asservissement 2 servos pour centrer le visage. """
import sys, serial, cv2.cv as cv, time
from optparse import OptionParser
# Paramètres détection Haar
min_size = (20, 20)
image_scale = 2
haar_scale = 1.2
min_neighbors = 2
haar_flags = cv.CV_HAAR_DO_CANNY_PRUNING
# Servos
max_pwm = 180; min_pwm = 1
midScreenWindow = 20 # zone morte centre écran (pixels)
panStepSize = 1 # pas pan (degrés)
tiltStepSize = 1 # pas tilt (degrés)
servoPanPosition = 90 # position initiale pan (X)
servoTiltPosition = 45 # position initiale tilt (Y)
panGpioPin = 2 # ID servo pan côté Arduino (pas numéro broche)
tiltGpioPin = 1 # ID servo tilt côté Arduino
def detect_and_draw(img, cascade):
gray = cv.CreateImage((img.width, img.height), 8, 1)
small_img = cv.CreateImage((cv.Round(img.width / image_scale),
cv.Round(img.height / image_scale)), 8, 1)
cv.CvtColor(img, gray, cv.CV_BGR2GRAY)
cv.Resize(gray, small_img, cv.CV_INTER_LINEAR)
cv.EqualizeHist(small_img, small_img)
midFace = None
if cascade:
faces = cv.HaarDetectObjects(small_img, cascade, cv.CreateMemStorage(0),
haar_scale, min_neighbors, haar_flags, min_size)
if faces:
for ((x, y, w, h), n) in faces:
pt1 = (int(x * image_scale), int(y * image_scale))
pt2 = (int((x + w) * image_scale), int((y + h) * image_scale))
cv.Rectangle(img, pt1, pt2, cv.RGB(255, 0, 0), 3, 8, 0)
x1, x2, y1, y2 = pt1[0], pt2[0], pt1[1], pt2[1]
midFace = (x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2)
return midFace
def move(servo, angle):
if min_pwm <= angle <= max_pwm:
ser.write(chr(255))
ser.write(chr(servo))
ser.write(chr(angle))
else:
print("Angle servo doit être entre 0 et 180.")
if __name__ == '__main__':
ser = serial.Serial(port='/dev/ttyUSB0', baudrate=115200, timeout=1)
parser = OptionParser(usage="usage: %prog [options] [camera_index]")
parser.add_option("-c", "--cascade", dest="cascade", default="./haarcascade_frontalface_alt.xml")
(options, args) = parser.parse_args()
cascade = cv.Load(options.cascade)
if len(args) != 1: parser.print_help(); sys.exit(1)
input_name = args[0]
if input_name.isdigit():
capture = cv.CreateCameraCapture(int(input_name))
cv.SetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH, 320)
cv.SetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT, 240)
else:
print("Spécifiez l'index caméra, ex: 0"); sys.exit(0)
move(panGpioPin, servoPanPosition)
move(tiltGpioPin, servoTiltPosition)
while True:
frame = cv.QueryFrame(capture)
if not frame: break
# ... (traitement image, détection visage, calcul erreur centre, mise à jour servos)
# Code complet dans le fichier original.
if cv.WaitKey(1) >= 0: break
Car.ino (Arduino – contrôle moteurs + servos + ultrasons)
#include
#define MA_1 2; #define MA_2 3; #define MB_1 4; #define MB_2 5
#define MC_1 6; #define MC_2 7; #define MD_1 8; #define MD_2 9
#define SERVOX_PIN 11; #define SERVOY_PIN 10
#define trigPin 13; #define echoPin 12
Servo servoy, servox;
int x = 90, prevX, y = 45, prevY;
int userInput[3], startbyte, servo, pos, i, State = LOW;
unsigned long previousMillis = 0, val = 100;
const long interval = 100;
bool scana = false;
void setup() {
inicializate();
}
void loop() {
if (Serial.available() > 2) {
startbyte = Serial.read();
if (startbyte == 255) {
for (i = 0; i < 2; i++) userInput[i] = Serial.read();
servo = userInput[0]; pos = userInput[1];
if (pos == 255) servo = 255;
switch (servo) {
case 1: servoy.write(pos); break;
case 2: servox.write(pos); range(pos); break;
case 3: scan(45); scan(65); break;
}
}
}
}
void scan(int val) {
while (Serial.available() == 0) {
servoy.write(val);
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
servox.write(pos);
if (State == LOW) { pos++; if (pos == 130) State = HIGH; }
else { pos--; if (pos == 40) { State = LOW; break; } }
}
}
}
long distancia() {
digitalWrite(trigPin, LOW); delayMicroseconds(2);
digitalWrite(trigPin, HIGH); delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH);
return (duration / 2) / 29.1;
}
void range(int pos) {
if (pos >= 80 && pos <= 100) moves();
else if (pos >= 100 && pos <= 180) { left(); delay(10); Stop(); }
else if (pos >= 1 && pos <= 80) { right(); delay(10); Stop(); }
}
void moves() {
long distance = distancia();
if (val <= 80) { reverse(); delay(50); Stop(); }
else if (val >= 140) { forward(); delay(50); Stop(); }
// ... logique suivi distance
}
void inicializate() {
Serial.begin(115200);
// Config broches moteurs, servos, ultrasons
servox.attach(SERVOX_PIN); servoy.attach(SERVOY_PIN);
}
void forward() { /* Moteurs avant */ }
void reverse() { /* Moteurs arrière */ }
void right() { /* Rotation droite */ }
void left() { /* Rotation gauche */ }
void Stop() { /* Arrêt */ }
void StopH() { /* Freinage (toutes broches HIGH) */ }
scanlinux.py (Python – détection ports série Linux)
#!/usr/bin/env python
import serial, glob
def scan():
return glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*') + \
glob.glob('/dev/ttyACM*') + glob.glob('/dev/serial/by-id/*')
if __name__ == '__main__':
print("Found ports:")
for name in scan(): print(name)
Pièces personnalisées et boîtiers
Script Python OpenCV + code Arduino : FaceTrakingCar.rar
Schémas
Contrôle moteurs et servos par Arduino (brochage variable).
Processus de fabrication
- Dé numérique Arduino sur écran LCD 16x2 – Générateur aléatoire à bouton poussoir
- Créer votre propre jeu de roulette à 37 LED avec Arduino Nano
- ATtiny85 Mini Arcade : Snake – Construire une console de jeu portable OLED
- Détecteur de distance portable – Kit DIY Arduino Nano avec HC-SR04, alimentation 18650 et boîtier imprimé 3D
- MobBob : Construisez votre propre robot Arduino, contrôlé via smartphone Android
- PiBot : Robot pianiste Arduino pour performance musicale de précision
- Projet de galvanoplastie au cuivre : Construisez un système intelligent avec Arduino UNO
- Jeu Pong sur Matrice NeoPixel avec Arduino Nano
- Robot Omnidirectionnel pour Suivi de Personne en Sécurité