Road To Robots – 1

Dans le précédent article de la série nous avons mis en place l’environnement pour développer en Rust sur Raspberry Pi, puis pour valider cet environnement nous avons fait clignoter une LED. Bien que cette étape soit indispensable, on est encore loin de faire quelque chose qui se rapproche d’un robot.

Dans cet article on va s’en rapprocher beaucoup plus !

Pour cela il y a une question que l’on doit se poser :

Qu’est-ce qu’un Robot ?

Il existe une multitude de robots, très différents les uns des autres : un robot industriel qui assemble des voitures, un robot aspirateur, un robot d’aide à la personne ou encore un droïde astromecano réparant votre vaisseau spatial après que celui-ci ait reçu un tir de blaster.

Mais, au final, ces robots sont tous composés de la même façon. Un robot est constitué de trois types d’éléments :

  • Des capteurs
  • Des actionneurs
  • Un cerveau

Capteurs

Les capteurs permettent au robot de récupérer des informations sur l’environnement dans lequel il évolue. Ce sont les yeux et les oreilles du robot en quelques sortes.

Ils sont de toutes sortes :

  • Capteur ultrasons, infrarouge, laser
  • Caméra
  • Capteur de température, pression atmosphérique, humidité
  • Capteur de contact, pression, boutons
  • Récepteur Radio, Bluetooth, Wi-Fi
  • etc.

Actionneurs

Les actionneurs permettent au robot d’agir sur le monde réel. Ils lui permettent par exemple de se déplacer, d’attraper des objets, de lancer des balles, d’allumer ou éteindre des lumières…

On y retrouve, entre autres :

  • Les moteurs
  • Les servomoteurs
  • Les solénoïdes
  • Les pompes
  • Les écrans
  • Les LEDs

Cerveau

C’est la partie qui contrôle le robot. C’est elle qui récupère les informations venant des capteurs pour les traiter et faire fonctionner les actionneurs.

Dans les différents articles de cette série le cerveau est un RaspberryPi qui exécute des programmes codés en Rust. Mais ça peut également être un Arduino ou n’importe quel autre microcontrôleur.

Dans l’article d’aujourd’hui, on va se concentrer sur un des capteurs les plus utilisés en robotique : le capteur à ultrasons.

Mesurer les distances avec un capteur à ultrasons

Le capteur à ultrasons permet de mesurer la distance entre celui-ci et l’objet le plus proche grace à l’écholocalisation, comme les sonars des sous-marins.

Ils fonctionnent de la façon suivante :

  • Le capteur émet des ondes ultrasons.
  • Lorsque celles-ci rencontrent un obstacle elles sont réfléchies par celui-ci, et reviennent vers le capteur.
  • Le capteur reçoit les ondes réfléchies après un certains temps.
  • Le capteur donne le temps entre l’envoi de l’onde son retour. Grace à la formule Distance = Vitesse x Temps, que nous avons apprise au collège, on peut déterminer la distance de l’objet. Pour rappel la vitesse du son dans l’air est d’environ 340 m/s.

Comme l’onde fait un aller-retour entre le capteur et l’obstacle, il faut diviser le temps par 2 pour le calcul.

Gif présentant une animation du fonctionnement du HC-SR04.
On voit les ondes qui partent de la capsule T, arrivent sur un objet, rebondissent et reviennent sur la capsule R.

Pour le capteur ultrasons on va utiliser un HC-SR04. Il n’est pas cher, facile à trouver et à utiliser. Cependant, comme rien n’est parfait, sa précision de mesure est limitée. En effet, les mesures peuvent avoir des erreurs de l’ordre de +/- 0.5 cm.

Photo d'un HC-SR04
HC-SR04

Fonctionnement du HC-SR04

Afin de faire une mesure de distance il faut commencer par envoyer un signal HIGH pendant au moins 10µs sur la broche Trig. Le capteur émet alors une série de 8 impulsions à 40kHz via la capsule émettrice, notée T sur le circuit.

Le capteur attends que, éventuellement, des ondes réfléchies retournent au capteur, via la capsule réceptrice, notée R sur le circuit. Une fois les ondes reçues, le capteur passe la broche ECHO à HIGH pendant la durée correspondant au temps que l’onde à mis pour revenir.

Je ne rentre pas dans le détail, les liens suivants expliqueront cela beaucoup mieux, et ça m’évitera de les paraphraser :

Branchements

Le branchement du capteur est simple, il suffit de brancher :

  • VCC -> 5V
  • GND -> GND
  • Trig -> GPIO
  • Echo -> GPIO

Attention cependant, nous utilisons un Raspberry Pi. Les GPIO de celui-ci supportent une tension maximale de 3,3V, or la sortie de la broche Echo est en 5V. Il faut donc faire un pont diviseur sur cette broche à l’aide d’une resistance de 330 Ohms et une de 470 Ohms au risque de griller le GPIO dans le cas contraire.

Schema du montage
Schema Fritzing de l'utilisation du capteur.

Code

Après quelques essais, il s’avère que la librairie rust_gpiozero ne permet pas de faire fonctionner le capteur. Ou en tout cas pas simplement.

C’est ce que je craignais en utilisant cette librairie. Il faut donc coder une librairie afin de faire fonctionner le capteur.

Pour cela je vais utiliser une autre librairie : rppal. Cette librairie permet d’accéder directement aux GPIO et des les contrôler plus finement.

J’ai donc écrit une structure DistanceSensor qui a deux attributs :

  • trigger : de type OutputPin, qui permet de déclarer la broche qui déclenche la mesure avec le capteur.
  • echo : de type InputPin, qui permet de déclarer la broche qui récupère le temps mis par l’onde pour revenir.
/// Structure for the distance sensor.
pub struct DistanceSensor {
    trigger: OutputPin,
    echo: InputPin
}

impl DistanceSensor {
   /// Construct a new DistanceSensor.
    /// 
    /// [`trigger_pin`]: pin used to trigger the measure.
    /// [`echo_pin`]: pin used to receive the echo.
    pub fn new(trigger_pin: u8, echo_pin: u8) -> Result<DistanceSensor, Error> {
        // Create the gpio handle.
        let gpio = Gpio::new()?;

        // Configure the trigger pin.
        let trigger = gpio.get(trigger_pin)?.into_output();

        // Configure thre echo pin.
        let echo = gpio.get(echo_pin)?.into_input();

        // Return the distance sensor.
        Ok(DistanceSensor { echo, trigger })
    }
}

La structure dispose d’une méthode get_distance qui déclenche une mesure avec le capteur et retourne la distance de l’obstacle le plus proche ou, s’il n’y en a pas dans la distance limite retourne -1.

const SPEED_OF_SOUND:f64 = 0.034029; //cm/micro second

/// Make a measurement with the sensor to return the distance of the closest object. If no object is found it returns -1.0.
pub fn get_distance(&mut self) ->f64 {
   // Start the measure.
   self.trigger.set_low();
   thread::sleep(Duration::from_micros(2));
   self.trigger.set_high();
   thread::sleep(Duration::from_micros(10));
   self.trigger.set_low();

   let mut init = Instant::now();
   let mut start = Instant::now();
   let mut duration = Duration::new(0, 0);

   // Wait the return of the ultrasound.
   while self.echo.is_low() {
      if init.elapsed().as_millis() > 30 {
         return -1.0;
      }
   }

   start = Instant::now();
   init = Instant::now();

   // Listen to echo to get the duration corresponding to the distance.
   while self.echo.is_high() {
      duration = start.elapsed();
      if init.elapsed().as_millis() > 30 {
          return -1.0;
      }
   }
   let micros = duration.as_micros(); // Convert duration to microseconds.

   // Convert the duration to a distance.
   (SPEED_OF_SOUND * micros as f64) / 2.0
}

On peut maintenant tester le circuit vu précédemment avec le code suivant :

use ::rust_gpiozero::*;
use std::error::Error;
use std::thread;
use std::time::{Duration, Instant};

mod distance_sensor;

fn main() -> Result<(), Box<dyn Error>> {
    println!("---Disance Sensor Tests Program---");
    let mut sensor = distance_sensor::DistanceSensor::new(18,24).unwrap();
    loop {
        let distance = sensor.get_distance();

        println!("Distance: {}cm", distance);        

        thread::sleep(Duration::from_millis(500));
    }
}

On obtient quelques choses dans ce genre là dans la console :

  • Lorsqu’il n’y a pas d’objet proche :
Image représentant les résultat lorsqu'il n'y a pas d'objets proches.
On peut voir des valeurs de distance entre 1m et 2,5m.
  • Lorsqu’il y a un objet proche :
Image représentant les résultat lorsqu'il n'y a pas d'objets proches.
On peut voir des valeurs de distance entre 11 et 12cm.

Mise en pratique

Essayons de mettre en pratique ce qu’on a vu jusqu’à maintenant. On va faire simple : Allumer une LED si un objet se trouve à une distance inférieure à un seuil.

Ce n’est pas grand chose, mais quand on y réfléchit bien, ça correspond à la définition d’un robot qu’on a vu au début de l’article :

  • Capteur : Capteur ultrasons
  • Actionneurs : LED
  • Cerveau : programme qui détecte si un objet est à une distance inférieure au seuil.

C’est pour l’instant un peu limité comme robot, mais c’est un début.

Circuit

Le montage est le même que précédemment on ajoute simplement une led branchée sur la broche 17.

Pour le code il suffit de modifier le main précédent de la façon suivante :

const THRESHOLD: f64 = 20.0;

fn main() -> Result<(), Box<dyn Error>> {
    let led = LED::new(17);
    println!("---Disance Sensor Tests Program---");
    let mut sensor = distance_sensor::DistanceSensor::new(18,24).unwrap();
    loop {
        let distance = sensor.get_distance();

        println!("Distance: {}cm", distance);        
        if distance >= 0.0 && distance <= THRESHOLD  {
            led.on();
        } else {
            led.off();
        }

        thread::sleep(Duration::from_millis(500));
    }
}

Ça fonctionne! La LED s’allume quand l’objet est suffisamment proche.

À défaut d’être un vrai robot, on peut dire qu’on a un début de système d’alarme très basique.

Conclusion

Nous voilà arrivé à la fin de cet article. Résumons ce que nous avons vu :

  • Nous avons appris à utiliser notre premier capteur. Au delà de l’aspect pédagogique, c’est un capteur très courant en robotique et il sera pas mal utilisé dans les prochains articles.
  • Nous avons vu que la librairie rust_gpiozero a des limites, et qu’il a fallu en utiliser une autre pour arriver à utiliser le capteur.
  • Nous avons créé une structure Rust nous permettant d’utiliser le capteur.
  • Nous avons utilisé de façon concrète le capteur en allumant une led si un objet se trouve à proximité.

Vous pouvez trouver le code source sur mon GitHub

Dans le prochain article nous allons commencer à faire bouger des choses pour rendre nos robots plus vivants.

Sources

Laissez un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.