Algorithme pour mélanger une entrée analogique 2 axes pour contrôler un entraînement de moteur différentiel

9

Je recherche des informations sur la façon de mettre en œuvre un mélange correct de 2 signaux analogiques de joystick (axe X et Y) pour contrôler un entraînement à moteur différentiel double (entraînement "comme un réservoir") en utilisant un uC (ATMega328p dans mon cas, mais la même chose devrait s'appliquer à tout uC avec entrées ADC et sorties PWM):

J'ai un stick analogique, qui donne 2 valeurs analogiques:

(direction) X: 0 à 1023
(accélérateur) Y: 0 à 1023

entrez la description de l'image ici

La position de repos est (direction et position neutre des gaz) 512.512 La commande des gaz
vers l'avant / la direction vers la gauche est 0,0, la direction
avant vers la droite est 1023,0,
etc.

Les moteurs sont contrôlés par 2 pilotes de pont en H, 2 broches PWM pour chacun (avant, arrière), comme ceci:
Moteur gauche: -255 à 255
Moteur droit: -255 à 255
(les valeurs positives activent la broche PWM avant, les négatives permettent l'inverse) Broche PWM, 0 désactive les deux)

L'objectif est de mélanger les signaux analogiques du joystick pour obtenir la réponse suivante:

a) Accélérateur vers l'avant, direction neutre = véhicule en marche avant
b) Accélérateur vers l'avant, direction gauche = véhicule vers l'avant et tourner à gauche
c) Accélérateur neutre, direction vers la gauche = véhicule tournant à gauche EN PLACE qui est le moteur droit complètement en avant, moteur gauche complètement en arrière

... et de même pour les autres combinaisons. Bien entendu, la sortie doit être "analogique", c'est-à-dire qu'elle doit permettre une transition progressive, par exemple de l'option a) à b) à c).

Le concept est:

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

Kamil Zadora
la source
(1) Notez que mon algorithme de base permet de contrôler la vitesse de «tourner sur place» lorsque le joystick est poussé, par exemple laissé d'un% de la pleine échelle. (2) Cette exigence doit avoir été résolue de nombreuses fois à ce jour. La communauté modèle devrait avoir des réponses à cela. (3) Si le récepteur traduit les commandes en vitesse de voie à l'aide de la rétroaction, le véhicule se comportera de la même manière que les conditions du sol changent. MAIS si les commandes sont traduites en puissance du moteur ou en tension d'entraînement, etc., les performances du véhicule varient en fonction des conditions du sol. - vraisemblablement 91) est préférable.
Russell McMahon,
Russell, j'ai beaucoup cherché sur Google pour la réponse et j'ai trouvé beaucoup de contrôleurs de moteur prêts à l'emploi pour se connecter directement au récepteur RC, mais pas beaucoup d'informations sur l'algorithme à l'intérieur.
Kamil Zadora
bonne journée! renho un cousin qui a essayé la paralysie infantile et la construction d'un fauteuil roulant leur programmation a bien fonctionné, mais la tension de sortie est trop faible! Aidez moi! J'utilise un arduino uno.
@Johnny bienvenue sur Electronics.Stackexchange! Veuillez consulter la FAQ pour comprendre le fonctionnement de ce site, et si vous avez une question à poser, veuillez utiliser le bouton spécifique dans le coin supérieur droit de la page.
clabacchio
Cela at-il fonctionné ???
Russell McMahon

Réponses:

4

Un mixage "correct" est sujet à débat :-).

Un problème est que vous devez prendre des décisions sur la vitesse à laquelle une piste se déplace sous les signaux purs d'un seul pot et quoi faire lorsque les signaux de l'autre pot sont inclus. Par exemple, si vous poussez complètement le pot FB (avant-arrière) et si les deux moteurs tournent ensuite à pleine vitesse vers l'avant, comment gérez-vous l'ajout d'une petite quantité de pot LR (gauche-droite). pour obtenir la rotation, vous devez avoir une piste qui va plus vite que l'autre. Donc, si vous roulez déjà à la vitesse de marche avant maximale sur les deux moteurs, vous devez diminuer l'une ou l'autre vitesse de la piste pour tourner. Mais, si vous vous étiez arrêté, aurait accéléré l'une ou l'autre piste pour obtenir le même résultat.

Donc, tout cela dit, voici une solution de départ simple et spontanée qui me semble être un bon début.

Si les pots sont mécaniquement indépendants, les deux peuvent être à 100% simultanément.
Si les deux sont sur un arrangement de type joystick, si Yaxis = 100% et Xaxis = 0%, alors l'ajout de B réduira généralement A. Un joystick pourrait être construit là où ce qui précède n'est pas vrai, mais ceux-ci sont inhabituels.
Supposons que le joystick est du type que l'augmentation de Y% lorsque X = 100% réduira X. D'autres hypothèses peuvent être faites.

FB = pot avant-arrière. Centre zéro, + Ve pour le mouvement vers l'avant du pot

LR = pot gauche droite. Centre zéro. + Ve pour pot à droite.

K est un facteur d'échelle initialement 1.
Si un résultat dépasse 100%, ajustez donc K résultat = 100% et utilisez également la même valeur K pour l'autre moteur.

  • Par exemple, si le résultat du moteur gauche = 125 et le résultat du moteur droit = ​​80 alors.
    Comme 125 x 0,8 = 100, définissez K = 0,8. Alors.
    Gauche = 125 x 0,8 = 100%. Droite = 80 x 0,8 = 64%.

Alors:

  • Moteur gauche = K x (Front_Back + Left_Right)

  • Moteur droit = ​​K x (Front_Back - Left_Right)

Contrôles de santé mentale:

  • LR = 0 (centré), FB = plein avant -> Les deux moteurs tournent à fond vers l'avant.

  • LR = complètement à gauche, FB = 0 ->
    Le moteur gauche tourne complètement en arrière, le
    moteur droit tourne complètement en avant.
    Le véhicule tourne dans le sens antihoraire.

  • FB était de 100%, Lr = 0%. Ajoutez 10% de LR à droite.
    L = FB + LR = 100% - + 10% R = FB-LR = 100% - - 10%

Si le plus grand axe <100%, l'échelle jusqu'à = 100%.
Mettez ensuite à l'échelle l'autre axe de la même valeur.

Russell McMahon
la source
Merci Russell - je vais essayer de l'implémenter sur ma configuration de modèle. BTW, mon joystick est capable de rester en avant tout en le déplaçant de gauche à droite et inversement
Kamil Zadora
1
Je suis actuellement chargé de résoudre le même problème au travail. J'ai un contrôleur 2 axes Wii Nunchuk, et il doit contrôler 2 moteurs exactement comme décrit dans la question. J'ai un peu de mal à comprendre la logique ici. À quoi se réfère exactement k1 / K1? L'un est en minuscules et l'autre en majuscules - sont-ils différents? Qu'est-ce que + Ve?
Tal
1
Cool - merci pour la clarification. J'avais besoin de cela écrit en Python, donc si je comprends bien, cela devrait le faire: pastebin.com/sWDakvLp . On dirait que je manque quelque chose? Semble fonctionner dans mon environnement de test - je devrai le connecter aux moteurs finaux que j'utiliserai pour être sûr.
Tal
1
1) La vitesse du moteur est contrôlée par PWM, qui ne prend que des valeurs de 0 à 100, c'est pourquoi j'ai utilisé 100 comme valeur maximale. 2) J'utilise abs pour trouver si une mise à l'échelle est nécessaire (comme vous l'avez dit) et pour obtenir le scale_factor. Si je me retrouve avec un facteur d'échelle de 0,8 par exemple, et que je l'utilise sur un nombre négatif, -125 * 0,8 = -100. La direction est maintenue. Je pense que cela fonctionne, sauf si je manque quelque chose. Je n'ai toujours pas eu l'occasion de l'essayer sur les moteurs finaux - mon patron va construire une plate-forme de test avec des moteurs connectés que je pourrai tester.
Tal
1
Je ne savais pas si mon code fonctionnerait réellement, j'ai donc défini le lien précédent de la boîte à outils pour expirer après une semaine. Puisqu'il semble fonctionner, voici un lien plus permanent avec quelques commentaires supplémentaires si quelqu'un rencontre à nouveau le problème: pastebin.com/EKguJ1KP . Je mettrais cela dans une réponse, mais apparemment je n'ai pas assez de représentant pour poster une réponse. Tout le code est basé sur la réponse de Russel McMahon - le mérite lui revient - merci Russel.
Tal
5

Voici une solution qui ne nécessite pas de chaînes if / else compliquées, ne réduit pas la puissance lorsque vous vous déplacez complètement vers l'avant ou la rotation en place, et permet des courbes et des transitions fluides du mouvement à la rotation.

L'idée est simple. Supposons que les valeurs du joystick (x, y) sont des coordonnées cartésiennes sur un plan carré. Imaginez maintenant un plan carré plus petit tourné à 45 ° à l'intérieur.

exemple d'avion

Les coordonnées du joystick vous donnent un point dans le grand carré, et le même point superposé dans le petit carré vous donne les valeurs du moteur. Il vous suffit de convertir les coordonnées d'un carré à l'autre, en limitant les nouvelles valeurs (x, y) aux côtés du petit carré.

Il existe de nombreuses façons d'effectuer la conversion. Ma méthode préférée est:

  1. Convertissez les coordonnées initiales (x, y) en coordonnées polaires.
  2. Faites-les pivoter de 45 degrés.
  3. Convertissez les coordonnées polaires en cartésien.
  4. Redimensionnez les nouvelles coordonnées à -1,0 / + 1,0.
  5. Fixez les nouvelles valeurs à -1,0 / + 1,0.

Cela suppose que les coordonnées initiales (x, y) sont dans la plage -1,0 / + 1,0. Le côté du carré intérieur sera toujours égal à l * sqrt(2)/2, donc l'étape 4 consiste à multiplier les valeurs par sqrt(2).

Voici un exemple d'implémentation 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'idée originale de cette méthode - avec une méthode de transformation beaucoup plus compliquée - est venue de cet article .

Pedro Werneck
la source
0

Voici un exemple de mélange de la mise en œuvre d'un algorithme tel que décrit par la réponse de 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);

}
Kamil Zadora
la source
Intéressant, ce code semble alimenter 2 broches analogiques vers 2 contrôleurs de moteur différents. J'essaierai d'adapter le code et de le modifier pour mes paramètres. Carte pilote Arduino Uno + 1 Sabertooth. 1 joystick vers la broche analogique A0 (x) la broche A1 (y) lisant et transmettant les valeurs à la broche PWM 10 et 3 allant à S1 et S2 de Sabertooth. Je pense que je suis proche, mais je suis confus sur la façon de configurer le commutateur Dips sur la carte Sabertooth. Pour l'instant, je teste la configuration du commutateur pour recevoir une entrée analogique, le commutateur 4 est toujours en position pour le variateur mais le remettra en mode indépendant plus tard pour de nouveaux tests. Je pense que cette origine
@ user20514 bienvenue sur electronics.stackexchange! Comme vous le remarquerez peut-être, ce n'est pas un forum mais un site de questions / réponses, donc l'espace des réponses n'est pas destiné à la discussion. N'hésitez pas à poser une nouvelle question si vous avez quelque chose à demander, ou utilisez des commentaires pour (en effet) commenter les questions et réponses existantes.
clabacchio
1
@Kamil - La vidéo est présentée comme privée. Est-il toujours disponible? youtube.com/watch?v=sGpgWDIVsoE
Russell McMahon
@RussellMcMahon réactivé :)
Kamil Zadora