Tappa 6

Firmware

Uno sketch Arduino per il XIAO ESP32S3 Sense che fa tutto: muove i servo, legge l’IMU, trasmette video e audio, ospita la web app di controllo e decide come camminare.

I file

Architettura

            ┌──────────── ciclo di controllo 50 Hz ────────────┐
 IMU ──────▶│ orientamento (filtro complementare)              │
 comandi ──▶│ generatore: pose │ cpg │ policy  ──▶ q[6] (rad) │──▶ servo
 (WS/HTTP)  │ limiti giunti, watchdog 1 s                      │
            └──────────────────────────────────────────────────┘
 camera ──▶ /stream (porta 81), /capture      mic ⇄ speaker ──▶ WS /audio
 telemetria ~5 Hz ──▶ WS /ws                  Wi-Fi / AP / mDNS pulcino.local

Tutti i generatori producono lo stesso risultato: sei angoli dei giunti in radianti con la convenzione dei segni condivisa. Solo il modulo servo li trasforma in gradi e impulsi.

I moduli, uno per uno

Servo

Converte ogni angolo con 90 + SERVO_DIR[i] · gradi(q) + CALIB_OFFSET[i], limita tra 5° e 175° e pilota i pin con ESP32Servo (timer LEDC 1–3, perché lo 0 è della camera) oppure il PCA9685.

IMU

Legge l’MPU6050 via I²C, rimappa gli assi nel frame del robot (x avanti, y sinistra, z su) e stima rollio e beccheggio con un filtro complementare. Fornisce anche il vettore gravità per la politica.

Pose

Pose a keyframe interpolate: stand (tutto a 0), rest (hip_roll +0,25, gambe larghe e servo poco caricati), wave (sposta il peso, alza e agita il piede destro, dice “ciao” e lampeggia gli occhi).

CPG

Camminata a oscillatori letta da gait.json: ogni giunto segue offset + amp·sin(2π·freq·t + fase), la gamba destra è sfasata di π. Il comando vx scala il passo, wz fa curvare, speed la frequenza.

Politica

Una piccola rete neurale (16 → 32 → 32 → 6, tanh) eseguita a 50 Hz. Se POLICY_VALID è 0 il firmware ripiega automaticamente sul CPG.

Rete e web

Wi-Fi di casa oppure Access Point Pulcino-XXXX, mDNS pulcino.local, server HTTP con la web app, API REST, WebSocket per comandi, telemetria e audio, video MJPEG sulla porta 81.

Audio

Microfono PDM interno a 16 kHz verso i client; audio ricevuto e suoni generati (bip, “ciao”) verso il MAX98357A via I²S.

Sicurezza

Watchdog di movimento: senza messaggi per 1 s il robot si ferma. Token opzionale per comandi e POST. Tutti gli angoli sono limitati ai limiti dei giunti.

config.h

Prima di caricare lo sketch apri config.h e controlla almeno queste righe:

#define WIFI_SSID   "la-tua-rete"   // vuoto = solo Access Point
#define WIFI_PASS   "la-tua-password"
#define API_TOKEN   ""              // non vuoto = comandi protetti con ?token=
#define USE_PCA9685 0               // 1 = variante con PCA9685

static const int8_t SERVO_DIR[6]    = { +1, +1, +1, -1, -1, -1 };
static const float  CALIB_OFFSET[6] = {  0,  0,  0,  0,  0,  0 };

Non pubblicare mai il tuo config.h con la password del Wi-Fi: se fai un fork, tienilo fuori dai commit.

Calibrazione: un solo array di offset

Tutta la calibrazione sta in un array di 6 numeri, CALIB_OFFSET[6], in gradi: la correzione di quanto ogni corno è entrato storto. Lo modifichi dalla pagina http://pulcino.local/calib (slider + “Salva”) o con l’API GET/POST /api/calib. I valori sono salvati nella memoria NVS del XIAO e hanno la precedenza su quelli scritti in config.h.

SERVO_DIR[6] invece dice in che verso gira ogni servo montato: se muovendo un giunto di +10° il piede va dalla parte sbagliata, cambia il segno di quella voce.

Pose e camminata

Il firmware ha tre generatori di movimento, selezionabili con {"cmd":"mode","m":"cpg|policy"} e {"cmd":"pose","name":"stand|rest|wave"}:

  1. pose: keyframe interpolati, per stare in piedi, riposare e salutare;
  2. cpg: oscillatori sinusoidali con parametri in gait.json (li puoi allenare nella Palestra e caricarli con POST /api/gait; vengono salvati in LittleFS);
  3. policy: la rete neurale addestrata in MuJoCo (vedi Apprendimento).

Anteprima del CPG predefinito (frequenza 1,6 Hz):

La politica neurale in 20 righe

Ogni 20 ms il firmware costruisce un’osservazione di 16 numeri (gravità, giroscopio, ultima azione, comando, fase del passo), la normalizza con OBS_MEAN/OBS_STD, la passa nella rete e ottiene 6 azioni tra −1 e 1:

q_target = POSE_STAND + azione · ACTION_SCALE   // {0.3, 0.6, 0.6} per roll/pitch/ankle
q = 0.6 · q_target + 0.4 · q_precedente          // filtro passa-basso
q = clip(q, limiti del giunto)

Sono circa 2 000 moltiplicazioni: all’ESP32-S3 bastano pochi decimi di millisecondo.

Web server e API

EndpointPortaA cosa serve
GET /80Web app di controllo (vedi Controllo)
GET /stream81Video MJPEG
GET /capture80Una foto JPEG
GET /api/status80Telemetria JSON
GET/POST /api/gait80Legge/scrive gait.json
GET/POST /api/calib80Legge/scrive gli offset
WS /ws80Comandi e telemetria
WS /audio80Audio PCM 16 kHz nei due sensi

La web app sta in web/index.html e viene incorporata nel firmware durante la compilazione: niente file system da caricare a parte.

Impostazioni di Arduino IDE

  1. Installa Arduino IDE 2. In Impostazioni → URL aggiuntivi per il Gestore schede aggiungi l’indice delle schede ESP32 di Espressif (https://espressif.github.io/arduino-esp32/package_esp32_index.json) e installa il pacchetto esp32 (versione 3.x).
  2. Installa le librerie indicate nel README del firmware (tra cui ESP32Servo).
  3. Scegli la scheda e le opzioni:
    SchedaXIAO_ESP32S3
    PSRAMOPI PSRAM (indispensabile per la camera)
    Flash size8MB
    Partition scheme8M with spiffs (3MB APP/1.5MB SPIFFS) o simile con file system
    USB CDC On BootEnabled (per vedere i messaggi sul monitor seriale)
    Monitor seriale115200 baud
  4. Apri firmware/pulcino/pulcino.ino, modifica config.h, collega il cavo USB-C (interruttore della batteria spento) e premi Carica.
  5. Se la scheda non viene vista: tieni premuto il tasto B (BOOT) mentre la colleghi, poi riprova.

Nel monitor seriale compare l’indirizzo del robot. Da lì: calibrazione e poi controllo.