Volevo condividere con voi un programma C# ed un Firmware Arduino che ho scritto per calcolare la posizione in una traiettoria punto-punto imponendo velocità iniziale e finale zero.
Ho utilizzato il polinomio cubico a0 + a1*t + a2*(t^2) + a3*(t^3) che permette una variazione "dolce" della posizione e della velocità, essenziale per non avere bruschi movimenti che potrebbero danneggiare i motori e causare vibrazioni.
Il programma C# l'ho scritto per provare l'algoritmo e generare i dati da graficare in Excel, il firmware Arduino è stato testato con un paio di servo low-cost (HD-1800A ed MG995), i risultati sono fortemente influenzati dal fatto che i servo in oggetto hanno un loro sistema di controllo analogico basato su potenzimetro per cercare la posizione corretta. Nelle prove si passeranno i valori della posizione angolare che i servo dovranno raggiungere per passare da qo=0 a qf=180 gradi in un tempo tf=400ms.
Programma C#
- Code: Select all
- static void Traiettoria()
 {
 File.Delete("res.csv");
 // Utilizzo polinomio cubico
 // a0 + a1*t + a2*(t^2) + a3*(t^3)
 // per calcolare la posizione ed imporre velocità iniziale e finale = 0
 double tf = 400; // tempo finale
 double q0 = 0; // posizione iniziale
 double qf = 180; // posizione finale
 double qf0 = qf - q0;
 double tf2 = Math.Pow(tf, 2);
 double tf3 = Math.Pow(tf, 3);
 string csv = string.Empty; // stringa per generare il file .csv
 for (double t = 1; t < tf + 1; t += 1)
 {
 double t2 = Math.Pow(t, 2);
 double t3 = Math.Pow(t, 3);
 // Calcolo coefficienti eq. polinomiale
 double a0 = q0;
 // a1 = 0, velocità iniziale = 0
 double a2 = (3 * qf0) / tf2;
 double a3 = (-2 * qf0) / tf3;
 double p = a0 + (a2 * t2) + (a3 * t3); // posizione
 double v = (2 * a2 * t) + (3 * a3 * t2); // velocità
 double a = (2 * a2) + (6 * a3 * t); // accelerazione
 csv += p + ";" + v + ";" + a + ";" + "\r\n";
 }
 File.WriteAllText("res.csv", csv); // scrivi il file con i risultati
 Process.Start("res.csv"); // apri il file (Excel)
 }
L'algoritmo viene valutato ogni unità di tempo, ecco i profili che si ottengono, grafici generati con Excel:
Posizione

Velocità

Accelerazione

Come si può vedere l'unico problema potrebbe essere l'accelerazione, si potrebbe dire che il motore "parte in quinta" anche se nella realtà l'accelerazione sarà una rampa più o meno ripida date le caratteristiche fisiche del motore.
Se siete interessati ad una trattazione più matematica dell'algoritmo e dei suoi parametri fatemi sapere che pubblico qualche info aggiuntiva.
Firmware Arduino
- Code: Select all
- // Utilizzo polinomio cubico
 // a0 + a1*t + a2*(t^2) + a3*(t^3)
 // per calcolare la posizione ed imporre velocità iniziale e finale = 0
 #include <Servo.h>
 Servo servo;
 const float tf = 400.0f; // tempo finale
 const float q0 = 0.0f; // posizione iniziale
 const float qf = 170.0f; // posizione finale
 const float qf0 = qf - q0;
 const float tf2 = pow(tf, 2);
 const float tf3 = pow(tf, 3);
 unsigned long startTime, time;
 void setup()
 {
 servo.attach(9); // servo sul pin9
 servo.write(0); // vai a 0°
 delay(1000); // aspetta il posizionamento
 startTime = millis(); // inizia a contare il tempo da ora
 }
 void loop()
 {
 time = millis() - startTime;
 servo.write(GetPosition(time));
 }
 
 float GetPosition(long t)
 {
 if(t > tf)
 { // Se siamo oltre il tempo finale
 return qf; // ritorna la posizione finale
 }
 
 // Calcola il quadrato e il cubo del tempo
 float t2 = pow(t, 2);
 float t3 = pow(t, 3);
 // Calcolo coefficienti eq. polinomiale
 float a0 = q0;
 // a1 = 0, velocità iniziale = 0
 float a2 = (3 * qf0) / tf2;
 float a3 = (-2 * qf0) / tf3;
 // Calcolo risultati
 float p = a0 + (a2 * t2) + (a3 * t3); // posizione
 //double v = (2 * a2 * t) + (3 * a3 * t2); // velocità
 //double a = (2 * a2) + (6 * a3 * t); // accelerazione
 
 return p;
 }
Ho effettuato delle prove visive, in effetti si nota che la velocità segue il profilo prima graficato, e delle prove di consumo energetico con una resistenza Shunt di 1.2 ohm e 6V di tensione.
Nelle immagini 1 è il movimento del servo "grezzo" da 180 a 0°, mentre 2 è il movimento tramite la traiettoria punto-punto.
Si nota subito una maggiore facilità a trovare il punto di arrivo da parte del controllo del servo ed un consumo energetico di picco minore oltre a meno variazioni, col vantaggio di emettere meno rumore elettromagnetico.
Servo da 9g HD-1800A http://www.hobbytronics.co.uk/datasheets/HD-1800A.pdf

Ho invertito i morsetti dell'oscilloscopio quindi il grafico risulta ribaltato, è chiaro però che torna quanto detto
Servo Standard MG995

Questo servo è "famoso" per far fatica (senza carico) a trovare il punto di arrivo, con diverse oscillazioni attorno al punto finale. Il posizionamento tramite la traiettoria mostra che il servo trova con più facilità il punto di arrivo.
Concludendo, seppur la traiettoria punto punto implementata tramite equazione polinomiale sia maggiormente efficace su motori DC, anche tramite i servo è possibile sperimentare qualcosa e si può avere qualche vantaggio.
Se l'argomento è di interesse posso trasformare questo topic in un tutorial. Fatemi sapere i vostri commenti. Purtroppo non conosco Bascom ma la conversione del codice in questo linguaggio non dovrebbe presentare grossi problemi. Tutte le immagini sono caricate su imageshack, non occupano spazio sul server.
Ciao
UPDATE: Utilizzando servo.writeMicroseconds() al posto di servo.write() è possibile ottenere una risoluzione quasi 8 volte maggiore (0.09° invece di 0.7°) e di conseguenza movimenti meno scattosi.
UPDATE2:


 
  
 