차동 모터 드라이브를 제어하기 위해 2 축 아날로그 입력을 혼합하는 알고리즘


9

uC (ATMega328p)를 사용하여 이중 차동 모터 드라이브 ( "탱크와 같은"드라이브)를 제어하기 위해 2 개의 아날로그 조이스틱 신호 (X Y 축)를 올바르게 혼합하는 방법에 대한 정보를 찾고 있지만 ADC 입력 및 PWM 출력을 가진 모든 uC) :

나는 2 개의 아날로그 값을 제공하는 아날로그 스틱을 가지고 있습니다 :

(방향) X : 0 ~ 1023
(스로틀) Y : 0 ~ 1023

여기에 이미지 설명을 입력하십시오

정지 위치는 (방향 및 스로틀 중립) 512,512
스로틀 전진 / 왼쪽 방향은 0,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 McMahon

Russell, 나는 anwser를 위해 많은 것을 봤고 나는 RC 컨트롤러에 직접 연결하기 위해 모터 컨트롤러를 갈 준비가되어 있음을 알았지 만 알고리즘에 대한 정보는 많지 않았습니다.
Kamil Zadora

좋은 날! 영아 마비와 휠체어 제작을 시도한 사촌 renho는 프로그램이 잘 작동했지만 출력 전압이 너무 낮습니다! 도와 줘요! arduino uno를 사용하고 있습니다.

@Johnny는 Electronics.Stackexchange에 오신 것을 환영합니다! 이 사이트의 작동 방식을 이해하려면 FAQ를 참조하십시오. 궁금한 점이 있으면 페이지 오른쪽 상단의 특정 단추를 사용하십시오.
clabacchio

작동 했습니까 ???
Russell McMahon

답변:


4

"적절한"혼합은 논쟁의 여지가있다 :-).

문제는 단일 포트의 순수 신호에서 트랙이 얼마나 빨리 이동하고 다른 포트의 신호가 포함될 때 수행 할 작업에 대한 결정을 내려야한다는 것입니다. 예를 들어, FB (앞뒤 포트)를 완전히 앞으로 밀고 두 모터가 최고 속도로 앞뒤로 움직 인 경우 소량의 LR (왼쪽-오른쪽) 포트 추가를 어떻게 처리합니까? 회전을하려면 한 트랙이 다른 트랙보다 빠르게 진행해야하므로 이미 두 모터에서 최고 전진 속도로 달리고있는 경우 회전하려면 한 개 또는 다른 트랙 속도를 줄여야합니다. 동일한 결과를 얻기 위해 하나 또는 다른 트랙을 가속화했을 것입니다.

그래서, 말한 모든 것, 여기 좋은 출발처럼 보이는 내 머리 속의 간단한 오프 커프 시작 솔루션이 있습니다.

포트가 기계적으로 독립적이라면 둘 다 동시에 100 % 일 수 있습니다.
둘 다 조이스틱 유형 배열에있는 경우 Yaxis = 100 % 및 Xaxis = 0 % 인 경우, 일부 B를 추가하면 일반적으로 A가 감소합니다. 위의 내용이 사실이 아닌 경우 조이스틱을 구성 할 수 있지만 이례적인 경우는 아닙니다.
조이스틱이 X = 100 % 일 때 Y %가 증가하면 X가 감소하는 유형이라고 가정합니다. 다른 가정도 가능합니다.

FB = 앞뒤 냄비. 냄비의 앞으로 움직임을위한 중심 0, + Ve

LR = 왼쪽 오른쪽 냄비. 중앙 제로. 오른쪽 냄비에 + Ve.

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 = 완전 fwd-> 두 모터가 전진 주행합니다.

  • LR = 완전 왼쪽, FB = 0->
    왼쪽 모터는 완전히 후진,
    오른쪽 모터는 완전히 후진 합니다.
    차량이 반 시계 방향으로 회전합니다.

  • FB는 100 %, Lr = 0 %이었다. 오른쪽에 LR의 10 %를 추가하십시오.
    L = FB + LR = 100 %-+ 10 % R = FB-LR = 100 %--10 %

가장 큰 축이 100 % 미만인 경우 = 100 %까지 조정합니다.
그런 다음 다른 축을 같은 양으로 조정하십시오.


Russell에게 감사합니다. 모델 설정에서이를 구현하려고합니다. BTW, 내 조이스틱은 왼쪽에서 오른쪽으로 패닝하는 동안 완전히 앞뒤로 이동할 수 있으며 다음과 매우 유사합니다. static.sparkfun.com/images/products/09032-03-L_i_ma.jpg
Kamil

1
나는 현재 직장에서 같은 문제를 해결하는 임무를 맡고 있습니다. wii nunchuk 2 축 컨트롤러가 있으며 질문에 설명 된대로 정확히 2 개의 모터를 제어해야합니다. 논리를 이해하는 데 약간의 어려움이 있습니다. k1 / K1은 정확히 무엇을 의미합니까? 하나는 소문자이고 하나는 대문자입니다. + Ve 란 무엇입니까?
Tal

1
쿨-설명에 감사드립니다. 파이썬으로 작성된 것이 필요하므로 올바르게 이해하면 pastebin.com/sWDakvLp 해야합니다 . 내가 빠진 것 같아요? 테스트 환경에서 작동하는 것 같습니다. 실제로 알고 자하는 최종 모터에 실제로 연결해야합니다.
Tal

1
1) 모터 속도는 0에서 100까지의 값만 취하는 PWM에 의해 제어되므로 100을 최대 값으로 사용했습니다. 2) abs를 사용하여 스케일링이 필요한지 (당신이 말한 것처럼) 확인하고 scale_factor를 얻습니다. 예를 들어 스케일 팩터가 0.8 인 경우 -125 * 0.8 = -100과 같이 음수로 사용합니다. 방향이 유지됩니다. 내가 빠진 것이 아니라면 그것이 효과가 있다고 생각합니다. 나는 여전히 최종 모터에서 시도해 볼 기회가 없었습니다. 내 상사는 테스트 할 수있는 모터가 연결된 테스트 플랫폼을 구축 할 것입니다.
Tal

1
내 코드가 실제로 작동하는지 확실하지 않으므로 일주일 후에 이전 pastebin 링크가 만료되도록 설정했습니다. 그것이 작동하는 것처럼 보이기 때문에 누군가가 문제를 다시 발견하면 pastebin.com/EKguJ1KP 과 더 많은 링크가 있습니다. 나는 이것을 대답에 넣었지만 분명히 대답을 게시 할 충분한 담당자가 없습니다. 모든 코드는 Russel McMahon의 답변을 기반으로합니다-신용은 그에게갑니다-감사합니다 Russel.
Tal

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)-Sabertooth의 S1 & S2로 전달되는 PWM 핀 10 & 3에 값을 읽고 전달합니다. 나는 가까이 있다고 생각하지만 Sabertooth 보드에 딥 스위치를 설정하는 방법에 혼란을 겪고 있습니다. 지금은 아날로그 입력을 수신하기 위해 스위치 설정을 사용하고 있지만 스위치 4는 여전히 차동 드라이브의 위치에 있지만 나중에 추가 테스트를 위해 독립 모드로 돌아갑니다. 제 생각에는이 오리지널

@ user20514 electronics.stackexchange에 오신 것을 환영합니다! 알다시피, 이것은 포럼이 아니라 Q & A 사이트이므로 답변을위한 공간은 토론을위한 것이 아닙니다. 궁금한 점이 있으면 언제든지 새로운 질문을하거나 기존 질문과 답변에 대한 의견을 말하십시오.
clabacchio

1
@Kamil-비디오가 비공개로 표시됩니다. 여전히 사용 가능합니까? youtube.com/watch?v=sGpgWDIVsoE
Russell McMahon

@RussellMcMahon reactivated :)
Kamil
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.