環境
この記事は以下の環境で動いています。
項目 | 値 |
---|---|
CPU | Core i5-8250U |
Ubuntu | 16.04 |
ROS | Kinetic |
インストールについてはROS講座02 インストールを参照してください。
またこの記事のプログラムはgithubにアップロードされています。ROS講座11 gitリポジトリを参照してください。
概要
arduinoは便利で使いやすいマイコンシステムです。基本的にはUbuntuが載るような「高レベル」のPCではモーターなどのハードウェアを扱うことはできません。そこでarduinoのような「低レベル」のマイコンを挟むことでROSとモーター等をつなぎます。
この記事では以下の内容について記述します。
- Arduinoをplatformioを用いてCLIで使う
- platformioをROSのディレクトリで管理する
- rosserialを使ってPCと通信する。
platformIOのインストール・使い方
platformIOはarduinoやmbedなどのマイコンの開発環境をCLIで使うためのツールです。公式のarduinoコンパイラはGUIで提供されているのでとっつきやすいのですがROSの環境と合わせるのが難しいです。ROSのプログラムとArduinoのプログラムを別々に管理してもよいのですが、バージョン管理の問題が起こったりするのでできるだけ同じ環境ですべてできることが理想です。そのためにArduinoもCLIのツールで用います。公式にもarduino CLIというCLIツールがありますが、裏でIDEが立ち上がりだいぶ重いみたいなので今回は見送ります。
使用するボード
今回はarduino megaを使用します。一番普及しているのはunoですがこれはピン数や機能が少なくロボットには不足します。megaなら複数のモーターを制御することも可能です。
インストール
pipでもインストールできますが以下の方法で行うのが一番確実です。
python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/develop/scripts/get-platformio.py)"
使うボードの選択
platform boards
で使えるボード(マイコン)の一覧が出ます。長いので`grep‘するのがおすすめです。例えば今回使うarduino megaは
megaatmega2560 ATMEGA2560 16MHz 248KB 8KB Arduino Mega or Mega 2560 ATmega2560 (Mega 2560)
と表示されるので1番最初に表示されているmegaatmega2560
という名前で使えば良いことがわかります。
シリアルポートの権限
標準では一般ユーザーはシリアルポートのアクセス権限がありません。以下のコマンドで永続的に権限が持てます。
sudo gpasswd --add $USER dialout
ワークスペースの作成
作る場所はどこでもよいですが~/mega_project
にします。後半はplatformio.ini
という設定ファイルにボードへの書き込みの設定を追加しています。
mkdir ~/mega_project
cd ~/mega_project/
platformio init -b megaatmega2560
echo "upload_port = /dev/ttyACM0" >> platformio.ini
echo "targets = upload" >> platformio.ini
プログラムの作成
以下のようなプログラムを~/mega_project/src/main.ino
に書きます。
void setup(){
pinMode(13, OUTPUT);
}
void loop(){
digitalWrite(13, HIGH);
delay(500);
digitalWrite(13, LOW);
delay(500);
}
-
setup()
は起動時に1度だけ最初に実行する関数です。 -
loop()
はsetup
後に無限に実行される関数です。 -
pinMode(13, OUTPUT);
は13番ピンを出力する関数です。 -
digitalWrite(13, HIGH);
は13番ピンをHIGH(5V)にする関数です。 -
digitalWrite(13, LOW);
は13番ピンをLOW(0V)にする関数です。
コンパイル&アップロード
以下のコマンドでプログラムのコンパイルとアップロードの両方を行います。ArduinoをUSBポートに刺してから実行してください。
cd ~/mega_project/
platformio run
arduinoのボードではLEDが点滅しているはずです。
rosパッケージに統合する。
arduinoのプログラムもROSパッケージと統合するのが便利です。統合といっても実際にやるのはcatkin_wsの中にplatformioのファイルを置くこと、起動用の実行ファイルを作るだけです。
ワークスペースの作成
ROSパッケージの中のhard_lecture/platformio/ino01
にワークスペースを作ります。
roscd hard_lecture
mkdir -p platformio/ino01
cd platformio/ino01
platformio init -b megaatmega2560
echo "upload_port = /dev/ttyACM0" >> platformio.ini
echo "targets = upload" >> platformio.ini
ソースコードの作成
hard_lecture/platformio/ino01/main.ino
にプログラムを書きます。内容はさっきと同様のLチカで良いでしょう。
コンパイル&アップロード用のスクリプトを作成
先ほどと同様にコマンドを実行してもよいですがあんまりROSっぽくないのでスクリプトを書いてrosrunで実行します。
echo "writing arduino"
echo "connect arduino on USB"
cd `dirname ${0}`/../platformio/ino01
platformio run
実行権限を付与します。
roscd hard_lecture
chmod +x scripts/write_ino01.sh
実行
以下のコマンドで実行できます。roscoreを実行している必要はありません。
rosrun hard_lecture write_ino01.sh
rosserialの使用
マイコンのプログラムの一番面倒な部分は通信です。PCとArduinoの通信を自作のプロトコルで作っていたら、その製作だけでも大変なことになってしまいます。そこでできたのがrosserialです。Arduinoでrosのpublisherやsubscriberが作れます。
今回は説明しませんがparameterの取得もできます。
インストール
sudo apt-get install ros-kinetic-rosserial
sudo apt-get install ros-kinetic-rosserial-arduino
ワークスペースの作成
roscd hard_lecture
mkdir -p platformio/ino02
cd platformio/ino02
platformio init -b megaatmega2560
echo "upload_port = /dev/ttyACM0" >> platformio.ini
echo "targets = upload" >> platformio.ini
ros_libのコピー
rosのメッセージヘッダをコピーします。以下のコマンドで現在のワークスペースのメッセージファイルをarduinoで使うために変換できます。arduinoのコンパイルに使うライブラリはhard_lecture/platformio/ino02/lib
にコピーします。lib
はplatformioのワークスペースごとの個別に使うlocalなライブラリの保管場所です。
platformioのワークスペースを作るごとに必要な作業です。
roscd hard_lecture/platformio/ino02/lib
rosrun rosserial_arduino make_libraries.py .
ソースコード
以下がrosserialでpublish、subscribeをするためのarduino側のソースコードです。いつものrosのプログラムと見た目は似ていますが、細部はいくらか違うので注意が必要です。
#include <ros.h>
#include <std_msgs/String.h>
#include <std_msgs/Bool.h>
ros::NodeHandle nh;
std_msgs::String str_msg;
ros::Publisher chatter("chatter", &str_msg);
char hello[13] = "hello world!";
void led_cb(const std_msgs::Bool& msg){
if(msg.data)digitalWrite(13, HIGH);
else digitalWrite(13, LOW);
}
ros::Subscriber<std_msgs::Bool> sub0("led", &led_cb);
void setup()
{
nh.initNode();
nh.advertise(chatter);
nh.subscribe(sub0);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(13, OUTPUT);
}
void loop()
{
str_msg.data = hello;
chatter.publish( &str_msg );
nh.spinOnce();
delay(500);
}
書き込み用のスクリプト
echo "writing arduino"
echo "connect arduino on USB"
cd `dirname ${0}`/../platformio/ino02
platformio run
実行
初めに書き込みを行います。
rosrun hard_lecture write_ino02.sh
この後実行します。
roscore
rosrun rosserial_python serial_node.py _port:=/dev/ttyACM0
この状態でrostopic list
をするとarduinoでpublish、subscriveした/chatter
と/led
のROSトピックが見えます。
rostopic echo /chatter
とするとdata: "hello world!"
と見えます。
rostopic pub -1 /led std_msgs/Bool "data: true"
とするとledが点灯します。
ros_libの必要なディレクトリ
hard_lecture/platformio/ino02/lib/ros_lib
以下には多数のディレクトリが生成されます。しかしすべてのディレクトリが必要なわけではありません。特に今回はgitで管理するのであまり多くの無駄なディレクトリを作りたくありません。
ディレクトリのうち常に必須なのはros
とrosserial_msgs
とros_lib直下のファイルのみです。
これとは別にarduinoのプログラムからリンクしているstd_msgs
が必要です。
コメント
Raspberry PiはLinuxが動いて、GPIOが多数あるのでこれだけでロボットが作れるように見えますが、Raspberry Pi上でROSを動かしてかつモーターなどの制御をするという方式はお勧めしません。理由は3つあります。
- 1つ目はデバッグ性の問題で、Raspberry Piでモーター制御等のハードウェア部分を作ってしまうと、制御プログラムがRaspberry Piありきの物になってしまいます。こうすると例えばハードウェア部分だけを手元のノートPCにつないでテストするということができなくなります。
- 2つ目はポートの性能の問題です。じつはRaspberry Piでは純粋なハードウェアPWMは1つしかなく、他は精度が劣るソフトウェアPWMです。またGPIOの数も30ピン程度と大規模なロボットを作ろうとすると簡単に不足します。この範囲を超えた場合はRaspberry Piを複数使うことになり面倒です。
- 3つ目はリアルタイム性の問題です。例えば100usごとに定期的な処理をするというのはArduinoでは簡単ですが、Raspberry PiはLinuxを使っているために難しいです。
参考
platformioのインストール
platformioの使い方
rosserialの使い方