Collegare in cloud i LEGO NXT ed estenderne le funzionalità

Pietro Chiro

5 mins min read

In questo tutorial vedremo come collegare un braccio a 3 gradi di libertà costruito con la piattaforma Lego NXT al cloud utilizzando il Raspberry pi3 e l'interfaccia Hbrain. Successivamente vedremo come scrivere una webapp che ci permetta di sfruttare le potenzialità del cloud per controllarlo.

Collegamenti

Come prima cosa prendete la scheda Raspberry e accendetela collegandola alla corrente. Collegatela al sito di Hotback (come imparato dal manuale). Prendete il lego NXT, accendetelo e collegate il suo cavo usb ad un ingresso qualunque della scheda Raspberry. Avete così collegato il lego NXT al cloud!

La webapp

Ora che abbiamo connesso l'NXT al cloud procediamo a creare una webapp che ci permetterà di controllarlo attraverso la tastiera del nostro computer. Come prima cosa scaricate, premendo sul pulsante clone or download i file che potete trovare qui.

Ora è necessario far sì che la nostra webapp sia in grado di dialogare con il nostro robot. Aprite il file html keyboard_robotarm con il blocco note e cercate la stringa start_ros('192.168.0.112', 'silverbot', '192.168.0.112', '192.168.0.112/bridge/');

Dovete ora sostituire questa stringa con quella che identifica il vostro robot. Per trovarla dirigetevi sul sito di Hotblack al quale avete prima connesso la vostra scheda Raspberry, aprite una qualunque webapp e aprite la sorgente della pagina premendo il tasto destro del mouse

All'interno del codice sorgente cercate la stringa start_ros('#...');, copiatela al posto di quella presente nel file html (aperto con il blocco note) keyboard_robotarm e salvate il tutto.

Possiamo ora testare la nostra app: aprite il file keyboard_robotarm nel browser e aprite la console cliccando col tasto destro del mouse e selezionando ispeziona. Refreshate la pagina (F5) e ogni volta che premete uno dei pulsanti wa, sd, ik la console dovrebbe restituirvi, stampandola a video, l'azione connessa al pulsante premuto.

Sketch ROS

Scriviamo ora lo sketch in ROS che farà comunicare il nostro robot con la webapp. Importiamo subito le librerie che utilizzeremo nel programma:

import dotbot_ros
import sys
import nxt
from nxt.motor import *
from geometry_msgs.msg import Twist
from time import sleep

Ora nel setup andiamo a includere la stringa self.NXT = nxt.locator.find_one_brick() per far sì che il programma cerchi il lego NXT attraverso la connessione usb e inizializziamolo:

print 'starting'   #stampa starting al lancio del programma
sys.stdout.flush() #forza la stampa sulla shell ROS

self.NXT = nxt.locator.find_one_brick()
self.m1 = Motor(self.NXT, PORT_A) #motore della porta A
self.m2 = Motor(self.NXT, PORT_B) #motore dell porta B
self.m3 = Motor(self.NXT, PORT_C) #motore della porta C
self.cnt = -1

Sempre nel setup creiamo un subscriber che sottoscriva il programma al messaggio della webapp da noi creata. Il nostro subscriber si sottoscriverà ad un topic chiamato/keyboard che scambia messaggi di tipo std_msgs/Twist inserendoli in una funzione callback chiamata xyz definita dai parmetri self e msg :

def setup(self):

     #...
     dotbot_ros.Subscriber("/keyboard", Twist, self.xyz)

def xyz(self, msg):

Procediamo ora a sfruttare i messaggi che giungono dalla nostra webapp nella nostra funzione xyz per definire i movimenti che eseguirà il nostro braccio. Il messaggio di tipo Twist spedisce, nel nostro caso, 3 messaggi diversi:

  • msg.linear.x che useremo per muovere il primo giunto

  • msg.linear.y che useremo per muovere il secondo giunto

  • msg.linear.z che useremo per muovere il terzo giunto

Per far sì che il nostro braccio si muova sfrutteremo l'attributo turn della classe Motor definito da 5 parametri:

  • power: la forza, nel range -127 /+128, con cui si attivano i motori. Valori inferiori a 64 sono altamente sconsigliati

  • tacho_units: il numero di gradi di cui si muoverà il motore. Il valore minimo che i motori sono in grado di leggere, già con difficoltà, è 5

  • brake: definisce se il motore si stoppa (True) dopo aver compiuto il movimento o meno (False)

  • timeout: numero di secondi dopo il quale viene mostrato un messaggio di errore nel caso il motore non si muova

  • emulate: da definire sempre come False

Per far sì che il giunto del nostro braccio si muova solamente nel caso in cui il messaggio proveniente dal topic sia diverso da 0 utilizziamo il costrutto if-else. in particolare il simbolo != significa diverso e la stringa self .m1.idle() dice semplicemente al motore definito dalla funzione m1 di non fare alcunchè:

def xyz(self, msg):

    if msg.linear.x != 0:
        self.m.turn(msg.linear.x*100, 5, True, 1, False)

    else:
        self.m1.idle()

A questo punto abbiamo creato la struttura per far muovere il primo giunto quando riceve un messaggio dal topic. Non ci resta che aggiungere il codice per fa muovere gli altri due, copiando il codice già scritto modificando semplicemente il tipo di messaggio del quale verificare la differenza da 0 e il motore da muovere:

def xyz(self, msg):

    if msg.linear.x != 0:
        self.m1.turn(msg.linear.x*100, 5, True, 1, False)

    else:
        self.m1.idle()


    if msg.linear.y != 0:
        self.m2.turn(msg.linear.x*100, 5, True, 1, False)

    else:
        self.m2.idle()


    if msg.linear.z != 0:
        self.m3.turn(msg.linear.x*100, 5, True, 1, False)

    else:
        self.m3.idle()

Codice completo

import dotbot_ros
import sys
import nxt
import nxt.locator
from nxt.motor import *
from geometry_msgs.msg import Twist
from time import sleep

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

    def setup(self):
        print 'starting'
        sys.stdout.flush()

        self.NXT = nxt.locator.find_one_brick()
        self.m1 = Motor(self.NXT, PORT_A) #motore della porta A
        self.m2 = Motor(self.NXT, PORT_B) #motore della porta B
        self.m3 = Motor(self.NXT, PORT_C) #motore della porta C
        self.cnt = -1

        dotbot_ros.Subscriber("/keyboard", Twist, self.xyz)

    def xyz(self, msg):

        if msg.linear.x != 0:
            self.m1.turn(msg.linear.x*100, 5, True, 1, False)

        else:
            self.m1.idle()


        if msg.linear.y != 0:
            self.m2.turn(msg.linear.y*100, 5, True, 1, False)

        else:
            self.m2.idle()


        if msg.linear.z != 0:
            self.m3.turn(msg.linear.z*100, 5, True, 1, False)

        else:
            self.m3.idle()