Algoritmo para mezclar entradas analógicas de 2 ejes para controlar un motor diferencial

9

Estoy buscando información sobre cómo implementar una mezcla adecuada de 2 señales analógicas de joystick (eje X e Y) para controlar un motor de doble diferencial (unidad tipo "tanque") usando un uC (ATMega328p en mi caso, pero lo mismo 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
(acelerador) Y: 0 a 1023

ingrese la descripción de la imagen aquí

La posición de reposo es (dirección y acelerador neutral) 512,512
Acelerador adelante / dirección izquierda es 0,0
Completo adelante-completo derecho es 1023,0
etc.

Los motores están controlados por 2 controladores de puente H, 2 pines PWM para cada uno (hacia adelante, hacia atrás), así:
Motor izquierdo: -255 a 255
Motor derecho: -255 a 255
(los valores positivos permiten el pin PWM hacia adelante, el negativo hacia atrás Pin PWM, 0 desactiva ambos)

El objetivo es mezclar señales analógicas de joystick para lograr la siguiente respuesta:

a) Acelerador hacia adelante, dirección neutral = vehículo moviéndose hacia adelante
b) Acelerador hacia adelante, dirección izquierda = vehículo moviéndose hacia adelante y girando hacia la izquierda
c) Acelerador neutral, dirección hacia la izquierda = vehículo girando hacia la izquierda EN LUGAR que es el motor derecho completamente adelante, el motor izquierdo completamente atrás

... y de manera similar para otras combinaciones. Por supuesto, la salida debe ser "analógica", es decir, debe permitir una transición gradual de, por ejemplo, de la opción a) a b) a c).

El concepto es:

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

Kamil Zadora
fuente
(1) Tenga en cuenta que mi algoritmo básico permite el control de la velocidad de "giro en el lugar" cuando se presiona el joystick, por ejemplo, se deja un% de la escala completa. (2) Este requisito debe haberse resuelto muchas veces por ahora. La comunidad modelo debería tener respuestas a esto. (3) Si el receptor traduce los comandos a la velocidad de la pista usando la retroalimentación, el vehículo se comportará de la misma manera a medida que cambien las condiciones del terreno. PERO si los comandos se traducen en potencia del motor o voltaje de conducción, etc., el rendimiento del vehículo variará con las condiciones del suelo. - presumiblemente 91) es preferible.
Russell McMahon
Russell, busqué mucho en Google para encontrar la respuesta y encontré muchos controladores de motor listos para usar para conectar directamente al receptor RC, pero no mucha información sobre el algoritmo en su interior.
Kamil Zadora
¡buen día! Renho, un primo que ha estado tratando con parálisis infantil y la construcción de una silla de ruedas, su programación funcionó bien, ¡pero el voltaje de salida es demasiado bajo! ¡ayuadame! Estoy usando un Arduino Uno.
@Johnny bienvenido a Electronics.Stackexchange! Consulte las preguntas frecuentes para comprender cómo funciona este sitio, y si tiene alguna pregunta que hacer, utilice el botón específico en la esquina superior derecha de la página.
clabacchio
Funcionó ???
Russell McMahon

Respuestas:

4

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

Un problema es que tienes que tomar decisiones sobre qué tan rápido se mueve una pista bajo señales puras de un solo bote y qué hacer cuando se incluyen las señales del otro bote. Por ejemplo, si empuja el potenciómetro FB (Forward-Backward pot) completamente hacia adelante, y si ambos motores funcionan a toda velocidad hacia adelante, ¿cómo lidia con la adición de una pequeña cantidad de potenciómetro LR (izquierda-derecha) que se agrega. para obtener la rotación, debe tener una pista que vaya más rápido que la otra. Por lo tanto, si ya está corriendo a la máxima velocidad de avance en ambos motores, debe disminuir una u otra velocidad de la pista para poder girar. Pero, si hubiera estado parado, usted habría acelerado una u otra pista para lograr el mismo resultado.

Entonces, todo lo dicho, aquí hay una solución de partida simple y descabellada que parece un buen comienzo.

Si las macetas 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 agregar algo de B generalmente reducirá A. Se podría construir un joystick donde lo anterior no es cierto, pero estos son inusuales.
Suponga que el joystick es del tipo que aumenta Y% cuando X = 100% reducirá X. Se pueden hacer otras suposiciones.

FB = olla frontal-posterior. Centro cero, + Ve para el movimiento hacia adelante de la olla

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

K es un factor de escala inicialmente 1.
Si algún resultado excede el 100%, ajuste K para que el resultado = 100% y use el mismo valor K para 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 (Frente_Atrás - Izquierda_Derecha)

Controles de cordura:

  • LR = 0 (centrado), FB = full fwd -> Ambos motores funcionan completamente hacia adelante.

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

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

Si el eje más grande <100%, escala hasta = 100%.
Luego escale otro eje en la misma cantidad.

Russell McMahon
fuente
Gracias Russell. Intentaré implementar esto en la configuración de mi modelo. Por cierto, mi joystick puede mantenerse hacia adelante mientras lo desplaza de izquierda a derecha y al revés, es muy similar a esto: static.sparkfun.com/images/products/09032-03-L_i_ma.jpg
Kamil Zadora
1
Actualmente tengo la tarea de resolver el mismo problema en el trabajo. Tengo un controlador wii nunchuk de 2 ejes, y necesita controlar 2 motores exactamente como se describe en la pregunta. Tengo algunos problemas para entender la lógica aquí. ¿A qué se refiere exactamente k1 / K1? Una es minúscula y la otra mayúscula: ¿son diferentes? ¿Qué es + Ve?
Tal
1
Genial, gracias por la aclaración. Necesitaba esto escrito en Python, así que si lo entiendo correctamente, debería hacerlo: pastebin.com/sWDakvLp . ¿Parece que me falta algo? Parece funcionar en mi entorno de prueba. Necesitaré conectarlo realmente a los motores finales que usaré para estar seguro.
Tal
1
1) La velocidad del motor es controlada por PWM, que solo toma valores de 0 a 100, por eso utilicé 100 como valor máximo. 2) Utilizo abs para encontrar si es necesario escalar (como dijiste) y para obtener el scale_factor. Si termino con un factor de escala de 0.8 por ejemplo, y lo uso en un número negativo, -125 * 0.8 = -100. La dirección se mantiene. Creo que funciona, a menos que me falte algo. Todavía no he tenido la oportunidad de probarlo en los motores finales: mi jefe construirá una plataforma de prueba con motores conectados que podré probar.
Tal
1
No estaba seguro de si mi código realmente funcionaría, así que configuré el enlace anterior de Pastebin para que caduque después de una semana. Como parece funcionar, aquí hay un enlace más permanente con algunos comentarios más si alguien vuelve a encontrar el problema: pastebin.com/EKguJ1KP . Pondría esto en una respuesta, pero aparentemente no tengo suficiente representante para publicar una respuesta. Todo el código se basa en la respuesta de Russel McMahon: el crédito va para él, gracias Russel.
Tal
5

Aquí hay una solución que no requiere complicadas cadenas if / else, 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.

plano de ejemplo

Las coordenadas del joystick le dan un punto en el cuadrado más grande, y el mismo punto superpuesto en el cuadrado más pequeño le da los valores del motor. Solo necesita convertir las coordenadas de un cuadrado a otro, limitando los nuevos valores (x, y) a los lados del cuadrado más pequeño.

Hay muchas formas de hacer la conversión. Mi método favorito es:

  1. Convierta las coordenadas iniciales (x, y) en coordenadas polares.
  2. Rotarlos en 45 grados.
  3. Convierta las coordenadas polares de nuevo a cartesiano.
  4. Cambie la escala de las nuevas coordenadas a -1.0 / + 1.0.
  5. Sujete los nuevos valores a -1.0 / + 1.0.

Esto supone que las coordenadas iniciales (x, y) están en el rango -1.0 / + 1.0. El lado del cuadrado interior siempre será igual a l * sqrt(2)/2, por lo que el paso 4 consiste en multiplicar los valores por sqrt(2).

Aquí hay un ejemplo de implementación de 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

La idea original de este método, con un método de transformación mucho más complicado, surgió de este artículo .

Pedro Werneck
fuente
0

A continuación se muestra un ejemplo de implementación del algoritmo de mezcla como se describe en la respuesta 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
fuente
Interesante, parece que este código está alimentando 2 pines analógicos a 2 controladores de motor diferentes. Intentaré adaptar el código y modificar mi configuración. Placa de conductor Arduino Uno + 1 Sabertooth. 1 joystick al pin analógico A0 (x) pinA1 (y) leyendo y pasando valores al pin PWM 10 y 3 yendo a S1 y S2 de Sabertooth. Creo que estoy cerca, pero me estoy confundiendo sobre cómo configurar el interruptor DIP en la placa Sabertooth. Por ahora, estoy tratando con la configuración del interruptor para recibir la entrada analógica, el interruptor 4 todavía está en posición para el accionamiento diferencial, pero lo volverá a poner en modo independiente más adelante para realizar más pruebas. Creo que este orig
@ user20514 bienvenido a electronics.stackexchange! Como puede observar, este no es un foro sino un sitio de preguntas y respuestas, por lo tanto, el espacio de respuestas no está destinado a discusión. Sea libre de hacer una nueva pregunta si tiene algo que hacer, o use los comentarios para (de hecho) comentar sobre las preguntas y respuestas existentes.
clabacchio
1
@Kamil: el video se muestra como privado. ¿Todavía está disponible? youtube.com/watch?v=sGpgWDIVsoE
Russell McMahon
@RussellMcMahon reactivado :)
Kamil Zadora