Nuova sezione libri disponibile!

HB Cloud Tutorial #3 - I Motori

Ludovico Russo

lettura in 7 minuti

I Motori sono una delle parti essenziali dei robot. In questo tutorial, vedremo come è possibile in modo semplice ed intuitivo implementare un programma in Python che controlla i motori in base a comandi inviati via Wifi al Robot.

Ecco nel dettaglio cosa vedremo:

  • Come gestire una coppia di motori in gpiozero.
  • Come sottoscriversi ad un topic ROS sfruttando le callback.

Circuito

Colleghiamo i motori al driver. L'alimentazione del driver va collegata alla batteria da 9V, ricordandoci di mettere in comune la massa della batteria con quella del raspberry.

Le due fasi dei motori, vanno collegate, rispettivamente, ai GPIO 16,19 (sinistra) e 20,26 (destra).

Importante: Ricordate di mettere in comune la massa (GND) dell'alimentazione dei motori con la massa del Raspberry Pi.

schema motori raspberry

Scriviamo il Codice

Per utilizzare i motori, useremo l'oggetto Robot della libreria gpiozero, che è in grado di gestire il movimento di un semplice robot a due ruote.

Programma di test

Per prima cosa, testiamo che i motori funzionino lanciando un semplice programma di test. Questo programma sfrutta solo la funzione Setup Scriviamo un brevissimo programma che controlla il robot facendogli fare semplici movimenti.

Per creare l'oggetto Robot dobbiamo passare al costruttore le coppie di PIN GPIO a cui sono collegati i due motori, sfruttando i parametri left e right:

self.robot = Robot(left=(16, 19), right=(20, 26))

Una volta inizializzato, possiamo sfruttare le funzioni Robot.left, Robot.right, Robot.forward e Robot.backward per farlo muovere, rispettivamente, a destra, sinistra, avanti e indietro. E la funzione Robot.stop per farlo fermare. Utilizziamo inoltre la funzione time.sleep della libreria time per far prolungare una certa azione nel tempo al robot.

Il tutto, può essere utilizzato come nel seguente programma per testare che i motori si muovino correttamente. Si noti che time.sleep chiede come unico parametro il tempo di attesa in secondi.

import dotbot_ros
from gpiozero import Robot

import time

class Node(dotbot_ros.DotbotNode):
    node_name = 'test_motor'

    def setup(self):
        self.robot = Robot(left=(16, 19), right=(20, 26))

        self.robot.forward()
        time.sleep(1)

        self.robot.backward()
        time.sleep(1)

        self.robot.left()
        time.sleep(1)

        self.robot.right()
        time.sleep(1)

        self.robot.stop()

Una volta lanciato questo programma, il robot dovrebbe iniziare a muoversi prima avanti e poi indietro, per poi girare a destra e a sinistra. Se qualcosa non funziona, controllate che i motori siano alimentati e che le masse siano messe in comune.

Sottoscriviamo ad un Topic ROS e usiamo le Callback

Controllare il robot in questo modo non gli permette di essere nè più nè meno di un semplice giocattolo. Proviamo quindi a fare qualcosa di più interessante: controllare il robot attraverso un topic.

Per farlo, sottoscriviamoci ad un topic chiamato speed di tipo std_msgs/Int16MultiArray. Per farlo, per prima cosa, dobbiamo capire cos’è e come si usa una funzione di callback.

Funzioni di Callback

Come già spiegato in precedenza, una funzione di callback è una funzione che non viene esplicitamente chiamata dal nostro programma, ma è automaticamente eseguita al verificarsi di un certo evento asincrono (cioè un evento che è generato al di fuori del nostro programma).

ROS sfrutta la callback come meccanismo per intercettare i messaggi inviati da su topic al quale il nodo è sottoscritto, e per processare i dati in modo immediato e istantaneo. In particolare, ROS chiede al programmatore di implementare una funzione di callback per ogni topic a cui il nodo è sottoscritto, e chiama automaticamente questa funzione ogni qualvolta un messaggio è invitato sul topic.

Implementiamo la funzione di callback per il topic speed

Ricapitolando: il nostro nodo si deve sottoscrivere al topic Speed, in cui vengono mandati comandi di velocità per il robot. Ogni volta che un messaggio viene inviato sul topic, il nodo deve processare il messaggio e controllare i motori di conseguenza.

Andiamo quindi ad implementare una funzione di callback, chiamata on_speed. Questa funzione (come tutte le funzioni di callback) avrà la seguente forma:

def on_speed(self, msg):
    pass

I due parametri che la funzione prende sono self (che rappresenta il nodo) e msg, che conterrà il messaggio scambiato dal topic.

Il messaggio std_msgs/Int16MultiArray contiene due valori (data[0] e data[1] rispettivamente per il motore destro e sinistro) che possono variare tra -255 e 255. La convenzione è che 255 è il massimo valore di velocità in avanti, -255 è il massimo all'indietro, 0 significa velocità nulla e ogni altro valore è un valore intermedio tra queste velocità.

Anche la classe Robot funziona in modo simile, ma i valori di velocità delle ruote possono variare tra -1.0 (massima velocità all'indietro) e 1.0 (massima velocità in avanti). La prima cosa che dovrò fare la funzione, quindi, è convertire questi valori e controllare che i valori finali siano nell'intervallo [-1, 1].

def on_speed(self, msg):
    v_dx = msg.data[0]/255.0
    v_sx = msg.data[1]/255.0

    #controllo che v_dx sia nel range [-1,1]
    if v_dx > 1.0:
        v_dx = 1.0
    elif v_dx < -1.0:
        v_dx = -1.0

    #controllo che v_sx sia nel range [-1,1]
    if v_sx > 1.0:
        v_sx = 1.0
    elif v_sx < -1.0:
        v_sx = -1.0

Una volta generati i due comandi di velocità (v_dx e v_sx), aggiungiamo una stringa per stamparne a video i valori finali utilizzando la funzione print:

def on_speed(self, msg):
       #...
       print 'v_dx', v_dx
       print 'v_sx', v_sx
       sys.stdout.flush()
       #...

Ricordate di aggiungere sempre la linea di codice sys.stdout.flush() (e importare il modulo sys con la stringa import sys per forzare la stampa effettiva sulla shell di DotBot-ROS. Ora non ci resta che settare questi valori per far muovere le ruote. Per farlo, ci viene incontro un utilissimo parametro dell'oggetto Robot: Robot.value, che si usa nel seguente modo:

self.robot.value = (v_sx, v_dx)

In particolare, questo parametro vuole entrambi i comandi di velocità contemporaneamente tra parentesi tonde (in python questa struttura si chiama Tupla) e, appena settato, automaticamente controlla le ruote con i valori richiesti!

La funzione on_speed, quindi, verrà completata in questo modo:

def on_speed(self, msg):
    v_dx = msg.data[0]/255.0
    v_sx = msg.data[1]/255.0

    #stampo a video i valori di v_dx e v_sx
    print 'v_dx', v_dx
    print 'v_sx', v_sx
    sys.stdout.flush()

    #controllo che v_dx sia nel range [-1,1]
    if v_dx > 1.0:
        v_dx = 1.0
    elif v_dx < -1.0:
        v_dx = -1.0

    #controllo che v_sx sia nel range [-1,1]
    if v_sx > 1.0:
        v_sx = 1.0
    elif v_sx < -1.0:
        v_sx = -1.0

    #controllo del robot
    self.robot.value = (v_dx, v_sx)

Sottoscrizione al topic

Una volta implementata la funzione di callback, non ci resta che sottoscriverci al topic speed per poterla correttamente utilizzare. Per farlo, nella funzione setup, aggiungiamo la seguente linea di codice:

dotbot_ros.Subscriber("speed", Int16MultiArray, self.on_speed)

ricordandoci di importare l'oggetto Int16MultiArray da std_msgs.msg

from std_msgs.msg import Int16MultiArray

Codice completo

Ecco il codice completo del nostro programma

import dotbot_ros
import sys
from gpiozero import Robot
from std_msgs.msg import Int16MultiArray

class Node(dotbot_ros.DotbotNode):
    node_name = 'example_motor'

    def setup(self):
        self.robot = Robot(left=(16, 19), right=(20, 26))
        dotbot_ros.Subscriber("speed", Int16MultiArray, self.on_speed)

    def on_speed(self, msg):
        v_dx = msg.data[0]/255.0
        v_sx = msg.data[1]/255.0

        #controllo che v_dx sia nel range [-1,1]
        if v_dx > 1.0:
            v_dx = 1.0
        elif v_dx < -1.0:
            v_dx = -1.0

        #controllo che v_sx sia nel range [-1,1]
        if v_sx > 1.0:
            v_sx = 1.0
        elif v_sx < -1.0:
            v_sx = -1.0

        #controllo del robot
        self.robot.value = (v_dx, v_sx)

Per inviare comandi di velocità, possiamo utilizzare l'app di test della nostra piattaforma.

Esercizi

Vi suggerisco alcuni esercizi per migliorare il codice.

Esercizio 1: Robot Joystick

La webapp joystic manda messaggi di tipo geometry_msgs/Vector3 sul topic joy contenenti due variabili msg.x e msg.y che contengolo la posizione del joystick in coordinate cartesiane. Si modifichi il programma in modo da intercettare questo messaggio e far muovere il robot in base alla posizione del joystick.

Esercizio 2: Stop del robot dopo N secondi

Un problema che (in base alle preferenze) potrebbe essere risolto o no, rigurda il fatto che se viene impostato un comando di velocità, il robot continuerà a muoversi finchè non gli viene impostato il comando di velocità (0,0).

Si provi a migliorare il programma in modo che il robot si fermi dopo N secondi (sceliti da programmatore) dall'ultimo comando di velocità ricevuto.

Per farlo suggerisco di utilizzare i seguenti accorgimenti:

  1. Creare una variabile che contiene il tempo di esecuzione dell'ultima chiamata della funzione di callback. Usando la funzione datetime.datetime.now.
  2. Settare l'esecuzione della funzione loop a frequenza alta (ad esempio, 20Hz).
  3. Nella funzione loop, controllare che la differenza in secondi tra l'ultima volta che è stata eseguita la funzione on_speed e il tempo attuale sia maggiore di N
  4. In caso affermativo, utilizzare la funzione Robot.stop per stoppare il robot.

Vi riporto, sotto, un esempio per ottenere la differenza in secondi tra due tempi

from datetime import datetime
import time

x = datetime.now()
time.sleep(2)
y = datetime.now()
seconds = (y-x).total_seconds()
# 2.003452

Ti è piaciuto questo post?

Registrati alla newsletter per rimanere sempre aggiornato!

Ci tengo alla tua privacy. Leggi di più sulla mia Privacy Policy.

Ti potrebbe anche interessare

HB Cloud Tutorial #1 - Uso dei Led
Iniziamo ad utilizzare la piattaforma di Cloud Robotics
HB Cloud Tutorial #2 - Uso dei Bottoni
Rieccomi con il secondo tutorial legato all'uso dei bottoni per il robot **DotBot-ROS**. In questo tutorial, vedremo come configurare ed utilizzare in Python un bottone attaccato ad un pin GPIO del Raspberry Pi 3.
Utilizzare la RaspiCam in streaming con la piattaforma Cloud
Breve tutorial che spiega come abilitare la RaspiCam su ROS e la piattaforma di Cloud Robotics
Installiamo ROS su Raspberry Pi
Un breve tutorial su come utilizzare ROS sul Raspberry Pi
Video Corso ROS Rokers - Completati i video di Introduzione
Sono disponibili online i video tutorial del corso di ROS partito dalla community Rokers
Inviare Goals alla Navigation Stack - versione nodo ROS Python
Inviare un goal all ROS navigation stack utilizzando un nodo Python
Controllare siBOT dalla piattaforma HBR
Come controllare il manipolatore siBOT utilizzando la piattaforma HBR
Sviluppare un rilevatore di fiamma con la visione artificiale
Sviluppare un rilevatore di fiamma con la visione artificiale
Scriviamo un Blog in Python e Flask
Tutorial su come implementare, a partire da zero, un blog personale utilizzando Python e Flask! Prima parte!
Un laboratorio di Fisica con Arduino e Python
Primi esperimenti con Arduino e Python per realizzare un semplice laboratorio di fisica sfruttando la potenza di Python e la versatilità di Arduino
Un IDE web Arduino sviluppato in Python e Flask
Un mio progetto dell'estate del 2015 che permette di programmare Arduino da un'interfaccia Web esposta da un Raspberry Pi
Utilizziamo Atom come editor di testo remoto su Raspberry Pi
Come usare Atom come editor di testo remoto per Raspberry Pi
Canopy: una Pythonica alternativa a Matlab
Presento questo interessante tool python che può essere considerato una buona alternativa a Matlab per l'analisi dei dati!
Spyder, un'altra alternativa in Python a Matlab
Una velocissima prova del tool interattivo Spyder per l'analisi scientifica in Python
Simuliamo il moto parabolico in Python e Spyder
Un piccolo tutorial per iniziare ad utilizzare Spyder con Python
Python + Arduino = Nanpy
Programmare Arduino in Python con Nanpy
Utilizzo di Nanpy con il sensore di temperatura/umidità della famiglia DHT
Come utilizzare Nanpy col sensore DHT di temperatura e Umidità
Accendere led con Arduino e Telegram
Un bot telegram in grado di controllare Arduino realizzato da 3 ragazzi del Liceo Stampacchia
Gestire le macchine virtuali senza sforzo con Vagrant
Introduzione ed esempio di utilizzo su Vagrant, il tool che semplifica la gestione delle macchine virtuali per lo sviluppo.
Implementiamo un bot Telegram con Python
Una semplice guida per iniziare a muovere i primi passi nello sviluppo di chatbot Telegram con Python
Pillole di Python: pyscreenshot
Una semplice tutorial che mostra il funzionamento della libreria pyscreenshot
Python Decorators
Introduzione ai decoratori in Python
TDD con Flask e PyTest per lo sviluppo di API REST. Parte 1
Tutorial su come usare il Test Driver Development (TDD) con Flask e PyTest per sviluppare delle semplici API REST
Implementiamo un bot Telegram con Python - I Comandi
Vediamo come gestire i comandi del nostro bot in Telegram
4 (+1) Libri su Python (in Inglese) da cui imparare
Una lista di libri su Python (in Inglese) da cui ho imparato a programmare
Virtualenv: gestiamo meglio le dipendenze in Python
A cosa servono e come si utilizzano i virtualenv Python
Leggere i codici a barre con OpenCV e zbar in Python
Come usare Python per leggere i codici a barre degli alimenti e ricavarne alcune informazioni utili
TDD con Flask e PyTest per lo sviluppo di API REST. Parte 2
Tutorial su come usare il Test Driver Development (TDD) con Flask e PyTest per sviluppare delle semplici API REST
Sviluppiamo un bot Telegram che legge i codici a barre degli alimenti
Implementiamo un bot Telegram in grado di leggere ed analizzare le immagini per la lettura ed interpretazione dei codici a barre
TDD con Flask e PyTest per lo sviluppo di API REST. Parte 3
Tutorial su come usare il Test Driver Development (TDD) con Flask e PyTest per sviluppare delle semplici API REST
Divertiamoci sviluppando UI da terminale con ASCIIMATICS
Le UI da terminale fanno molto anni '80, però sono sempre diventerti da implementare. Oggi vi voglio introdurre ad una semplice libreria per creare questo tipo di applicazione.
Sviluppiamo un Robot con tecnologie Open Source
Inizio una serie di videoguide, in cui voglio introdurvi al mondo della robotica di servizio in modo pratico, facendo vedere come è possibilile, sfruttando tecnologie completamente Open Source, quali Arduino, Raspberry Pi, ROS e Docker, costruire un piccolo robot di Servizio.
Parliamo come GMaps: come creare file audio con gtts (Google Text to Speech) in Python
gtts è una libreria in Python per sfruttare le API di Google Text to Speech per generare file audio dal testo
Robot Open Source - Introduzione a Docker
È disponibile il video "Introduzione a Docker".
I chatbot possono Parlare? Sviluppiamo un bot telegram che manda messaggi vocali
Usiamo le API di sintesi vocale di google per creare un bot in grado di mandare messaggi vocali
Robot Open Source - Docker e Raspberry
È disponibile il video "Docker e Raspberry".