ELAI S.r.l.

Il robot punta al contrario? Quando fare la media degli angoli è un errore

Due direzioni quasi identiche possono dare una media opposta: scopriamo perché, come calcolare la media circolare e quando il risultato è ambiguo.

Il robot punta al contrario? Quando fare la media degli angoli è un errore

Due misure vicine, una risposta dalla parte opposta

Immaginiamo due stime dell’orientamento di un pezzo su una tavola rotante. Una indica +179°, l’altra −179°. Le frecce sono quasi sovrapposte: entrambe puntano verso sinistra, a un solo grado dalla direzione di 180°. La media aritmetica, però, somma i due numeri e divide per due, ottenendo 0°. La freccia media punta verso destra. Un programma potrebbe quindi trasformare due osservazioni quasi concordi in un orientamento opposto, pur eseguendo correttamente somma e divisione.

La domanda non è soltanto come evitare quel risultato. Vogliamo capire quale media stiamo calcolando, che cosa minimizza e quando non esiste una direzione media affidabile. Costruiremo il metodo dai vettori sul cerchio, svolgeremo il caso numerico e confronteremo due diverse nozioni di distanza. Il titolo descrive un rischio del modello matematico: non stiamo raccontando un incidente, né un test su un robot EL-AI. Tutte le misure sono sintetiche e i calcoli sono riproducibili.

Un angolo non è un punto su una retta

Per una lunghezza, 179 e −179 sono valori molto lontani. Per una direzione, aggiungere un giro completo non cambia la freccia: −179° e 181° rappresentano lo stesso orientamento. Se riscriviamo la seconda osservazione come 181°, la media ordinaria diventa 180°. Non abbiamo modificato il pezzo, soltanto il rappresentante numerico. Una media di direzioni dovrebbe restare equivalente quando a qualsiasi osservazione aggiungiamo o togliamo 360°; la media aritmetica dei rappresentanti non ha questa proprietà.

L’oggetto studiato è quindi una direzione planare, non il numero totale di giri di un asse. Un giunto che ha compiuto tre rivoluzioni può trovarsi nella stessa direzione di un giunto che non ne ha compiute, ma posizione svolta, limiti meccanici e stato del cavo possono essere diversi. In quel caso eliminare i giri sarebbe perdere informazione. Prima del metodo dobbiamo scegliere che cosa rappresenta il dato. Questo articolo riguarda il cerchio delle direzioni, non una soluzione per tutte le variabili articolari.

Trasformare le misure in frecce

A ogni angolo theta associamo il vettore unitario u(theta)=(cos theta, sin theta). La sua lunghezza è uno, quindi conta la direzione e non un’ampiezza arbitraria. Le funzioni trigonometriche lavorano in radianti: per convertire gradi in radianti moltiplichiamo per pi/180. Sommiamo separatamente le componenti orizzontali e verticali, poi dividiamo per il peso totale. Indichiamo le due medie con c e s. I pesi w_i sono non negativi e la loro somma W deve essere positiva; pesi uguali danno la media non pesata.

W = sum_i w_i > 0\nc = (1/W) sum_i w_i cos(theta_i)\ns = (1/W) sum_i w_i sin(theta_i)\nrho = sqrt(c^2+s^2)\nmu = atan2(s,c), provided rho > 0

atan2 ricava l’angolo usando entrambe le componenti, riconoscendo il quadrante; arctan(s/c) da sola perderebbe quell’informazione e richiederebbe una divisione per c. Il valore rho è la lunghezza del vettore medio, compresa fra zero e uno: vettori concordi si rinforzano, opposti si cancellano. mu è una direzione solo quando la risultante non è zero. Nel codice adottiamo una soglia numerica per trattare i residui floating point: la distingueremo da qualsiasi soglia di qualità fisica.

Il caso guida, numero per numero

Per +179° e −179°, il coseno di entrambi è circa −0,9998476952. I seni valgono circa +0,0174524064 e −0,0174524064, quindi si cancellano. Con pesi uguali otteniamo c≈−0,9998476952, s≈0 e rho≈0,9998476952. La risultante è lunga e punta a sinistra: atan2 restituisce 180°, equivalente a −180°. Le due osservazioni distano soltanto 2° percorrendo l’arco più breve. Il salto fra −180° e +180° riguarda l’etichetta numerica, non un giro reale.

Aggiungere 360° alla prima osservazione mantiene lo stesso risultato direzionale, come verifica il codice. Se invece la prima misura pesa tre volte la seconda, otteniamo circa 179,4999619°: la media si avvicina alla prima. Il peso esprime una scelta del modello di fusione, non si giustifica da solo chiamandolo “confidenza”. Per interpretarlo come affidabilità servirebbe un modello degli errori e delle dipendenze fra misure. Due sensori con lo stesso errore sistematico possono dare rho vicino a uno e puntare entrambi nella direzione sbagliata.

Quale errore minimizza questa media?

Abbiamo costruito una formula, ma vogliamo capire perché sia una media e non soltanto una ricetta. Misuriamo la distanza rettilinea fra le punte delle frecce unitarie: è una corda del cerchio. Cerchiamo una direzione mu che minimizzi la somma pesata di queste distanze al quadrato. Questa funzione obiettivo è adimensionale: non è un errore di posizione in metri e non è ancora la distanza angolare al quadrato. La scelta della distanza è parte del problema statistico.

J_chord(mu) = sum_i w_i ||u(theta_i)-u(mu)||^2 ||u(theta)-u(mu)||^2 = 2 - 2 cos(theta-mu) J_chord(mu) = 2W - 2W[c cos(mu)+s sin(mu)] = 2W - 2W rho cos(mu-alpha) alpha = atan2(s,c), if rho > 0

La prima identità usa il prodotto scalare di due vettori unitari: ciascun quadrato di norma vale uno, il prodotto vale coseno della differenza. Poi sviluppiamo cos(theta−mu) e raccogliamo le somme già chiamate c e s. Se rho è positivo, il costo è minimo quando il coseno finale vale uno, cioè mu=alpha modulo un giro. Se rho è zero, il costo vale 2W per ogni mu: tutte le direzioni sono equivalenti per questo obiettivo. Abbiamo così dimostrato insieme la formula e il suo caso di indeterminazione.

Nel caso +179°/−179°, il costo cordale della media aritmetica 0° è circa 7,99939078; quello della media circolare 180° è circa 0,0006092194, usando due pesi unitari. Non sono percentuali di successo di un robot: sono valori della funzione appena definita. Il grande rapporto fra i costi rende evidente il problema della rappresentazione nel caso scelto, ma non misura una distanza di collisione o la qualità di un sistema di visione.

Quando la risultante scompare

Prendiamo due direzioni esattamente opposte, 0° e 180°, con pesi uguali. I vettori si cancellano: c=s=0 nel modello esatto. Il dato non privilegia alcuna direzione media cordale. La media aritmetica 90° non scopre un’informazione nascosta, sceglie un numero legato alla rappresentazione. Nel calcolo floating point sin(pi) può lasciare un residuo di circa 10⁻¹⁶: atan2 può allora restituire un numero, ma quel numero non risolve l’ambiguità matematica.

La nostra implementazione restituisce None quando rho≤10⁻¹², una politica esplicita per questo esperimento. Non è una soglia certificata di fusione sensoriale né un risultato statistico sulla fiducia. Un’applicazione dovrebbe fissare il criterio in base a rumore, unità del problema e comportamento previsto quando non può produrre una stima. La documentazione SciPy di circmean avverte che con risultante zero il valore restituito è dipendente dall’implementazione: il nostro comportamento è diverso e dichiarato, e non abbiamo eseguito SciPy per inventare un confronto.

Un risultato definito può essere molto sensibile

Sostituiamo 180° con 179,999°. La risultante non è più esattamente nulla: rho≈8,72665×10⁻⁶ e la media vale circa +89,9995°. Se invece usiamo −179,999°, una direzione fisica distante soltanto 0,002° dalla precedente, la media diventa circa −89,9995°. Le due medie sono quasi opposte. Non abbiamo scoperto un nuovo salto di rappresentazione: anche le frecce medie sono lontane. Vicino all’equilibrio fra direzioni opposte, una piccola perturbazione sceglie quale delle molte direzioni quasi equivalenti risulta minima.

Possiamo leggere la sensibilità dalla derivata di atan2. Una piccola variazione delle componenti dc e ds produce, al primo ordine, dmu=(c ds−s dc)/rho², con dmu in radianti. La norma del gradiente rispetto a (c, s) è 1/rho: quando la risultante si accorcia, lo stesso errore nelle componenti può amplificarsi nell’angolo. È una relazione locale, valida per perturbazioni abbastanza piccole e rho positivo, non un intervallo di confidenza. Il codice esplora epsilon da 1° a 0,001° per rendere visibile questa sensibilità.

Sinistra: vettori a ±179°, media circolare a 180° e media aritmetica a 0° sul cerchio unitario. Destra: lunghezza risultante per la coppia 0° e 180°−epsilon, con entrambi gli assi logaritmici. Dati sintetici e calcoli Python, non misure su robot.
Sinistra: vettori a ±179°, media circolare a 180° e media aritmetica a 0° sul cerchio unitario. Destra: lunghezza risultante per la coppia 0° e 180°−epsilon, con entrambi gli assi logaritmici. Dati sintetici e calcoli Python, non misure su robot.

La distanza lungo il cerchio dà un’altra media

La corda è il segmento rettilineo che unisce due punti; l’arco segue il bordo del cerchio. Per piccoli scarti le due misure sono vicine, ma per scarti ampi differiscono. Una media intrinseca può minimizzare la somma dei quadrati degli scarti angolari più brevi, riportati in un intervallo di ampiezza un giro. Non è in generale la stessa media cordale. Chiamare un metodo “circolare” non basta a identificarne l’obiettivo: dobbiamo sapere quale distanza penalizza.

Usiamo tre osservazioni 0°, 0°, 180°. La risultante pesa due frecce verso destra contro una verso sinistra: rho=1/3 e la media cordale è 0°. Per la distanza angolare, nel semicerchio da 0° a 180° il costo è 2mu²+(180°−mu)². Derivando rispetto a mu otteniamo 6mu−360°=0, quindi mu=60°. Il semicerchio negativo dà un secondo minimo a −60°. Il costo angolare è 21.600 gradi quadrati nei due minimi, contro 32.400 a zero. Viceversa il costo cordale è 4 a zero e circa 5 a ±60°. Non c’è contraddizione: gli obiettivi sono diversi.

Abbiamo espresso questo confronto in gradi per facilitarne la lettura. Convertire tutti gli angoli in radianti moltiplica i costi angolari per lo stesso fattore (pi/180)², senza cambiare i minimi. Il codice confronta i valori dichiarati, mentre la derivazione sui due semicerchi ne mostra la minimalità. Non sosteniamo che una media sia sempre più “vera” dell’altra: il criterio dovrebbe derivare dall’errore che l’applicazione vuole ridurre. Soprattutto, dati multimodali possono meritare più ipotesi invece di un singolo valore riassuntivo.

Caso, gradiMedia aritmeticaMedia circolare cordalerho
179, -17901800.9998476952
179, 181180-1800.9998476952
0, 18090—~0
0, 179.99989.999589.99950.00000872665
0, 0, 18060~01/3

Il trattino indica che il nostro metodo non restituisce una direzione sotto la soglia numerica. I valori +180° e −180° sono equivalenti. La tabella distingue due problemi: una media ordinaria può dipendere dal taglio dell’intervallo, mentre una media circolare può essere corretta per il proprio obiettivo ma poco informativa o sensibile. Conservare anche rho e l’insieme delle osservazioni aiuta a non perdere questa distinzione; rho da solo non ricostruisce la distribuzione completa.

Svolgere gli angoli non è la stessa operazione

Un’alternativa frequente consiste nell’aggiungere giri interi per rendere continua una sequenza temporale, operazione chiamata unwrapping. Se sappiamo che un asse passa da 179° a −179° avanzando di poco, possiamo rappresentare il secondo campione come 181°. Ma questa scelta usa una relazione temporale o un’informazione a priori. Per misure simultanee senza un riferimento non esiste sempre un modo unico di scegliere i giri. Se fra due campioni il moto può superare mezzo giro, la sola regola del salto più piccolo può scegliere il ramo sbagliato. Non abbiamo eseguito un algoritmo di unwrapping né simulato una traiettoria: qui ne distinguiamo soltanto le ipotesi.

Anche il riferimento geometrico deve coincidere. Un angolo misurato rispetto alla camera e uno rispetto alla base del robot non possono essere fusi soltanto perché entrambi sono espressi in gradi. Servono convenzioni di asse, verso positivo e trasformazioni fra riferimenti coerenti. La media circolare non corregge una calibrazione sbagliata e non riallinea timestamp diversi. La domanda di questo articolo è volutamente più circoscritta: supponiamo che le osservazioni siano già direzioni planari confrontabili, poi studiamo il modo in cui le riassumiamo.

Che cosa esegue il codice e che cosa resta fuori

experiment.py usa Python 3.14.0 e la libreria math. Conserva osservazioni, pesi, componenti, rho, medie e costi; non impiega casualità, quindi non c’è seed da dichiarare. Il calcolo pesato usa fsum per le somme e rifiuta input vuoti, pesi negativi, pesi non finiti e totale nullo. Il confronto numerico usa tolleranze esplicite per gli assert, mentre il caso antipodale è spiegato anche in aritmetica esatta. Il listato breve in fondo contiene il caso guida; il codice completo nell’archivio aggiunge gli altri casi e il confronto degli obiettivi. plot.py genera le quattro figure con Matplotlib, con font cinese indicato nelle istruzioni.

Una sola passata richiede un numero di operazioni proporzionale alle osservazioni e uno stato aggregato costante: totale dei pesi, somma dei coseni e somma dei seni. Il nostro script salva più dati per poterli ispezionare, quindi questa proprietà dell’algoritmo non descrive tutta la memoria del programma. Non abbiamo misurato tempo di esecuzione su microcontrollori, precisione di encoder o comportamento di un controllore. Dalla rapidità di tre somme non si può dedurre la latenza di un sistema robotico completo.

La documentazione primaria SciPy 1.18.0 consultata distingue circmean, basata sulla risultante complessa, da Rotation.mean, definita tramite una perdita cordale sulle matrici di rotazione. È utile per non confondere il caso planare con rotazioni tridimensionali generali: non proponiamo di mediare separatamente tre angoli di Eulero usando questa pagina come garanzia. Le API non sono state eseguite, e i nostri risultati non sono attribuiti a SciPy. Non citiamo come letto il libro o il paper riportati nei riferimenti della documentazione. Questo è un approfondimento matematico didattico, non una nuova ricerca peer-reviewed.

La risposta al problema iniziale

Il robot può ricevere una direzione opposta quando trattiamo una coordinata circolare come un numero su una retta. Nel caso guida la rappresentazione tramite frecce e la media circolare eliminano quell’errore: da +179° e −179° otteniamo la direzione di 180°. Ma il risultato non autorizza a fidarsi sempre della media: risultanti piccole, gruppi opposti, scelta della distanza, riferimenti e giri accumulati cambiano ciò che possiamo concludere. La soluzione non è una formula magica; è scegliere la geometria del dato e riconoscere quando un singolo orientamento non riassume bene le osservazioni.

Per EL-AI la robotica industriale e collaborativa è una direzione di esplorazione indicata dall’utente. Il collegamento applicativo resta qui un’ipotesi: i calcoli non sono una prova su un robot, non dimostrano sicurezza e non attestano prodotti, clienti o installazioni. L’evidenza disponibile è quella delle identità derivate e dei casi sintetici eseguiti. Per passare a una cella reale servirebbero protocollo sensoriale, calibrazione e verifiche dinamiche e di sicurezza specifiche, che questo articolo non sostituisce.

Fonti e materiali riproducibili

SciPy 1.18.0 — scipy.stats.circmean, definition and zero-resultant behaviour.

SciPy 1.18.0 — Rotation.mean, chordal objective.

import math
angles=[179,-179]
c=math.fsum(math.cos(math.radians(a)) for a in angles)/len(angles)
s=math.fsum(math.sin(math.radians(a)) for a in angles)/len(angles)
rho=math.hypot(c,s)
mean=math.degrees(math.atan2(s,c)) if rho>1e-12 else None
assert abs(abs(mean)-180)<1e-10
print(sum(angles)/len(angles),mean,rho)

Codice, dati e istruzioni · JSON. Calcoli didattici eseguiti con Python 3.14.0; figure con Matplotlib 3.11.2. Analisi con assistenza AI, senza dichiarare peer review o revisione umana. Copertina originale ImageGen, illustrativa: non documenta persone, sedi o installazioni EL-AI. Fonti consultate il 24 ottobre 2026.