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';
}
}
コメントを残す