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)が好ましい。
ラッセルマクマホン

ラッセル、私はアンサーをたくさんググってみましたが、RCレシーバーに直接接続するためにすぐに使えるモーターコントローラーがたくさん見つかりましたが、内部のアルゴリズムに関する情報はあまりありませんでした。
Kamil Zadora 2011

良い一日!乳児の麻痺と車椅子の建設を試みてきたいとこrenhoのプログラミングはうまくいきましたが、出力電圧が低すぎます!助けて!arduino unoを使用しています。

@JohnnyはElectronics.Stackexchangeへようこそ!このサイトがどのように機能するかを理解するにはFAQをご覧ください。質問がある場合は、ページの右上隅にある特定のボタンを使用してください。
クラバッキオ

うまくいきましたか???
ラッセルマクマホン

回答:


4

「適切な」混合は議論の余地があります:-)。

問題は、単一のポットからの純粋な信号の下でトラックがどれだけ速く移動するか、他のポットからの信号が含まれているときにどうするかを決定する必要があることです。たとえば、FB(前方-後方ポットを完全に前方に押し、両方のモーターが前方にフルスピードで回転する場合)に追加された少量のLR(左-右)ポットの追加にどのように対処しますか。回転を得るには、1つのトラックをもう1つのトラックより速くする必要があります。したがって、すでに両方のモーターで最大前進速度で走行している場合は、回転させるために1つまたは他のトラック速度を下げる必要があります。しかし、静止している場合は同じ結果を達成するために、1つまたは他のトラックを加速したでしょう。

ですから、言ったとおり、これは私の頭から簡単なオフの開始ソリューションであり、良いスタートのようです。

ポットが機械的に独立している場合、両方を同時に100%にすることができます。
両方がジョイスティックタイプの配置にある場合、Yaxis = 100%およびXaxis = 0%の場合、Bを追加すると通常はAが減少します。ジョイスティックは、上記が当てはまらない場合に構築できますが、これらは異常です。
ジョイスティックは、X = 100%のときにY%を増加させるとXが減少するタイプであると仮定します。他の仮定も可能です。

FB =フロントバックポット。センターゼロ、+ 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(Front_Back + Left_Right)

  • 右モーター= 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%になるまでスケーリングします。
次に、同じ量で他の軸をスケーリングします。


ラッセルに感謝します-私はこれを私のモデル設定に実装しようとします。ところで、私のジョイスティックは、左から右にパンしながら、完全に前に進むことができます。
Kamil Zadora

1
私は現在、職場で同じ問題を解決する任務を負っています。私はwii nunchuk 2軸コントローラーを持っていますが、質問に記載されているとおりに2つのモーターを正確に制御する必要があります。ここでロジックを理解するのに少し問題があります。k1 / K1は正確には何を指しますか?1つは小文字、もう1つは大文字です-違いますか?+ Veとは?
Tal

1
クール-説明してくれてありがとう。私はこれをPythonで書く必要があったので、私が正しく理解していれば、これでうまくいくはずです:pastebin.com/sWDakvLp。何か不足しているように見えますか?私のテスト環境で動作しているようです-確実に知るために、使用する最終モーターに実際に接続する必要があります。
Tal

1
1)モーター速度はPWMによって制御され、0から100までの値のみを取るため、最大値として100を使用しました。2)absを使用して、スケーリングが必要かどうかを確認し(あなたが言ったように)、scale_factorを取得します。たとえば、スケール係数が0.8になり、それを負の数で使用すると、-125 * 0.8 = -100になります。方向は維持されます。私は何かを逃していない限り、それは機能すると思います。私はまだ最終的なモーターでそれを試す機会がありませんでした-私の上司は私がテストできるモーターが取り付けられたテストプラットフォームを構築します。
Tal

1
私のコードが実際に機能するかどうかわからなかったので、前のペーストビンリンクを1週間後に期限切れになるように設定しました。動作しているように見えるので、問題が再び発生した場合のコメントがいくつかある永続的なリンクを次に示します。pastebin.com / EKguJ1KP。これを回答に入れましたが、回答を投稿するのに十分な担当者がいないようです。すべてのコードは、ラッセルマクマホンの答えに基づいています。クレジットは彼のおかげです-ラッセルに感謝します。
Tal

5

複雑なif / elseチェーンを必要とせず、完全に前方に移動したり、所定の位置で回転したりするときに電力を削減せず、滑らかな曲線と移動から回転への移行を可能にするソリューションを次に示します。

アイデアは簡単です。(x、y)ジョイスティックの値が正方形の平面上のデカルト座標であると仮定します。小さな正方形の平面を45度回転させて想像してみてください。

サンプル平面

ジョイスティックの座標は大きい方の四角形にポイントを与え、小さい方の四角形に重ねられた同じポイントがモーターの値を与えます。座標を1つの正方形から別の正方形に変換し、新しい(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

以下は、ラッセルマクマホンの回答で説明されているミキシングアルゴリズムの実装例です。

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ジョイスティックからアナログpinA0(x)pinA1(y)に値を読み取り、値をPWMピン10&3に渡してSabertoothのS1&S2に送信します。私は近いと思いますが、Sabertoothボードにディップスイッチをセットアップする方法について混乱しています。ここでは、アナログ入力を受信するためのスイッチセットアップを試してみます。スイッチ4はまだ差動ドライブの位置にありますが、後でさらにテストするために、独立モードに戻します。私はこの元のことだと思います

@ user20514 electronics.stackexchangeへようこそ!お気づきかもしれませんが、これはフォーラムではなくQ&Aサイトなので、回答スペースはディスカッション用ではありません。質問がある場合は自由に新しい質問をするか、コメントを使用して既存の質問と回答について(実際に)コメントしてください。
clabacchio

1
@Kamil-ビデオは非公開として表示されます。まだ入手可能ですか? youtube.com/watch?v=sGpgWDIVsoE
ラッセルマクマホン

@RussellMcMahonが再アクティブ化されました:)
Kamil Zadora
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.