混合2轴模拟输入以控制差动电动机驱动的算法


9

我正在寻找有关如何使用uC(在我的情况下为ATMega328p)正确混合2个模拟操纵杆信号(X轴和Y轴)以控制双差动电机驱动器(“类似油箱的”驱动器)的信息,但是同样适用于任何具有ADC输入和PWM输出的uC):

我有一个模拟摇杆,可提供2个模拟值:

(方向)X:0至1023
(油门)Y:0至1023

在此处输入图片说明

静止位置是(方向和节气门中立)512,512
节气门前进/方向左为0,0
全前进-全右为1023,0,依此类推

电机由2个H桥驱动器控制,每个都有2个PWM引脚(正向,反向)控制,例如:
左电机:-255至255
右电机:-255至255
(正值启用正向PWM引脚,负启用反向PWM引脚,0均禁用)

目的是将操纵杆模拟信号混合以达到以下响应:

a)节气门前进,空档方向=车辆向前行驶
b)节气门前进,左方向=车辆向前行驶并向左转弯
c)油门空档,左方向=车辆向左转弯

...以及类似的其他组合。当然,输出应为“模拟”,即应允许从例如选项a)到b)到c)的逐步过渡。

这个概念是:

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


(1)请注意,我的基本算法允许在按下操纵杆(例如,以满刻度的%左移)的情况下控制“原位转弯”的速度。(2)到现在为止,必须多次解决此要求。模型社区应该对此有答案。(3)如果接收器使用反馈将命令转换为跟踪速度,则车辆的行为与地面条件的变化大致相同。但是,如果将命令转换为电动机功率或驱动电压等,则车辆性能将随地面条件而变化。-最好是91)。
罗素·麦克马洪

罗素(Russell),我在google上搜索了很多有关anwser的信息,发现已经准备好很多电机控制器直接连接到RC接收器,但是里面没有太多关于算法的信息。
卡米尔·扎多拉

美好的一天!表弟renho一直在尝试婴儿瘫痪症,并制造了轮椅,他们的编程效果很好,但是输出电压太低了!帮我!我正在使用arduino uno。

@Johnny欢迎来到Electronics.Stackexchange!请查看常见问题解答以了解本网站的工作方式,如果您有任何疑问,请使用页面右上角的特定按钮。
clabacchio

工作正常了吗?
罗素·麦克马洪

Answers:


4

“适当的”混合尚有争议:-)。

问题是您必须决定在单个锅中的纯信号下轨道移动的速度以及包含另一个锅中的信号时该怎么做。例如,如果您将FB(前进后退电位计)完全向前推动,并且如果两个电动机都以全速前进,那么您如何处理添加的少量LR(左右)电位计。要获得旋转,您必须使一条轨道的速度快于另一条轨道的速度。因此,如果您已经在两个电动机上都以最大前进速度运行,则必须降低一个或其他轨道的速度才能转弯。但是,如果您一直静止不动,本来可以加速一条或另一条轨道来达到相同的结果。

因此,所有这些,这是我脑海中一个简单的即席启动解决方案,看来是个不错的开始。

如果锅在机械上是独立的,那么两者可以同时处于100%的位置。
如果两者都在操纵杆类型的排列上,如果Yaxis = 100%并且Xaxi​​s = 0%,则添加一些B通常会减少A。可以在上述情况不成立的情况下构造操纵杆,但这是不寻常的。
假定操纵杆的类型是当X = 100%时增加Y%会减少X。可以做出其他假设。

FB =前后锅。零位居中,+ Ve用于锅的向前运动

LR =左右锅。居中零位。+右为锅。

K最初是比例因子1。
如果任何结果超过100%,则将K调整为结果= 100%,并且对其他电动机也使用相同的K值。

  • 例如,如果左马达结果= 125,右马达结果= 80,则为。
    当125 x 0.8 = 100时,设置K = 0.8。然后。
    左= 125 x 0.8 = 100%。右= 80 x 0.8 = 64%。

然后:

  • 左电机= K x(前_后+左_右)

  • 右马达= K x(前_后-左_右)

健全性检查:

  • LR = 0(居中),FB =正转->两个电机均正转运行。

  • LR =左全速,FB = 0->
    左电动机向后全速运行,
    右电动机正向全速运行。
    车辆逆时针旋转。

  • FB为100%,Lr = 0%。向右添加LR的10%。
    L = FB + LR = 100%-+ 10%R = FB-LR = 100%--10%

如果最大轴<100%,则缩放直到= 100%。
然后按相同数量缩放其他轴。


谢谢罗素-我将尝试在模型设置中实现这一点。顺便说一句,我的操纵杆能够在向左和向右平移时保持完全向前,与此非常相似:static.sparkfun.com/images/products/09032-03-L_i_ma.jpg
Kamil

1
我目前的任务是解决工作中的相同问题。我有一个wii nunchuk 2轴控制器,它需要完全按照问题中的描述控制2台电动机。我在理解这里的逻辑时遇到了一些麻烦。k1 / K1到底指的是什么?一种是小写字母,一种是大写字母-它们不同吗?什么是+ Ve?
塔尔

1
酷-感谢您的澄清。我需要用Python编写的代码,因此,如果我理解正确,应该这样做:pastebin.com/sWDakvLp。看起来我什么都没有吗?似乎可以在我的测试环境中工作-我需要将其确实连接到我肯定会使用的最终电动机上。
2016年

1
1)电动机速度由PWM控制,PWM仅接受0-100的值,这就是为什么我使用100作为最大值。2)我使用abs查找是否需要缩放(如您所说)并获取scale_factor。例如,如果最终得到0.8的比例因子,并将其用于负数,则-125 * 0.8 = -100。方向保持不变。我认为这是可行的,除非我缺少任何东西。我仍然没有机会在最终的电机上进行尝试-我的老板将构建一个带有可测试电机的测试平台。
2016年

1
我不确定我的代码是否会真正起作用,所以我将先前的pastebin链接设置为在一周后过期。由于它似乎可以正常工作,因此,如果有人再次遇到问题,这里是一个更永久的链接,提供了一些评论:pastebin.com/EKguJ1KP。我将其放入答案中,但是显然我没有足够的代表来发布答案。所有代码均基于Russel McMahon的回答-感谢他-感谢Russel。
2016年

5

这是一种解决方案,不需要复杂的if / else链,在完全向前移动或原位旋转时不降低动力,并允许平滑的曲线以及从移动到旋转的过渡。

这个想法很简单。假设(x,y)操纵杆值为正方形平面上的笛卡尔坐标。现在,想象一个较小的方形平面在其中旋转45º。

示例飞机

操纵杆坐标会在较大的正方形中为您提供一个点,而在较小的正方形中叠加的相同点将为您提供电动机值。您只需要将坐标从一个正方形转换为另一个正方形,即可将新的(x,y)值限制在较小正方形的边上。

有很多方法可以进行转换。我最喜欢的方法是:

  1. 将初始(x,y)坐标转换为极坐标。
  2. 将它们旋转45度。
  3. 将极坐标转换回笛卡尔坐标。
  4. 将新坐标重新缩放为-1.0 / + 1.0。
  5. 将新值固定为-1.0 / + 1.0。

假设初始(x,y)坐标在-1.0 / + 1.0范围内。内部正方形的边将始终等于l * sqrt(2)/2,因此步骤4只是将值乘以sqrt(2)

这是一个示例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

该方法的原始思想(具有更复杂的转换方法)来自本文


0

下面是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);

}

有趣的是,该代码看起来像是将2个模拟引脚馈送到2个不同的电机控制器。我将尝试修改代码并对其设置进行修改。Arduino Uno + 1个Sabertooth驱动器板。1个操纵杆到模拟引脚A0(x)引脚A1(y)读取值并将值传递到PWM引脚10&3,并到达Sabertooth的S1&S2。我想我已经接近了,但是我对如何在Sabertooth板上设置拨码开关感到困惑。目前,我正准备使用开关设置来接收模拟输入,开关4仍处于差动驱动的位置,但稍后将使其返回独立模式以进行进一步测试。我认为这个orig

@ user20514欢迎来到electronics.stackexchange!您可能会注意到,这不是论坛而是Q&A网站,因此答案的空间并不意味着讨论。如果您有任何要问的问题,请随意提问,或使用评论(确实)对现有问题和答案发表评论。
clabacchio

1
@Kamil-视频显示为私人视频。它仍然可用吗? youtube.com/watch?v=sGpgWDIVsoE
罗素·麦克马洪

@RussellMcMahon已重新激活:)
卡米尔·
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.