Estoy buscando información sobre cómo implementar una combinación adecuada de 2 señales analógicas de joystick (eje X e Y) para controlar un accionamiento de motor diferencial dual (accionamiento "similar a un 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
La posición de reposo es (dirección y acelerador neutral) 512,512
Acelerador hacia adelante/dirección izquierda es 0,0
Totalmente hacia adelante-totalmente a la derecha es 1023,0
etc.
Los motores están controlados por 2 controladores de puente H, 2 pines PWM para cada uno (adelante, atrás), así:
Motor izquierdo: -255 a 255
Motor derecho: -255 a 255
(los valores positivos habilitan el pin PWM de avance, los valores negativos habilitan el retroceso pin PWM, 0 deshabilita 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 avanzando
b) Acelerador hacia adelante, dirección izquierda = vehículo avanzando y girando a la izquierda
c) Acelerador neutral, dirección izquierda = vehículo girando a la izquierda EN SU LUGAR que es el motor derecho completamente hacia adelante, el motor izquierdo completamente hacia 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, por ejemplo, de la opción a) a b) a c).
El concepto es:
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 potenciómetro y qué hacer cuando se incluyen señales del otro potenciómetro. Por ejemplo, si empuja el potenciómetro FB (Adelante-Atrás) completamente hacia adelante, y si ambos motores funcionan 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 tener una pista yendo más rápido que la otra. Entonces, si ya está corriendo a la velocidad máxima de avance en ambos motores, debe disminuir la velocidad de una u otra pista para poder girar. Pero, si hubiera estado parado, habría acelerado una u otra pista para lograr el mismo resultado.
Entonces, dicho todo esto, aquí hay una solución inicial simple e improvisada de mi cabeza 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 agregar algo de B generalmente reducirá A. Se podría construir un joystick donde lo anterior no es cierto, pero esto es inusual.
Suponga que el joystick es del tipo que aumenta Y% cuando X = 100% reducirá X. Se pueden hacer otras suposiciones.
FB = pote delantero-trasero. Centro cero, +Ve para el movimiento hacia adelante de la olla
LR = bote izquierdo derecho. Centro cero. +Ve para bote a la derecha.
K es un factor de escala inicialmente 1.
Si algún resultado excede el 100 %, entonces ajuste K para que el resultado sea = 100 % y use el mismo valor de K para otro motor también.
Después:
Motor izquierdo = K x (Frontal_Atrás + Izquierda_Derecha)
Motor derecho = K x (Frontal_Atrás - Izquierda_Derecha)
Controles de cordura:
LR = 0 (centrado), FB = full fwd -> Ambos motores funcionan completamente hacia adelante.
LR = completamente 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 contrario a las agujas del reloj.
FB fue 100%, Lr = 0%. Agregue 10% de LR a la derecha.
L = FB+LR = 100 %- + 10 % R = FB-LR = 100 %- - 10 %
Si el eje mayor < 100 %, escalar hasta = 100 %.
Luego escala otro eje por la misma cantidad.
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 el lugar, y permite curvas suaves y transiciones de movimiento a giro.
La idea es sencilla. Suponga que los valores del joystick (x, y) son coordenadas cartesianas en un plano cuadrado. Ahora imagina un plano cuadrado más pequeño girado 45º dentro de él.
Las coordenadas del joystick te dan un punto en el cuadrado más grande, y el mismo punto superpuesto en el cuadrado más pequeño te 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:
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 simplemente 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 .
A continuación se muestra un ejemplo de implementación de 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);
}
Russel McMahon
kamil zadora
usuario22423
clabacchio
Russel McMahon
chris stratton