DJI RoboMaster M3508P19とC620をArduinoからCAN通信で動かす
教育用のロボット部品として販売されている,RoboMaster用モーターですが,仕事面でもかなり使えそうということで,とある案件のために試験的に購入してみました.
価格はモーターとコントローラ合わせて2万円くらいで,ちょっとお高めですが,このサイズ+パワーかつ,コントローラも扱いやすいとなると,かなりお買い得感があります.
実際,サイズが結構小さいので,スペック上は要求スペックを満たしていてもパワーが足りず,扱いづらいんじゃないかと思っていたのですが,パワフルすぎて逆にびっくりしています.
その分,発熱も結構あります.
Arduinoから動かす
このモーターの難点というか少し面倒くさいところとして,プロトコルがCANとPWM(PPM)しか無いということです.
回転数や角度のフィードバックが欲しいならCANは必須です.
しかし,ArduinoにはCANポートはありません.
STマイコンやESPだとついているんですけど...
それで若干避けていたという部分もあったのですが,MCP2515というCANをSPIに変換するモジュールを使用すると,30分くらいですんなり動かせました.
ただ,実際に動かしてみたって記事が少ないので,今回こうして記事にかいているわけですが,もしかしたら簡単すぎて誰も記事に書いていないだけかもしれません.
本日のレシピ
材料
- DJI RoboMaster M3508 P19
- DJI C620
- Arduino Mega
- MCP2515モジュール(https://amzn.asia/d/75u3C8L)
- ジャンパワイヤ オス-メス 6本
- ジャンパピン 1個
- GHコネクタ 2pin(https://amzn.asia/d/gUsOiof)
- XT30コネクタ
- 安定化電源
XT60コネクタはいっぱい持っていますが,XT30は持っていなかったので近くのラジコンショップに駆け込んだらありました.
配線
こんな感じに配線してください.
わかりづらい画像ですみません.
今回はモーターとArduinoは1対1で接続していますので,お互い終端抵抗をつけます.
MCP2515はジャンパピンをつけることで終端抵抗がONになります.
コントローラはスライドスイッチをONにするだけです.
プロトコル
普通のCAN(CAN HS)です.
通信スピードは1MbpsでDLCは8バイトです.
送信
モーターにはそれぞれ1ー8の中でIDを割り振ることができ,最大8個まで同一ネットワークで動作させることができます.
モーターを動かすには,電流値指定して送信する必要があります.
一つのCANIDで4つまでモーターを動かすことができるので,効率的にモーターを動かすことができます.
最大最小は-20A〜20Aとなっており,-16384〜0〜16384の範囲に変換し,2バイトに分けて送ります.
ID:0x200 | モータID | データ |
---|---|---|
Data[0] | モーター1 | 上位バイト |
Data[1] | モーター1 | 下位バイト |
Data[2] | モーター2 | 上位バイト |
Data[3] | モーター2 | 下位バイト |
Data[4] | モーター3 | 上位バイト |
Data[5] | モーター3 | 下位バイト |
Data[6] | モーター4 | 上位バイト |
Data[7] | モーター4 | 下位バイト |
ID:0x1FF | モータID | データ |
---|---|---|
Data[0] | モーター5 | 上位バイト |
Data[1] | モーター5 | 下位バイト |
Data[2] | モーター6 | 上位バイト |
Data[3] | モーター6 | 下位バイト |
Data[4] | モーター7 | 上位バイト |
Data[5] | モーター7 | 下位バイト |
Data[6] | モーター8 | 上位バイト |
Data[7] | モーター8 | 下位バイト |
受信
受信はそれぞれのモーターからデータが送られてきます.
受信するCANIDが20Xとなっており,Xには設定したモーターIDが割り当てられています.
例えばモーターコントローラのIDを1にしたら,CANID:0x201でフィードバック情報が送られてきます.
角度は0~360の範囲が0-8192で送られてきます.
rpmはそのままの数値です.
電流(トルク)はおそらく-16384〜0〜16384の範囲です.
温度はそのままの数値です.
ID:0x20X | モータID | データ |
---|---|---|
Data[0] | 角度 | 上位バイト |
Data[1] | 角度 | 下位バイト |
Data[2] | rpm | 上位バイト |
Data[3] | rpm | 下位バイト |
Data[4] | 電流(トルク) | 上位バイト |
Data[5] | 電流(トルク) | 下位バイト |
Data[6] | 温度 | 1バイト |
Data[7] | 割り当てなし | 割り当てなし |
プログラム
Arduino Megaのスケッチの参考例を載せます.
今回はmcp_can(https://github.com/coryjfowler/MCP_CAN_lib)
というライブラリを使用します.
MCP2515自体は,SPI通信でArduinoと繋がっています.
motor_ouput_current_Aの変数に電流値を入れることで制御できるようになっています.
最大最小は-20A〜20Aですのでその範囲内を指定してください.
パワーがすごいので最初は1Aくらいから試してみると良いと思います.
シリアルモニタを開くとフィードバック値を表示するようにしています.
#include <mcp_can.h>
#include <SPI.h>
long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
byte txBuf[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
long Pre_millis;
MCP_CAN CAN0(53);
int16_t fmap(double x, double in_min, double in_max, int16_t out_min, int16_t out_max);
void setup()
{
Serial.begin(115200);
if (CAN0.begin(MCP_ANY, CAN_1000KBPS, MCP_8MHZ) == CAN_OK)
{
Serial.println("CAN0: Init OK!");
CAN0.setMode(MCP_NORMAL);
}
else
{
Serial.println("CAN0: Init Fail!");
}
Pre_millis = millis();
}
void loop()
{
double motor_ouput_current_A = 0.0;
int16_t motor_ouput_current_Byte = fmap(motor_ouput_current_A, 0, 20, 0, 16384);//2バイトに変換
txBuf[0] = (motor_ouput_current_Byte >> 8) & 0xFF;//上位バイト
txBuf[1] = motor_ouput_current_Byte & 0xFF;//下位バイト
//Send
if (millis() - Pre_millis > 20) { // Period: 20ms
CAN0.sendMsgBuf(0x200, 0, 8, txBuf);
Pre_millis = millis();
}
//Receive
if (CAN0.checkReceive() == CAN_MSGAVAIL)
{
CAN0.readMsgBuf(&rxId, &len, rxBuf);
Serial.print("Recive ID: ");
Serial.print(rxId, HEX);
Serial.print(" Data: ");
int16_t angle = rxBuf[0] << 8 | rxBuf[1];
int16_t rpm = rxBuf[2] << 8 | rxBuf[3];
int16_t amp = rxBuf[4] << 8 | rxBuf[5];
int8_t temp = rxBuf[6];
Serial.print(angle);
Serial.print(",");
Serial.print(rpm);
Serial.print(",");
Serial.print(amp);
Serial.print(",");
Serial.println(temp);
}
}
int16_t fmap(double x, double in_min, double in_max, int16_t out_min, int16_t out_max) {
return (x - in_min) * ((double)(out_max - out_min)) / (in_max - in_min) + out_min;
}