Algorytm mieszania 2-osiowego wejścia analogowego w celu sterowania różnicowym napędem silnikowym


9

Szukam informacji o tym, jak zaimplementować prawidłowe miksowanie 2 analogowych sygnałów joysticka (oś X i Y) do sterowania podwójnym napędem różnicowym (napęd „zbiornikowy”) za pomocą sterownika uC (w moim przypadku ATMega328p), ale to samo powinno dotyczyć dowolny uC z wejściami ADC i wyjściami PWM):

Mam analogowy drążek, który daje 2 wartości analogowe:

(kierunek) X: od 0 do 1023
(przepustnica) Y: od 0 do 1023

wprowadź opis zdjęcia tutaj

Pozycja spoczynkowa to (neutralny kierunek i przepustnica) 512,512
Przepustnica do przodu / kierunek w lewo to 0,0
Całkowite do przodu-pełne prawo to 1023,0
itp.

Silniki są sterowane przez 2 sterowniki mostka H, ​​dla każdego z 2 pinów PWM (do przodu, do tyłu), tak:
Lewy silnik: od -255 do 255
Prawy silnik: od -255 do 255
(wartości dodatnie włączają pin PWM do przodu, ujemny włącza wsteczny Pin PWM, 0 wyłącza oba)

Celem jest zmiksowanie sygnałów analogowych joysticka w celu uzyskania następującej odpowiedzi:

a) Przepustnica do przodu, kierunek neutralny = pojazd poruszający się do przodu
b) Przepustnica do przodu, kierunek lewy = pojazd poruszający się do przodu i skręca w lewo
c) Przepustnica neutralna, kierunek lewy = pojazd skręca w lewo W MIEJSCU, który jest prawym silnikiem całkowicie do przodu, lewy silnik całkowicie do tyłu

... i podobnie w przypadku innych kombinacji. Oczywiście wyjście powinno być „analogowe”, co oznacza, że ​​powinno umożliwiać stopniowe przejście z na przykład z opcji a) do b) do c).

Koncepcja jest następująca:

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


(1) Zauważ, że mój podstawowy algorytm pozwala kontrolować prędkość „skrętu w miejscu”, gdy joystick jest popychany np. W lewo o% pełnej skali. (2) Wymaganie to musiało być do tej pory rozwiązane wiele razy. Społeczność modelowa powinna mieć na to odpowiedzi. (3) Jeżeli odbiornik przekształci polecenia w prędkość toru za pomocą sprzężenia zwrotnego, pojazd będzie zachowywał się mniej więcej tak samo, jak zmiana warunków gruntowych. ALE jeśli polecenia zostaną przetłumaczone na moc silnika lub napięcie napędu itp., Osiągi pojazdu będą się różnić w zależności od warunków gruntowych. - przypuszczalnie 91) jest korzystniejszy.
Russell McMahon,

Russell, dużo szukałem anwsera i znalazłem wiele gotowych do użycia sterowników silników do bezpośredniego podłączenia do odbiornika RC, ale niewiele informacji o algorytmie w środku.
Kamil Zadora

dobry dzień! renho, kuzyn, który próbował paraliżu dziecięcego i konstrukcji wózka inwalidzkiego, ich programowanie działało dobrze, ale napięcie wyjściowe jest zbyt niskie! Pomóż mi! Używam arduino uno.

@Johnny witamy w Electronics.Stackexchange! Zapoznaj się z często zadawanymi pytaniami, aby zrozumieć, jak działa ta strona, a jeśli masz pytanie, użyj konkretnego przycisku w prawym górnym rogu strony.
clabacchio

Zadziałało ???
Russell McMahon,

Odpowiedzi:


4

„Właściwe” miksowanie jest otwarte na debatę :-).

Problem polega na tym, że musisz podejmować decyzje o tym, jak szybko ścieżka porusza się pod czystymi sygnałami z jednej puli i co zrobić, gdy uwzględniane są sygnały z drugiej puli. Na przykład, jeśli popchniesz FB (potencjometr do przodu i do tyłu całkowicie do przodu, a jeśli oba silniki pracują na pełnych obrotach do przodu, jak sobie poradzisz z dodaniem niewielkiej ilości puli LR (lewy prawy). uzyskać obrót, musisz mieć jeden tor poruszający się szybciej niż drugi. Jeśli więc biegniesz już z maksymalną prędkością do przodu na obu silnikach, musisz zmniejszyć jedną lub drugą prędkość toru, aby skręcić. Ale jeśli stałeś nieruchomo, przyspieszyłby jeden lub drugi utwór, aby osiągnąć ten sam wynik.

Tak więc wszystko, co powiedziałem, oto proste rozwiązanie wyjściowe z mojej głowy, które wydaje się dobrym początkiem.

Jeśli doniczki są mechanicznie niezależne, oba mogą być na 100% jednocześnie.
Jeśli oba są ustawione w układzie joysticka, jeśli Yaxis = 100% i Xaxis = 0%, wówczas dodanie części B zwykle zmniejsza A. A Joystick można skonstruować tam, gdzie powyższe nie jest prawdą, ale są one niezwykłe.
Załóżmy, że joystick jest tego typu, że zwiększenie Y%, gdy X = 100% zmniejszy X. Można przyjąć inne założenia.

FB = pula z przodu i z tyłu. Centrum zero, + Ve dla ruchu doniczki do przodu

LR = lewa prawa pula. Centrum zero. + Ve za pulę po prawej stronie.

K jest początkowo współczynnikiem skali 1.
Jeśli jakikolwiek wynik przekracza 100%, dostosuj K, więc wynik = 100% i użyj tej samej wartości K również dla innego silnika.

  • np. jeśli wynik dla lewego silnika = 125, a wynik dla prawego silnika = 80, to.
    Jako 125 x 0,8 = 100, ustaw K = 0,8. Następnie.
    Lewy = 125 x 0,8 = 100%. Racja = 80 x 0,8 = 64%.

Następnie:

  • Lewy silnik = K x (Front_Back + Left_Right)

  • Prawy silnik = K x (Front_Back - Left_Right)

Kontrole poczytalności:

  • LR = 0 (wyśrodkowany), FB = pełny fwd -> Oba silniki pracują całkowicie do przodu.

  • LR = całkowicie lewy, FB = 0 ->
    Lewy silnik obraca się całkowicie do tyłu,
    prawy silnik obraca się całkowicie do przodu.
    Pojazd obraca się przeciwnie do ruchu wskazówek zegara.

  • FB wynosiła 100%, Lr = 0%. Dodaj 10% LR po prawej stronie.
    L = FB + LR = 100% - + 10% R = FB-LR = 100% - - 10%

Jeśli największa oś <100%, skaluj do = 100%.
Następnie przeskaluj drugą oś o tę samą wartość.


Dziękuję Russell - spróbuję zaimplementować to w konfiguracji mojego modelu. BTW, Mój joystick jest w stanie poruszać się do przodu podczas przesuwania go od lewej do prawej, a na odwrót, jest bardzo podobny do tego: static.sparkfun.com/images/products/09032-03-L_i_ma.jpg
Kamil Zadora

1
Obecnie mam za zadanie rozwiązać ten sam problem w pracy. Mam 2-osiowy kontroler wii nunchuk i musi on kontrolować 2 silniki dokładnie tak, jak opisano w pytaniu. Mam trochę problemów ze zrozumieniem logiki tutaj. Do czego dokładnie odnosi się k1 / K1? Jeden to małe litery, a drugi to wielkie litery - czy są różne? Co to jest + Ve?
Tal

1
Fajnie - dziękuję za wyjaśnienie. Potrzebowałem tego napisanego w Pythonie, więc jeśli dobrze rozumiem, powinien to zrobić: pastebin.com/sWDakvLp . Czy wygląda na to, że czegoś mi brakuje? Wygląda na to, że działa w moim środowisku testowym - muszę go podłączyć do ostatecznych silników, których będę używać, aby się upewnić.
Tal

1
1) Prędkość silnika jest kontrolowana przez PWM, który przyjmuje tylko wartości od 0 do 100, dlatego użyłem 100 jako wartości maksymalnej. 2) Używam abs, aby sprawdzić, czy skalowanie jest potrzebne (jak powiedziałeś) i uzyskać współczynnik_skalowania. Jeśli na przykład otrzymam współczynnik skali 0,8 i użyję go jako liczby ujemnej, -125 * 0,8 = -100. Kierunek jest zachowany. Myślę, że to działa, chyba że czegoś mi brakuje. Nadal nie miałem okazji wypróbować go na końcowych silnikach - mój szef zbuduje platformę testową z dołączonymi silnikami, na których będę mógł przetestować.
Tal

1
Nie byłem pewien, czy mój kod rzeczywiście zadziała, więc ustawiłem, aby poprzedni link do pastebin wygasł po tygodniu. Ponieważ wydaje się, że działa, oto bardziej trwały link z kilkoma dodatkowymi komentarzami, jeśli ktoś ponownie napotka problem: pastebin.com/EKguJ1KP . Umieściłbym to w odpowiedzi, ale najwyraźniej nie mam wystarczającej liczby przedstawicieli, aby opublikować odpowiedź. Cały kod oparty jest na odpowiedzi Russela McMahona - podziękowania należą się mu - dzięki Russel.
Tal.

5

Oto rozwiązanie, które nie wymaga skomplikowanych łańcuchów if / else, nie zmniejsza mocy podczas pełnego ruchu do przodu lub obracania się w miejscu i pozwala na płynne zakrzywienie i przejście od przejścia do wirowania.

Pomysł jest prosty. Załóżmy, że wartości (x, y) joysticka są współrzędnymi kartezjańskimi na płaszczyźnie kwadratowej. Teraz wyobraź sobie mniejszą kwadratową płaszczyznę obróconą w niej o 45º.

przykładowy samolot

Współrzędne joysticka dają punkt na większym kwadracie, a ten sam punkt nałożony na mniejszy kwadrat daje wartości silnika. Trzeba tylko przekonwertować współrzędne z jednego kwadratu na drugi, ograniczając nowe wartości (x, y) do boków mniejszego kwadratu.

Istnieje wiele sposobów konwersji. Moja ulubiona metoda to:

  1. Konwertuj początkowe współrzędne (x, y) na współrzędne biegunowe.
  2. Obróć je o 45 stopni.
  3. Przekształć współrzędne biegunowe z powrotem na kartezjańskie.
  4. Przeskaluj nowe współrzędne do -1,0 / + 1,0.
  5. Zablokuj nowe wartości na -1.0 / + 1.0.

Zakłada się, że początkowe współrzędne (x, y) mieszczą się w zakresie -1.0 / + 1.0. Bok wewnętrznego kwadratu zawsze będzie równy l * sqrt(2)/2, więc krok 4 polega na pomnożeniu wartości sqrt(2).

Oto przykładowa implementacja języka 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

Oryginalny pomysł na tę metodę - o znacznie bardziej skomplikowanej metodzie transformacji - pochodzi z tego artykułu .


0

Poniżej znajduje się przykład implementacji algorytmu mieszania opisany przez odpowiedź Russela McMahona:

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

}

Co ciekawe, ten kod wygląda, jakby zasilał 2 styki analogowe do 2 różnych sterowników silnika. Spróbuję dostosować kod i zmodyfikować moje ustawienia. Arduino Uno + 1 karta sterownika Sabertooth. 1 joystick do analogowego pinA0 (x) pinA1 (y) odczytuje i przekazuje wartości do PWM pin 10 i 3 przechodząc do S1 i S2 Sabertooth. Chyba jestem blisko, ale zaczynam się mylić, jak ustawić przełącznik DIP na planszy Sabertooth. Na razie walczę z konfiguracją przełącznika, aby odbierać wejście analogowe, przełącznik 4 jest nadal w pozycji dla napędu różnicowego, ale przywróci go później w tryb niezależny do dalszych testów. Myślę, że ten orig

@ user20514 witamy na stronie electronics.stackexchange! Jak można zauważyć, nie jest to forum, ale strona z pytaniami i odpowiedziami, dlatego przestrzeń odpowiedzi nie jest przeznaczona do dyskusji. Jeśli masz coś do zadania, możesz zadać nowe pytanie lub użyć komentarzy, aby (rzeczywiście) skomentować istniejące pytania i odpowiedzi.
clabacchio

1
@Kamil - Film jest wyświetlany jako prywatny. Czy nadal jest dostępny? youtube.com/watch?v=sGpgWDIVsoE
Russell McMahon

@RussellMcMahon reaktywowany :)
Kamil Zadora
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.