Algoritmo per la miscelazione di un ingresso analogico a 2 assi per controllare un azionamento differenziale del motore


9

Sto cercando informazioni su come implementare la corretta miscelazione di 2 segnali analogici del joystick (asse X e Y) per controllare un doppio motore differenziale (azionamento "simile al serbatoio") usando un uC (ATMega328p nel mio caso, ma lo stesso dovrebbe valere per qualsiasi uC con ingressi ADC e uscite PWM):

Ho una levetta analogica, che fornisce 2 valori analogici:

(direzione) X: da 0 a 1023
(acceleratore) Y: da 0 a 1023

inserisci qui la descrizione dell'immagine

La posizione di riposo è (direzione e folle dell'acceleratore) 512.512
Acceleratore in avanti / la direzione a sinistra è 0,0
Full avanti-a destra a destra è 1023,0
ecc.

I motori sono controllati da 2 driver H-bridge, 2 pin PWM per ciascuno (avanti, indietro), in questo modo:
Motore sinistro: da -255 a 255
Motore destro: da -255 a 255
(valori positivi abilitano il pin PWM in avanti, negativo abilita indietro Pin PWM, 0 disabilita entrambi)

L'obiettivo è mescolare i segnali analogici del joystick per ottenere la seguente risposta:

a) Acceleratore in avanti, direzione neutra = veicolo che si muove in avanti
b) Acceleratore in avanti, direzione sinistra = veicolo che si muove in avanti e svolta a sinistra
c) Neutrale a farfalla, direzione a sinistra = veicolo che gira a sinistra IN POSIZIONE che è il motore a destra in avanti, il motore a sinistra in senso inverso

... e similmente per altre combinazioni. Naturalmente, l'output dovrebbe essere "analogico", ovvero dovrebbe consentire la transizione graduale, ad esempio dall'opzione a) a b) a c).

Il concetto è:

http://www.lynxmotion.com/images/html/build123.htm


(1) Notare che il mio algoritmo di base consente il controllo della velocità di "rotazione sul posto" quando il joystick viene premuto, ad esempio lasciato da una% del fondo scala. (2) Questo requisito deve essere stato risolto molte volte ormai. La comunità modello dovrebbe avere risposte a questo. (3) Se il ricevitore traduce i comandi in velocità di traccia utilizzando il feedback, il veicolo si comporterà all'incirca al variare delle condizioni del terreno. MA se i comandi vengono tradotti in potenza del motore o tensione di marcia ecc., Le prestazioni del veicolo varieranno in base alle condizioni del terreno. - presumibilmente 91) è preferibile.
Russell McMahon,

Russell, ho cercato su Google molto per il pescatore e ho trovato molti controller di motore pronti per l'uso per connettersi direttamente al ricevitore RC ma non ci sono molte informazioni sull'algoritmo all'interno.
Kamil Zadora,

buona giornata! renho un cugino che ha provato la paralisi infantile e la costruzione di una sedia a rotelle la loro programmazione ha funzionato bene, ma la tensione di uscita è troppo bassa! aiutami! Sto usando un Arduino Uno.

@Johnny benvenuto in Electronics.Stackexchange! Consulta le FAQ per capire come funziona questo sito e se hai una domanda da porre, utilizza il pulsante specifico nell'angolo in alto a destra della pagina.
clabacchio

Ha funzionato ???
Russell McMahon,

Risposte:


4

La miscelazione "corretta" è aperta al dibattito :-).

Un problema è che devi prendere decisioni sulla velocità con cui una traccia si muove sotto segnali puri da un singolo piatto e cosa fare quando vengono inclusi i segnali dell'altro piatto. Ad esempio, se si spinge l'FB (Forward-Backward pot completamente in avanti) e se entrambi i motori funzionano a tutta velocità, come si fa a gestire l'aggiunta di una piccola quantità di LR (Left-Right) aggiunta. per ottenere la rotazione devi avere una pista più veloce dell'altra. Quindi, se stai già correndo alla massima velocità di avanzamento su entrambi i motori, devi ridurre l'una o l'altra velocità della pista per girare. Ma se eri fermo avrebbe accelerato l'una o l'altra traccia per ottenere lo stesso risultato.

Quindi, tutto ciò che ha detto, ecco una semplice soluzione di partenza fuori dal polso dalla mia testa che sembra un buon inizio.

Se i vasi sono meccanicamente indipendenti, entrambi possono essere contemporaneamente al 100%.
Se entrambi sono su una disposizione del tipo di joystick, se Yaxis = 100% e Xaxis = 0%, l'aggiunta di qualche B di solito ridurrà A. Un joystick potrebbe essere costruito dove quanto sopra non è vero, ma questi sono insoliti.
Supponiamo che il joystick sia del tipo che aumentando Y% quando X = 100% ridurrà X. Altre ipotesi possono essere fatte.

FB = piatto fronte-retro. Centro zero, + Ve per il movimento in avanti del piatto

LR = Pentola sinistra destra. Centro zero. + Ve per pentola a destra.

Inizialmente K è un fattore di scala 1.
Se un risultato supera il 100%, regolare K in modo tale che risultato = 100% e utilizzare lo stesso valore K anche per altri motori.

  • ad es. se risultato motore sinistro = 125 e risultato motore destro = 80 allora.
    Come 125 x 0,8 = 100, impostare K = 0,8. Poi.
    Sinistra = 125 x 0,8 = 100%. Destra = 80 x 0,8 = 64%.

Poi:

  • Motore sinistro = K x (Front_Back + Left_Right)

  • Motore destro = K x (Front_Back - Left_Right)

Controlli di sanità mentale:

  • LR = 0 (centrato), FB = full fwd -> Entrambi i motori girano completamente in avanti.

  • LR = tutto a sinistra, FB = 0 -> Il
    motore sinistro gira completamente all'indietro, il
    motore destro gira completamente in avanti.
    Il veicolo ruota in senso antiorario.

  • FB era del 100%, Lr = 0%. Aggiungi il 10% di LR a destra.
    L = FB + LR = 100% - + 10% R = FB-LR = 100% - - 10%

Se l'asse più grande <100%, scala fino a = 100%.
Quindi ridimensionare gli altri assi della stessa quantità.


Grazie Russell, proverò ad implementarlo nella configurazione del mio modello. A proposito, il mio joystick è in grado di andare avanti mentre lo muovo da sinistra a destra e viceversa, è molto simile a questo: static.sparkfun.com/images/products/09032-03-L_i_ma.jpg
Kamil Zadora

1
Al momento sono incaricato di risolvere lo stesso problema sul lavoro. Ho un controller a 2 assi wii nunchuk e deve controllare 2 motori esattamente come descritto nella domanda. Ho un po 'di problemi a capire la logica qui. A cosa si riferisce esattamente k1 / K1? Uno è in minuscolo e uno in maiuscolo - sono diversi? Che cos'è + Ve?
Tal,

1
Fantastico - grazie per il chiarimento. Avevo bisogno di questo scritto in Python, quindi se ho capito bene, questo dovrebbe farlo: pastebin.com/sWDakvLp . Sembra che mi manchi qualcosa? Sembra funzionare nel mio ambiente di prova - dovrò collegarlo effettivamente ai motori finali che userò per sapere con certezza.
Tal,

1
1) La velocità del motore è controllata da PWM, che accetta solo valori da 0 a 100, motivo per cui ho usato 100 come valore massimo. 2) Uso gli addominali per scoprire se è necessario il ridimensionamento (come hai detto tu) e per ottenere scale_factor. Se finisco con un fattore di scala di 0,8 per esempio, e lo uso su un numero negativo, -125 * 0,8 = -100. La direzione è mantenuta. Penso che funzioni, a meno che non mi manchi qualcosa. Non ho ancora avuto la possibilità di provarlo sui motori finali - il mio capo costruirà una piattaforma di prova con motori collegati che potrò testare.
Tal,

1
Non ero sicuro che il mio codice avrebbe effettivamente funzionato, quindi ho impostato la scadenza del precedente link pastebin dopo una settimana. Dal momento che sembra funzionare, ecco un link più permanente con qualche altro commento se qualcuno si imbatte nuovamente in questo problema: pastebin.com/EKguJ1KP . Lo metterei in una risposta, ma a quanto pare non ho abbastanza rappresentante per pubblicare una risposta. Tutto il codice si basa sulla risposta di Russel McMahon - il merito va a lui - grazie Russel.
Tal

5

Ecco una soluzione che non richiede complicate catene if / else, non riduce la potenza quando si sposta in avanti o si ruota in posizione e consente curve e transizioni fluide dal movimento alla rotazione.

L'idea è semplice Supponiamo che i valori del joystick (x, y) siano coordinate cartesiane su un piano quadrato. Ora immagina un piano quadrato più piccolo ruotato di 45º al suo interno.

piano di esempio

Le coordinate del joystick forniscono un punto nel riquadro più grande e lo stesso punto sovrapposto nel riquadro più piccolo fornisce i valori del motore. Devi solo convertire le coordinate da un quadrato all'altro, limitando i nuovi valori (x, y) ai lati del quadrato più piccolo.

Esistono molti modi per eseguire la conversione. Il mio metodo preferito è:

  1. Converti le coordinate iniziali (x, y) in coordinate polari.
  2. Ruotali di 45 gradi.
  3. Converti le coordinate polari in cartesiane.
  4. Ridimensiona le nuove coordinate su -1,0 / + 1,0.
  5. Blocca i nuovi valori su -1,0 / + 1,0.

Ciò presuppone che le coordinate iniziali (x, y) siano comprese nell'intervallo -1,0 / + 1,0. Il lato del quadrato interno sarà sempre uguale a l * sqrt(2)/2, quindi il passaggio 4 consiste semplicemente nel moltiplicare i valori per sqrt(2).

Ecco un esempio di implementazione di Python.

import math

def steering(x, y):
    # convert to polar
    r = math.hypot(x, y)
    t = math.atan2(y, x)

    # rotate by 45 degrees
    t += math.pi / 4

    # back to cartesian
    left = r * math.cos(t)
    right = r * math.sin(t)

    # rescale the new coords
    left = left * math.sqrt(2)
    right = right * math.sqrt(2)

    # clamp to -1/+1
    left = max(-1, min(left, 1))
    right = max(-1, min(right, 1))

    return left, right

L'idea originale di questo metodo - con un metodo di trasformazione molto più complicato - è nata da questo articolo .


0

Di seguito è riportato un esempio dell'implementazione dell'algoritmo di missaggio come descritto dalla risposta di Russel McMahon:

http://www.youtube.com/watch?v=sGpgWDIVsoE

//Atmega328p based Arduino code (should work withouth modifications with Atmega168/88), tested on RBBB Arduino clone by Modern Device:
const byte joysticYA = A0; //Analog Jostick Y axis
const byte joysticXA = A1; //Analog Jostick X axis

const byte controllerFA = 10; //PWM FORWARD PIN for OSMC Controller A (left motor)
const byte controllerRA = 9;  //PWM REVERSE PIN for OSMC Controller A (left motor)
const byte controllerFB = 6;  //PWM FORWARD PIN for OSMC Controller B (right motor)
const byte controllerRB = 5;  //PWM REVERSE PIN for OSMC Controller B (right motor)
const byte disablePin = 2; //OSMC disable, pull LOW to enable motor controller

int analogTmp = 0; //temporary variable to store 
int throttle, direction = 0; //throttle (Y axis) and direction (X axis) 

int leftMotor,leftMotorScaled = 0; //left Motor helper variables
float leftMotorScale = 0;

int rightMotor,rightMotorScaled = 0; //right Motor helper variables
float rightMotorScale = 0;

float maxMotorScale = 0; //holds the mixed output scaling factor

int deadZone = 10; //jostick dead zone 

void setup()  { 

  //initialization of pins  
  Serial.begin(19200);
  pinMode(controllerFA, OUTPUT);
  pinMode(controllerRA, OUTPUT);
  pinMode(controllerFB, OUTPUT);
  pinMode(controllerRB, OUTPUT);  

  pinMode(disablePin, OUTPUT);
  digitalWrite(disablePin, LOW);
} 

void loop()  { 
  //aquire the analog input for Y  and rescale the 0..1023 range to -255..255 range
  analogTmp = analogRead(joysticYA);
  throttle = (512-analogTmp)/2;

  delayMicroseconds(100);
  //...and  the same for X axis
  analogTmp = analogRead(joysticXA);
  direction = -(512-analogTmp)/2;

  //mix throttle and direction
  leftMotor = throttle+direction;
  rightMotor = throttle-direction;

  //print the initial mix results
  Serial.print("LIN:"); Serial.print( leftMotor, DEC);
  Serial.print(", RIN:"); Serial.print( rightMotor, DEC);

  //calculate the scale of the results in comparision base 8 bit PWM resolution
  leftMotorScale =  leftMotor/255.0;
  leftMotorScale = abs(leftMotorScale);
  rightMotorScale =  rightMotor/255.0;
  rightMotorScale = abs(rightMotorScale);

  Serial.print("| LSCALE:"); Serial.print( leftMotorScale,2);
  Serial.print(", RSCALE:"); Serial.print( rightMotorScale,2);

  //choose the max scale value if it is above 1
  maxMotorScale = max(leftMotorScale,rightMotorScale);
  maxMotorScale = max(1,maxMotorScale);

  //and apply it to the mixed values
  leftMotorScaled = constrain(leftMotor/maxMotorScale,-255,255);
  rightMotorScaled = constrain(rightMotor/maxMotorScale,-255,255);

  Serial.print("| LOUT:"); Serial.print( leftMotorScaled);
  Serial.print(", ROUT:"); Serial.print( rightMotorScaled);

  Serial.print(" |");

  //apply the results to appropriate uC PWM outputs for the LEFT motor:
  if(abs(leftMotorScaled)>deadZone)
  {

    if (leftMotorScaled > 0)
    {
      Serial.print("F");
      Serial.print(abs(leftMotorScaled),DEC);

      analogWrite(controllerRA,0);
      analogWrite(controllerFA,abs(leftMotorScaled));            
    }
    else 
    {
      Serial.print("R");
      Serial.print(abs(leftMotorScaled),DEC);

      analogWrite(controllerFA,0);
      analogWrite(controllerRA,abs(leftMotorScaled));  
    }
  }  
  else 
  {
  Serial.print("IDLE");
  analogWrite(controllerFA,0);
  analogWrite(controllerRA,0);
  } 

  //apply the results to appropriate uC PWM outputs for the RIGHT motor:  
  if(abs(rightMotorScaled)>deadZone)
  {

    if (rightMotorScaled > 0)
    {
      Serial.print("F");
      Serial.print(abs(rightMotorScaled),DEC);

      analogWrite(controllerRB,0);
      analogWrite(controllerFB,abs(rightMotorScaled));            
    }
    else 
    {
      Serial.print("R");
      Serial.print(abs(rightMotorScaled),DEC);

      analogWrite(controllerFB,0);
      analogWrite(controllerRB,abs(rightMotorScaled));  
    }
  }  
  else 
  {
  Serial.print("IDLE");
  analogWrite(controllerFB,0);
  analogWrite(controllerRB,0);
  } 

  Serial.println("");

  //To do: throttle change limiting, to avoid radical changes of direction for large DC motors

  delay(10);

}

Interessante, questo codice sembra alimentare 2 pin analogici a 2 controller motore diversi. Proverò ad adattare il codice e modificarlo per le mie impostazioni. Arduino Uno + 1 scheda driver Sabertooth. 1 joystick al pin analogico A0 (x) pinA1 (y) lettura e passaggio dei valori al pin PWM 10 e 3 andando a S1 e S2 di Sabertooth. Penso di essere vicino ma mi sto confondendo su come impostare il dip-switch sulla scheda Sabertooth. Per ora sto cercando di configurare l'interruttore per ricevere l'ingresso analogico, l'interruttore 4 è ancora in posizione per l'azionamento differenziale ma lo riporterà in modalità indipendente in seguito per ulteriori test. Penso che questo orig

@utente20514 benvenuto su electronics.stackexchange! Come potresti notare, questo non è un forum ma un sito di domande e risposte, pertanto lo spazio delle risposte non è destinato alla discussione. Sii libero di porre una nuova domanda se hai qualcosa da chiedere o usa i commenti per (effettivamente) commentare domande e risposte esistenti.
clabacchio

1
@Kamil - Il video viene mostrato come privato. È ancora disponibile? youtube.com/watch?v=sGpgWDIVsoE
Russell McMahon

@RussellMcMahon riattivato :)
Kamil Zadora,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.