Capire la mobilità del futuro con le auto robot, parte 2
Benvenuti alla seconda puntata della nostra nuova serie di blog sulle auto robot. Nella prima parte abbiamo conosciuto le basi del veicolo di base. Questa volta vogliamo discutere le possibilità di controllo a distanza, sviluppare un sistema di codici con cui determinare i livelli di velocità dell'auto robot e infine realizzare un semplice controllo a distanza con un telecomando a infrarossi.
Perché ho bisogno di livelli di velocità per un'auto RC per microcontrollori Arduino?
1. Per controllare la velocità del motore da un altro dispositivo, c'è fondamentalmente la possibilità di determinare e trasmettere continuamente i valori con un potenziometro (poti), oppure di aumentare o diminuire il valore del passo di velocità premendo un pulsante. Le immagini seguenti mostrano alcuni esempi selezionati, nella prima immagine i comandi del joystick:
Nella seconda immagine vediamo i telecomandi a infrarossi, un'applicazione per smartphone con ricevitore Bluetooth e la tastiera LCD1602, che può essere utilizzata con un ricetrasmettitore a 433 MHz, tra le altre cose:
Per ottenere uno schema uniforme, è opportuno utilizzare la funzione map() per ridurre i valori analogici del piatto in valori che possano essere trasmessi via radio. Il convertitore analogico-digitale fornisce numeri a 10 bit, cioè valori compresi tra 0 e 1023. La posizione centrale dei mini-joystick è di circa 511. Dividendo il valore per 100, un joystick può determinare due valori (direzione x e y) compresi tra 0 e 10 o, se si utilizza la funzione map(), tra 1 e 9. Questo valore è sufficiente per inserire la velocità e la velocità di un veicolo. Questo è sufficiente per inserire la velocità e per le curve. Indipendentemente dal fatto che si determini un valore di 11 o 9, l'arresto significa un valore di 5 nella direzione y e per la guida in rettilineo anche un valore di 5.
Guardando il controller con i due joystick (parte del kit del braccio robotico Mini 4DOF con joystick e servoazionamento), data la varietà di sistemi diversi, ho scelto un codice tra 1111 e 9999 che è facile da trasmettere. La prima cifra può essere utilizzata per la direzione y del joystick sinistro, la seconda cifra per la direzione x del joystick sinistro, la terza e la quarta cifra per un secondo joystick opzionale o per alcuni tasti. Nel primo esempio con il telecomando IR, abbiamo bisogno solo delle prime due cifre del codice a quattro cifre.
Il controllo della velocità dell'auto RC per Arduino
Il controllo della velocità dei motori avviene con la modulazione di larghezza degli impulsi (PWM). Il valore del cosiddetto duty cycle è un numero a 8 bit, cioè un valore compreso tra 0 e 255 (=2 alla potenza di 8 -1). Se applichi al motore una tensione compresa tra 0 e 6 V con un alimentatore regolabile, noterai che fino a circa 1,5 V non si nota nulla. Poi il motore inizia a ronzare, ma non si muove. Se misuri contemporaneamente la corrente, noterai un valore relativamente alto. L'energia viene convertita in calore e ronzio: non è proprio il massimo. A partire da 2,4 - 3 volt, il motore inizia a girare, l'amperaggio scende leggermente quando il motore è scarico. Successivamente, la velocità aumenta in base alla tensione applicata. Quando si regola la tensione verso il basso, il motore gira fino a meno di 2 volt, ma se si ferma, ad esempio a causa di una maggiore resistenza di attrito o di un ostacolo, non riparte. Conclusione: i valori inferiori a circa 2,4 volt devono essere evitati, l'ingresso deve essere impostato su 0 (zero) per evitare un'inutile usura e consumo di energia. Ad eccezione dell'arresto (codice=5), è necessario un Duty Cycle compreso tra circa il 40% e il 100% con una tensione di alimentazione di 6V per i motori.
L'alimentazione dell'auto RC per Arduino
Abbiamo già discusso diverse possibilità per l'alimentazione nella prima parte. Quattro batterie AA da 1,5 V ciascuna forniscono 6 V, la tensione massima dei piccoli motori gialli. Quattro batterie AA da 1,2 V ciascuna non sono sufficienti per l'alimentazione. È quindi necessario un portabatterie per 6 batterie, che fornisce 7,2 volt. Due batterie agli ioni di litio (tensione nominale di 3,7 V) forniscono oltre 8 volt quando sono completamente cariche. Quindi ha senso impostare un fattore nel programma che limiti la tensione del livello di pilotaggio più alto a 6V.
Controllare l'auto RC con Arduino
Se le istruzioni di guida vengono impartite con dei pulsanti, che possono essere anche in un'app per smartphone, il valore appropriato viene aumentato o diminuito per il valore di uscita 55xx (arresto) senza superare il massimo.
Leggere correttamente il codice
L'assegnazione della prima cifra del codice ai valori richiesti delle fasi della corsa avviene tramite l'indice di un elenco con i rispettivi valori numerici.
Conclusione: con un codice a quattro cifre, possiamo controllare la velocità e la curva con le prime due cifre, mentre le altre due cifre sono utilizzate per altre funzioni (non richieste all'inizio). Quindi, ad esempio, codice95xx per la velocità massima in rettilineo, 55xx per l'arresto, 77xx per l'avanzamento a destra.
y ↓ 0 x→ |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
9 |
← |
|
|
↑ |
|
|
→ |
||
8 |
← |
|
|
↑ |
|
|
→ |
||
7 |
← |
|
|
↑ |
|
|
→ |
||
6 |
← |
|
|
↑ |
|
|
→ |
||
5 |
← |
|
|
0 |
|
|
→ |
||
4 |
← |
|
↙ |
|
↓ |
|
↘ |
|
→ |
3 |
← |
|
↙ |
|
↓ |
|
↘ |
|
→ |
2 |
← |
|
↙ |
|
↓ |
|
↘ |
|
→ |
1 |
← |
|
↙ |
|
↓ |
|
↘ |
|
→ |
Ora vogliamo costruire la nostra prima Smart Robot Car con il kit, un microcontrollore ATmega328 (progetto UNO R3), un MotorShield V2 e un trasmettitore e ricevitore IR.
Il Motor Shield V2 può controllare fino a quattro motori, utilizzando il cosiddetto bus I2C con i collegamenti SDA (=Serial Data) all'ingresso analogico A4 e SCL (=Serial Clock) ad A5 per il collegamento delle linee di controllo. Adafruit ha anche sviluppato e fornito una libreria di programmi adatta a questo scopo. Attenzione: le librerie per i Motor Shield V1 e V2 non sono compatibili.
Immagine Motor Shield V2 con modifica:
Connettori a presa saldata (connettori femmina) per il collegamento di apparecchiature aggiuntive
Indipendentemente dalla Motor Shield che vuoi utilizzare - V1 o V2 -, è opportuno saldare connettori aggiuntivi per entrambe le Motor Shield per poter collegare in seguito trasmettitori/ricevitori Bluetooth o a 433 MHz o sensori. Maggiori informazioni in merito nei prossimi post del blog. Il ricevitore IR ha bisogno di alimentazione solo dai pin 3 e 4 e dal pin 2 per il ricevitore IR. Il saldatore può rimanere freddo.
Per il controllo utilizzeremo innanzitutto il piccolo telecomando a infrarossi di Funduino e un ricevitore IR. Anche se il sensore IR "nudo" è sufficiente, ti consiglio di utilizzare la piccola scheda breakout, perché in questo caso un LED lampeggia quando vengono ricevuti i segnali IR; uno strumento prezioso quando si cerca un guasto.
Lo sketch è composto da due parti collaudate: Lo sketch di esempio di Armin Joachimsmeyer, che ha aggiunto alla sua fantastica libreria di programmi IRremote, e uno sketch di Robot Car realizzato dall'autore sulla base della libreria di programmi di Adafruit.
Il codice del programma per l'auto RC per Arduino
/* Codice di esempio per Robot Car con Motor Shield V2 e ricevitore IR, al 20220515
* basato sulla libreria Adafruit Motor shield V2, copyright Adafruit Industries LLC, 2009
* e SimpleReceiver.cpp, parte di Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote
* Licenza MIT Copyright (c) 2020-2022 Armin Joachimsmeyer
* modificato per Funduino
* Motor Shield V2 utilizza I2C con SDA=A4 e SCL=A5
* Il ricevitore IR utilizza il pin 2
************************************************************************************
* Con la presente si concede il permesso, a titolo gratuito, a chiunque ottenga una copia di questo software e dei file di documentazione ad esso associati (vedi pagina 5)
* di questo software e dei file di documentazione associati (il "Software"), di utilizzare il Software senza alcuna restrizione, compresi i seguenti diritti
* nel Software senza alcuna restrizione, compresi, senza limitazioni, i diritti di
* di utilizzare, copiare, modificare, unire, pubblicare, distribuire, concedere in sublicenza e/o vendere
* copie del Software, e di permettere alle persone a cui il Software è fornito
* di farlo, alle seguenti condizioni:
*
* Il suddetto avviso di copyright e il presente avviso di autorizzazione devono essere inclusi in tutte le copie o parti sostanziali del Software
* copie o parti sostanziali del Software.
*
* IL SOFTWARE VIENE FORNITO "COSÌ COM'È", SENZA GARANZIE DI ALCUN TIPO, ESPRESSE O IMPLICITE,
* INCLUSE, A TITOLO ESEMPLIFICATIVO E NON ESAUSTIVO, LE GARANZIE DI COMMERCIABILITÀ, IDONEITÀ PER UNO SCOPO PARTICOLARE E NON VIOLAZIONE
* SCOPO PARTICOLARE E NON VIOLAZIONE. IN NESSUN CASO GLI AUTORI O I TITOLARI DEL COPYRIGHT
* SARANNO RESPONSABILI DI QUALSIASI RECLAMO, DANNO O ALTRA RESPONSABILITÀ, SIA IN UN'AZIONE LEGALE CHE IN UN ILLECITO O ALTRO, DERIVANTI DA
* CONTRATTO, ILLECITO O ALTRO, DERIVANTI DA, IN RELAZIONE AL SOFTWARE O ALL'USO O AD ALTRI RAPPORTI CON ESSO
* O ALL'USO O AD ALTRI RAPPORTI CON IL SOFTWARE.
*************************************************************************************/
#include <Adafruit_MotorShield.h>
// Crea l'oggetto motor shield con l'indirizzo I2C predefinito 0x60
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Seleziona quale "porta" M1, M2, M3 o M4.
Adafruit_DCMotor *motor1 = AFMS.getMotor(2);
Adafruit_DCMotor *motor2 = AFMS.getMotor(3);
// definiamo il protocollo per il controllo remoto, per maggiori informazioni consulta il codice di esempio SimpleReceiver.cpp
#define DECODE_NEC // Include Apple e Onkyo, anche per il piccolo telecomando Funduino
//#define INFO // Per visualizzare informazioni preziose dal decodificatore universale per i protocolli di larghezza o distanza degli impulsi
#define IR_RECEIVE_PIN 2 // invece di #include "PinDefinitionsAndMore.h"
#include <Arduino.h>
#includere <IRremote.hpp>
// Segnale del ricevitore IR collegato al Pin2, VCC al Pin3, GND al Pin4
int IR_GND = 4;
int IR_VCC = 3;
int x = 0;
int y = 0;
int left = 0;
int destra = 0;
int codice = 5555;
int speedL = 0;
float factor = 1.8; // Correzione per speedLevel 255/100 * 6V/VBatt
void setup() {
Serial.begin(9600);
Serial.println("Test del motore!");
Serial.println("Motorshield v2 - Test motore DC!");
se (!AFMS.begin()) { // create with the default frequency 1.6KHz
Serial.println("Could not find Motor Shield. Check wiring.");
while (1);
}
Serial.println("Motor Shield trovato.");
// Solo per sapere quale programma è in esecuzione sul mio Arduino
Serial.println(F("START " __FILE__ " da " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE));
// Avvia il ricevitore
IrReceiver.begin(IR_RECEIVE_PIN); //, ENABLE_LED_FEEDBACK);
Serial.print(F("Pronto a ricevere i segnali IR dei protocolli: "));
printActiveIRProtocols(&Serial);
Serial.print(F("al pin "));
Serial.println(IR_RECEIVE_PIN);
// inizializzare i pin digitali come uscita per l'alimentazione
pinMode(IR_GND,OUTPUT);
pinMode(IR_VCC,OUTPUT);
digitalWrite(IR_GND,LOW);
digitalWrite(IR_VCC,HIGH);
} // fine della configurazione
void loop() {
se (IrReceiver.decode()) {
// Stampa un breve riassunto dei dati ricevuti
IrReceiver.printIRResultShort(&Serial);
se (IrReceiver.decodedIRData.protocol == UNKNOWN) {
// We have an unknown protocol here, print more info
IrReceiver.printIRResultRawFormatted(&Serial, true);
}
Serial.println();
/*
* importante!!! Abilita la ricezione del valore successivo,
* poiché la ricezione si è interrotta dopo la fine dell'attuale pacchetto di dati ricevuti.
*/
delay(100); // Debounce, nessun fast retry
IrReceiver.resume(); // Abilita la ricezione del valore successivo
/*
* Infine, controlla i dati ricevuti ed esegue le azioni in base al comando ricevuto
*/
if (IrReceiver.decodedIRData.command == 0x46) {
if (code<9000) code = code + 1000;
Serial.print("Code = ");
Serial.println(code);
}
else if (IrReceiver.decodedIRData.command == 0x15) {
if (code>2000) code = code - 1000;
Serial.print("Code = ");
Serial.println(code);
}
else if (IrReceiver.decodedIRData.command == 0x43) {
if ((code-1000*int(code/1000))<900) code = code + 100;
Serial.print("Code = ");
Serial.println(code);
}
else if (IrReceiver.decodedIRData.command == 0x44) {
if (code-1000*int(code/1000) > 200) code = code - 100;
Serial.print("Code = ");
Serial.println(code);
}
se (IrReceiver.decodedIRData.command == 0x40) {
code = 5555;
Serial.print("Code = ");
Serial.println(code);
}
altrimenti {
Serial.print("invalid code");
}
motore();
}
} // fine del ciclo
void motor(){
int speedLevel[9]={-100,-80,-60,-40,0,40,60,80,100};
y = int(codice / 1000);
x = int((codice - 1000*y) / 100);
speedL = speedLevel[y-1];
Serial.print("codice = ");
Serial.print(codice);
Serial.print(" y =");
Serial.print(y);
Serial.print(" x =");
Serial.print(x);
Serial.print(" velocitàL =");
Serial.println(speedL);
//Correzione dei passi di velocità per la curva
se (x==1){
right = speedL+16;
left = speedL-16;
}
altrimenti se (x==2){
right = speedL+13;
left = speedL-13;
}
se (x==3) {
right = speedL+10;
left = speedL-10;
}
se (x==4) {
right = speedL+7;
left = speedL-7;
}
se (x==6) {
right = speedL -7;
left = speedL+7;
}
se (x==7) {
right = speedL-10;
left = speedL+10;
}
se (x==8) {
right = speedL-13;
left = speedL+13;
}
se (x==9) {
right = speedL-16;
left = speedL+16;
}
altrimenti {
right = speedL;
left = speedL;
}
//Input dei passi di guida per "sinistra" e "destra
Serial.print("sinistra = ");
Serial.print(left);
Serial.print(" destra = ");
Serial.println(right);
se (left < 40 & left > -40) {
motor1->run(RELEASE);
}
se (destra < 40 & destra > -40) {
motor2->run(RELEASE);
}
se (sinistra>=40) {
if (left>100) left=100;
motor1->run(FORWARD);
motor1->setSpeed(left * factor);
}
se (destra>=40) {
if (right>100) right=100;
motor2->run(FORWARD);
motor2->setSpeed(right * factor);
}
se (sinistra<= -40) {
if (left<-100) left=-100;
motor1->run(BACKWARD);
motor1->setSpeed(-left * factor);
}
se (destra<= -40) {
if (right<-100) right=-100;
motor2->run(BACKWARD);
motor2->setSpeed(-right * factor);
}
} // fine del motore
Spiegazione del codice del programma per l'auto RC
Dopo aver incluso le librerie Motor Shield V2 e IRremote, i due motori vengono istanziati con il numero alla morsettiera del controller e alcune variabili globali vengono dichiarate (tipo di dati) e inizializzate (valore iniziale).
Nella funzione setup() vengono inizializzati l'interfaccia seriale e il ricevitore IR e i pin per l'alimentazione del ricevitore IR vengono impostati come uscite con lo stato HIGH o LOW.
Nella funzione loop(), prima viene ricevuto il segnale del telecomando IR, poi il codice per il controllo del motore viene cambiato a seconda del tasto premuto. I tasti cursore in alto e in basso modificano la prima cifra, a sinistra e a destra la seconda cifra del codice nell'intervallo di valori da 1 a 9. Il tasto X porta all'arresto con il codice 5555. La terza e la quarta cifra del codice non sono ancora significative. Alla fine, la funzione autodefinita motor() viene richiamata senza argomenti, poiché abbiamo definito le variabili in modo globale, cioè valide per tutte le funzioni.
Nella funzione autodefinita motor(), che utilizzeremo anche in altre configurazioni con altri telecomandi, i livelli di velocità per il motore destro e sinistro sono determinati dal codice. Il valore percentuale del livello di velocità viene infine convertito nel valore PWM per setSpeed utilizzando il fattore definito all'inizio.
L'auto robot è ora pronta per essere utilizzata. Funziona bene finché riesci a mantenere la connessione ottica tra il telecomando e il ricevitore IR. Tuttavia, durante la guida su strada, ho sperimentato che la forte luce del sole ostacola la ricezione. Ecco perché sto passando al radiocomando. A presto.