Algorithmus zum Mischen eines 2-Achsen-Analogeingangs zur Steuerung eines Differentialmotorantriebs

9

Ich suche nach Informationen darüber, wie man das richtige Mischen von 2 analogen Joystick-Signalen (X- und Y-Achse) implementiert, um einen doppelten Differentialmotorantrieb ("tankähnlicher" Antrieb) unter Verwendung eines uC (in meinem Fall ATMega328p) zu steuern, aber dasselbe sollte für gelten beliebige uC mit ADC-Eingängen und PWM-Ausgängen):

Ich habe einen analogen Stick, der 2 analoge Werte ergibt:

(Richtung) X: 0 bis 1023
(Gas) Y: 0 bis 1023

Geben Sie hier die Bildbeschreibung ein

Die Ruheposition ist (Richtung und Gas Neutral) 512.512 Gas
vorwärts / Richtung links ist 0,0
Voll vorwärts-voll rechts ist 1023,0
usw.

Die Motoren werden von 2 H-Brückentreibern gesteuert, jeweils 2 PWM-Pins (vorwärts, rückwärts), wie
folgt : Linker Motor: -255 bis 255
Rechter Motor: -255 bis 255
(positive Werte aktivieren den Vorwärts-PWM-Pin, negative aktivieren den Rückwärtsgang PWM-Pin, 0 deaktiviert beide)

Das Ziel ist es, analoge Joystick-Signale zu mischen, um folgende Reaktion zu erzielen:

a) Gas vorwärts, Richtung Neutral = Fahrzeug fährt vorwärts
b) Gas vorwärts, Richtung links = Fahrzeug fährt vorwärts und links ab
c) Gas neutral, Richtung links = Fahrzeug links abbiegen IN PLACE, das ist der rechte Motor voll vorwärts, der linke Motor voll rückwärts

... und ähnlich für andere Kombinationen. Natürlich sollte der Ausgang "analog" sein, dh er sollte einen schrittweisen Übergang von beispielsweise von Option a) nach b) nach c) ermöglichen.

Das Konzept lautet:

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

Kamil Zadora
quelle
(1) Beachten Sie, dass mein grundlegender Algorithmus die Geschwindigkeitsregelung für das Eindrehen ermöglicht, wenn der Joystick gedrückt wird, z. B. um ein Prozent des Skalenendwerts. (2) Diese Anforderung muss inzwischen viele Male neu gelöst worden sein. Die Modellgemeinschaft sollte Antworten darauf haben. (3) Wenn der Empfänger die Befehle unter Verwendung einer Rückmeldung in Spurgeschwindigkeit umwandelt, verhält sich das Fahrzeug ungefähr so, wie sich die Bodenbedingungen ändern. ABER wenn die Befehle in Motorleistung oder Antriebsspannung usw. übersetzt werden, variiert die Fahrzeugleistung mit den Bodenbedingungen. - vermutlich 91) ist vorzuziehen.
Russell McMahon
Russell, ich habe viel nach der Antwort gegoogelt und ich habe viele einsatzbereite Motorsteuerungen gefunden, die direkt mit dem RC-Empfänger verbunden werden können, aber nicht viele Informationen über den darin enthaltenen Algorithmus.
Kamil Zadora
Schönen Tag! Renho, ein Cousin, der versucht hat, eine Kinderlähmung und einen Rollstuhl zu bauen, hat gut programmiert, aber die Ausgangsspannung ist zu niedrig! Hilf mir! Ich benutze ein Arduino Uno.
@ Johnny Willkommen bei Electronics.Stackexchange! Bitte lesen Sie die FAQ, um zu verstehen, wie diese Website funktioniert. Wenn Sie Fragen haben, verwenden Sie bitte die entsprechende Schaltfläche in der oberen rechten Ecke der Seite.
Clabacchio
Hat es funktioniert ???
Russell McMahon

Antworten:

4

"Richtiges" Mischen steht zur Debatte :-).

Ein Problem ist, dass Sie entscheiden müssen, wie schnell sich ein Track unter reinen Signalen aus einem einzelnen Pot bewegt und was zu tun ist, wenn Signale aus dem anderen Pot enthalten sind. Wenn Sie beispielsweise den FB-Topf (Vorwärts-Rückwärts-Topf vollständig vorwärts schieben und beide Motoren dann mit voller Geschwindigkeit vorausfahren), wie gehen Sie mit dem Hinzufügen einer kleinen Menge LR-Topf (links-rechts) um Um eine Rotation zu erreichen, muss eine Spur schneller als die andere sein. Wenn Sie also bereits mit beiden Motoren mit maximaler Vorwärtsgeschwindigkeit fahren, müssen Sie die eine oder andere Spurgeschwindigkeit verringern, um sich zu drehen. Aber wenn Sie still gestanden haben hätte die eine oder andere Strecke beschleunigt, um das gleiche Ergebnis zu erzielen.

Alles in allem ist hier eine einfache Startlösung von der Stange, die mir als guter Start erscheint.

Wenn die Töpfe mechanisch unabhängig sind, können beide gleichzeitig zu 100% sein.
Wenn sich beide auf einer Joystick-Anordnung befinden, wenn Yaxis = 100% und Xaxis = 0%, wird durch Hinzufügen von etwas B normalerweise A reduziert. Ein Joystick könnte konstruiert werden, wenn das oben Gesagte nicht zutrifft, aber diese sind ungewöhnlich.
Angenommen, der Joystick ist von dem Typ, dass eine Erhöhung von Y% bei X = 100% X verringert. Andere Annahmen können getroffen werden.

FB = Front-Back-Pot. Mitte Null, + Ve für Vorwärtsbewegung des Topfes

LR = linker rechter Topf. Null zentrieren. + Ve für Topf rechts.

K ist anfangs ein Skalierungsfaktor 1.
Wenn ein Ergebnis 100% überschreitet, stellen Sie K so ein, dass Ergebnis = 100% ist, und verwenden Sie denselben K-Wert auch für andere Motoren.

  • zB wenn das Ergebnis des linken Motors = 125 und das Ergebnis des rechten Motors = 80 ist.
    Als 125 x 0,8 = 100 setze K = 0,8. Dann.
    Links = 125 x 0,8 = 100%. Rechts = 80 x 0,8 = 64%.

Dann:

  • Linker Motor = K x (Front_Back + Left_Right)

  • Rechter Motor = K x (Front_Back - Left_Right)

Sanity Checks:

  • LR = 0 (zentriert), FB = voll vorwärts -> Beide Motoren laufen voll vorwärts.

  • LR = ganz links, FB = 0 ->
    Linker Motor läuft voll rückwärts,
    rechter Motor läuft voll vorwärts.
    Fahrzeug dreht sich gegen den Uhrzeigersinn.

  • FB war 100%, Lr = 0%. Fügen Sie rechts 10% LR hinzu.
    L = FB + LR = 100% - + 10% R = FB-LR = 100% - - 10%

Wenn die größte Achse <100% ist, skalieren Sie bis = 100%.
Skalieren Sie dann die andere Achse um denselben Betrag.

Russell McMahon
quelle
Vielen Dank, Russell. Ich werde versuchen, dies in meinem Modell-Setup zu implementieren. Übrigens, mein Joystick kann beim Schwenken von links nach rechts voll nach vorne und umgekehrt. Er ist dem sehr ähnlich: static.sparkfun.com/images/products/09032-03-L_i_ma.jpg
Kamil Zadora
1
Ich bin derzeit damit beauftragt, das gleiche Problem bei der Arbeit zu lösen. Ich habe einen 2-Achsen-Controller von wii nunchuk, der 2 Motoren genau wie in der Frage beschrieben steuern muss. Ich habe ein bisschen Probleme, die Logik hier zu verstehen. Worauf bezieht sich k1 / K1 genau? Einer ist Kleinbuchstaben und einer ist Großbuchstaben - sind sie unterschiedlich? Was ist + Ve?
Tal
1
Cool - danke für die Klarstellung. Ich brauchte dies in Python geschrieben. Wenn ich es richtig verstehe, sollte es dies tun: pastebin.com/sWDakvLp . Sieht es so aus, als würde mir etwas fehlen? Scheint in meiner Testumgebung zu funktionieren - ich muss es tatsächlich an die endgültigen Motoren anschließen, die ich verwenden werde, um sicher zu sein.
Tal
1
1) Die Motordrehzahl wird von PWM gesteuert, die nur Werte von 0 bis 100 annimmt, weshalb ich 100 als Maximalwert verwendet habe. 2) Ich benutze abs, um herauszufinden, ob eine Skalierung erforderlich ist (wie Sie sagten) und um den scale_factor zu erhalten. Wenn ich zum Beispiel einen Skalierungsfaktor von 0,8 habe und ihn für eine negative Zahl verwende, ist -125 * 0,8 = -100. Die Richtung wird beibehalten. Ich denke, es funktioniert, es sei denn, mir fehlt etwas. Ich hatte noch keine Gelegenheit, es an den endgültigen Motoren zu versuchen - mein Chef wird eine Testplattform mit Motoren bauen, auf denen ich testen kann.
Tal
1
Ich war mir nicht sicher, ob mein Code tatsächlich funktionieren würde, daher habe ich den vorherigen Pastebin-Link so eingestellt, dass er nach einer Woche abläuft. Da es zu funktionieren scheint, gibt es hier einen dauerhafteren Link mit ein paar weiteren Kommentaren, falls jemand erneut auf das Problem stößt: pastebin.com/EKguJ1KP . Ich würde dies in eine Antwort einfügen, aber anscheinend habe ich nicht genug Repräsentanten, um eine Antwort zu posten. Der gesamte Code basiert auf der Antwort von Russel McMahon - Dank geht an ihn - danke Russel.
Tal
5

Hier ist eine Lösung, die keine komplizierten if / else-Ketten erfordert, die Leistung beim Vorwärtsbewegen oder Drehen nicht verringert und glatte Kurven und Übergänge vom Bewegen zum Drehen ermöglicht.

Die Idee ist einfach. Angenommen, die (x, y) Joystickwerte sind kartesische Koordinaten auf einer quadratischen Ebene. Stellen Sie sich nun eine kleinere quadratische Ebene vor, die um 45 ° gedreht ist.

Beispiel Flugzeug

Die Joystick-Koordinaten geben Ihnen einen Punkt im größeren Quadrat, und der gleiche Punkt, der im kleineren Quadrat überlagert ist, gibt Ihnen die Motorwerte. Sie müssen nur die Koordinaten von einem Quadrat in das andere konvertieren und die neuen (x, y) Werte auf die Seiten des kleineren Quadrats beschränken.

Es gibt viele Möglichkeiten, die Konvertierung durchzuführen. Meine Lieblingsmethode ist:

  1. Konvertieren Sie die Anfangskoordinaten (x, y) in Polarkoordinaten.
  2. Drehen Sie sie um 45 Grad.
  3. Konvertieren Sie die Polarkoordinaten zurück in kartesisch.
  4. Skalieren Sie die neuen Koordinaten auf -1,0 / + 1,0 neu.
  5. Klemmen Sie die neuen Werte auf -1,0 / + 1,0.

Dies setzt voraus, dass die Anfangskoordinaten (x, y) im Bereich von -1,0 / + 1,0 liegen. Die Seite des inneren Quadrats ist immer gleich l * sqrt(2)/2, also geht es in Schritt 4 nur darum, die Werte mit zu multiplizieren sqrt(2).

Hier ist ein Beispiel für eine Python-Implementierung.

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

Die ursprüngliche Idee für diese Methode - mit einer viel komplizierteren Transformationsmethode - stammt aus diesem Artikel .

Pedro Werneck
quelle
0

Unten finden Sie ein Beispiel für die Implementierung eines Mischalgorithmus, wie von Russel McMahon beschrieben:

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);

}
Kamil Zadora
quelle
Interessanterweise sieht dieser Code so aus, als würde er 2 analoge Pins an 2 verschiedene Motorsteuerungen weiterleiten. Ich werde versuchen, den Code anzupassen und für meine Einstellungen zu ändern. Arduino Uno + 1 Sabertooth-Treiberplatine. 1 Joystick zum analogen PinA0 (x) PinA1 (y) liest und leitet Werte an den PWM-Pin 10 & 3 weiter, der zu S1 & S2 von Sabertooth geht. Ich glaube, ich bin nah dran, aber ich bin verwirrt darüber, wie man den Dip-Schalter auf dem Sabertooth-Board einrichtet. Im Moment versuche ich, den Schalter einzurichten, um den Analogeingang zu empfangen. Der Schalter 4 befindet sich noch in der Position für den Differentialantrieb, wird ihn aber später für weitere Tests wieder in den unabhängigen Modus versetzen. Ich denke, dass orig
@ user20514 Willkommen bei electronic.stackexchange! Wie Sie vielleicht bemerken, handelt es sich hierbei nicht um ein Forum, sondern um eine Q & A-Site. Daher ist der Raum für Antworten nicht zur Diskussion gedacht. Bitte stellen Sie eine neue Frage, wenn Sie etwas zu fragen haben, oder verwenden Sie Kommentare, um (tatsächlich) Kommentare zu vorhandenen Fragen und Antworten abzugeben.
Clabacchio
1
@Kamil - Das Video wird als privat angezeigt. Ist es noch verfügbar? youtube.com/watch?v=sGpgWDIVsoE
Russell McMahon
@ RussellMcMahon reaktiviert :)
Kamil Zadora