Algoritmo para mezclar entrada analógica de 2 ejes para controlar un motor diferencial

8

Estoy buscando información sobre cómo implementar la mezcla adecuada de 2 señales analógicas de joystick (X y eje Y) para controlar un variador de motor diferencial doble ("como un tanque") usando un uC (ATMega328p en mi caso, pero igual debería aplicarse a cualquier uC con entradas ADC y salidas PWM):

Tengo un stick analógico, que da 2 valores analógicos:

(dirección) X: 0 a 1023
(aceleración) Y: 0 a 1023

Laposicióndereposoes(direcciónyaceleraciónneutra)512,512
Elaceleradorhaciaadelante/direcciónizquierdaes0,0
Laderechacompletahaciaadelantees1023,0
etc.

Losmotoressoncontroladospor2controladoresdepuenteH,2pinesPWMparacadauno(haciaadelante,haciaatrás),así:
Motorizquierdo:-255a255
Motorderecho:-255a255
(losvalorespositivoshabilitanelpinPWMdirecto,elpinPWMinversohabilitadonegativo,0deshabilitaambos)

Elobjetivoesmezclarseñalesanalógicasdejoystickparalograrlasiguienterespuesta:

a)aceleraciónhaciaadelante,direcciónneutral=vehículoavanzando
b)Aceleradorhaciaadelante,direcciónizquierda=vehículoavanzandoygirandoalaizquierda
c)Neutrodelacelerador,direcciónizquierda=vehículogirandoalaizquierdaENELLUGARquetieneelmotorderechohaciaadelante,elmotorizquierdocompletamentehaciaatrás

...ysimilarmenteparaotrascombinaciones.Porsupuesto,lasalidadeberíaser"analógica", es decir, debería permitir una transición gradual de, por ejemplo, de la opción a) a b) a c).

El concepto es:

    
pregunta Kamil Zadora

3 respuestas

4

La mezcla "adecuada" está abierta a debate :-).

Un problema es que tiene que tomar decisiones acerca de qué tan rápido se mueve una pista bajo señales puras de un solo potenciómetro y qué hacer cuando se incluyen las señales de otro potenciómetro. Por ejemplo, si empuja el FB (potenciómetro Adelante-Atrás completamente hacia adelante, y si ambos motores se mueven a toda velocidad hacia adelante, ¿cómo lidiar con la adición de una pequeña cantidad de potenciómetro LR (Izquierda-Derecha) que se agrega? para obtener la rotación, debe haber una pista que vaya más rápido que la otra. Por lo tanto, si ya está funcionando a la velocidad máxima de avance en ambos motores, debe disminuir una u otra velocidad de la pista para girar. Pero, si hubiera estado parado, usted habría acelerado una u otra pista para lograr el mismo resultado.

Así que, dicho todo esto, aquí hay una solución de inicio sencilla y simple que parece un buen comienzo.

Si las ollas son mecánicamente independientes, ambas pueden estar al 100% simultáneamente.
 Si ambos están en una disposición de tipo joystick, si Yaxis = 100% y Xaxis = 0%, entonces la adición de algo de B generalmente reducirá A. Se podría construir un joystick donde lo anterior no es cierto, pero estos son inusuales.  Supongamos que el joystick es del tipo que al aumentar Y% cuando X = 100% reducirá X. Se pueden hacer otras suposiciones.

FB = pote delantero-trasero. Centro cero, + Ve para movimiento de olla hacia adelante

LR = olla izquierda derecha. Centro cero + Ve por la olla a la derecha.

K es un factor de escala inicialmente 1.
Si cualquier resultado excede el 100%, entonces ajuste K para que resulte = 100% y use el mismo valor K para el otro motor también.

  • por ejemplo, si el resultado del motor izquierdo = 125 y el resultado del motor derecho = 80 entonces.
    Como 125 x 0.8 = 100, establezca K = 0.8. Entonces.
    Izquierda = 125 x 0.8 = 100%. Derecha = 80 x 0.8 = 64%.

Entonces:

  • Motor izquierdo = K x (Front_Back + Left_Right)

  • Motor derecho = K x (Front_Back - Left_Right)

Controles de validez:

  • LR = 0 (centrado), FB = fwd completo - > Ambos motores funcionan a toda velocidad.

  • LR = completo a la izquierda, FB = 0 - >
    El motor izquierdo funciona completamente hacia atrás,
    El motor derecho funciona hacia adelante.
    El vehículo gira en sentido antihorario.

  • FB fue del 100%, Lr = 0%. Añadir 10% de LR a la derecha.
    L = FB + LR = 100% - + 10% R = FB-LR = 100% - - 10%

Si el eje mayor < 100%, escala hasta = 100%.
 Luego escalar otros ejes en la misma cantidad.

    
respondido por el Russell McMahon
5

Aquí hay una solución que no requiere complicarse si / else encadena, no reduce la potencia cuando se mueve completamente hacia adelante o gira en su lugar, y permite curvas suaves y transiciones de movimiento a giro.

La idea es simple. Suponga que los valores del joystick (x, y) son coordenadas cartesianas en un plano cuadrado. Ahora imagine un plano cuadrado más pequeño girado 45º dentro de él.

Lascoordenadasdeljoysticktedanunpuntoenelcuadradomásgrande,yelmismopuntosuperpuestoenelcuadradomáspequeñotedalosvaloresdelmotor.Solonecesitasconvertirlascoordenadasdeuncuadradoaotro,limitandolosnuevosvalores(x,y)alosladosdelcuadradomáspequeño.

Haymuchasformasdehacerlaconversión.Mimétodofavoritoes:

  1. Conviertalascoordenadasiniciales(x,y)encoordenadaspolares.
  2. Gíralosen45grados.
  3. Conviertalascoordenadaspolaresdenuevoacartesiano.
  4. Vuelvaaescalarlasnuevascoordenadasa-1.0/+1.0.
  5. Fijelosnuevosvaloresa-1.0/+1.0.

Estosuponequelascoordenadasiniciales(x,y)estánenelrango-1.0/+1.0.Elladodelcuadradointernosiempreseráigualal*sqrt(2)/2,porloqueelpaso4essimplementemultiplicarlosvaloresporsqrt(2).

AquíhayunejemplodeimplementacióndePython.

importmathdefsteering(x,y):#converttopolarr=math.hypot(x,y)t=math.atan2(y,x)#rotateby45degreest+=math.pi/4#backtocartesianleft=r*math.cos(t)right=r*math.sin(t)#rescalethenewcoordsleft=left*math.sqrt(2)right=right*math.sqrt(2)#clampto-1/+1left=max(-1,min(left,1))right=max(-1,min(right,1))returnleft,right

Laideaoriginaldeestemétodo,conunmétododetransformaciónmuchomáscomplicado,surgióde este artículo .

    
respondido por el Pedro Werneck
0

A continuación se muestra un ejemplo de la implementación del algoritmo de mezcla como lo describe la respuesta de Russel McMahon:

enlace

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

}
    
respondido por el Kamil Zadora

Lea otras preguntas en las etiquetas