ROSで動かすロボットカー作り(きれいに地図を作成できるようにしてみた編)
前回はHector SLAMを使って地図が更新できるようにいろいろセットアップできるようにしてみました
ところが、ここで作成された地図は思ってたのと違う結果になってしまいどうしたことか…という状態でした。
そんな中久々にROS UG JPのLT大会があり、前回エントリーをネタにLTしてきました。
このスライドをTwitterで投稿したら思いのほかバズって笑ってしまったのはここだけの話ですw。
で、そのツイートのリプに出力された地図がおかしい原因の解消に繋がりそうな有力情報がありました
こんにちは。
— nisshan_ にっしゃん (@nisshan_) 2022年5月9日
(rosjpの発表、お疲れ様でした!)
なんとなくなのですが、hector slamで地図がきれいに出来ない原因はtfの設定が上手くいってないから、な気がします。
この方の記事が参考になると思います。https://t.co/kFIECxysp8
それだぁぁーーーー!!!!
確かに言われて見れば地図を作成されるときってTFの座標系が表示されてそこから軌跡がrvizに出力されるはずなのにそんな様子がひとつもなかったんです。
というわけで、このリプをヒントに地図がきれいに生成されるように試してみました。(といっても頂いた記事の内容を参考にしただけですが…)
今回の成果物はいつもどおり以下のレポジトリのbacklog/step7
ブランチで切っています。
mapping_default.launchの修正
以下の記事を参考にlauchファイルをいくつか修正していきます。
hector_slam
ディレクトリのhector_mapping/launch/mapping_default.launch
のなかの5,6行目を修正します。
- <arg name="base_frame" default="base_footprint"/> - <arg name="odom_frame" default="nav"/> + <arg name="base_frame" default="base_link"/> + <arg name="odom_frame" default="base_link"/>
そして54行目のコメントアウトを解除します。
- <!--<node pkg="tf" type="static_transform_publisher" name="map_nav_broadcaster" args="0 0 0 0 0 0 map nav 100"/>--> + <node pkg="tf" type="static_transform_publisher" name="map_nav_broadcaster" args="0 0 0 0 0 0 map nav 100"/>
最終的に以下のようになっていれば大丈夫です。
<?xml version="1.0"?> <launch> <arg name="tf_map_scanmatch_transform_frame_name" default="scanmatcher_frame"/> <arg name="base_frame" default="base_link"/> <arg name="odom_frame" default="base_link"/> <arg name="pub_map_odom_transform" default="true"/> <arg name="scan_subscriber_queue_size" default="5"/> <arg name="scan_topic" default="scan"/> <arg name="map_size" default="2048"/> <node pkg="hector_mapping" type="hector_mapping" name="hector_mapping" output="screen"> <!-- Frame names --> <param name="map_frame" value="map" /> <param name="base_frame" value="$(arg base_frame)" /> <param name="odom_frame" value="$(arg odom_frame)" /> <!-- Tf use --> <param name="use_tf_scan_transformation" value="true"/> <param name="use_tf_pose_start_estimate" value="false"/> <param name="pub_map_odom_transform" value="$(arg pub_map_odom_transform)"/> <!-- Map size / start point --> <param name="map_resolution" value="0.050"/> <param name="map_size" value="$(arg map_size)"/> <param name="map_start_x" value="0.5"/> <param name="map_start_y" value="0.5" /> <param name="map_multi_res_levels" value="2" /> <!-- Map update parameters --> <param name="update_factor_free" value="0.4"/> <param name="update_factor_occupied" value="0.9" /> <param name="map_update_distance_thresh" value="0.4"/> <param name="map_update_angle_thresh" value="0.06" /> <param name="laser_z_min_value" value = "-1.0" /> <param name="laser_z_max_value" value = "1.0" /> <!-- Advertising config --> <param name="advertise_map_service" value="true"/> <param name="scan_subscriber_queue_size" value="$(arg scan_subscriber_queue_size)"/> <param name="scan_topic" value="$(arg scan_topic)"/> <!-- Debug parameters --> <!-- <param name="output_timing" value="false"/> <param name="pub_drawings" value="true"/> <param name="pub_debug_output" value="true"/> --> <param name="tf_map_scanmatch_transform_frame_name" value="$(arg tf_map_scanmatch_transform_frame_name)" /> </node> <node pkg="tf" type="static_transform_publisher" name="map_nav_broadcaster" args="0 0 0 0 0 0 map nav 100"/> </launch>
tutorial.launchの修正
続いて同じくhector_slamディレクトリのhector_slam_launch/launch/tutorial.launch
のuse_sim_time
をfalse
にしてリアルタイム動作にします。
これで実機で問題なく地図が作成されるようになります。
<?xml version="1.0"?> <launch> <arg name="geotiff_map_file_path" default="$(find hector_geotiff)/maps"/> <param name="/use_sim_time" value="false"/> <node pkg="rviz" type="rviz" name="rviz" args="-d $(find hector_slam_launch)/rviz_cfg/mapping_demo.rviz"/> <include file="$(find hector_mapping)/launch/mapping_default.launch"/> <include file="$(find hector_geotiff)/launch/geotiff_mapper.launch"> <arg name="trajectory_source_frame_name" value="scanmatcher_frame"/> <arg name="map_file_path" value="$(arg geotiff_map_file_path)"/> </include> </launch>
このファイルから先程修正したmapping_default.launch
を呼び出していることがわかると思います。
hector_slam.launchの修正
最後にmy_roberのlaunchディレクトリ内の hector_slam.launch
を先程修正した tutorial.launch
を呼び出すように修正します。
<!-- hector_slam --> - <include file="$(find hector_mapping)/launch/mapping_default.launch" /> - <include file="$(find hector_geotiff)/launch/geotiff_mapper.launch"> - <arg name="trajectory_source_frame_name" value="scanmatcher_frame"/> - <arg name="map_file_path" value="$(arg geotiff_map_file_path)"/> - </include> + <include file="$(find hector_slam_launch)/launch/tutorial.launch"/> </launch>
動作確認
それでは動作確認です。
以前用意したJoystickノードを使ってゲームパッドでロボットを動かすことで地図が作成されていきます。
このときJoystickノードから速度を配信するときにはなるべくゆっくりに設定するのがコツです。特にangular.z
に関してはめちゃくちゃゆっくりにしないとちゃんと軌跡が描画されずTFの設定がちゃんとできてないときの汚い地図になります。
それに気をつけながら地図作成してみると、動画ではわかりにくいですがちゃんと軌跡に沿って空間情報が作成されていることが確認できます。
そして完成した地図です、前回とは比べ物にならないぐらいきれいな地図ができました!
まとめ
今回はHector SLAM できれいに地図が作成されるようにリベンジをしてきました。
ROSで位置、姿勢推定するうえでtfはかなり重要なキーワードで理解するのが大変ですが、これがわからないとSLAMが出来ないということに気付かされました。アドバイスくれたnisshan_ さん、ありがとうございました!
これでナビゲーションも順調に行ってくれるといいな…
BIOSパスワードのかかったThinkpadを突破してみた
その昔大学入学した頃何もPCの知識がないときに大学推奨PCを使っていました。
大学推奨のPCはクソスペックな上に値段も馬鹿みたいに高いです。
PCを買い替えてから今までずっと放置されていましたが、ついにそんなPCを手放しました。大須の佐古前装備さんで買取お願いしたら一応まだ動くこともあって意外といい値段付きました。
PCパーツやラップトップ売るなら佐古前装備さん超オススメです!
で手放す代わりにおなじみ大須のパウでジャンクのThinkpadを買いました(どうしてそうなったw)
大学から使ってたPCを手放して一度使ってみたかったThinkpadをジャンクで買ってみた
— KMiura (@k_miura_io) 2022年5月8日
BIOSロックがかかってるみたいなのでそれを解除していくところからやっていく#パウもりあげ隊 pic.twitter.com/G3JbxKAPEX
お店にはいくつかジャンクのThinkpadがあって、こいつが一番見た目のダメージがひどくなかったので選びましたが、BIOSロックがかかっているのでPCのスペックを確認できないのがジャンク理由です。
ちなみに裏に張ってあったシール跡からどうやらIBMで昔業務用に使われていたものだったみたいです(Thinkpadは元々IBMが製造してましたもんね)。
ググってみたら工夫すればパスワードを突破できるみたいなので今回はこのBIOSパスワードを突破してみたいと思います。
用意するもの
- Thinkpad x201i(パウで購入、メモリなし)
- メモリ 2GB(PC3-8500)
- ドライバー
- 縫い針
- ピンセット
まずは動作確認
試しに電源入れてみます。
BIOSがかかっているだけで通電は大丈夫でした。バッテリーもまだ生きてます。
とりあえず見た目の損傷もないしますます直しがいありますね。
突破法1:BIOSリセット
キーボードを固定しているネジ(底面にキーボードのマークがあるところ)をはずしてキーボードを外せます。
黄色いカバーがついてるところがボタン電池です。つながっているコネクターを外してしばらく放電させてみます。
つなげ直して電源を入れましたが解除できませんでした。
というわけでこのThinkpadにはスーパーバイザーパスワード(SVP)がかかっているので今度はSVPを解除する方法を試します。
突破法2:針を刺してみる
SVPがかかっていることが分かったところで突破方法を試してみます。
なるべく分解したくないので針を使ってeepromの信号を乱してみます。
↑の記事を参考に縫い針をピアに当てて起動を試みましたが何度やってもだめでした…
突破法3:eepromのi2cの信号ピンをショートさせる
針でうまく信号を乱せなかったのでeepromの信号ピン(SDA, SCL)をショートさせます。
eepromは基板を外す必要があるので、ハードウェア保守マニュアルをみながら外せるところを外して分解します。
分解するときは写真を撮っておくと安心です。
SDA、SCLの位置は以下の記事を参考にしました
SDAとSCLは隣り合わせみたいなのでピンセットを使って両方のピンをはさみます。
そして電源を入れると無事にSVPを突破してBIOSにアクセスできました!そして画面もきれいなので普通にOS突っ込めば問題なく使える状態になりました。
パスワードも無効になって一安心です。
ThinkpadのSVP突破してなんとかBIOS開けた!
— KMiura (@k_miura_io) 2022年5月15日
画面も大丈夫そうだしあとはOS突っ込めば普通にパソコンとして動かせそうだな#パウもりあげ隊 pic.twitter.com/LJFE3Xalhc
キーボードの交換&OSインストール
というわけで分解したやつを元に戻してPCとして使える状態に戻りました。ここでよくありがちなネジの締め忘れでネジが余るという事態は起きませんでした(逆にネジを紛失して不足する事態もありませんでしたw)
無事に元の状態に組み上がったところで魔改造をします。僕はキーボードは英語配列派なので英語配列のキーボードに交換します(簡単にキーボードを交換できるという理由でThinkpadを買っています)。
キーボードはヤフオクで唯一売られてた新品を買いました。
そしてキーボードだけ無駄に新品のPCが出来上がりましたw。
そしてSSDを用意してOSをインストールします。
mSATAのSSDを使いたかったですが残念ながらこのモデルはストレージには対応してないっぽかったので普通に2.5インチのやつを使いました。
インストールするOSは例のごとくUbuntuです。
Ubuntu 20.04のインストールは慣れたもんです。
そして、無事にOSが立ち上がりました。
キーボードの反応も問題ないし無事にパスワードがかかってたPCが復活しました!
まとめ
今回はパスワードがかかったThinkpadを使えるようにしました。見た目もひどくないからこそこういうパターンはちょっとクセがありますが動かせるとかなり格安でPCを使えるようになるのでぜひ挑戦してみましょう!
まぁ今回はキーボードを交換したので余計お金かかりましたけどねw
ROSで動かすロボットカー作り(SLAMをできるようにしてみた編その2)
前回はgmappingを使ってSLAMをやろうとするところまでで終わりました
このブログの中では地図が更新されなくて多分机の上で動かすだけだと更新されないと思ってました。
が、これは大きな勘違いで正確にはodometryのデータを一つも用意してなかったのが原因です。
gmappingではロボットの移動距離の情報としてodometryをロボット側で配信する必要がありますが、それができてませんでした。
ただ、今作っているロボットにはロータリーエンコーダーとかを使ってモーターの回転数取れればいいのですが、なるべく楽したいのでLidarセンサーだけで地図を作成する方法を模索しました。
今回の成果物もいつも通り以下のレポジトリの backlog/chapter6
ブランチで切っています。
用意するもの
前回と同様のものですが、一応載せておきます
- Raspberry Pi 4
- YdLidar X2L
動作環境
今回もRaspberry Pi上で試すのでRaspberry Piの動作環境を紹介します
- Raspberry Pi OS Legacy(Busterベース)
- ROS melodic
LidarセンサーだけでSLAMをやる方法
ググったらいくつか候補が出てきたので試しました。
候補1:gmapping + laser_scan_matcher
gmapping単体だとodomデータが足りなくて地図が更新されませんが、laser_scan_matcherを使ってLidarのデータの変化量から移動距離を算出します。
この方法ならセンサーを用意したり自分でロジックを書かなくても地図が更新されるようになるみたいです。
ただ、Raspberry Pi OSだとlaser_scan_matcherを使うために必要な依存パッケージが多いのとビルドエラーが出まくっているので今回は却下しました。
候補2:hector_slam
gmappingの代わりにhector_slamを使う方法が候補にありました。
これならodomデータがなくてもパッケージ単体で地図を生成できるらしいです。
試しに動かしてみたらセンサー単体を動かすだけでも簡単に地図を更新できるようになったので今回はこの方法で地図を生成してみました。
hector_slamをインストール
まずは依存パッケージをインストールします。
sudo apt install libflann-dev libeigen3-dev libglvnd-dev
ワークスペースのディレクトリ直下のsrcディレクトリ内にhector_slamのソースコードをクローンします。
git clone https://github.com/tu-darmstadt-ros-pkg/hector_slam
ビルドを実行します。
catkin build
パッケージを追加したのでセットアップスクリプトを実行します。
source devel/setup.bash
これで必要な物は揃いました。gmappingのときと同じぐらい簡単にインストールが終わりました。
とりあえず動かしてみる
インストールはできたので試しに机の上でセンサーを動かしてみます。
今回は以下のlaunchファイルを使います。
ファイル名は hector_slam.launch
とします。
<launch> <!-- X2L --> <node name="ydlidar_node" pkg="ydlidar_ros" type="ydlidar_node" output="screen" respawn="false" > <param name="port" type="string" value="/dev/ydlidar"/> <param name="baudrate" type="int" value="115200"/> <param name="frame_id" type="string" value="base_link"/> <param name="resolution_fixed" type="bool" value="true"/> <param name="auto_reconnect" type="bool" value="true"/> <param name="reversion" type="bool" value="false"/> <param name="angle_min" type="double" value="-180" /> <param name="angle_max" type="double" value="180" /> <param name="range_min" type="double" value="0.1" /> <param name="range_max" type="double" value="12.0" /> <param name="ignore_array" type="string" value="" /> <param name="frequency" type="double" value="8"/> <param name="samp_rate" type="int" value="3"/> <param name="isSingleChannel" type="bool" value="true"/> </node> <!-- tf --> <node pkg ="tf" type="static_transform_publisher" name="map_to_odom" args="0.0 0.0 0.0 0.0 0.0 0.0 /map /nav 40"/> <node pkg ="tf" type="static_transform_publisher" name="odom_to_base_link" args="0.0 0.0 0.0 0.0 0.0 0.0 /nav /base_footprint 40"/> <node pkg="tf" type="static_transform_publisher" name="base_link_to_laser" args="0.2245 0.0 0.2 0.0 0.0 0.0 /base_footprint /base_link 40" /> <!-- hector_slam --> <include file="$(find hector_mapping)/launch/mapping_default.launch" /> <include file="$(find hector_geotiff)/launch/geotiff_mapper.launch" /> </launch>
gmappingのときは直でパラメータを指定してましたが、hector_slamではデフォルトで用意されているlaunchファイルを呼び出すようにしています。
ちなみに上記のlaunchファイルは以下の記事を参考にX2L向けにアレンジしました
実際に動かしてみます。
以下のコマンドでlaunchファイルを動かします。
roslaunch my_rober hector_slam.launch
そして可視化は以下のコマンドでrvizを起動します。(前回の同じ設定ファイルを読み込んでますが、他のSLAMパッケージでも使えるのでファイル名を変えてます)
rosrun rviz rviz -d $(find $(pwd) -name slam.rviz)
起動したらセンサーをいろいろ動かしてみるとそれに合わせて地図が更新されていることが確認できます。
gmapping にodomデータが必要になるの気づかなかったのでHector SLAMにピボットした
— KMiura (@k_miura_io) 2022年5月4日
これならlidarだけで簡単に地図出来そう#ROS #rosjp pic.twitter.com/1zZDmM51AF
navigationパッケージのセットアップ
無事に動作することが分かったところで実際に地図を作成していきます。
ただ今のままだとロボットを走らせて地図を作成してもその地図を保存する手段がありません。
地図を保存するためには navigation
パッケージの map_server
を使用します。
そこで地図を作成する前にnavigationパッケージをセットアップしていきます。
まずは以下のコマンドで依存ライブラリをインストールします。
sudo apt install libbullet-dev libsdl-image1.2-dev libsdl-dev
ワークスペースディレクトリ直下のsrcディレクトリ上でnavigationパッケージとその依存パッケージをクローンします。
git clone -b melodic-devel https://github.com/ros-planning/navigation.git git clone -b melodic-devel https://github.com/ros/geometry2.git git clone -b ros1 https://github.com/ros-planning/navigation_msgs.git
ビルドを実行します。
catkin build
hector_slamと同様にセットアップスクリプトを実行します。
source devel/setup.bash
地図を作成する
いよいよ地図を作成していきます。
まずは以下の記事を参考にJoystickでロボットを動かす準備をします。
地図を作成する場所にロボットを設置したら以下のコマンドでマッピング用のノードを起動します
roslaunch my_rober hector_slam.launch
これでゲームコントローラーでロボットを操作しながら地図を作成します。
一通り地図を作成したら以下のコマンドで地図のデータを保存します。
rosrun map_server map_saver
作成した地図はこんな感じです。
Lidarのみで作成しているだけに結構ぐちゃぐちゃした地図になってますね…
まとめ
今回はhector_slamを使ってSLAMに挑戦しました。
Lidarだけでマッピングできると思ってやってましたが、やっぱり急な方向転換に弱いせいか精度は今ひとつでしたね。
今度は精度の高い地図を作成するためにどうすればいいか試行錯誤していこうと思います。
ROSで動かすロボットカー作り(SLAMをできるようにしてみた編)
※最初に
このエントリーのオチはパッケージの僕の理解不足で地図が作成されませんでした。
とにかくてっとり速く動いたパッケージのセットアップ方法を知りたい方は以下のエントリーをどうぞ
以前Lidarセンサーを動かして無事にセンサーの情報を取得することができたので、今回はこのLidarセンサーを使ってSLAMをできるようにしてみます。
Lidarセンサーを試した記事はこちら
今回の成果物はいつものように以下のレポジトリの backlog/step5
ブランチで切っています。
用意するもの
ロボットには他にもいくつかパーツ使っていますが、今回のブログで使う機材だけを紹介します
- Raspberry Pi 4
- YdLidar X2L
動作環境
今回はRaspberry Pi上で試すのでRaspberry Piの動作環境を紹介します
- Raspberry Pi OS Legacy(Busterベース)
- ROS melodic
Gmappingのインストール
まずはSLAMに必要なパッケージをインストールします。
今回は最もメジャーなSLAMパッケージであるGmappingをインストールします。
ワークスペースディレクトリ直下のsrcディレクトリ内で以下のコマンドを実行してパッケージのソースコードをクローンしてきます。
cd rober_catkin_ws/src git clone https://github.com/ros-perception/slam_gmapping
続いて、依存パッケージであるopenslam-gmappingも合わせてクローンします。
git clone https://github.com/ros-perception/openslam_gmapping.git
これで必要なパッケージのインストールができたので、ビルドします。
catkin build
エラーが出なければインストールは完了です。
lauchファイルの用意
続いてマッピング用のlaunchファイルを用意します。
Gmapping向けにX2LでSLAMできるようにLidar, gmapping, tfの各ノードの設定をしたlaunchファイルが以下の内容です。
ファイル名は slam_gmapping.launch
とします
<launch> <!-- X2L --> <node name="ydlidar_node" pkg="ydlidar_ros" type="ydlidar_node" output="screen" respawn="false" > <param name="port" type="string" value="/dev/ydlidar"/> <param name="baudrate" type="int" value="115200"/> <param name="frame_id" type="string" value="base_link"/> <param name="resolution_fixed" type="bool" value="true"/> <param name="auto_reconnect" type="bool" value="true"/> <param name="reversion" type="bool" value="false"/> <param name="angle_min" type="double" value="-180" /> <param name="angle_max" type="double" value="180" /> <param name="range_min" type="double" value="0.1" /> <param name="range_max" type="double" value="12.0" /> <param name="ignore_array" type="string" value="" /> <param name="frequency" type="double" value="8"/> <param name="samp_rate" type="int" value="3"/> <param name="isSingleChannel" type="bool" value="true"/> </node> <!-- gmapping --> <node pkg="gmapping" type="slam_gmapping" name="mapper"> <param name="maxUrange" value="8.0" type="double" /> <param name="delta" value="0.03" /> <param name="xmax" value="30" type="double" /> <param name="ymax" value="30" type="double" /> <param name="xmin" value="-30" type="double" /> <param name="ymin" value="-30" type="double" /> </node> <!-- tf --> <node pkg ="tf" type="static_transform_publisher" name="map_to_odom" args="0.0 0.0 0.0 0.0 0.0 0.0 /map /nav 40"/> <node pkg ="tf" type="static_transform_publisher" name="odom_to_base_link" args="0.0 0.0 0.0 0.0 0.0 0.0 /nav /base_footprint 40"/> <node pkg ="tf" type="static_transform_publisher" name="base_link_to_laser" args="0.2245 0.0 0.2 0.0 0.0 0.0 /base_footprint /base_link 40" /> </launch>
動かしてみる
いよいよ動かしてみます。
以下のコマンドでslam_gmapping.launchを起動します
roslaunch my_rober slam_gmapping.launch
エラーなくログに以下の1行が表示されたらセンサーは正常に起動しています。
[YDLIDAR INFO] Now YDLIDAR is scanning ......
続いてRvizでマッピングした内容を表示します。
ワークスペースのディレクトリ上で以下のコマンドを実行し、設定ファイルを読み込んでRvizを起動します。
rosrun rviz rviz -d $(find $(pwd) -name gmapping.rviz)
実行すると以下のようにLidarから作成した地図が表示されるようになります。
ACアダプターに接続していて大きく移動してないので地図は更新されることはありません。
多分ロボットが自由に動かせる状態にして動き回れば更新させられるはずです…
ここでSLAMで必要になる座標情報を扱うtfで配信されているフレームのtransformツリーを確認するために以下のコマンドを実行します。
rosrun tf view_frames
実行すると 以下のようにtransformツリーのframes.pdf
が出力されます。
まとめ
今回はRaspberry PiでSLAMをできるようにしてみました。
SLAMはなかなか日本語の文献がなくて、しかもYDLidar向けだとなかなか情報が見つからなくてここまでできるのに結構時間がかかりました。
この辺の知識は学生時代に授業でやってたことだとは思うのですが、真面目に授業受ければよかったなと後悔しましたw。
これでSLAMがなんとかできるようになったので次回は実際に走行させて地図を作成していきます。
ROSで動かすロボットカー作り(ゲームコントローラーで操作できるようにしてみた編)
去年aliexpressの独身セールではいろいろ爆買いをしてたのですが、その中でノーブランドなUSBで接続できるコントローラーを買いました。
このコントローラーは前回動かしたLidarセンサーを買ったときと同じタイミングで買いました。
僕は普段ゲームはマイクラをやるぐらいでコントローラーを使うようなゲームはしてません。
そう、まさにROSでゲームコントローラーを使いたくて買ってましたが半年ぐらい放置してましたw。
というわけで今回はこの積んでたコントローラーを使ってロボットカーを操作できるようにしてみました。
今回の成果物は以下のレポジトリのbacklog/step4
ブランチで切っています。ちなみに直進と回転のメッセージが同時に配信されたときに直進の値関係なく回転するというバグがあったので、前回切ったブランチからrober.pyのロジックを変更しています。
実行環境
- Ubuntu 20.04
- ROS noetic
とりあえずメッセージを受け取ってみる
まずはROSでジョイスティックのメッセージを配信するパッケージをインストールします。
以下のコマンドでパッケージをインストールできます。本来は有名メーカーのコントローラーのROS用ドライバーをインストールする必要がありますが、ビルドエラーが発生するのと今回のコントローラーはなくても動かせたのでドライバーはインストールせずいきます。
sudo apt install ros-noetic-joy
それではジョイスティックを動かすノードを起動します。
以下のコマンドでroscoreを立ち上げます。
roscore
コントローラーをパソコンに接続したら別ターミナルを開いて以下のコマンドでジョイスティックノードを起動します。
rosrun joy joy_node
更に別ターミナルを開いて以下のコマンドを実行するとjoy_nodeから配信されるメッセージを購読できます。
rostopic echo /joy
ボタンやレバーを操作すると以下のようにリストの値の変化を確認できます。
ボタンやレバーを操作したときと離したときでそれぞれメッセージが配信されることが確認できます。
--- header: seq: 1613 stamp: secs: 1648992334 nsecs: 825536911 frame_id: "/dev/input/js0" axes: [-0.0, 1.0, 0.0, 0.0, 0.0, 0.0] buttons: [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] --- header: seq: 1614 stamp: secs: 1648992334 nsecs: 841519585 frame_id: "/dev/input/js0" axes: [-0.0, 1.0, 0.0, 0.0, 0.0, 0.0] buttons: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] --- header: seq: 1615 stamp: secs: 1648992334 nsecs: 914614853 frame_id: "/dev/input/js0" axes: [-0.0, -0.0, 0.0, 0.0, 0.0, 0.0] buttons: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ---
Pythonでロボットをコントロールする
これで無事にコントローラーで値を取れるようになったので今度はPythonでロボットを操作してみます。
実装
先程確認したjoy_nodeからのメッセージを参考にしながらロボットの操作を実装すると以下のコードになります。
ファイル名は control_rober_joystick.py
とします。
#! /usr/bin/env python3 import rospy from rospy.exceptions import ROSInterruptException from sensor_msgs.msg import Joy from geometry_msgs.msg import Twist class Contorller: def __init__(self): rospy.init_node('controller', anonymous=True) self.linear_x = 0 self.angular_zr = 0 self.angular_zl = 0 self.pub = rospy.Publisher('cmd_vel', Twist, queue_size=10) self.sub = rospy.Subscriber('joy', Joy, self.joy_callback) rospy.on_shutdown(self.shutdown_callback) rospy.Timer(rospy.Duration(1.0), self.timer_callback) def joy_callback(self, joy_msg): # Get Joy Event self.linear_x = joy_msg.axes[1] self.angular_zr = joy_msg.buttons[1] self.angular_zl = joy_msg.buttons[3] def timer_callback(self, event): twist = Twist() twist.linear.x = int(self.linear_x) * 0.5 twist.angular.z = int(self.angular_zl - self.angular_zr) * 0.5 rate = rospy.Rate(10) for i in range(5): self.pub.publish(twist) rospy.loginfo("linear_x: %f, angular_z: %f", twist.linear.x, twist.angular.z) rate.sleep() def shutdown_callback(self): rospy.loginfo("Stop") rospy.sleep(1) if __name__ == '__main__': try: rospy.loginfo("Start") Contorller() rospy.spin() except ROSInterruptException: pass
これまでのシリーズで書いてきたコードとよく似ていますが、いくつか解説したいと思います。
まずは、コントローラーからのイベントを受け取る方法ですが、以下のコールバック関数を使用します。
ジョイスティックのイベントがオブジェクトで格納されているのであとはそこから必要な値を受け取るようになっています。どの値を受け取るかどうかはコントローラーによって変わると思うので、実際に動かしながらjoy_nodeから配信されるメッセージを確認するのがいいかと思います。
def joy_callback(self, joy_msg): # Get Joy Event self.linear_x = joy_msg.axes[1] self.angular_zr = joy_msg.buttons[1] self.angular_zl = joy_msg.buttons[3]
そして次にメッセージを配信するにあたり以下の1行は僕の中では割と工夫したことです。angularの指定はボタンを押したか押してないか(1か0)が値として格納されています。つまり片方が1であればもう片方は必ず0になるようになっています。仮に別のボタンのイベントを拾って両方反応したとしてもプラマイゼロになるので回転は0として送られるわけです。
twist.angular.z = int(self.angular_zl - self.angular_zr) * 0.5
lauchファイルを用意
今回のプログラムは先程動かしたjoy_nodeが動いていることでそこからメッセージを購読してロボットに動きを配信する挙動になっています。
つまりjoy_nodeを起動しているのが前提なので、プログラムとセットで起動できるようにlaunchファイルを用意します。
ファイル名は joystick_control.launch
とします。
<launch> <node name="joystick" pkg="joy" type="joy_node" /> <node name="control_rober" pkg="my_rober" type="control_rober_joystick.py" output="screen" /> </launch>
動作確認
それでは動作確認です。
今回は遠隔でロボットを動かそうと思うので、以前の記事を参考にマスターとスレーブの設定をしておきます。
その後、マスターでroscoreを起動します。
roscore
ロボット側では以下のコマンドを実行して司令の入力待ち状態にしておきます。
rosrun my_rober rober.py
コントローラーをつないでいるPCでは以下のコマンドを実行してコントローラーを動かせるようにします。
roslaunch my_rober joystick_control.launch
実際にロボットを動かしてみたのがこちらの動画です。
ちゃんとコントローラーの操作に合わせて車輪の動きが変わっているのがお分かりいただけると思います。
ゲームコントローラーからロボットを動かせるようになった!
— KMiura (@k_miura_io) 2022年4月6日
コードを改造して変なところにバグが出てることに気づくの時間かかったけどなんとか想定通りに動いてよかったよかった(ハードウェアのテストどうにか出来ないかな…#rosjp pic.twitter.com/tq4RMbylM8
まとめ
今回はゲームコントローラーを使ってロボットを操作してみました。
普通にゲームコントローラーを動かそうとすると専用のインターフェースやドライバーが必要だったりしますが、ROSにはそのようなパッケージも含まれているので本当に便利です。
これを応用すればアームロボットの操縦もコントローラーで簡単にできるようになるはずです。
これでロボットを操縦できる状態になったので、これでロボットのマッピングをするために必要なものが準備できました。
というわけで次回は以前動かしたLidarセンサーを使ってロボットを動かしてみようと思います。
Dell Venue 8 Proにlubuntuを入れてROSもインストールしてみた
つい最近、佐古前装備さんでジャンクPCを漁っていたらWindowsのタブレットPCを見つけました。
それがDellの Venue 8 Proです。
佐古前装備はパソコンやパーツ、周辺機器等を扱うお店でタブレットやスマホはめったに販売することがありません。
そのせいかタブレットの価格設定がバグっててジャンク価格でなんと1100円でしたw。
ちなみにジャンク理由はタッチパネルの反応がおかしいとのことです。
ヤフオクではあまり出ていませんでしたが、それでも1000円台の販売はそんなにないのでかなりお買い得です。
以前買ったジャンクのタブレットPCと同じ価格で有名メーカーのタブレットが買えるのはかなりラッキーな気がして迷わず買いました。
買った後に電源を入れてみたら普通に画面が映りました!(OSはWindows8.1が買ったときから入ってました)
そしてタッチパネルに関しては画面の縁の反応が悪いぐらいで大方の画面が普通に反応してたので悪くないです。
これでLinux突っ込んでもちゃんと動く気がします。
というわけで今回はこのタブレットにUbuntuを突っ込んでみます。
インストールメディアを起動しようとしたら…
以前タブレットにインストールしたときと同様にインストールメディアを起動しようとしたらめちゃくちゃ重くて結構イライラしましたw。
何度か再インストールしたりして試行錯誤して2、3日経過しましたがうまく行かなかったです…
というわけでUbuntuのインストールは断念してUbuntuよりも軽量な派生ディストリビューションのlubuntuをインストールしてみました。
lubuntuのインストール
インストールメディアの作成
まずはlubuntuのOSイメージをダウンロードすることにしました。
以下のリンクからLTSの20.04のダウンロードします。
OSのインストール
インストールメディアの作成手順は以下の記事の通りなので、割愛します。
Ubuntuの派生ディストリビューションで使用するイメージが違う程度なのでOSのインストールも同じ手順でやってもらえばOKです。
インストール完了したら、インストールメディアを外して再起動したら以下のようにデスクトップ画面が表示されたら成功です。
ROSのインストール
OSを入れたところで今度はlubuntuにROSを入れてみます。
インストールは以下のリンクの手順通りにできます。
Ubuntuの派生なのでUbuntuのときと同様の手順で大丈夫です。
Turtlesimを動かしてみる
それでは試しにTurtlesimを動かしてみます。
ワークスペースの作成し適当な名前のパッケージを作成します(今回はワークスペース名はcatkin_ws
、パッケージ名はhelloworld
とします)。
cd ~/catkin_ws/src catkin_create_pkg helloworld
helloworld
ディレクトリを開いて以下のlauchファイルを作成します(ファイル名はcontrol_turtle.launch
とします)。
<launch> <group ns ="sim"> <node name="turtlesim" type="turtlesim_node" pkg="turtlesim"/> <node name="teleop_key" type="turtle_teleop_key" pkg="turtlesim" /> </group> <launch>
このlaunchファイルを実行して以下のようにturtlesimをteleopkeyで動かせるようになったらROSは正常に動作しています。
タブレットにrosつっこんでみたら意外と動くぞ!
— KMiura (@k_miura_io) 2022年3月28日
これをロボットのモニタリング端末にするんだ#rosjp pic.twitter.com/rfdAEig2Nb
まとめ
今回はジャンクのタブレットPCにROSを入れてみました。
以前のジャンクタブレットはタッチパネルが反応しなくて困っていましたが、今回のやつは特に特別なセットアップしなくてもタッチパネルがちゃんと反応してたので多分前回セットアップしたタブレットの調子が悪いだけということが分かりました。
ただ、今回のタブレットではUbuntuよりも軽量とはいえブラウザの挙動が固まったりして全体的に動作がもっさりしてたので、使い方に気をつけないといけないですね…
今後はこのタブレットでROSを動かしてちょっとしたロボットのモニタリングなんかやってみたいなと思います(処理落ちしないか心配ですが…)
Arducam Pico4ML TinyMLを触ってみた
Switch Scienceで面白そうなボードを見つけたので買ってみました。
最近注目されているTiny MLのためのRaspberry Pico互換ボードです。
もともとマイコンのような安価なガジェットを使った高度な処理をするというところに魅力を感じていて、そのポテンシャルを触って体感したいなと思ってました。
というわけで今回は一通りデモを触るだけですがボードを動かしてみたいと思います。
開封の儀
付属品はマニュアルとスイッチ付きのマイクロUSBケーブルです。
このスイッチ付きっていうところがファームウェアを書き込む作業をするときのことを想定されていていいですね!
環境構築
こちらで紹介されている方法でRaspberry Pi 400で構築していきます(デモプログラムを動かすだけなら飛ばしてもいいですが、今後自作モデルを動かすために一応やっておきます)。
以下のコマンドでスクリプトをクローンしてセットアップしていきます
git clone https://github.com/raspberrypi/pico-setup.git ./pico-setup/pico_setup.sh
スクリプトを実行したら一度Raspberry Piを再起動してUARTの設定を完了させます。
デモプログラムを動かす
環境構築を終えたら今回使用するデモプログラムを動かしてみます。
以下のコマンドでサンプルプログラムのレポジトリをクローンします。
git clone https://github.com/ArduCAM/pico-tflmicro cd pico-tflmicro
このレポジトリにはビルド済みのファームウェアが用意されているのでいくつか試してみましょう。
micro speech
まずは特定の単語に反応するデモを試します。
書き込むときには BOOTSEL
ボタンを押しながらボードの電源を入れて書き込みモードにします。(このときにスイッチ付きのケーブルを使えばそこで電源のON・OFFができるのでとても効率いいです)
以下のコマンドでRaspberry Piのストレージとマイコンボードのストレージをマウントします。
sudo mkdir -p /mnt/pico sudo mount /dev/sda1 /mnt/pico
マウントしたら以下のコマンドで使用するuf2ファイルをコピーします。
sudo cp bin/micro_speech.uf2 /mnt/pico sudo sync
書き込みを終えると以下のようにボードに実装されたマイクに「yes」か「no」を話して認識するとディスプレイに文字が表示されるようになります。(動画ではyesしか認識してませんが頑張ればnoも認識できますw)
とりあえずWake Wordのデモしてみた
— KMiura (@k_miura_io) 2022年3月18日
小さいのにやるなコイツ(このデモはyesにしか反応しないw pic.twitter.com/AfB0gNpzS5
ちなみにスマートスピーカーのWake Word(「Alexa」とか「OK, Google」みたいに最初の声掛けのこと)の認識にはこのTiny MLが使われているそうです。
person detection
次はカメラを使って人を識別するデモを試します。
書き込みモードにしたら以下のコマンドでファームウェアを書き込みます。
sudo mkdir -p /mnt/pico sudo mount /dev/sda1 /mnt/pico sudo cp bin/person_detection_int8.uf2 /mnt/pico sudo sync
書き込んだら以下のように人の画像にカメラを向けると人の認識率が表示されるようになります。
Person detection もそこそこいい感じの精度が出てる!
— KMiura (@k_miura_io) 2022年3月18日
エッジコンピューティングの可能性を感じる(言ってみたかっただけ pic.twitter.com/q9ezRqqkGJ
画像認識は定番のデモですが、やっぱり小さいガジェットで識別するとなると楽しいもんですね。
magic wand
最後はmagic wandという魔法の杖のようにボードを振り回してそのジェスチャーを認識するデモをやってみます。
書き込みモードにしたら以下のコマンドでファームウェアを書き込みます。
sudo mkdir -p /mnt/pico sudo mount /dev/sda1 /mnt/pico sudo cp bin/magic_wand_ble.uf2 /mnt/pico sudo sync
以下のようにボードを持ちながら手で数字を書くジェスチャーをするとディスプレイにその軌跡と認識した数字・識別率が表示されます
Magic Wand もやってみた
— KMiura (@k_miura_io) 2022年3月19日
MNISTの派生かもしれんがジェスチャーの手書き認識も面白いな pic.twitter.com/XDMBVlxsI0
これはマイコンならではのセンサーを使ったデモで面白いですね。
まとめ
というわけでTiny MLのマイコンボードを触ってみました。
ソースコードがC++なのでソースコードをいじるにはちょっと気合が入りますが、いずれ自前モデルを用意してなにかデモ作ってみたいですね。
それはいつかまたやります(こういうときに限って結局やらなかったり…)