見出し画像

OdriveとArduinoをCAN通信してBLDCを回す!

ArduinoとODrivev3.6をCAN通信してBLDCを回している方が、誰もいなくてイライラしたので私が書きます。

先日のNHK学ロボで他校がodriveでCAN通信していたので私もやってみたくなりました。

私の実験環境はこちら

6/6
まずはodrivetoolを使ってモーターを回します。
まず大前提として、各開発環境に合わせたエンコーダーの値やモーターの値をodrivetoolのconfigに設定していきます。それは下のページを見て行ってください。これが結構大変でしたが、がんばれ。

ヒントはtabキーを使うことです。
odrivetoolの中で、
$odrv0.
というところまで入力した段階でTabキーを押すと候補が出てきます。これはとても重要です。もしかしたら常識なのかもしれない。

上のページで設定をする皆さんへの注意喚起

v3.6のODriveを使用中の方は水色のバーをタップしないといけません。
私はこれのせいで時間を無駄にしました。


=====================
上のmotorconfigを見ても何が何だかわからなかった皆様へ

まずは、使いたいブラシレスモーターのデータシートを探しましょう。

今回は試しにHP-z4035-14を回すときの回し方を教えます。
エンコーダーはAMT102を使っています。

※エンコーダなしではODriveではモーターは回りません
※AMT102以外のエンコーダを使う場合には設定が別途ひつようになります。


まずはデータシートを探しましょう。
Googleでhp-z4035-14 datasheetと検索します。
すると、以下のページが見つかります。

俺が使うのは一番右の14

このデータシートから得ないといけない情報は、
巻き数(Pole)と最大電流です。

このモーターの巻き数は14。最大電流は60Aですね。

巻き数というのはモーターの中に立っているコイルの数で、データシートに乗っていない場合にはモーターの隙間から数えてください。

この二つの情報をODriveのconfigに保存していきましょう。
まずはanaconda promptでodrivetoolと入力し、odrivetoolを起動してください。

※odrivetoolって何って方はもっかい英語のページのodrivetoolのところを読んでanacondapromptのインストールからやってください。

次は巻き数を登録します。データシートでは14Tとなっていたので、このモーターのコイルは14本です。しかしながら、Odrivetoolの設定ではPole_Pairsを設定するので、ペアになるので÷2して、7と設定します。

次は最大電流の設定

そして最後に忘れてはいけないのが、

はい。ここまでやったら、電源を入れなおしましょう。

==============================

まずはキャリブレーションをします

モーターのキャリブレーションに成功しました。

続いてモーターをodrivetoolを使って回してみます。

回りましたー
回転スピードのコントロールで回しました。
うまくコントロールできています。

続いて
位置制御で回していきます。


上手く回りました。

6/7
本日はpcがうまく動かないので、can通信用のケーブルを作りました。

6/8
本日はarduino側のコードを書いていくために必要な情報をまとめます。

arduinoでcan通信するためにarduinoにつけているhatはmcp2515です。
mcp2515のcs(chip selecrt)ピンとarduinoの何番ピンが接続されているのかを調べます。

csピンはmcp2515とarduinoがSPI通信するために使われる。

データシートを読むと

https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Stand-Alone-CAN-Controller-with-SPI-20001801J.pdf


チップの右上から3番目のピンがcsであることがわかるね。
したら、テスターで、arduinoの9番ピンとチップのcsピンの通電チェックを行うよ。
したら、つながっているかどうかわかる!

そして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を使って


これで私のボーレートは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のハートビートを確認することができます。


できました!

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を回すという目標を達成することができました!

それでは自由に制御していきましょう!

自由に制御

ご不明な点がありましたら、コメントで教えてください。



この記事が気に入ったらサポートをしてみませんか?