![見出し画像](https://assets.st-note.com/production/uploads/images/107750616/rectangle_large_type_2_43651fe45daf8839c359506ef26939b8.jpeg?width=1200)
OdriveとArduinoをCAN通信してBLDCを回す!
ArduinoとODrivev3.6をCAN通信してBLDCを回している方が、誰もいなくてイライラしたので私が書きます。
先日のNHK学ロボで他校がodriveでCAN通信していたので私もやってみたくなりました。
私の実験環境はこちら
![](https://assets.st-note.com/img/1686053776283-epS8okJJb6.jpg?width=1200)
6/6
まずはodrivetoolを使ってモーターを回します。
まず大前提として、各開発環境に合わせたエンコーダーの値やモーターの値をodrivetoolのconfigに設定していきます。それは下のページを見て行ってください。これが結構大変でしたが、がんばれ。
ヒントはtabキーを使うことです。
odrivetoolの中で、
$odrv0.
というところまで入力した段階でTabキーを押すと候補が出てきます。これはとても重要です。もしかしたら常識なのかもしれない。
上のページで設定をする皆さんへの注意喚起
![](https://assets.st-note.com/img/1686276981614-LtQN9CkP8E.png?width=1200)
v3.6のODriveを使用中の方は水色のバーをタップしないといけません。
私はこれのせいで時間を無駄にしました。
=====================
上のmotorconfigを見ても何が何だかわからなかった皆様へ
まずは、使いたいブラシレスモーターのデータシートを探しましょう。
今回は試しにHP-z4035-14を回すときの回し方を教えます。
エンコーダーはAMT102を使っています。
※エンコーダなしではODriveではモーターは回りません
※AMT102以外のエンコーダを使う場合には設定が別途ひつようになります。
まずはデータシートを探しましょう。
Googleでhp-z4035-14 datasheetと検索します。
すると、以下のページが見つかります。
![](https://assets.st-note.com/img/1708639644929-w7k8jrSsOO.png?width=1200)
このデータシートから得ないといけない情報は、
巻き数(Pole)と最大電流です。
このモーターの巻き数は14。最大電流は60Aですね。
巻き数というのはモーターの中に立っているコイルの数で、データシートに乗っていない場合にはモーターの隙間から数えてください。
この二つの情報をODriveのconfigに保存していきましょう。
まずはanaconda promptでodrivetoolと入力し、odrivetoolを起動してください。
※odrivetoolって何って方はもっかい英語のページのodrivetoolのところを読んでanacondapromptのインストールからやってください。
次は巻き数を登録します。データシートでは14Tとなっていたので、このモーターのコイルは14本です。しかしながら、Odrivetoolの設定ではPole_Pairsを設定するので、ペアになるので÷2して、7と設定します。
![](https://assets.st-note.com/img/1708640334896-PJGQw5FrOk.png?width=1200)
次は最大電流の設定
![](https://assets.st-note.com/img/1708640356152-wYlTs1UVLx.png?width=1200)
そして最後に忘れてはいけないのが、
![](https://assets.st-note.com/img/1708640373870-waGaN4sWrQ.png)
はい。ここまでやったら、電源を入れなおしましょう。
==============================
まずはキャリブレーションをします
![](https://assets.st-note.com/img/1686055788992-BbIKfTB4DA.png?width=1200)
![](https://assets.st-note.com/img/1686055795821-HQ2KGvchRz.jpg?width=1200)
モーターのキャリブレーションに成功しました。
続いてモーターをodrivetoolを使って回してみます。
![](https://assets.st-note.com/img/1686056032497-PVU8XnGdyQ.png?width=1200)
回りましたー
回転スピードのコントロールで回しました。
うまくコントロールできています。
続いて
位置制御で回していきます。
![](https://assets.st-note.com/img/1686056349824-G1IP3U829A.png?width=1200)
上手く回りました。
6/7
本日はpcがうまく動かないので、can通信用のケーブルを作りました。
![](https://assets.st-note.com/img/1686198153782-KNZ1kdNCno.jpg?width=1200)
6/8
本日はarduino側のコードを書いていくために必要な情報をまとめます。
arduinoでcan通信するためにarduinoにつけているhatはmcp2515です。
mcp2515のcs(chip selecrt)ピンとarduinoの何番ピンが接続されているのかを調べます。
csピンはmcp2515とarduinoがSPI通信するために使われる。
データシートを読むと
![](https://assets.st-note.com/img/1686198939449-hVpMK3whSN.png?width=1200)
チップの右上から3番目のピンがcsであることがわかるね。
したら、テスターで、arduinoの9番ピンとチップのcsピンの通電チェックを行うよ。
したら、つながっているかどうかわかる!
![](https://assets.st-note.com/img/1686199137184-jgrcZahSE2.jpg?width=1200)
そしてcanのシールドのメーカーのサイトに例が載っていることが判明しました。
githubのライブラリーをarduino ideにダウンロードすると、exampleの中にcan receive checkというものがあったので、これを使用して、Odriveのハートビートを聞きたいと思う。
下のファイルの13ページを参照
receive checkのexampleを開いて、必要な部分を書き換えていきます。
// demo: CAN-BUS Shield, receive data with check mode
// send data coming to fast, such as less than 10ms, you can use this way
// loovee, 2014-6-13 #include <SPI.h>
#define CAN_2515
// #define CAN_2518FD
// Set SPI CS Pin according to your hardware
#if defined(SEEED_WIO_TERMINAL) && defined(CAN_2518FD)
// For Wio Terminal w/ MCP2518FD RPi Hat:
// Channel 0 SPI_CS Pin: BCM 8
// Channel 1 SPI_CS Pin: BCM 7
// Interupt Pin: BCM25
const int SPI_CS_PIN = BCM8;
const int CAN_INT_PIN = BCM25; #else
// For Arduino MCP2515 Hat:
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2; #endif
#ifdef CAN_2518FD #include "mcp2518fd_can.h"
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin #endif
#ifdef CAN_2515 #include "mcp2515_can.h"
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin #endif
void setup() {
SERIAL_PORT_MONITOR.begin(115200);
while (CAN_OK != CAN.begin(CAN_500KBPS)) { // init can bus : baudrate = 500k
SERIAL_PORT_MONITOR.println("CAN init fail, retry...");
delay(100);
}
SERIAL_PORT_MONITOR.println("CAN init ok!");
}
void loop() {
unsigned char len = 0;
unsigned char buf[8];
if (CAN_MSGAVAIL == CAN.checkReceive()) { // check if data coming
CAN.readMsgBuf(&len, buf); // read data, len: data length, buf: data buf
unsigned long canId = CAN.getCanId();
SERIAL_PORT_MONITOR.println("-----------------------------");
SERIAL_PORT_MONITOR.print("Get data from ID: 0x");
SERIAL_PORT_MONITOR.println(canId, HEX);
for (int i = 0; i < len; i++) { // print the data
SERIAL_PORT_MONITOR.print(buf[i], HEX);
SERIAL_PORT_MONITOR.print("\t");
}
SERIAL_PORT_MONITOR.println();
}
}
/*********************************************************************************************************
END FILE
***************************
まずは41行目のボーレートの設定です。
while (CAN_OK != CAN.begin(CAN_500KBPS)) {
コードでは500kとなっていますが、自分のodriveのボーレート設定に合わせなくてはいけません。
odriveのボーレートを調べるにはodrivetoolを使って
![](https://assets.st-note.com/img/1686274616749-0t0YHvfcQM.png)
これで私のボーレートは250kとわかりました。
コードの41行目は
while (CAN_OK != CAN.begin(CAN_150KBPS)) {
のように書き換えます。
次に39行目のシリアルモニターの設定部分を変えます。
SERIAL_PORT_MONITOR.begin(9600);
9600に設定します。
書き換え後のコード↓
// demo: CAN-BUS Shield, receive data with check mode
// send data coming to fast, such as less than 10ms, you can use this way
// loovee, 2014-6-13 #include <SPI.h>
#define CAN_2515
// #define CAN_2518FD
// Set SPI CS Pin according to your hardware
#if defined(SEEED_WIO_TERMINAL) && defined(CAN_2518FD)
// For Wio Terminal w/ MCP2518FD RPi Hat:
// Channel 0 SPI_CS Pin: BCM 8
// Channel 1 SPI_CS Pin: BCM 7
// Interupt Pin: BCM25
const int SPI_CS_PIN = BCM8;
const int CAN_INT_PIN = BCM25; #else
// For Arduino MCP2515 Hat:
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2; #endif
#ifdef CAN_2518FD #include "mcp2518fd_can.h"
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin #endif
#ifdef CAN_2515 #include "mcp2515_can.h"
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin #endif
void setup() {
SERIAL_PORT_MONITOR.begin(9600);
while (CAN_OK != CAN.begin(CAN_250KBPS)) { // init can bus : baudrate = 500k
SERIAL_PORT_MONITOR.println("CAN init fail, retry...");
delay(100);
}
SERIAL_PORT_MONITOR.println("CAN init ok!");
}
void loop() {
unsigned char len = 0;
unsigned char buf[8];
if (CAN_MSGAVAIL == CAN.checkReceive()) { // check if data coming
CAN.readMsgBuf(&len, buf); // read data, len: data length, buf: data buf
unsigned long canId = CAN.getCanId();
SERIAL_PORT_MONITOR.println("-----------------------------");
SERIAL_PORT_MONITOR.print("Get data from ID: 0x");
SERIAL_PORT_MONITOR.println(canId, HEX);
for (int i = 0; i < len; i++) { // print the data
SERIAL_PORT_MONITOR.print(buf[i], HEX);
SERIAL_PORT_MONITOR.print("\t");
}
SERIAL_PORT_MONITOR.println();
}
}
/*********************************************************************************************************
END FILE
******************************************************
そしたらarduinoにコードを書き込んで、odriveと2本のcanで接続すればシリアルモニタでodriveのハートビートを確認することができます。
![](https://assets.st-note.com/img/1686275020255-yJUWyjDLWg.png?width=1200)
できました!
odriveからのハートビートが来ています。
続いてarduinoからodriveにCAN通信で命令を送信してモーターのキャリブレーションを行おうと思います。
今回も先程と同様にCANシールドのメーカーが用意しているexampleを使用したいと思います。
// demo: CAN-BUS Shield, send data
// loovee@seeed.cc
#include <SPI.h>
#define CAN_2515
// #define CAN_2518FD
// Set SPI CS Pin according to your hardware
#if defined(SEEED_WIO_TERMINAL) && defined(CAN_2518FD)
// For Wio Terminal w/ MCP2518FD RPi Hat:
// Channel 0 SPI_CS Pin: BCM 8
// Channel 1 SPI_CS Pin: BCM 7
// Interupt Pin: BCM25
const int SPI_CS_PIN = BCM8;
const int CAN_INT_PIN = BCM25; #else
// For Arduino MCP2515 Hat:
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2; #endif
#ifdef CAN_2518FD #include "mcp2518fd_can.h"
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin #endif
#ifdef CAN_2515 #include "mcp2515_can.h"
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin #endif
void setup() {
SERIAL_PORT_MONITOR.begin(115200);
while(!Serial){};
while (CAN_OK != CAN.begin(CAN_500KBPS)) { // init can bus : baudrate = 500k
SERIAL_PORT_MONITOR.println("CAN init fail, retry...");
delay(100);
}
SERIAL_PORT_MONITOR.println("CAN init ok!");
}
unsigned char stmp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
void loop() {
// send data: id = 0x00, standrad frame, data len = 8, stmp: data buf
stmp[7] = stmp[7] + 1;
if (stmp[7] == 100) {
stmp[7] = 0;
stmp[6] = stmp[6] + 1;
if (stmp[6] == 100) {
stmp[6] = 0;
stmp[5] = stmp[5] + 1;
}
}
CAN.MCP_CAN::sendMsgBuf(0x00, 0, 8, stmp);
delay(100); // send data per 100ms
SERIAL_PORT_MONITOR.println("CAN BUS sendMsgBuf ok!");
}
// END FILE
では先程と同様の部分をまずは書き換えていきます。
43行目を250kにかえましょう。
そして53行目から62行目までのコードは今回は不要なので削除しましょう。
stmp[7] = stmp[7] + 1;
if (stmp[7] == 100) {
stmp[7] = 0;
stmp[6] = stmp[6] + 1;
if (stmp[6] == 100) {
stmp[6] = 0;
stmp[5] = stmp[5] + 1;
}
}
ではいよいよodriveに命令していきます。
このコードはとても分かりやすいのですが、
unsigned char stmp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
これがODriveに送るデータです。
この数字の組み合わせを変えることによってODriveにさせる行動が変わります。
今のままだと100ミリ秒ごとにodriveに命令が送られていて、高頻度すぎるので、65行目のdelayを10秒ほどに伸ばしてください。
CAN.MCP_CAN::sendMsgBuf(0x00, 0, 8, stmp);
delay(10000); // send data per 100ms
SERIAL_PORT_MONITOR.println("CAN BUS sendMsgBuf ok!");
送る命令についてはodriveのwikiの新しいほうに書かれているものが参考になります。
今回はodriveにキャリブレーションを行わせるコードなので、
コードの50行目を以下のように変更
unsigned char stmp[8] = {07, 0, 0, 0, 0, 0, 0, 0};
コードの64行目を以下のように変更
CAN.MCP_CAN::sendMsgBuf(0x07, 0, 8, stmp);
変更後のコード全体が以下
// demo: CAN-BUS Shield, send data
// loovee@seeed.cc
#include <SPI.h>
#define CAN_2515
// #define CAN_2518FD
// Set SPI CS Pin according to your hardware
#if defined(SEEED_WIO_TERMINAL) && defined(CAN_2518FD)
// For Wio Terminal w/ MCP2518FD RPi Hat:
// Channel 0 SPI_CS Pin: BCM 8
// Channel 1 SPI_CS Pin: BCM 7
// Interupt Pin: BCM25
const int SPI_CS_PIN = BCM8;
const int CAN_INT_PIN = BCM25; #else
// For Arduino MCP2515 Hat:
// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 9;
const int CAN_INT_PIN = 2; #endif
#ifdef CAN_2518FD #include "mcp2518fd_can.h"
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin #endif
#ifdef CAN_2515 #include "mcp2515_can.h"
mcp2515_can CAN(SPI_CS_PIN); // Set CS pin #endif
void setup() {
SERIAL_PORT_MONITOR.begin(9600);
while(!Serial){};
while (CAN_OK != CAN.begin(CAN_250KBPS)) { // init can bus : baudrate = 500k
SERIAL_PORT_MONITOR.println("CAN init fail, retry...");
delay(100);
}
SERIAL_PORT_MONITOR.println("CAN init ok!");
}
unsigned char stmp[8] = {07, 0, 0, 0, 0, 0, 0, 0};
void loop() {
// send data: id = 0x00, standrad frame, data len = 8, stmp: data buf
stmp[7] = stmp[7] + 1;
if (stmp[7] == 100) {
stmp[7] = 0;
stmp[6] = stmp[6] + 1;
if (stmp[6] == 100) {
stmp[6] = 0;
stmp[5] = stmp[5] + 1;
}
}
CAN.MCP_CAN::sendMsgBuf(0x07, 0, 8, stmp);
delay(10000); // send data per 100ms
SERIAL_PORT_MONITOR.println("CAN BUS sendMsgBuf ok!");
}
// END FILE
では実行してみてください。
モーターが回りました!
モーターが回らないときには次の項目をcheckしてください。
odrive側のモーターやエンコーダーの設定は終わっているか?
odrivetoolを使ってのフルキャリブレーションは成功したか
arduinoのリセットボタンを押してみる
odriveの電源を再起動する
これでArduinoとODriveをCAN通信してBLDCを回すという目標を達成することができました!
それでは自由に制御していきましょう!
自由に制御
ご不明な点がありましたら、コメントで教えてください。
この記事が気に入ったらサポートをしてみませんか?