ODriveでBLDCモーターの制御 #03 Arduino


前回、GUIでテスト動作が確認できたODriveですが、今回は外部のプログラムからODriveの位置制御を行いたいと思います。

Githubを見ると、Arduinoを用いたasciiプロトコルのサンプルコードが軽量で使いやすそうだったので、まずはArduinoからODriveにコマンドを送って制御するのを試してみたいと思います。

あと、追加要素としてPS2コントローラーも繋いでみました。

Arduino UNOとODriveの接続は、

  • Arduino D8 – ODrive GPIO1
  • Arduino D9 – ODrive GPIO2
  • Arduino GND – ODrive GND

という感じに接続しています。

使い方としては、GithubのArduinoライブラリをArduino IDEに読み込ませると、Arduino IDEのサンプルにODriveArduinoTestが追加されます。
ライブラリを読み込ませる説明はこちらに書かれています。

使用するArduinoに合わせて、「Set up serial pins to the ODrive」の部分をコメントアウト/コメント解除する必要があります。UNOの場合は、UNOのところをコメント解除します。

まずは、サンプルをそのまま書き込んだところです。

シリアルコンソールから

  • “0”を送ると、キャリブレーション
  • “s”を送ると、サンプル動作
  • “b”を送ると、電源電圧の表示
  • “p”を送ると、現在位置の表示

が実行されます。

リセットが実装されていないため、ODriveがエラーを吐いた際は、ODriveの電源を再投入して再起動する必要があります。

PS2コントローラーでODriveを制御する

次にPS2コントローラーで制御します。
ODriveArduinoTestにはリセットコマンドがなかったので、それも送れるようにしました。

PS2コントローラーの取得は、Arduino用PS2インターフェース・ライブラリを使わせていただきました。

Arduino用PS2インターフェース・ライブラリは、少し古いので、GPSX.hの
「#include <wiring.h>」をコメントアウトして、「#include <Arduino.h>」に書き換える必要があるところだけ、注意が必要です。

動作する様子はこちらです。

PS2コントローラーにて、

  • 〇を押すと、 0.0f の位置に
  • □を押すと 10.0f の位置に
  • ×を押すとリセット
  • △を押すとキャリブレーション&クローズドループモード
  • L1ボタンを押している間、左アナログスティックで制御

という感じになっています。

ドキュメントを読んでもピンと来てなかったのですが、サンプルソースを読んだらODriveのasciiプロトコルはかなり簡単仕様で、改造もしやすい感じでした。
プロトコル的にエラーとか何も考えてない感じはありますが。

どちらかというと、前回同様にやはりODriveの設定が大変で、動作時のパラメータが設定リミットに触れるとすぐにエラー状態になって、ODriveが停止してしまいます。エラーを回避するパラメータの決定と対策が大変そうですね。

次回は、Arduinoではなく、PCから直接asciiコマンドを送ってみたいと思います。PCからODriveを制御するアプリに関しても、考えてみようと思います。

せっかくなのでPSコントローラーでODriveを制御するソースコード、貼っておきます。

#include <GPSXClass.h>
#include <HardwareSerial.h>
#include <SoftwareSerial.h>
#include <ODriveArduino.h>

// Printing with stream operator helper functions
template<class T> inline Print& operator <<(Print &obj,     T arg) { obj.print(arg);    return obj; }
template<>        inline Print& operator <<(Print &obj, float arg) { obj.print(arg, 4); return obj; }

////////////////////////////////
// Set up serial pins to the ODrive
////////////////////////////////

// Below are some sample configurations.
// You can comment out the default Teensy one and uncomment the one you wish to use.
// You can of course use something different if you like
// Don't forget to also connect ODrive GND to Arduino GND.

// Teensy 3 and 4 (all versions) - Serial1
// pin 0: RX - connect to ODrive TX
// pin 1: TX - connect to ODrive RX
// See https://www.pjrc.com/teensy/td_uart.html for other options on Teensy
//HardwareSerial& odrive_serial = Serial1;

// Arduino Mega or Due - Serial1
// pin 19: RX - connect to ODrive TX
// pin 18: TX - connect to ODrive RX
// See https://www.arduino.cc/reference/en/language/functions/communication/serial/ for other options
// HardwareSerial& odrive_serial = Serial1;

// Arduino without spare serial ports (such as Arduino UNO) have to use software serial.
// Note that this is implemented poorly and can lead to wrong data sent or read.
// pin 8: RX - connect to ODrive TX
// pin 9: TX - connect to ODrive RX
 SoftwareSerial odrive_serial(8, 9);

// ODrive object
ODriveArduino odrive(odrive_serial);

void setup(){
  
  // ODrive uses 115200 baud
  odrive_serial.begin(115200);
  
  // Serial to PC
  Serial.begin(115200);
  while (!Serial) ; // wait for Arduino Serial Monitor to open

  //PSX init
  PSX.mode(PSX_PAD1, MODE_ANALOG, MODE_LOCK);
  PSX.motorEnable(PSX_PAD1, MOTOR1_ENABLE, MOTOR2_ENABLE);
  // Poll current state once.
  PSX.updateState(PSX_PAD1);
  
  Serial.println("ODriveArduino");
  Serial.println("Setting parameters...");
  for (int axis = 0; axis < 2; ++axis) {
    odrive_serial << "w axis" << axis << ".controller.config.vel_limit " << 30000.0f << '\n';
    odrive_serial << "w axis" << axis << ".motor.config.current_lim " << 11.0f << '\n';
  }
  Serial.println("Ready!");
}

void loop(){
  
  float pos_m0 = 0.0f ;
  int requested_state;
  int motornum = 0;

  PSX.updateState(PSX_PAD1);

  // 〇が押されたら 0.0f の位置に
  if (PRESSED_CIRCLE(PSX_PAD1)) {
    Serial.println("Pressed circle");
    pos_m0 = 0.0f ;
    odrive.SetPosition(0, pos_m0);
  }

  // □が押されたら 10.0f の位置に
  if (PRESSED_SQUARE(PSX_PAD1)) {
    Serial.println("Pressed square");
    pos_m0 = 10.0f ;
    odrive.SetPosition(0, pos_m0);
  }

  // ×が押されたらリセット
  if (PRESSED_CROSS(PSX_PAD1)) {
    Serial.println("Pressed cross");
    odrive_serial << "sr\n";
    Serial << "rest odrive" << '\n';
  }

  // △が押されたらキャリブレーション&クローズドループモード
  if (PRESSED_TRIANGLE(PSX_PAD1)) {
    Serial.println("Pressed triangle");
      requested_state = ODriveArduino::AXIS_STATE_FULL_CALIBRATION_SEQUENCE;
      Serial << "Axis" << "0" << ": Requesting state " << requested_state << '\n';
      if(!odrive.run_state(motornum, requested_state, true, 25.0f)) return;

      requested_state = ODriveArduino::AXIS_STATE_CLOSED_LOOP_CONTROL;
      Serial << "Axis" << "0" << ": Requesting state " << requested_state << '\n';
      if(!odrive.run_state(motornum, requested_state, false /*don't wait*/)) return;
  }

  // L1ボタンを押している間、左アナログスティックで制御
  if (IS_DOWN_L1(PSX_PAD1)) {
    pos_m0 = (ANALOG_LEFT_Y(PSX_PAD1) - 127) / 5;
    odrive.SetPosition(0, pos_m0);
    Serial << "Pos: " << pos_m0 << '\n';
  }
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

アーカイブ

広告

Top