From cb230fc1853e281eb782683b72b4b656fe0e378f Mon Sep 17 00:00:00 2001 From: hellososhi Date: Mon, 7 Aug 2023 06:18:17 +0000 Subject: [PATCH] deploy: 504969d5d4b48a3452e6ae07d50df12d476b93da --- course/chap5/index.xml | 1 - course/chap5/three-dimensions/index.html | 1 - index.json | 2 +- index.xml | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/course/chap5/index.xml b/course/chap5/index.xml index b0526df..5d55432 100644 --- a/course/chap5/index.xml +++ b/course/chap5/index.xml @@ -274,7 +274,6 @@ subscribeし、<br> <details class="spoiler " id="spoiler-4"> <summary>(開発PC)物体検出を行う</summary> <p><pre><code class="language-bash">(開発PC)(docker)# cd catkin_ws; catkin_make; source devel/setup.bash -(開発PC)(docker)# roscd three-dimensions_tutorial; cd yolov3/weights; ./download_weights.sh (開発PC)(docker)# rosrun three-dimensions_tutorial object_detection.py # rvizで`/detection_result`を表示し結果を確認してみよう。 (開発PC)(docker)# rosrun three-dimensions_tutorial detection_distance.py diff --git a/course/chap5/three-dimensions/index.html b/course/chap5/three-dimensions/index.html index e1145cb..df24450 100644 --- a/course/chap5/three-dimensions/index.html +++ b/course/chap5/three-dimensions/index.html @@ -230,7 +230,6 @@ (開発PC)$ ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x

(開発PC)RealSenseのトピックの可視化

(開発PC)(docker)# rviz
 

rviz上で

を可視化して違いを確認してみましょう。

(開発PC)物体検出を行う

(開発PC)(docker)# cd catkin_ws; catkin_make; source devel/setup.bash
-(開発PC)(docker)# roscd three-dimensions_tutorial; cd yolov3/weights; ./download_weights.sh
 (開発PC)(docker)# rosrun three-dimensions_tutorial object_detection.py
 # rvizで`/detection_result`を表示し結果を確認してみよう。
 (開発PC)(docker)# rosrun three-dimensions_tutorial detection_distance.py
diff --git a/index.json b/index.json
index 23d23e6..c77532d 100644
--- a/index.json
+++ b/index.json
@@ -1 +1 @@
-[{"authors":null,"categories":null,"content":"TRAILに関する最新情報をお届けします.\n","date":-62135596800,"expirydate":-62135596800,"kind":"term","lang":"en","lastmod":-62135596800,"objectID":"2525497d367e79493fd32b198b28f040","permalink":"https://matsuolab.github.io/roomba_hack_course/authors/admin/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/authors/admin/","section":"authors","summary":"TRAILに関する最新情報をお届けします.","tags":null,"title":"TRAIL Admin","type":"authors"},{"authors":["jumpei-arima"],"categories":null,"content":"","date":-62135596800,"expirydate":-62135596800,"kind":"term","lang":"en","lastmod":-62135596800,"objectID":"451d184f9c1e53301faf72f3ade4d5c6","permalink":"https://matsuolab.github.io/roomba_hack_course/authors/jumpei-arima/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/authors/jumpei-arima/","section":"authors","summary":"","tags":null,"title":"有馬 純平","type":"authors"},{"authors":["tatsuya-matsushima"],"categories":null,"content":"人間と共生できるような適応的なロボットの開発と,そのようなロボットを作ることにより生命性や知能を構成的に理解することに興味があります. 特に現在は,深層生成モデルを用いた環境のダイナミクスのモデリング(世界モデル)・モデルベース強化学習・メタ模倣学習に関して研究を行っています.\n","date":-62135596800,"expirydate":-62135596800,"kind":"term","lang":"en","lastmod":-62135596800,"objectID":"96eea6d6aacba886dc9c6ad4c3b6f83f","permalink":"https://matsuolab.github.io/roomba_hack_course/authors/tatsuya-matsushima/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/authors/tatsuya-matsushima/","section":"authors","summary":"人間と共生できるような適応的なロボットの開発と,そのようなロ","tags":null,"title":"松嶋 達也","type":"authors"},{"authors":["yuya-ikeda"],"categories":null,"content":"","date":-62135596800,"expirydate":-62135596800,"kind":"term","lang":"en","lastmod":-62135596800,"objectID":"4c9f99a2ef4096b81e6d0a08880581e8","permalink":"https://matsuolab.github.io/roomba_hack_course/authors/yuya-ikeda/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/authors/yuya-ikeda/","section":"authors","summary":"","tags":null,"title":"池田 悠也","type":"authors"},{"authors":null,"categories":null,"content":" 開発環境 ロボットシステムの開発環境に使われている要素の概要を理解する   ROSとは ロボット開発によく用いられるROSの概要を理解する   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"f1fb9b3e706d6a8e7af6ee8a67c187b1","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap1/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap1/","section":"course","summary":"ロボットシステムの基礎知識","tags":null,"title":"Chapter 1","type":"book"},{"authors":null,"categories":null,"content":" ROSのパッケージ・ワークスペース ROSのパッケージ管理について理解しよう   ロボットシステムにおけるセンシング・アクチュエーション・通信① センサの値を読み取りロボットを動かしてみよう   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"b03bbdc309591542a3a0cf48966f3d87","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap2/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap2/","section":"course","summary":"センシング・アクチュエーション・通信①","tags":null,"title":"Chapter 2","type":"book"},{"authors":null,"categories":null,"content":" ロボットシステムにおけるセンシング・アクチュエーション・通信② 複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう   ロボットシステムにおけるセンシング・アクチュエーション・通信③ 複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"b891a66c1b2d8231b078e52b380c46a1","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap3/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap3/","section":"course","summary":"センシング・アクチュエーション・通信②","tags":null,"title":"Chapter 3","type":"book"},{"authors":null,"categories":null,"content":" 自己位置推定   ナビゲーション   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"ef4ff386bad3919cafa8bb6a9000e1a5","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap4/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap4/","section":"course","summary":"自律移動","tags":null,"title":"Chapter 4","type":"book"},{"authors":null,"categories":null,"content":" 三次元画像処理   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"c5b6edce25a3188d94c102a439d09587","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap5/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap5/","section":"course","summary":"3次元画像認識","tags":null,"title":"Chapter 5","type":"book"},{"authors":null,"categories":null,"content":" serviceとactionlib   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"8f714aaf4a960d30c5c5c987c0a4c5d1","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap6/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap6/","section":"course","summary":"複雑なロボットシステムの実装・分散処理","tags":null,"title":"Chapter 6","type":"book"},{"authors":null,"categories":null,"content":"","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"2f5bc53e4f4c671993cb760f7d80fa51","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap7/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap7/","section":"course","summary":"最終プロジェクト準備","tags":null,"title":"Chapter 7","type":"book"},{"authors":null,"categories":null,"content":" 最終プロジェクト   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"2adf7951016803e68618153c45700bf0","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap8/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap8/","section":"course","summary":"最終プロジェクト","tags":null,"title":"Chapter 8","type":"book"},{"authors":null,"categories":null,"content":"  -- Table of Contents  Program overview Courses in this program Meet your instructor    Python programming skills - Statistical concepts and how to apply them in practice - Gain experience with the Scikit, including data visualization with Plotly and data wrangling with Pandas -- Program overview 実ロボット(roomba)を利用した演習を通じて、ロボットシステムの仕組みから、センシング・認識・行動について研究開発に必要な最低限の知識や実装スキルを習得する。\nCourses in this program  Chapter 1 ロボットシステムの基礎知識\n  Chapter 2 センシング・アクチュエーション・通信①\n  Chapter 3 センシング・アクチュエーション・通信②\n  Chapter 4 自律移動\n  Chapter 5 3次元画像認識\n  Chapter 6 複雑なロボットシステムの実装・分散処理\n  Chapter 7 最終プロジェクト準備\n  Chapter 8 最終プロジェクト\n  Meet your instructor TRAIL Admin Are there prerequisites? There are no prerequisites for the first course.\n --  Begin the course   ","date":1611446400,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1611446400,"objectID":"97ede4bb026d52eb5ee887238b8270ea","permalink":"https://matsuolab.github.io/roomba_hack_course/course/","publishdate":"2021-01-24T00:00:00Z","relpermalink":"/roomba_hack_course/course/","section":"course","summary":"roombaを用いた実ロボットシステム入門","tags":null,"title":"📊 ロボットシステム入門","type":"book"},{"authors":null,"categories":null,"content":"Learn ここまでトピックを使った通信を使ってロボットシステムを構築してきました. トピック通信は,メッセージをpublish・subscribeすることで通信する,相手を仮定しない非同期な通信方法でした.\nしかし,システムを構築する場合には,「相手ノードの処理の結果を呼び出し側のノードで受け取って活用したい」 といった場合もあります.\nこのような比較的複雑な通信を実現するための通信方式として,ROSではサービス(service)とアクション(actionlib)が用意されています.\nservice これまで利用してきたトピック通信は,通信の相手を仮定しない(相手がいようといまいと関係ない)ため, ロボットシステムに特有な非同期通信・処理を簡単に実現することができました.\n一方で,他のノードに対して「特定の処理の依頼をして,その結果を待ちたい」場合など,同期的・双方向な通信が必要になることがあります. 例えば,あるノードの設定を変更をして,それが成功したかどうかを知りたい場合などに使えます. サービスを使った通信は,「クライアント・サーバ」型の通信(クライアントサーバモデル, client-server model)となり, クライアントがサーバにリクエストを送ると,サーバ側で処理を行い,クライアントにレスポンスを返します.\npythonでは,rospyモジュールのrospy.Service()や rospy.ServiceProxy()を使用することで,サーバ・クライアント を簡単に実装することができます(参考).\nまた,以下のように,コマンドライン上でサービスを活用する方法も用意されています.\n# サーバを呼び出す $ rosservice call \u0026lt;ServiceName\u0026gt; \u0026lt;Arguments\u0026gt; # 存在するサービスの一覧を表示 $ rosservice list # サービスのメッセージ型を表示 $ rosservice type \u0026lt;ServiceName\u0026gt;  actionlib ここまで,トピック通信を使うことで相手を仮定しない非同期通信を,サービスを使った通信を行うことで相手のレスポンスを待つ同期的な通信を実現できることを見てきました.\nサービスによる通信では,クライアントはサーバからのレスポンスを待つため,サーバで長い時間がかかるような処理を行う(計算量が大きい,または,移動に時間がかかるなど)場合には,クライアントの処理が長い間停止してしまうという問題があります.\nそのため,処理の呼び出し側のプログラムをブロックせずに,かつ,処理の結果(や途中経過)を知れるような非同期通信が欲しくなります. この要求を満たすのが,ROSのアクション(actionlib)です.\nactionlibは,実はトピック通信の組み合わせとして構成されており,goal(命令),result(処理の結果),feedback(途中経過),status(サーバの状態),cancel(命令の取り消し)の5つのトピックからなります. このあたりの仕様は,qiitaのROS講座が詳しいので参照してください.\npythonでは,actionlibのサーバやクライアントも,\nimport actionlib  したのちに,他の通信方式と同様にactionlib.SimpleActionServerとして,簡単に作成できます(ドキュメント).\n今回の演習では,簡単のためaction serverの作成は行いません. 変わりに,移動のためのactionとして,move_baseパッケージの中で定義されているmove_baseというactionを使うことにしましょう.\n実はこのパッケージは\nroslaunch navigation_tutorial navigation.launch  を実行してmove_baseノードを起動した際に既に利用されていました.\n move_baseパッケージの詳細はドキュメントを参照してください.\n同様に,action clientもactionlib.SimpleActionClientを利用することで簡単に作成できます.\n例えば,move_baseのaction clientを実装する際には,\nimport actionlib import tf from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal from geometry_msgs.msg import Quaternion action_client = actionlib.SimpleActionClient('move_base', MoveBaseAction) action_client.wait_for_server() # action serverの準備ができるまで待つ goal = MoveBaseGoal() # goalのメッセージの定義 goal.target_pose.header.frame_id = 'map' # マップ座標系でのゴールとして設定 goal.target_pose.header.stamp = rospy.Time.now() # 現在時刻 # ゴールの姿勢を指定 goal.target_pose.pose.position.x = X goal.target_pose.pose.position.y = Y q = tf.transformations.quaternion_from_euler(0, 0, YAW) # 回転はquartanionで記述するので変換 goal.target_pose.pose.orientation = Quaternion(q[0], q[1], q[2], q[3]) action_client.send_goal(goal) # サーバにゴールを送信  のようにクライアントのsend_goalメソッドでゴールを指定できます.\nその後,\naction_client.wait_for_result(rospy.Duration(30))  とすると,結果が返ってくるまで(この場合30秒間)クライアントの処理をブロックすることができ,\nresult = action_client.wait_for_result(rospy.Duration(30))  とすることで,result変数に処理の結果を格納できます.\n演習 【jetson・開発マシン】起動準備 cd roomba_hack git fetch git checkout feature/integrate (jetson) ./RUN-DOCKER-CONTAINER.sh (開発マシン) ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x   【開発マシン】scriptベースのnavigationを実行してみる (開発マシン)(docker) roslaunch navigation_tutorial navigation.launch (開発マシン)(docker) rosrun navigation_tutorial topic_goal.py (開発マシン)(docker) rosrun navigation_tutorial action_goal.py   【開発マシン】RealSenseで検出した障害物をコストマップに追加してみよう (開発マシン)(docker) roslaunch three-dimensions_tutorial detection_pc.launch   (総合課題)障害物を避けながらnavigationする Lidarに映らない物体も画像ベースで検出しコストマップに追加することでナビゲーション時にぶつからないようにしましょう。\nヒント\n 物体検出結果に基づいて物体部分以外をマスクしたデプス画像をpublishする depth2pc.launchでそれをsubscribeし、point(cloud)に変換する 変換されたpointからmap座標系での位置を取得する costmapに反映する move_baseアクションを使ってナビゲーションを実装しよう.  するとactionがタイムアウトした場合や,KeyboardInterruptされた場合にcancel_goalメソッドを使うことでactionをキャンセルできるように拡張できるはずです.    さらに,PyTorchを使用した自作の分類器やネット上の分類器をシステムに組み込んで(例えばセグメンテーションモデルなど),よりよく動作するように改良してみましょう.\n","date":1690329600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1690329600,"objectID":"ba96fee21f24c18e95f236953fe70e74","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap6/service-actionlib/","publishdate":"2023-07-26T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap6/service-actionlib/","section":"course","summary":"","tags":null,"title":"serviceとactionlib","type":"book"},{"authors":null,"categories":null,"content":"複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう\nLearn 前回使用したsimple_control.py では,速度,角速度,時間を指定し, \u0026ldquo;速度 * 時間\u0026rdquo; あるいは \u0026ldquo;角速度 * 時間\u0026rdquo; という演算を行うことで, ロボットを意図した場所へ移動させる命令を与えていました.\nしかし,この制御の仕方には,いくつかの問題があります.\n ホイールと地面との間に滑りがあった場合,ロボットは指定した距離より小さい距離しか移動しない可能性がある. ロボット本体の問題で,指定した速度よりも実際の速度が大きいまたは小さい場合, ロボットは指定した位置には移動しない可能性がある.  これらは,動作の結果を考慮せず,はじめに指定した速度と時間にのみ従って動く,という制御の仕方のために起こります.\nこのように,あらかじめ指定された制御信号にのみ基づいて制御を行い, その結果(フィードバック情報)を考慮しない制御の仕方を フィードフォワード制御(開ループ制御)と呼びます. フィードフォワード制御は,制御対象が予測可能で外乱が少ない場合や, システムが簡潔である場合に使用されることがあります.\n一方で,センサーからのフィードバック情報を利用して制御信号を修正する制御の仕方を フィードバック制御(閉ループ制御)と呼びます. フィードバック制御は,制御対象の予測が難しく,外乱が大きい場合に有効です.\n今回は,ロボットのセンサ情報を用いるフィードバック制御 によってロボットをより柔軟に動かしてみましょう. オドメトリとLiDARという2種類のセンサの情報を用います.\nフィードバック制御を行なってみましょう.-- オドメトリのセンサ情報を用いた制御 まずは,ロボットのタイヤの回転量から計算される移動距離である(ホイール)オドメトリ(odometry)を使った制御をしてみましょう.\nオドメトリのメッセージ(/odom)の中身を見てみよう roombaのオドメトリの情報は,/odomトピックにpublishされています.\n$ rostopic echo /odom  を実行するとメッセージとしてどのような情報が流れているかがわかります. $ rostopic echo -n 1 /odom root@dynamics:~/roomba_hack# rostopic echo -n 1 /odom header: seq: 2115 stamp: secs: 1649692132 nsecs: 791056254 frame_id: \u0026quot;odom\u0026quot; child_frame_id: \u0026quot;base_footprint\u0026quot; pose: pose: position: x: -0.014664691872894764 y: -0.0010878229513764381 z: 0.0 orientation: x: 0.0 y: 0.0 z: 0.0056752621080531414 w: 0.9999838955703261 covariance: [0.08313143998384476, 0.00019857974257320166, 0.0, 0.0, 0.0, 0.004368376452475786, 0.00019857988809235394, 0.015032557770609856, 0.0, 0.0, 0.0, -0.26573312282562256, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0043683769181370735, -0.26573312282562256, 0.0, 0.0, 0.0, 6.021446704864502] twist: twist: linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] ---  \nまた,\n$ rostopic type /odom  を実行すると,メッセージとして,nav_msgs/Odometry型が使われていることがわかります. $ rostopic type /odom root@dynamics:~/roomba_hack# rostopic type /odom nav_msgs/Odometry  \nnav_msgs/Odometry型のドキュメントを確認すると,このメッセージはposeとtwistで構成されていることがわかります.\n  poseは(child_frameから見た)ロボットの推定姿勢(位置と回転角)を表していて,covarianceにはその不確かさを表す共分散が記録されています.\n  一方,twistは(child_frameから見た)ロボットの速度を表していて,poseと同様にcovarianceにはその不確かさを表す共分散が記録されています.\n  なお,メッセージ型の定義は,コマンドで\n$ rosmsg info nav_msgs/Odometry  を実行しても確認できます. $ rosmsg info nav_msgs/Odometry root@dynamics:~/roomba_hack# rosmsg info nav_msgs/Odometry std_msgs/Header header uint32 seq time stamp string frame_id string child_frame_id geometry_msgs/PoseWithCovariance pose geometry_msgs/Pose pose geometry_msgs/Point position float64 x float64 y float64 z geometry_msgs/Quaternion orientation float64 x float64 y float64 z float64 w float64[36] covariance geometry_msgs/TwistWithCovariance twist geometry_msgs/Twist twist geometry_msgs/Vector3 linear float64 x float64 y float64 z geometry_msgs/Vector3 angular float64 x float64 y float64 z float64[36] covariance  \nクォータニオン(quaternion) さて,/odomのトピックでは,ロボットの回転角はクォータニオン(quaternion)で記述されています.\nクォータニオンは,日本語では四元数と呼ばれ,3次元空間上での回転角を表現する方法の一つで,4つの要素を持つベクトルで表現されます.\nクォータニオンによる3次元回転の表現は,角度を連続的にかつ簡潔に表現できるためROSではよく用いられます(その他には,オイラー角による表現や回転行列による表現があります).\nそれぞれの回転角に関する表現のメリット・デメリットを調べてみましょう(「ジンバルロック」などのキーワードで調べるとよりよく理解できると思います).\nクォータニオンからオイラー角へは,tfパッケージのtf.transformations.euler_from_quaternionを使うことで変換できます(ドキュメント).\n実装の例を見てみる それでは,オドメトリ/odomの情報を使った制御の実装の例としてnavigation_tutorialパッケージの中のsimple_control2.pyを見てみましょう(github).\nソースコードを読んでみよう 画面にウィンドウを2つ並べるなど,githubのソースコードをみながら以下の解説を読むことをお勧めします.\nsimple_control2.py #!/usr/bin/env python3 import numpy as np import rospy import tf from geometry_msgs.msg import Twist from nav_msgs.msg import Odometry class SimpleController: def __init__(self): rospy.init_node('simple_controller', anonymous=True) # Publisher self.cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10) # Subscriber odom_sub = rospy.Subscriber('/odom', Odometry, self.callback_odom) self.x = None self.y = None self.yaw = None while self.x is None: rospy.sleep(0.1) def callback_odom(self, data): self.x = data.pose.pose.position.x self.y = data.pose.pose.position.y self.yaw = self.get_yaw_from_quaternion(data.pose.pose.orientation) def go_straight(self, dis, velocity=0.3): vel = Twist() x0 = self.x y0 = self.y while(np.sqrt((self.x-x0)**2+(self.y-y0)**2)\u0026lt;dis): vel.linear.x = velocity vel.angular.z = 0.0 self.cmd_vel_pub.publish(vel) rospy.sleep(0.1) self.stop() def turn_right(self, yaw, yawrate=-0.5): vel = Twist() yaw0 = self.yaw while(abs(self.yaw-yaw0)\u0026lt;np.deg2rad(yaw)): vel.linear.x = 0.0 vel.angular.z = yawrate self.cmd_vel_pub.publish(vel) rospy.sleep(0.1) self.stop() def turn_left(self, yaw, yawrate=0.5): vel = Twist() yaw0 = self.yaw while(abs(self.yaw-yaw0)\u0026lt;np.deg2rad(yaw)): vel.linear.x = 0.0 vel.angular.z = yawrate self.cmd_vel_pub.publish(vel) rospy.sleep(0.1) self.stop() def stop(self): vel = Twist() vel.linear.x = 0.0 vel.angular.z = 0.0 self.cmd_vel_pub.publish(vel) def get_yaw_from_quaternion(self, quaternion): e = tf.transformations.euler_from_quaternion( (quaternion.x, quaternion.y, quaternion.z, quaternion.w)) return e[2] if __name__=='__main__': simple_controller = SimpleController() try: simple_controller.go_straight(1.0) simple_controller.turn_left(90) simple_controller.turn_right(90) except rospy.ROSInitException: pass   かんたんな説明 上記ソースコードの大枠のみを抜き出すと,以下のようになっています. simple_control2.pyの大枠 #!/usr/bin/env python3 # 1 import ~ # 2 class SimpleController: # 3 def __init__(self): # 4 pass def callback_odom(self, data): # 5 pass def go_straight(self, dis, velocity=0.3): pass def turn_right(self, yaw, yawrate=-0.5): pass def turn_left(self, yaw, yawrate=0.5): pass def stop(self): pass def get_yaw_from_quaternion(self, quaternion): pass if __name__=='__main__': # 6 simple_controller = SimpleController() # 7 pass  \nそれぞれについて簡潔に解説します.\n#1 shebang shebang(シバン)と呼ばれるもので,このファイルを実行する際に,どのプログラムを使って実行するかを指定する. #!/usr/bin/env python3 と書いてあるので,このファイルはpython3で実行するのだとコンピュータに教えている.\n #2 import import numpy as np import rospy import tf from geometry_msgs.msg import Twist from nav_msgs.msg import Odometry   pythonの標準の関数(printなど)だけでは機能が足りないので,別のモジュールをインポートしている. このソースコードでは,numpy, rospy, tf というモジュールをインポートしている.  rospyというのが,pythonでrosを使うためのモジュール.   また,geometry_msgs.msgというモジュールからTwistというデータ型, nav_msgs.msgというモジュールからOdometoryというデータ型をそれぞれインポートしている.   #3 class  SimpleControllerという名前のクラスを定義している. クラスはオブジェクトの設計図のようなもの.  オブジェクトとは,データとそのデータの振る舞いをまとめたもの.    classの例1 以下のようなクラスを定義したとする.\nclass Car: def __init__(self, color, speed): self.color = color self.speed = speed self.fuel = 100 def drive(self): self.fuel -= 20 print(\u0026lsquo;drove!') print(f\u0026rsquo;残りの燃料は{self.fuel}リットルです\u0026rsquo;)\ndef charge(self): self.fuel = 100 print(\u0026lsquo;charged!') print(f\u0026rsquo;残りの燃料は{self.fuel}リットルです\u0026rsquo;)\ndef info(self): print(f\u0026rsquo;色は{self.color}です') print(f\u0026rsquo;速度は{self.speed}km/hです') print(f\u0026rsquo;残りの燃料は{self.fuel}リットルです') \n以下のように使える.\nmycar = Car('red', 200) mycar.drive() #drove! #残りの燃料は80リットルです\nmycar.drive() #drove! #残りの燃料は60リットルです\nmycar.charge() #charged! #残りの燃料は100リットルです\nmycar.info() #色はredです #速度は200km/hです #残りの燃料は100リットルです \n classの例2 pythonのstring型や,int型,list型も,実はオブジェクトである.\npeople = ['Alice', 'Bob', 'Charlie'] people.append('Dave') print(people) #[\u0026lsquo;Alice\u0026rsquo;, \u0026lsquo;Bob\u0026rsquo;, \u0026lsquo;Charlie\u0026rsquo;, \u0026lsquo;Dave\u0026rsquo;] \n上の例では,list型のオブジェクトpeopleに対して,appendというメソッド(そのオブジェクトが持つ関数)を呼び出し,新しい要素を追加している.\n  #4 コンストラクタ  コンストラクタ__init__()とは,オブジェクト生成時に呼び出される関数のこと. 初期化のための関数というイメージ.   #5 メソッドの定義  メソッドとは,オブジェクトが持つ関数のこと. classの定義の中では,self.method_name(引数1, 引数2)という形で呼び出すことができる. オブジェクトの外から使用するときには,上のCarの例のように,object_name.method_name(引数1, 引数2)という形で呼び出すことができる. 定義の第一引数には,必ずselfを指定する.これは,そのオブジェクト自身を指す.  呼び出すときにはselfは省略する.     #6 ファイル実行時の処理  このif文の中の処理は,ファイルを直接実行したときにのみ実行される. __name__は特殊な変数で,ファイルを直接実行したときには'__main__'という値を持つ.  importされたときには__name__にはファイル名が入るため,このif文の中の処理は実行されない. #!/usr/bin/env python3 print(__name__)  とだけ記述したファイルを実行してみると,ふるまいが理解しやすいかもしれない.\n     より詳細な理解 simple_control2.pyについてより詳細に解説します.\nコンストラクタ def __init__(self): rospy.init_node('simple_controller', anonymous=True) # Publisher self.cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10) # Subscriber odom_sub = rospy.Subscriber('/odom', Odometry, self.callback_odom) self.x = None self.y = None self.yaw = None while self.x is None: rospy.sleep(0.1)  上にも述べた通り,__init__はコンストラクタと呼ばれ,オブジェクト生成時に自動で呼び出される関数です. 各行について順番に見ていきます.\nrospy.init_node('simple_controller', anonymous=True)   simple_controllerという名前のノードを作成しています.  self.cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)   rospy.Publisher()によって,上で作成したノードがpublisherとして機能することを宣言しています. このノードは,/cmd_velというトピックに対して,Twistというデータ型のメッセージを送信しています.  Twistの情報は,geometry_msgsのドキュメントより確認できます. Twistは速度3成分と角速度3成分を格納するデータ型です.   第2引数のqueue_sizeは,メッセージを送信する命令が,許容できる周期より短い場合に,メッセージをキューにためておく数を指定します.  ここでは,10個までキューにためておくことを指定しています. たまった数が10個より少ない場合には,古い順にメッセージをパブリッシュしていきます. たまった数が10個に達した場合には,最も古いメッセージを破棄します.    odom_sub = rospy.Subscriber('/odom', Odometry, self.callback_odom)   rospy.Subscriber()によって,作成したノードがsubscriberとして機能することを宣言しています. このノードは,/odomというトピックから,Odometryというデータ型のメッセージを受信しています.  Odometryの情報は,nav_msgsのドキュメントより確認できます. Odometryは,位置と姿勢,及び速度と角速度を格納するデータ型です. ここでは,Odometryには,ルンバの現在の位置や運動の様子が格納されており,それを受信しています.   rospy.Subscriber()の第三引数として,self.callback_odomというコールバック関数を指定しています.  subscriberにはコールバック関数を指定する必要があります. コールバック関数とは,subscriberがメッセージを受信したときに実行される関数のことです.  コールバック関数は,メッセージを引数として実行します.   コールバック関数の中身は,後述します.    self.x = None self.y = None self.yaw = None   アトリビュートを定義しています. クラスの定義の中でself.\u0026lt;name\u0026gt;の形式で表される変数は,そのクラスのオブジェクトが持つ\u0026quot;attribute(アトリビュート)\u0026ldquo;と呼ばれます.  アトリビュートには,そのclassの定義の中であればどこからでもアクセスできます.   ここでは,self.x, self.y, self.yawというアトリビュートを定義しています.  これらのアトリビュートは,後述するコールバック関数の中で値が更新されます. このアトリビュートには,ロボットの現在の位置や姿勢が格納されます.    while self.x is None: rospy.sleep(0.1)   self.xがNoneである間,0.1秒間隔で待機し続けます.   コールバック関数 def callback_odom(self, data): self.x = data.pose.pose.position.x self.y = data.pose.pose.position.y self.yaw = self.get_yaw_from_quaternion(data.pose.pose.orientation)   コンストラクタの項目で説明した通り,subscriberを定義する際にはコールバック関数を指定する必要があります. コールバック関数はsubscriberがメッセージを受信した際,そのメッセージを引数として実行する関数でした. 上のコールバック関数では,引数dataには,Odometry型のメッセージが格納されます.  data.pose.pose.positionという書き方によって,Odometry型の中の,位置を表すpositionという要素にアクセスしています. 同様に,data.pose.pose.orientationという書き方によって,Odometry型の中の,姿勢を表すorientationという要素にアクセスしています.   コンストラクタで定義していたアトリビュートself.x, self.y, self.yawに,メッセージから得られた位置と姿勢を格納しています.  self.yawに値を格納する時に使用している関数get_yaw_from_quaternion()については,後述します.   このコールバック関数が一度でも呼ばれると,self.xに入っているNoneの値が上書きされ,コンストラクタの中のwhile文が終了します.   get_yaw_from_quaternion関数 go_straight関数やturn_right関数とは順番が前後しますが,先にget_yaw_from_quaternion関数について説明します.\ndef get_yaw_from_quaternion(self, quaternion): e = tf.transformations.euler_from_quaternion( (quaternion.x, quaternion.y, quaternion.z, quaternion.w)) return e[2]   tfモジュールのeuler_from_quaternion関数を利用しています.  euler_from_quaternion()は,クォータニオン(4要素)を引数として,オイラー角(3要素)を返す関数です. クォータニオンについてはLearn/オドメトリのセンサ情報を用いた制御/クォータニオンの項目で説明しました.   Odometry型のメッセージのうち,姿勢を表すorientationという要素は,クォータニオンで表されているため,オイラー角で制御したい場合には,この関数を用いてオイラー角に変換する必要があります. ルンバはxy平面上を動くため,z軸周りのオイラー角さえわかれば十分です.そのため,このget_yaw_from_quaternion関数ではオイラー角のz軸成分(第2成分)のみを返しています.   go_straight関数 def go_straight(self, dis, velocity=0.3): vel = Twist() x0 = self.x y0 = self.y while(np.sqrt((self.x-x0)**2+(self.y-y0)**2)\u0026lt;dis): vel.linear.x = velocity vel.angular.z = 0.0 self.cmd_vel_pub.publish(vel) rospy.sleep(0.1) self.stop()  ロボットを直進させる関数です.順に説明します\n vel = Twist()   Twist型のオブジェクトを生成しています.  x0 = self.x y0 = self.y   self.x, self.yには,コールバック関数で更新された(直前の)ルンバの位置が格納されています.それをx0, y0に代入しています.  while(np.sqrt((self.x-x0)**2+(self.y-y0)**2)\u0026lt;dis): vel.linear.x = velocity vel.angular.z = 0.0 self.cmd_vel_pub.publish(vel) rospy.sleep(0.1)   点(self.x, self.y)と点(x0, y0)の距離が指定したdisより小さい間,while以下の処理を繰り返します.  velの並進成分のx成分に,引数で指定したvelocityの値を格納します. velの回転成分のz成分(z軸周りの角速度)に,0を格納します. コンストラクタの中で定義したpublisherであるself.cmd_vel_pubの.publish()関数を用いて,publishを行います.  引数にvelを指定しているので,確かにTwist型のメッセージをパブリッシュしています.   rospy.sleep(0.1)で0.1秒待ち,次のループに入ります.    self.stop()   while文が終了したら,ロボットを停止させます.  stop関数については,説明を省略します.   turn_right, turn_left関数についても,go_straight関数の説明と同様なので省略します.   単純に \u0026ldquo;(角)速度 * 時間\u0026rdquo; によって移動の姿勢を指定しているのではなく,オドメトリのセンサ情報を使いながら, 目標の姿勢に到達するように制御していることを再度強調しておきます.\npythonのコードの読み方についての基本的な説明は上の説明で尽きているので, 余力があれば(なくても)各自roomba_hackリポジトリ 上の気になったコードを読んでみましょう.\n演習 【jetson・開発マシン】それぞれdockerコンテナを起動 . jetsonでdockerコンテナを起動\n(開発PC):~$ ssh roomba_dev1 (jetson):~$ cd ~/group_a/roomba_hack (jetson):~/group_a/roomba_hack ./RUN-DOCKER-CONTAINER.sh (jetson)(docker):~/roomba_hack#  開発PCでdockerコンテナを起動\n(開発PC):~$ cd ~/group_a/roomba_hack (開発PC):~/group_a/roomba_hack ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x (開発PC)(docker):~/roomba_hack#   【jetson】ROSマスタ,各種ノードを起動 (jetson)(docker):~/roomba_hack# roslaunch roomba_bringup bringup.launch   ROSメッセージの可視化 【開発PC】topicの確認 /odomの型を確認\n(開発PC)(docker):~/roomba_hack# rostopic type /odom  /odomの中身を確認\n(開発PC)(docker):~/roomba_hack# rostopic echo /odom   オドメトリを使ったフィードバック制御 simple_control2.pyを実行してみよう.\n開発PCでteleopのコードを実行しましょう\n(開発PC)(docker):~/roomba_hack# roslaunch roomba_teleop teleop.launch  このプログラムを動かすときには,コントローラのYボタンを押してからBボタンを押してautoモードにしておきましょう.\n1メートルほど前に進んだあと,左に90度程度旋回し,右に90度程度旋回したら成功です.\n(開発PC)(docker):~/roomba_hack# rosrun navigation_tutorial simple_control2.py  try it! simple_control2.pyの中身を読んでコードを変更してみよう\n","date":1687392e3,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1687392e3,"objectID":"2ee7ed9f0716848dbd6ffe877377e500","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap3/sensing2/","publishdate":"2023-06-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap3/sensing2/","section":"course","summary":"複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう\n","tags":null,"title":"ロボットシステムにおけるセンシング・アクチュエーション・通信②","type":"book"},{"authors":null,"categories":null,"content":" これは 2023 年度の情報です。今後変更の可能性があります。   最終課題について 最終課題発表会の日程は8/30です。\nそれまでに各チームで以下のタスクを行うコードを準備してください。 また、チームでGitHubにリポジトリを作成し、準備したコードをアップロードしてください。\nすべてのタスクを完璧に実装しようとすると少し大変なので、まずは、ヒントを参考に「はじめの一歩」を実装するのを目指しましょう。\nルールの質問、実装のやり方などわからないことは #q-roomba で質問お願いします! また、角川でも質問対応デーを設ける予定です。\nルール 1チームずつTask1とTask2の2つのタスクを行い、その合計点を競います。\nそれぞれのタスクは図の環境で行われます。(角川にある環境です。)\n  タスクがおこなわれるフィールド図  それぞれのTaskのStaring Pointではルンバの自己位置を合わせることはできますが、一度ルンバが動き始めたら人が介入することができないことに注意してください。(リスタートを除く)\nTask1 ルール Task1では、3分の制限時間の間で、Room1のStarting Pointからルンバを自律移動させ、Area1内に落ちている5つの物体を避けながらRoom2へと移動することが目標になります。\n途中でロボットが停止した場合などは再びStarting Pointからリスタートすることができます。\nまた、Area1内に落ちている5つの物体を検出し、正しく分類することで追加ポイントを獲得することができます。\nタスク終了時にArea1内で出てきた物体の名前をターミナルに表示してください。\nまた、5つの物体は以下の8個の物体からランダムに選択されます。\n  Task1で出現する物体  物体の名前は左から順に\n chips can mini soccor ball rubic cube banana apple strawberry toy plane wood block です。  実際に使用する物体は角川に置いてあります。\n 採点基準 ゴール得点と物体検出得点それぞれ100点満点です。また、ゴール得点と物体検出得点それぞれの得点が負になることはありません。\nゴール得点内訳(100点満点)\n制限時間以内にゴール(Room2)に辿り着けたら100点、物体に衝突するたびに物体ごとに-20点、加えて、ロボットをStarting Pointからリスタートする度に-10点\n物体検出得点内訳(100点満点)\n検出結果の分類結果が正しければ物体ごとに+20点、間違っていたら-20点\n ヒント はじめの一歩\n Task1はまず、ゴールへ移動するコードを作成することを目標にしましょう (スクリプトでナビゲーションを行うためのヒント参照) 余裕があれば、他のヒントを参照して高得点を目指していきましょう  スクリプトでナビゲーションを行うためのヒント\n スクリプトでナビゲーションを行うコード(https://matsuolab.github.io/roomba_hack_course/course/chap6/service-actionlib/ の演習)を参考に、指定した位置へ移動するコードを書いてみましょう  障害物を避けながらnavigationするためのヒント\n 物体検出器を改善するためのヒントおよび https://matsuolab.github.io/roomba_hack_course/course/chap6/service-actionlib/ の総合課題を参照し、課題物体を検出し、それをコストマップに追加することで障害物を避けるコードを書いてみましょう  より正確な自己位置推定を行うためのヒント\n amclのパラメータ調整 (参考: https://matsuolab.github.io/roomba_hack_course/course/chap4/localization/ の演習)をしてみましょう gmappingを用いてより正確な地図を作成 (参考: https://matsuolab.github.io/roomba_hack_course/course/chap4/localization/ の演習)してみましょう emcl(https://github.com/ryuichiueda/emcl)等の異なる自己位置推定アルゴリズムを使ってみましょう Lidarの位置を合わせをより正確に(https://github.com/matsuolab/roomba_hack/blob/master/catkin_ws/src/roomba/roomba_description/urdf/roomba.urdf.xacro を編集)合わせてみましょう  物体検出器を改善するためのヒント\n 自作データを用いて既存モデルの学習 (参考: https://eng-memo.info/blog/yolo-original-dataset/) を行ってみましょう   Task2 ルール Task2では、3分の制限時間の間で、Room2のStarting Pointからルンバを自律移動させ、Area2にいる2人の人のうち、手を振っている人の前で停止することが目標になります。\nまた、2人の人がArea2にある2つの椅子に座っており、そのうち一方のみが手を振っていることが保証されています。\n途中でロボットが停止した場合などは再びStarting Pointからリスタートすることができます。\n 採点基準 Task2全体で150点満点です、また、Task2の得点が負になることはありません。\n時間以内にいずれかの人の前に移動が成功し、その場で停止する(人の50cm以内に到達する)+70点\n上に加え、正しい人(手を振っている人)の前に移動が成功する+80点\n加えて、ロボットをStarting Pointからリスタートする度に-10点\n ヒント はじめの一歩\n Task2はまず、人を検出して移動するコードを作成することを目標にしましょう (人を検出して移動するためのヒント参照) 余裕があれば手を振っている人の検出にチャレンジしてみましょう  人を検出して移動するためのヒント\n 人を検出する部分については三次元画像処理で扱ったYOLOv3で検出することができます 検出した結果に対応する深度画像から距離を取得することで、人の近くへ移動するコードを書くことができます (参考: https://matsuolab.github.io/roomba_hack_course/course/chap5/three-dimensions/ のdetection_distance.py) さらに余裕があれば、深度画像を点群等に変換するとより正確な移動が可能になります  手を振っている人の検出\n 学習済みのKeypoint R-CNNを用いることで人間の手や腕の位置などのキーポイントを推定することができます。  ","date":1687392e3,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1687392e3,"objectID":"b048517e8455bfa798e5d9368632cd92","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap8/final_project/","publishdate":"2023-06-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap8/final_project/","section":"course","summary":"","tags":null,"title":"最終プロジェクト","type":"book"},{"authors":null,"categories":null,"content":"ロボットシステムの開発環境に使われている要素の概要を理解する\nLearn Linuxコマンド コンピュータやロボットの研究・開発では、LinuxというOS(Operating System)がよく使用されます。\nROSはLinux(とくにUbuntuというディストリビューション)で動作することを想定されているため、ROSを使うにはある程度Linuxの知識が必要になります。 Linuxについてあまり知らないという方は、前もって調べておくと良いでしょう(参考)。 特に、Linuxコマンドについて軽く知っておくとROS自体の理解もスムーズになります。\n\u0026quot;\u0026quot;\u0026quot; ここではLinuxのコマンドを十分に説明できないので、コマンドの例を挙げることに留めます。 詳しいサイトに沿って自分で使いながら覚えていくことをお勧めします。 \u0026quot;\u0026quot;\u0026quot; # カレントディレクトリ(自分が今いるディレクトリ)を確認 pwd # ディレクトリ内のファイル・ディレクトリの表示 ls # カレントディレクトリを変更 cd \u0026lt;DirectoryName\u0026gt; # ディレクトリ作成 mkdir \u0026lt;NewDirectoryName\u0026gt; # ディレクトリ削除 rmdir \u0026lt;DirectoryName\u0026gt; # ファイル作成 touch \u0026lt;NewFileName\u0026gt; # ファイル消去 rm \u0026lt;FileName\u0026gt; # ファイルの内容を表示 cat \u0026lt;FileName\u0026gt;  エディタ プログラミング言語でコードを書くときに使用するツールをEditor(エディタ)と呼びます。\nテキストファイルを編集するツールなのでメモ帳でも同じ事ができるのですが、エディタには便利な機能が多く、効率よくコードが編集できるようになります。\n研究室のPCでは以下の二つが使用できます。\n vim  シンプルで使いやすいエディタ。 macだと標準で使える。  ターミナルでvimtutorを実行するとチュートリアルが受けられる.   Windowsだとインストールする必要がある。   vscode  visual studio code ダウンロードする必要がある さまざまな機能を追加でき、とても便利 詳しい動画    Git/GitHub  Gitとは   ファイルのバージョン管理が簡単にできるツール。\n使い方を知っておくと開発が快適になります。\n  詳しい記事(コマンドなし)\n  詳しい記事(コマンドあり)\n   GitHubとは   gitで管理しているファイルを他の人と共有できるようにするサービス。\nチーム開発で大変重宝します。\n自分が書いたコードを公開して世界中の人に使ってもらったり、他の人が公開しているコードを使わせてもらうこともできます。\n  https://github.co.jp/\n  詳しい記事\n  GitとGitHubを用いたワークフロー\n  このサイトもGitHubを使って開発しています。(このサイトのリポジトリ)\n  公開されているリポジトリの例\n https://github.com/gundam-global-challenge/gundam_robot      Docker   Dockerとは\n「データやプログラムを隔離できる」仕組み。\n  例えば、コンピュータの中でシステムAとシステムBを動かしたいとします。そして、これらは他のシステムCに依存しているものとします。\nもし、AがCのver.2にのみ対応していて、BはCのver.3にのみ対応していた場合、Bを実行できるようにCをver.3にしてしまうと、Aが実行できなくなってしまいます。\n  このような場合、dockerコンテナを複数作成し(コンテナI、コンテナIIとします)、\n コンテナIの中にはプログラムAとCのバージョン2をインストールして使い、 コンテナIIの中にはプログラムBとCのバージョン3をインストールして使用する  のようなことことができます。\n    コンテナの中の環境はPC本体の環境からは隔離されるため、安全に開発をすることができます(他の人が書いたプログラムが動かなくなるということも防げる)。\n    詳しい記事\n  : -f   ``` - Docker Image ``` # Docker image一覧 docker images # Docker Imageのダウンロード docker pull : # 削除 docker rmi  # 不要なDocker imageを消す docker image prune ``` - Docker Container ``` # Docker containerの起動 docker run   # Docker container一覧 docker ps -a # Docker containerに接続 docker exec -it  bash ``` -- ` - コンテナの名前の指定 - `--rm` - コンテナを抜けた際に自動的にコンテナを削除する - `--gpus all` - コンテナに全gpuを渡す - gpuの個数を指定する場合は all の代わりに数字(0, 1,...) - gpuを指定する場合は `--gpus '\"device=0,1\"'` - `-v ` - コンテナ内にホストのディレクトリをマウントする - `-p :` - ホストのポートをコンテナのポートにマップする - コンテナ内でwebサーバを動かす場合などに使う - `--net=host` - コンテナとホストでネットワークを共有する(IPアドレスなどが同じになる) - ROSノードをコンテナ内で動かす場合などはこれを使うと楽 - `--privileged` - コンテナからのデバイスへのアクセスを許可 - コンテナからWEBカメラにアクセスしたいときなど -- ssh   sshとは\n  sshを使用することで、開発PCからルンバに載っているjetsonというコンピュータを遠隔で操作することや、各自のPCから開発PCを遠隔で操作することができる。\n  @ -p  -i  ``` -- 演習 演習には個人PC, 開発PC, ルンバに搭載されているjetsonの3種類のコンピュータを用います。\n開発PC : robot_dev系, hsr_dev系\njetson : roomba_dev系\n【ssh】開発用PCにsshする 個人PCから開発PCにsshする\n(個人PC):~$ vim ~/.ssh/config (個人PC):~$ ssh robot_dev2  sshに成功すると\nrobot_dev2@robot-dev2:~$  などと表記が変わり、開発PCに接続できたことが確認できます。\n -- 【Linuxコマンド】グループのディレクトリを作成し移動する (開発PC):~$ mkdir 23_group_x (開発PC):~$ cd 23_group_x   【git】roomba_hackリポジトリをcloneし移動する (開発PC):~/23_group_x$ git clone https://github.com/matsuolab/roomba_hack.git (開発PC):~/23_group_x$ ls # 23_group_xディレクトリの中に新しいディレクトリができていることを確認 (開発PC):~/23_group_x$ cd roomba_hack (開発PC):~/23_group_x/roomba_hack$ ls  https://github.com/matsuolab/roomba_hack をそのままダウンロードできたことが確認できると思います。\n 【git】ブランチを確認する git branchコマンドを使ってみましょう。\n# ローカルブランチの一覧を表示 (開発PC):~/23_group_x/roomba_hack$ git branch   【docker】roomba_hackの開発環境のdockerイメージをビルドする # shellファイルの中身をcatコマンドで確認してみます。 (開発PC):~/23_group_x/roomba_hack$ cat BUILD-DOCKER-IMAGE.sh # ファイルの中身が表示される # ファイルの最後の # docker build . -f docker/${DOCKERFILE_NAME} -t ${IMAGE_NAME}:${TAG_NAME} --build-arg BASE_IMAGE=${BASE_IMAGE} # の部分でDockerイメージのビルドを実行するようです。 # shellファイルを実行してdockerイメージのビルドを行います。 (開発PC):~/23_group_x/roomba_hack$ ./BUILD-DOCKER-IMAGE.sh  「イメージ」とは、dockerコンテナを作成するための素となるもので、コンテナの設計図のようなものです。上のコマンドを実行することで、roomba_hackの開発環境のdockerイメージが作成されます(まだコンテナ自体は作っていません)。\n 【ssh】jetsonにsshする 開発用PCからルンバに載っているjetson nanoへsshします。\n(開発PC):~/23_group_x/roomba_hack$ ssh roomba_dev2 roomba_dev2@roomba-dev-jetson2:~$  先頭の表記がroomba_dev2@roomba-dev-jetson2と変わり、jetsonへ接続されたことがわかります。\njetsonでも同様にグループのディレクトリを作成し、移動し、roomba_hackリポジトリをcloneしてみましょう。\n 【ssh】VNCを使う 個人PCから開発PCにsshで接続\n(個人PC):~$ ssh robot_dev2 -L 5900:localhost:5900  手元のVNC viewerでlocalhost:5900を開く\n -- ","date":1686009600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1686009600,"objectID":"2a1ada4f20c1997bdd8592ad8bf5f9b1","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap1/%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83/","publishdate":"2023-06-06T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap1/%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83/","section":"course","summary":"ロボットシステムの開発環境に使われている要素の概要を理解する\n","tags":null,"title":"開発環境","type":"book"},{"authors":null,"categories":null,"content":"Learn  RGBDカメラを用いて三次元画像処理を行いましょう。\nRGBDカメラについて RGBDカメラとは、色(RGB)に加え、深度(Depth)情報を取得できるカメラのことです。 周りの環境を三次元の空間として認識することで、ロボットはより複雑にふるまうことができます。 比較的安価でよく利用されるRGBDカメラとして、Intel社製のRealSenseやMicrosoft社製のXtion(エクシオン)などがあります。\nRealSense 今回はRGBDカメラとしてRealSenseD435を使用します。\nROSで用いる際には標準のラッパーを使用します。\nroslaunch realsense2_camera rs_camera.launch  を実行すると、2種類のトピック\n/camera/color/image_raw (RGB画像)\n/camera/depth/image_raw (デプス画像)\nが利用できるようになります。\nこれらのトピックはいずれもsensor_msgs/Image型です。\nRealSenseはRGB画像モジュールとデプス画像モジュールが物理的に離れています。 このため、これら2つのトピックはいずれも画像データではあるものの、ピクセルの位置関係が対応しておらず、そのまま画像処理に利用することはできません。\nRealSenseを使用するためのlaunchファイル(rs_camera.launch)を起動する際に \u0026ldquo;align_depth\u0026quot;パラメータを\u0026quot;true\u0026quot;に指定することで、デプス画像をRGB画像のピクセルに対応するように変換した /camera/aligned_depth_to_color/image_rawトピックが使用できるようになります。\nただし、roomba_bringupパッケージのbringup.launchファイルの\n \u0026lt;include file=\u0026quot;$(find realsense2_camera)/launch/rs_camera.launch\u0026quot; if=\u0026quot;$(arg realsense)\u0026quot;\u0026gt; \u0026lt;arg name=\u0026quot;align_depth\u0026quot; value=\u0026quot;true\u0026quot;/\u0026gt; \u0026lt;/include\u0026gt;  の箇所がこの操作に対応していため、今回は特別な操作をせずとも /camera/aligned_depth_to_color/image_rawトピックを使用できます。\n物体検出 まずは3次元情報を扱わず、RGB画像/camera/color/image_rawのみを用いて画像検出を行ってみましょう。\n以下は、 three-dementions_tutorialパッケージの object_detection.py です。\n /camera/color/image_rawをsubscribeし、 物体検出アルゴリズムであるYOLOv8に入力し、 物体検出の結果をbounding boxとして描画し、 /detection_resultとしてpublish  の処理を行っています。\n#!/usr/bin/env python3 import copy from typing import List import cv2 import rospy from cv_bridge import CvBridge from sensor_msgs.msg import Image from ultralytics import YOLO from ultralytics.engine.results import Results class ObjectDetection: def __init__(self): rospy.init_node('object_detection', anonymous=True) # Publisher self.detection_result_pub = rospy.Publisher('/detection_result', Image, queue_size=10) # Subscriber rospy.Subscriber('/camera/color/image_raw', Image, self.callback_rgb) self.bridge = CvBridge() self.rgb_image = None self.model = YOLO('yolov8n.pt') def callback_rgb(self, data): cv_array = self.bridge.imgmsg_to_cv2(data, 'bgr8') self.rgb_image = cv_array def process(self): while not rospy.is_shutdown(): if self.rgb_image is None: continue results: List[Results] = self.model.predict(self.rgb_image) # plot bounding box tmp_image = copy.deepcopy(self.rgb_image) for result in results: boxes = result.boxes.cpu().numpy() names = result.names for xyxy, conf, cls in zip(boxes.xyxy, boxes.conf, boxes.cls): if conf \u0026lt; 0.5: continue x1, y1, x2, y2 = map(int, xyxy[:4]) cls_pred = cls tmp_image = cv2.rectangle(tmp_image, (x1, y1), (x2, y2), (0, 255, 0), 3) tmp_image = cv2.putText(tmp_image, names[cls_pred], (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) # publish image detection_result = self.bridge.cv2_to_imgmsg(tmp_image, \u0026quot;bgr8\u0026quot;) self.detection_result_pub.publish(detection_result) if __name__ == '__main__': od = ObjectDetection() try: od.process() except rospy.ROSInitException: pass  画像データを物体検出モデルに入力として渡すためには、その画像データの型がnp.ndarray型である必要があります。 そのため、コールバック関数で、受け取ったsensor_msgs/Image型の画像データをnp.ndarray型に変換しています。\ncv_array = self.bridge.imgmsg_to_cv2(data, 'bgr8')  の部分がこの処理に対応します。\nsubscriberを宣言するときにコールバック関数を指定して、 subscribeしたデータをこの関数に渡すという基本的な処理の流れは、 scanなど他のトピックを扱うsubscriberと同じです。\nここで、YOLOの推論部分をコールバック関数内で行っていないことに注意しましょう。 一見、新しいデータが入ってくるときのみに推論を回すことは合理的に見えますが、 センサの入力に対してコールバック関数内の処理が重いと処理するデータが最新のものからどんどん遅れてしまいます。 コールバック関数はセンサデータの最低限の処理にとどめ、重い処理は分けて書くことを意識しましょう。\nまた、ここでは既存の物体検出モジュールを使用しましたが、PyTorchなどで自作したモデルも同様の枠組みで利用することができます。\n三次元画像処理 次に、RGB画像とデプス画像を統合し、検出した物体までの距離を測定してみましょう。\n/camera/color/image_raw (RGB画像) /camera/aligned_depth_to_color/image_raw (整列されたDepth画像)\nはピクセル同士が対応するように処理されてはいるものの、 パブリッシュされた時刻は独立しているため、 併せて使用するには時刻の同期を行う必要があります。\n画像の時刻同期にはmessage_filtersがよく使われます。\nmessage_filters.ApproximateTimeSynchronizerを使い、以下のようにSubscriberを作成します。\n#!/usr/bin/env python3 import copy from typing import List import cv2 import message_filters import rospy from cv_bridge import CvBridge from sensor_msgs.msg import Image class DetectionDistance: def __init__(self): rospy.init_node('detection_distance', anonymous=True) # Publisher self.detection_result_pub = rospy.Publisher('/detection_result', Image, queue_size=10) # Subscriber rgb_sub = message_filters.Subscriber('/camera/color/image_raw', Image) depth_sub = message_filters.Subscriber('/camera/aligned_depth_to_color/image_raw', Image) message_filters.ApproximateTimeSynchronizer([rgb_sub, depth_sub], 10, 1.0).registerCallback(self.callback_rgbd) self.bridge = CvBridge() self.rgb_image, self.depth_image = None, None def callback_rgbd(self, data1, data2): cv_array = self.bridge.imgmsg_to_cv2(data1, 'bgr8') cv_array = cv2.cvtColor(cv_array, cv2.COLOR_BGR2RGB) self.rgb_image = cv_array cv_array = self.bridge.imgmsg_to_cv2(data2, 'passthrough') self.depth_image = cv_array # 後略  この例では、\n/camera/color/image_rawと\n/camera/aligned_depth_to_color/image_rawの\nトピックを同時(1.0秒までのずれを許容する)に受け取った場合のみ、 2つの画像データをコールバック関数callback_rgbdに渡します。\nそれでは、three-dementions_tutorial パッケージのdetection_distance.py を見てみましょう。\n物体を検出し、その物体までの距離を測定するスクリプトです。\n#!/usr/bin/env python3 import copy from typing import List import cv2 import message_filters import rospy from cv_bridge import CvBridge from sensor_msgs.msg import Image from ultralytics import YOLO from ultralytics.engine.results import Results class DetectionDistance: def __init__(self): rospy.init_node('detection_distance', anonymous=True) # Publisher self.detection_result_pub = rospy.Publisher('/detection_result', Image, queue_size=10) # Subscriber rgb_sub = message_filters.Subscriber('/camera/color/image_raw', Image) depth_sub = message_filters.Subscriber('/camera/aligned_depth_to_color/image_raw', Image) message_filters.ApproximateTimeSynchronizer([rgb_sub, depth_sub], 10, 1.0).registerCallback(self.callback_rgbd) self.bridge = CvBridge() self.rgb_image, self.depth_image = None, None self.model = YOLO('yolov8n.pt') def callback_rgbd(self, data1, data2): cv_array = self.bridge.imgmsg_to_cv2(data1, 'bgr8') cv_array = cv2.cvtColor(cv_array, cv2.COLOR_BGR2RGB) self.rgb_image = cv_array cv_array = self.bridge.imgmsg_to_cv2(data2, 'passthrough') self.depth_image = cv_array def process(self): while not rospy.is_shutdown(): if self.rgb_image is None: continue # inference tmp_image = copy.copy(self.rgb_image) results: List[Results] = self.model.predict(self.rgb_image, verbose=False) # plot bouding box for result in results: boxes = result.boxes.cpu().numpy() names = result.names if len(boxes.xyxy) == 0: continue x1, y1, x2, y2 = map(int, boxes.xyxy[0][:4]) cls_pred = boxes.cls[0] tmp_image = cv2.rectangle(tmp_image, (x1, y1), (x2, y2), (0, 255, 0), 3) tmp_image = cv2.putText(tmp_image, names[cls_pred], (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) cx, cy = (x1+x2)//2, (y1+y2)//2 print(names[cls_pred], self.depth_image[cy][cx]/1000, \u0026quot;m\u0026quot;) # publish image tmp_image = cv2.cvtColor(tmp_image, cv2.COLOR_RGB2BGR) detection_result = self.bridge.cv2_to_imgmsg(tmp_image, \u0026quot;bgr8\u0026quot;) self.detection_result_pub.publish(detection_result) if __name__ == '__main__': dd = DetectionDistance() try: dd.process() except rospy.ROSInitException: pass  基本的には物体検出のスクリプトと同じですが、\ncx, cy = (x1+x2)//2, (y1+y2)//2 print(names[cls_pred], self.depth_image[cy][cx]/1000, \u0026quot;m\u0026quot;)  でbounding boxの中心座標を変換し、対応する距離をメートル単位で表示しています。\n整列されたデプス画像を用いているため、RGB画像に基づき算出した座標をそのまま指定できます。\n点群の作成 上の例ではRGB画像とDepth画像を用いて、物体位置の代表となる点とロボット位置の関係を扱うことができました。\nしかし、代表となる点以外の深度データも三次元空間に直接マッピングできると、 物体の大きさや形状といった情報も扱うことができ、 環境情報をより直感的・統一的に扱うことができるように思われます。\nそこでDepth画像から点群と呼ばれるデータを作成することを考えます。\n点群とは三次元座標値 (X, Y, Z) で構成された点の集まりのことです。各点の情報として、三次元座標値に加え色の情報 (R, G, B) が加わることもあります。 デプス画像はカメラの内部パラメータを用いることによって点群データに変換することができます。(参考)\n今回は、デプス画像を点群データに変換するためのROSの外部パッケージである depth_image_proc を使用して点群を作成します。\n外部パッケージは~/catkin_ws/src等のワークスペースに配置し、ビルドしパスを通すことで簡単に使用できます。\ndepth_image_procのwikiを参考に以下のようなlaunchファイルを作成しました。\n\u0026lt;?xml version=\u0026quot;1.0\u0026quot;?\u0026gt; \u0026lt;launch\u0026gt; \u0026lt;node pkg=\u0026quot;nodelet\u0026quot; type=\u0026quot;nodelet\u0026quot; name=\u0026quot;nodelet_manager\u0026quot; args=\u0026quot;manager\u0026quot; /\u0026gt; \u0026lt;node pkg=\u0026quot;nodelet\u0026quot; type=\u0026quot;nodelet\u0026quot; name=\u0026quot;nodelet1\u0026quot; args=\u0026quot;load depth_image_proc/point_cloud_xyz nodelet_manager\u0026quot;\u0026gt; \u0026lt;remap from=\u0026quot;camera_info\u0026quot; to=\u0026quot;/camera/color/camera_info\u0026quot;/\u0026gt; \u0026lt;remap from=\u0026quot;image_rect\u0026quot; to=\u0026quot;/camera/aligned_depth_to_color/image_raw\u0026quot;/\u0026gt; \u0026lt;remap from=\u0026quot;points\u0026quot; to=\u0026quot;/camera/depth/points\u0026quot;/\u0026gt; \u0026lt;/node\u0026gt; \u0026lt;/launch\u0026gt;  このlaunchファイルを実行すると\n/camera/color/camera_infoと\n/camera/aligned_depth_to_color/image_rawを\nsubscribeし、\n/camera/depth/pointsをpublishするノードが作成されます。\n/camera/color/camera_infoは sensor_msgs/CameraInfo型のトピックです。 カメラパラメータやフレームid、タイムスタンプなどの情報を保持しており、点群の変換に利用されます。\n/camera/aligned_depth_to_color/image_rawはRGB画像に合わせて整列されたDepth画像であるため、 /camera/depth/camera_infoではなく\n/camera/color/camera_infoを指定しています。\n(開発PC)(docker)# roslaunch three-dimensions_tutorial depth2pc.launch  を実行し、/camera/depth/pointsトピックをrvizで可視化をすると三次元空間に点群データが表示されているのが確認できます。\n演習 (開発PC, jetson)起動準備 (jetson)$ ./RUN-DOCKER-CONTAINER.sh (jetson)(docker)# roslaunch roomba_bringup bringup.launch (開発PC)$ ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x   (開発PC)RealSenseのトピックの可視化 (開発PC)(docker)# rviz  rviz上で\n /camera/color/image_raw /camera/depth/image_raw /camera/aligned_depth_to_color/image_raw  を可視化して違いを確認してみましょう。\n (開発PC)物体検出を行う (開発PC)(docker)# cd catkin_ws; catkin_make; source devel/setup.bash (開発PC)(docker)# roscd three-dimensions_tutorial; cd yolov3/weights; ./download_weights.sh (開発PC)(docker)# rosrun three-dimensions_tutorial object_detection.py # rvizで`/detection_result`を表示し結果を確認してみよう。 (開発PC)(docker)# rosrun three-dimensions_tutorial detection_distance.py   (開発PC)外部パッケージを使用 (開発PC)(docker)# cd ~/external_catkin_ws/src (開発PC)(docker)# git clone https://github.com/ros-perception/image_pipeline (開発PC)(docker)# cd ../; catkin build; source devel/setup.bash (開発PC)(docker)# cd ~/roomba_hack/catkin_ws; source devel/setup.bash (開発PC)(docker)# roslaunch three-dimensions_tutorial depth2pc.launch (開発PC)(docker)# roslaunch navigation_tutorial navigation.launch  rvizで/camera/depth/pointsトピックを追加して確認してみましょう。\n 余裕がある人向け 物体を検出し、特定の物体の手前まで移動するスクリプトを作ってみましょう。\nヒント\n 物体検出結果に基づいて物体部分以外をマスクしたデプス画像をpublishする depth2pc.launchでそれをsubscribeし、point(cloud)に変換する 変換されたpointからmap座標系での位置を取得する navigation_tutorial/scripts/set_goal.py (map座標系で指定した位置・姿勢までナビゲーションするスクリプト)などを参考に、その位置へとナビゲーションする  PyTorchを使用した自作の分類器やネット上の分類器をシステムに組み込んでみましょう。\nLidarに映らない物体も画像ベースで検出しコストマップに追加することでナビゲーション時にぶつからないようにしましょう。\n","date":1642809600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1642809600,"objectID":"6efadb7100ad083ad0bc43c5fd3c3b1a","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap5/three-dimensions/","publishdate":"2022-01-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap5/three-dimensions/","section":"course","summary":"","tags":null,"title":"三次元画像処理","type":"book"},{"authors":null,"categories":null,"content":"Learn 前回の演習では、オドメトリを用いてロボットを制御しました。\nルンバが用いているホイールオドメトリは、ホイールの回転量を足し合わせることで算出しています。 長い距離を動かしたり、長時間動かしているとセンサの僅かな誤差の積み重ねで徐々にずれが大きくなってしまいます。\nそこで今回は、オドメトリ情報だけでなく、地図とLiDARスキャン情報も同時に使いながら、ロボット自身の尤もらしい位置を推定していきましょう。\nROSにおける座標系の扱い 座標系を扱うモチベーション ロボット頭部の距離画像カメラ(デプスカメラ)を用いて、2メートル前にりんごを見つけたとします。\nこの時、ロボットのハンドを2メートル前に動かしても、りんごに触れられるとは限りません。\nロボットには体積があり、各センサやアクチュエータ(ここではカメラとハンド)は距離的に離れているためです。\nセンサから得た情報を用いてロボットが何らかの動作を行うには、得たセンサの情報や動作の指令がどの座標系を前提としているのかを意識する必要があり、また、異なる座標系の変換を適切に行う必要があります。\n tf tfは、\n ロボット座標系 センサの座標系 ロボットの関節の座標系 部屋の座標系 物体の座標系 地図の座標系  など多くの座標系同士を繋げ、ロボットシステム上で座標系の管理をしてくれるROSのモジュールです。\n$ rosrun rqt_tf_tree rqt_tf_tree  を実行すると、各座標系の間の関係を可視化することができます。\ntfは、座標系の関係をツリー構造で管理します。親の座標系が複数あることは許されません。\n今回自己位置推定を行うにあたり用いる座標系の関係は以下のようになります。\n  tfツリーをrqtで可視化  ここで、odom座標系は、オドメトリの算出を始めた位置(起動した位置)を原点とした座標系で、 ホイールオドメトリの値から、ロボットの基準となるbase_footprint座標系を繋げています。 base_footprint座標系の下には、ルンバロボットの構成要素であるセンサ類やホイールなどの座標系が子として繋がっています。\n一番親にいるmap座標系は、地図の原点を基準とした座標系ですが、 この座標系におけるロボットの座標系(base_footprint)を繋げること、 つまり、ロボットが地図上のどこにいるのかを決めることが、 自己位置推定の目的になります。\n今回の場合、base_footprintの親には既にodomがいるため、 map座標系とodom座標系を繋げることで、全体をひとつのツリーとして管理することができます。\n自己位置推定 地図が事前に与えられているという前提のもと、 その地図上のどこにロボットがいるのか、 LiDARやOdometryなどのセンサ情報を用いて推定することを自己位置推定といいます。\nここでは、自己位置推定法の一般的な手法の一つであるMCL(Monte Carlo Localization)について説明します。 MCLは、モンテカルロ法(Monte Carlo method)と呼ばれる確率的な手法を用いて、ロボットの位置推定を行います。\n以下のような手順で動作します。\n パーティクルの生成\nロボットの位置を表す候補となるパーティクル(粒子)を生成します。 パーティクルは、ロボットの姿勢(位置と角度)の候補を表す状態ベクトルです。 パーティクルの重み付け\nセンサデータやロボットの制御入力(移動量など)との一致の度合いに応じて、各パーティクルに重みを割り当てます。 センサデータには、LiDARのスキャンデータやカメラの画像データなどが使われます。 リサンプリング\n重み付けされたパーティクルから、新たなパーティクルを生成します。 重みが大きいパーティクルほど、新しいセットに多く含まれる可能性が高くなります。 これにより、より確からしい位置候補が残るようになります。 パーティクルの移動\nロボットが移動すると、パーティクルも同様に移動します。 パーティクルの移動には、ロボットの制御入力やノイズモデルが使用されます。 ロボットの制御入力と実際の移動距離には誤差が含まれるため、 この時パーティクルの分布は広がります。 パーティクルの更新\nロボットがセンサデータを取得すると、各パーティクルの重みが再計算されます。 予測されたセンサーデータと実際のセンサーデータの一致度に基づいて重みが更新されます。 推定された位置の計算\n更新された重みに基づいて、推定されたロボットの位置を計算します。 一般に、重み付き平均や最も重みの大きいパーティクルの位置が使用されます。  MCLは、センサデータのノイズや環境の不確実性に対して柔軟に対応できるため、自己位置推定の際によく使用されます。 ただし、パーティクル数が十分でない場合や、環境の動きが大きい場合には、正確な位置推定が難しくなる可能性があります。 また、MCLは計算コストが高いため、リアルタイムの応用には制約があります。\nMCLのパフォーマンスを向上させるためには、 パーティクル数やリサンプリングの戦略の選択、センサモデルの精度向上など、 いくつかの改良手法があります。\nまた、他の自己位置推定手法との組み合わせや統合が行われることもあります。\nroomba_hackでは、amcl(Adaptive Monte Carlo Localization)パッケージと emcl(MCL with Expansion resetting)パッケージが用意されています。\n  Monte Carlo Localization(Particle Filter) Dieter Fox et al. 1999, using sonar. http://www.doc.ic.ac.uk/~ajd/Robotics/RoboticsResources/montecarlolocalization.gif  [詳しい文献]\n \u0026ldquo;Probabilistic Robotics\u0026rdquo; by Sebastian Thrun, Wolfram Burgard, Dieter Fox  邦訳:\u0026ldquo;確率ロボティクス\u0026rdquo; 上田隆一    launchファイルとrosparam 自己位置推定では、初期位置がどこか、レーザーのスペックや、パーティクルの数など数十個のパラメータを保持します。\nこれらをプログラム内部で記述するのではなく、launchファイル内で指定することが可能です。 rosでは、rosparamという形でパラメータを管理することが可能です。\n以下に、今回用いるamcl.launch を示します。 launchファイルはxml形式で記述され、paramを指定すること以外にも、 launchファイル実行時に引数で指定可能なargや、トピック名などのリマップをすることも可能です。\nlaunchの詳しい書き方は、rosのドキュメントを参照してください。\n\u0026lt;?xml version=\u0026quot;1.0\u0026quot;?\u0026gt; \u0026lt;launch\u0026gt; \u0026lt;arg name=\u0026quot;use_map_topic\u0026quot; default=\u0026quot;true\u0026quot;/\u0026gt; \u0026lt;arg name=\u0026quot;odom_topic\u0026quot; default=\u0026quot;/odom\u0026quot; /\u0026gt; \u0026lt;arg name=\u0026quot;scan_topic\u0026quot; default=\u0026quot;/scan\u0026quot; /\u0026gt; \u0026lt;node pkg=\u0026quot;amcl\u0026quot; type=\u0026quot;amcl\u0026quot; name=\u0026quot;amcl\u0026quot; output=\u0026quot;screen\u0026quot;\u0026gt; \u0026lt;remap from=\u0026quot;scan\u0026quot; to=\u0026quot;$(arg scan_topic)\u0026quot;/\u0026gt; \u0026lt;remap from=\u0026quot;odom\u0026quot; to=\u0026quot;$(arg odom_topic)\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;use_map_topic\u0026quot; value=\u0026quot;$(arg use_map_topic)\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_pose_x\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_pose_y\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_pose_a\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_cov_xx\u0026quot; value=\u0026quot;0.1*0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_cov_yy\u0026quot; value=\u0026quot;0.1*0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_cov_aa\u0026quot; value=\u0026quot;0.3*3.14\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;gui_publish_rate\u0026quot; value=\u0026quot;10.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_max_beams\u0026quot; value=\u0026quot;2.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_min_range\u0026quot; value=\u0026quot;0.15\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_max_range\u0026quot; value=\u0026quot;12.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_z_hit\u0026quot; value=\u0026quot;0.8\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_z_short\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_z_max\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_z_rand\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_sigma_hit\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_lambda_short\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_model_type\u0026quot; value=\u0026quot;likelihood_field\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_likelihood_max_dist\u0026quot; value=\u0026quot;2.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;min_particles\u0026quot; value=\u0026quot;100\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;max_particles\u0026quot; value=\u0026quot;1000\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;kld_err\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;kld_z\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;update_min_d\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;update_min_a\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;resample_interval\u0026quot; value=\u0026quot;1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;transform_tolerance\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;recovery_alpha_slow\u0026quot; value=\u0026quot;0.001\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;recovery_alpha_fast\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_frame_id\u0026quot; value=\u0026quot;odom\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_model_type\u0026quot; value=\u0026quot;diff\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha1\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha2\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha3\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha4\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha5\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;/node\u0026gt; \u0026lt;/launch\u0026gt;  演習 【jetson・開発マシン】それぞれdockerコンテナを起動 jetsonでdockerコンテナを起動\n(開発PC):~$ ssh roomba_dev1 (jetson):~$ cd ~/group_a/roomba_hack (jetson)::~/group_a/roomba_hack$ git pull (jetson):~/group_a/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh (jetson)(docker):~/roomba_hack# roslaunch roomba_bringup bringup.launch  開発PCでdockerコンテナを起動\n(開発PC):~$ cd ~/group_a/roomba_hack (開発PC):~/group_a/roomba_hack$ git pull (開発PC):~/group_a/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x   gmappingで地図作成 (開発PC)(docker) roslaunch navigation_tutorial gmapping.launch  地図の保存。map.pgm(画像データ)とmap.yaml(地図情報)が保存される。\n(開発PC)(docker) rosrun map_server map_saver  ~/roomba_hack/catkin_ws/src/navigation_tutorial/map の下に保存する。\n amclをlaunchして、自己位置推定する localizationノードと地図サーバーを同時に起動。\n(開発PC)(docker) roslaunch navigation_tutorial localization.launch (開発PC)(docker) roslaunch roomba_teleop teleop.launch (開発PC)(docker) rviz -d /root/roomba_hack/catkin_ws/src/navigation_tutorial/configs/navigation.rviz   初期位置の指定(rvizの2D Pose Estimate) コントローラで移動させてみて自己位置を確認 rqt_tf_treeを見てみる   amclのparamをチューニングする launchファイルの中身を見てみて、値を変えてみる。\n各パラメータの意味はamclのページを参照。\n例えば、・・・\n initial_cov_** を大きくしてみて、パーティクルがちゃんと収束するかみてみる。 particleの数(min_particles、max_particles)を変えてみて挙動をみてみる。   launchファイルの拡張 localization.launchファイルに以下を追加してteleop.launchとrvizが同時に起動するようにしてみよう。\n\u0026lt;!-- teleop.launchを起動--\u0026gt; \u0026lt;include file=\u0026quot;$(find roomba_teleop)/launch/teleop.launch\u0026quot;\u0026gt; \u0026lt;/include\u0026gt; \u0026lt;!-- rvizを起動--\u0026gt; \u0026lt;node pkg=\u0026quot;rviz\u0026quot; type=\u0026quot;rviz\u0026quot; name=\u0026quot;navigation_rviz\u0026quot; args=\u0026quot;-d $(find navigation_tutorial)/configs/navigation.rviz\u0026quot;/\u0026gt;  ","date":1642809600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1642809600,"objectID":"29d802971ebbc674f8dd6b80aeddda91","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap4/localization/","publishdate":"2022-01-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap4/localization/","section":"course","summary":"","tags":null,"title":"自己位置推定","type":"book"},{"authors":null,"categories":null,"content":"複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう\nLearn LiDARのスキャンデータを使って,障害物を回避してみよう 次に,LiDARでスキャンしたデータを使って,障害物を回避するようなプログラムを作ってみましょう.\nLiDARスキャンのメッセージ(/scan)の中身を見てみよう LiDARは,Light Detection And Rangingの略で,レーザ光を使って離れた場所にある物体形状や距離を測定するためのセンサです. 近年では,自動車の自動運転にも用いられることの多いセンサの一つです.\nroombaに搭載されたLiDARセンサ(rplidar)の値は,/scanのトピックに流れていて,rostopic echo /scanをしてみるとメッセージとしてどんな情報が流れているかわかります.\n大きなデータなので今回はテキストに掲載するのは省略しますが,rostopic type /scanをしてみると,メッセージとして,sensor_msgs/LaserScan型が使われていることがわかります. rostopic type /scan root@dynamics:~/roomba_hack# rostopic type /scan sensor_msgs/LaserScan  \nsensor_msgs/LaserScan型の定義を確認してみましょう. メッセージ型の定義は,ドキュメントのほか,rosmsg info sensor_msgs/LaserScanすることでもコマンドから確認できます. rosmsg info sensor_msgs/LaserScan root@dynamics:~/roomba_hack# rosmsg info sensor_msgs/LaserScan std_msgs/Header header uint32 seq time stamp string frame_id float32 angle_min float32 angle_max float32 angle_increment float32 time_increment float32 scan_time float32 range_min float32 range_max float32[] ranges float32[] intensities  \nangle_minにはスキャンの開始角度,angle_maxにはスキャンの終了角度がラジアンで記録されています. angle_incrementは,計測した間隔がラジアンで記録されています. range_maxにはスキャンの間で検出された最大の距離,range_minには最小の距離がメートルで記録されています.\nrvizでLiDARスキャンの値を可視化してみよう rvizでLiDARのスキャン結果を可視化してみましょう.\nLaserScanをAddして,topicに/scanを設定すると,以下のように,ロボットを中心にLiDARによって計測された障害物が赤く表示されます.\n  LiDARスキャンをrvizで可視化  LiDARを使って障害物を回避しよう それでは,LiDARスキャン/scanの情報を使った制御の実装の例としてnavigation_tutorialパッケージの中のavoidance.pyをみてみましょう.\navoidance.py #!/usr/bin/env python3 import numpy as np import rospy from geometry_msgs.msg import Twist from sensor_msgs.msg import LaserScan class Avoidance: def __init__(self): rospy.init_node('avoidance', anonymous=True) # Publisher self.cmd_vel_pub = rospy.Publisher('/planner/cmd_vel', Twist, queue_size=10) # Subscriber scan_sub = rospy.Subscriber('/scan', LaserScan, self.callback_scan) self.min_range = None def callback_scan(self, data): fov = np.deg2rad(60) min_range = data.range_max min_idx = -1 angle = data.angle_min for idx, r in enumerate(data.ranges): angle += data.angle_increment if -fov\u0026lt;angle\u0026lt;fov: if r\u0026lt;min_range: min_range = r min_idx = idx if min_idx \u0026lt; len(data.ranges)/2.0: self.direction = \u0026quot;RIGHT\u0026quot; else: self.direction = \u0026quot;LEFT\u0026quot; self.min_range = min_range def process(self): r = rospy.Rate(10) while not rospy.is_shutdown(): vel = Twist() if self.min_range is not None: if self.min_range \u0026gt;= 0.4: vel.linear.x = 0.2 vel.angular.z = 0.0 else: vel.linear.x = 0.0 if self.direction == \u0026quot;RIGHT\u0026quot;: vel.angular.z = 0.5 elif self.direction == \u0026quot;LEFT\u0026quot;: vel.angular.z = -0.5 self.cmd_vel_pub.publish(vel) r.sleep() if __name__=='__main__': avoidance = Avoidance() try: avoidance.process() except rospy.ROSInitException: pass   このプログラムでは,LiDARを使って進行方向に存在する障害物を見つけ,それを回避しながら進むようにロボットを制御しています.具体的には,\n ロボットの進行方向に物体がなかったら直進 ロボットの右側に障害物があったら左回転 ロボットの左側に障害物があったら右回転  することで障害物を回避(ぶつかる前に方向転換)しています.\nでは,プログラムの中身を見ていきます.\n/odomを使った制御の場合と同様に,ノードを定義する際に,コマンドを送るパブリッシャと,LiDARスキャンのデータを読み取るサブスクライバを作成します.\nclass Avoidance: def __init__(self): rospy.init_node('avoidance', anonymous=True) # Publisher self.cmd_vel_pub = rospy.Publisher('/planner/cmd_vel', Twist, queue_size=10) # Subscriber scan_sub = rospy.Subscriber('/scan', LaserScan, self.callback_scan) self.min_range = None  /scanのコールバックは,\ndef callback_scan(self, data): fov = np.deg2rad(60) min_range = data.range_max min_idx = -1 angle = data.angle_min for idx, r in enumerate(data.ranges): angle += data.angle_increment if -fov\u0026lt;angle\u0026lt;fov: if r\u0026lt;min_range: min_range = r min_idx = idx if min_idx \u0026lt; len(data.ranges)/2.0: self.direction = \u0026quot;RIGHT\u0026quot; else: self.direction = \u0026quot;LEFT\u0026quot; self.min_range = min_range  となっており,正面から左右60度の範囲内で最も短い距離をself.min_rangeに格納し,それが右側にあるのか左側にあるのかをself.directionに格納しています..\nこのプログラムを実行するとprocessメソッドが(0.1秒おきに)常に実行されます.\ndef process(self): r = rospy.Rate(10) while not rospy.is_shutdown(): vel = Twist() if self.min_range is not None: if self.min_range \u0026gt;= 0.4: vel.linear.x = 0.2 vel.angular.z = 0.0 else: vel.linear.x = 0.0 if self.direction == \u0026quot;RIGHT\u0026quot;: vel.angular.z = 0.5 elif self.direction == \u0026quot;LEFT\u0026quot;: vel.angular.z = -0.5 self.cmd_vel_pub.publish(vel) r.sleep()  processメソッド内部では,格納されたself.min_rangeが0.4(メートル)より大きい場合は,ロボットの前に何もないと判断して直進,小さい場合は,self.directionの値を見て,RIGHTであれば右に障害物があると判断して左回転,LEFTであれば左に障害物があると判断して右回転するようなプログラムになっています.\nそれでは,実際にLiDARを使って障害物を回避するプログラムを実行してみましょう.\n演習 【開発PC】topicの確認 /scanの型を確認\n(開発PC)(docker):~/roomba_hack# rostopic type /scan  /scanの中身を確認\n(開発PC)(docker):~/roomba_hack# rostopic echo /scan   LiDARスキャンを使ったフィードバック制御 avoidance.pyを実行してみよう.\nこのプログラムを動かすときには,コントローラのYボタンを押してからBボタンを押してautoモードにしておきましょう.\n今回はせっかくなので,launchfileから起動してみましょう. このlaunchfileは,navigation_tutorialパッケージの中のlaunchフォルダの中にあるavoidance.launchに記述されています(github).\n(開発PC)(docker):~/roomba_hack# roslaunch navigation_tutorial avoidance.launch  ロボットの進行方向に障害物があるときに,それを避けるように方向転換したら成功です.\ntry it! avoidance.pyの中身を読んでコードを変更してみよう\n","date":1687392e3,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1687392e3,"objectID":"4115bb19a4232d2372987cbff0c843fc","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap3/sensing3/","publishdate":"2023-06-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap3/sensing3/","section":"course","summary":"複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう\n","tags":null,"title":"ロボットシステムにおけるセンシング・アクチュエーション・通信③","type":"book"},{"authors":null,"categories":null,"content":"ロボット開発によく用いられるROSの概要を理解する\nLearn ROSの概要 ROS(Robot Operating System)は、ロボット・アプリケーション開発に便利な機能を提供するフレームワークです。 フレームワークとは、プログラミング言語を特定の目的に特化させて使うためのツールのことです。 具体的には以下にあげる機能を提供しています。\n  メッセージ通信\n プロセス間、コンピュータ間の通信ライブラリが提供されています。    デバイスドライバ\n  ROSに対応しているセンサやアクチュエータを搭載したロボットであれば、違うロボットであってもほぼ同じソースコードを使用して動かすことができます。\nroombaを動かすために書いたソースコードをそのまま使用してHSRを動かす、といったことができます。\n  https://github.com/ros-drivers\n  http://wiki.ros.org/Sensors\n    ライブラリ\n ロボットを動作させるソフトウェア(ナビゲーション、マニピュレーション)の基本機能の大半が提供されています。    視覚化ツール\n ロボットの内部状態やセンサ出力を2次元、3次元で視覚化するRvizや3次元動力学シミュレータのGazeboなどが提供されています。    パッケージ管理\n 多種多様なプログラミング言語(python, C++, \u0026hellip;)、依存関係で記述されたプログラム同士を統合的に使用することが可能です。 これにより、経路計画など処理が重いプロセスはC++でコードを書き、画像認識など機械学習系のプロセスはpythonでコードを書く、といったこともできるようになります。    ROSのメッセージ通信 ロボットを動かす際には、多くのプログラムを並列して実行し、それぞれがデータをやりとりする必要があります。 ROSはそのようなプログラム間の通信に必要な機能を提供しています。\n  ノード(node)\n ROSでは、一つのプログラム単位を「ノード(node)」と呼びます。 基本的には、一つのファイルが一つのノードに対応しています。 各ノードは次に述べるtopic、service、actionlibの三つの通信方法を使って、他のノードとデータのやり取りを行います。    トピック(topic)\n  ROSでの、最も基本的なデータ通信の経路を「トピック(topic)」と呼びます。\n  ノードはメッセージをトピックへ向けて配信(Publish)し、また購読する(Subscribe)ことで他のノードと情報を共有することができます。\n  配信を行うノードをPublisher、購読を行うノードをSubscriberと呼びます。ノードはこのどちらかに二分することができるというわけではなく、実際には一つのノードがpublisherであり、subscriberでもあるという状況がほとんどです。\n  トピックには名前が付けられており、同じトピックに複数のノードがデータを送ったり、複数のノードが同じデータを受け取ることができます。\n  メッセージ(message)\nトピックへ配信したり、購読したりするときのROSのデータ型のことを「メッセージ(message)」と呼びます。 メッセージの型はmsgファイルに記述されており、使用するプログラミング言語に依存しないデータ形式になっています。\n以下に、物体やロボットの位置を表す時によく用いるgeomemtry_msgs/PoseStamped型のmsgファイルを示します。 位置情報の時間や座標フレームの情報が含まれるheaderと座標位置を表すposeで定義されています。\nstd_msgs/Header header uint32 seq time stamp string frame_id geometry_msgs/Pose pose geometry_msgs/Point position float64 x float64 y float64 z geometry_msgs/Quaternion orientation float64 x float64 y float64 z float64 w  各行の左側にはデータ型が、右側には変数名が記述されています。\n    Topic通信のイメージ  \n    サービス(service)\n  「サービス(service)」も、ノードが他のノードと通信するための手段の一つです。topicより少し複雑な通信の仕方を提供します.\n  サービスには、サービスを提供するノード(service server)とサービスを要求するノード(service client)があります。\n  サービスは以下のような流れで使用されます。\n clientがserverに引数を渡す。 引数を受け取ったserverが何らかのプログラムを実行する。 serverは行為の結果を返り値としてclientに返す。 clientはその返り値に応じて後の挙動を変える。      サービスにおいて送受信されるデータの型は.srvファイルに記述されています。\n  メッセージと同様使用言語に依存しないデータ形式ですが、メッセージと異なるのは、引数と戻り値の二つの形式を定義する必要があるところです。\n  以下に、srvの例としてstd_srvs/SetBoolを示します。\nこのように引数と戻り値の間に---を入れて定義します。\nbool data --- bool success string message      アクション(actionlib)\n アクションもノード間通信の一つの手段です。serviceよりもさらに複雑な通信ができます。 サービスは動作の終了時にのみserverからclientに結果を返すのに対し、アクションは動作の途中経過をclientに渡すことができます。 ここでは詳しい説明を省略します。    ROSマスタ(ROS master)\n  「ROSマスタ(ROS master)」は、ノード、トピックおよびサービスの名前登録を行い、それぞれのノードが他のノードから見えるようにする役割を担っています。\n  通信するノード名とトピック名およびサービス名の対応が決定した後、ノード同士が「peer-to-peer」で通信します。\n  ROSマスタを起動するには「roscore」というコマンドを実行します(が、RoombaやHSRをつかうときにはこのコマンドが自動で実行されることが多いため、あまり意識する機会はないかもしれません)。\n      ROS通信  -- ROSと連動するソフトウェア ROSは以下のようなソフトウェアと連動して使うためのパッケージを提供しています。簡単な説明にとどめるので、詳しい使い方は必要になった際に調べてください。\n  OpenCV\n豊富な機能を持つ2D画像処理用のライブラリです。 カメラで撮影した画像を処理する際に使用します。\n  PCL(Point Cloud Library)\n 3次元点群処理のライブラリ。 HSRやRoombaにはRGBDカメラが搭載されています。DはDepthという意味で、画像の各ピクセルに距離情報を対応させたDepth画像を取得することができます。\nこのような三次元の点群の情報を処理する際にPCLを使うと便利です。     OpenSLAM\n 地図を効果的に使うことで、より安定したロボットのナビゲーションを行うことができます。 移動ロボットの自己位置推定と地図生成を同時に行うSLAM(Simultaneous Localization and Mapping)という手法は、それだけで一つの研究分野になる程奥深い分野で、活発に研究が行われています。 OpenSLAMは、SLAMのソースコードを公開するためのプラットフォームを提供しており、様々なSLAMの手法を実装しています。    これ以外にも多くのツールがROSと連動しています。\n可視化ツール ロボット内部の大量のデータが正しく処理されているか知りたい場合、変数の中身の数値などを直接みるのは大変です。直感的にわかりづらいためミスも増えます。\n可視化をすることで、開発やデバッグがより効率よく進められます。\n  rqt\nrqtはROSのGUIフレームワークで、様々なツールを提供しています。\nノードの状態を可視化するrqt_graph(下図1)、メッセージの値を時系列に沿ってプロットするrqt_plot(下図2)などがあります。\n  図1 rqt_graph wikipediaより引用    図2 rqt_plot wikipediaより引用      RViz\nロボットの三次元モデルや座標系、測定した三次元点群などを可視化するツールです。\n三次元空間の情報以外に、カメラに写っている画像なども表示できます。\n    gazebo\nオープンソースのロボット用三次元動力学シミュレータ。\n説明は割愛します。\n  演習 roombaドライバを起動し、動作することを確認する   jetsonにアクセスする\n(開発PC):~$ ssh roomba_dev1 (jetson):~$    dockerコンテナを起動する\n (jetson):~$ cd ~/23_group_x/roomba_hack (jetson):~/23_group_x/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh # このファイルを実行することでdockerコンテナを作成し、コンテナの中に入る。 root@roomba-dev-jetson:~/roomba_hack# # 上のように表示されればコンテナ内部に入れています。  今後docker内部であることは(docker)と表記します。\n  roomba driverなどを起動するlaunchファイルを実行する\nこのタイミングでルンバの電源が入っているかを確認しておきましょう。\n(jetson)(docker):~/roomba_hack# roslaunch roomba_bringup bringup.launch  起動に成功すればルンバからピッと短い音が鳴り、ターミナルには赤い文字が出続けるはずです。\n   開発PCでdockerコンテナを起動する   開発PCでdockerコンテナを起動する\n(開発PC):~$ cd ~/23_group_x/roomba_hack (開発PC):~/23_group_x/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x # xにはroomba_devの後につく数字を入れてください。   先ほどjetson内でdockerコンテナを起動しましたが、今回は開発PC内でコンテナを起動します。 このとき引数にjetsonのIPアドレスを入れることで、jetson内のROSマスタ(前述)に、開発PCからアクセスできるようにしています。    パッケージのビルド\n(開発PC)(docker):~/roomba_hack# cd catkin_ws (開発PC)(docker):~/roomba_hack/catkin_ws# ls # catkin_ws内に存在するディレクトリを確認する。 (開発PC)(docker):~/roomba_hack/catkin_ws# catkin_make # いろいろな出力が生成される。 (開発PC)(docker):~/roomba_hack/catkin_ws# ls # 再度catkin_ws内に存在するディレクトリを確認する。  ここで、buildとdevelというディレクトリが生成されていると、うまくいっています。\n build\nC++のコードを用いる際に、コンパイルされたファイルが生成されるディレクトリ。pythonを使っているときにはほとんど意識しない。 devel\n様々なファイルを含んでいるが、特にsetupファイルが重要。\nこのファイルを実行することで、現在いるワークスペースに含まれるコードを使用するようにROSの環境が設定される。    setupファイルを実行する\n(開発PC)(docker):~/roomba_hack/catkin_ws# source devel/setup.bash # setupファイルを実行     コントローラーを使ってロボットを動かす   コントローラーを起動\nコントローラーが開発PCに接続されていることを確認してください。\n(開発PC)(docker):~/roomba_hack/catkin_ws# roslaunch roomba_teleop teleop.launch    コントローラのモード\n 移動・停止 自動・マニュアル ドッキング・アンドッキング    コントローラによる操縦\n 移動ロック解除 L1を押している時のみ移動コマンドが動作します。 左ジョイスティック 縦方向で前進速度(手前に倒すとバック)、横方向は回転速度に対応しています。 左矢印 それぞれ、一定に低速度で前進・後退・回転します。       Roombaの情報を取得する   開発PCの新しいターミナルでdockerコンテナに入る\nbringup.launch及びteleop.launchを実行したターミナルは実行中のプログラムに占領されているので、開発PCで新しくターミナルを開いてコンテナの中に入ります。\nすでに開発PCで起動されているコンテナに入る場合は、\n(開発PC):~/23_group_x/roomba_hack$ docker exec -it roomba_hack bash # docker exec -it \u0026lt;コンテナ名\u0026gt; bash で起動中のコンテナに入ることができる。  または\n(開発PC):~/23_group_x/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh  のいずれかのコマンドで入ることができます。\n  Roombaの情報を取得する\nさまざまなコマンドを使ってRoombaの情報を取得してみましょう。\n(開発PC)(docker):~/roomba_hack# rosnode list # ノードの一覧を表示する (開発PC)(docker):~/roomba_hack# rostopic list # トピックの一覧を表示する (開発PC)(docker):~/roomba_hack# rostopic echo /cmd_vel # /cmd_velというトピックの中身を表示する # teleop.launchを実行している状態でコントローラーを操作すると、/cmd_velの中身が変化することがわかる。 (開発PC)(docker):~/roomba_hack# rqt_graph # ノードとトピックの関係を表示 (開発PC)(docker):~/roomba_hack# rviz # rvizを起動     プロセスの終了・dockerコンテナから出る   プロセスの終了\n一部のプログラムは終了するまで処理を続けるため、明示的に終了させる必要があります。\n多くのプログラムはCtrl+Cで終了します。\n  dockerコンテナ・ターミナルから出る\nコマンドライン上で\nexit  を実行することで、\n コンテナの中にいる場合はコンテナ外にでる。 sshしている場合はsshを終了する。 ターミナルを使用している場合はターミナルを終了する。  ことができます。また、Ctrl+Dでも同様のことができます。\n  ","date":1686009600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1686009600,"objectID":"20855653654f6e28e5120898d90797bd","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap1/ros/","publishdate":"2023-06-06T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap1/ros/","section":"course","summary":"ロボット開発によく用いられるROSの概要を理解する\n","tags":null,"title":"ROSとは","type":"book"},{"authors":null,"categories":null,"content":"ROSのパッケージ管理について理解しよう\nLearn ROSのパッケージ ROSでは、特定の機能やタスクを実現するためのコードやファイルをまとめてパッケージとして管理します。 各パッケージは独立して開発・保守され、他のパッケージと組み合わせて使用することができます。また、各パッケージが独立していることで、一度開発した機能を他のプロジェクトで再利用することも容易になります。\n例として、navigation_tutorialパッケージのファイル構成を示します。\nnavigation_tutorial ├── CMakeLists.txt ├── launch │ ├── amcl.launch │ ├── avoidance.launch │ ├── gmapping.launch │ ├── go_straight.launch │ ├── localization.launch │ ├── map_server.launch │ ├── move_base.launch │ └── navigation.launch ├── package.xml ├── params │ ├── base_global_planner_params.yaml │ ├── base_local_planner_params.yaml │ ├── costmap_common_params.yaml │ ├── dwa_local_planner_params.yaml │ ├── global_costmap_params.yaml │ ├── local_costmap_params.yaml │ └── move_base_params.yaml ├── scripts │ ├── avoidance.py │ ├── simple_control2.py │ └── simple_control.py └── src ├── avoidance.cpp └── go_straight.cpp  scriptsディレクトリ、srcディレクトリの役割 一般的に、scriptsディレクトリ内にpythonのプログラムが、srcディレクトリ内にC++のプログラムが配置されます。\n作成したプログラムはrosrunコマンドで実行することができます。\n$ rosrun \u0026lt;package name\u0026gt; \u0026lt;file name\u0026gt; # pythonファイルの場合 $ rosrun navigation_tutorial simple_control2.py # C++ファイルの場合 $ rosrun navigation_tutorial go_straight  実行時にパッケージ名を指定するので、現在どこのディレクトリにいるかに関係なく実行が可能です。\n launchディレクトリの役割 launchディレクトリにはlaunchファイルが配置されています。\nlaunchファイルは複数のROSノードを一括で起動するための設定が書かれたファイルです。複数のノードを起動したい時に、必要なファイルを一つ一つコマンドラインで実行していくことは大変ですが、launchファイルを用いることで一括で起動することができます。\nlaunchファイルはroslaunchコマンドで実行することができます。\n$ roslaunch \u0026lt;package name\u0026gt; \u0026lt;launch file name\u0026gt; # 例 $ roslaunch navigation_tutorial move_base.launch  .pyファイルや.cppファイルと同様、実行時にパッケージを指定するので、現在どこのディレクトリにいるかに関係なく実行が可能です。\n ROSのワークスペース 新しいROSのパッケージの開発は、一般にワークスペースと呼ばれる作業スペースのもとで行われます。\nワークスペースは、パッケージのビルドや実行に必要なファイルをまとめて管理するためのディレクトリです。catkin_makeコマンドを実行することで作成することができます。ワークスペースのディレクトリ名は任意ですが、よくcatkin_wsという名前が使われます。\nroomba_hackリポジトリ下にあるcatkin_wsのファイル構成を示します。\ncatkin_ws ├── build ├── devel └── src ├── CMakeLists.txt ├── navigation_tutorial │ ├── CMakeLists.txt │ ├── launch │ ├── package.xml │ ├── params │ ├── scripts │ └── src └── roomba ├── roomba_bringup │ ├── CMakeLists.txt │ ├── config │ ├── launch │ └── package.xml ├── roomba_description │ ├── CMakeLists.txt │ ├── config │ ├── launch │ ├── meshes │ ├── package.xml │ └── urdf ├── roomba_gazebo │ ├── CMakeLists.txt │ ├── launch │ └── package.xml └── roomba_teleop ├── CMakeLists.txt ├── include ├── launch ├── package.xml └── src  catkin_ws内でよく使用されるcatkin_makeコマンドについて説明します。\n$ cd catkin_ws # catkin_wsに移動 $ catkin_make  のように使用します。この時、以下の手順が実行されています。\n ワークスペースのsrcディレクトリ内に配置されたROSパッケージを検出し、ビルド対象として認識する。 各パッケージの依存関係を解決し、ビルドが必要な場合はコンパイルを行う。 ビルドが完了したパッケージをdevelディレクトリに配置する。  また、catkin_makeコマンドの実行後に\n$ source devel/setup.bash  を実行することで環境変数に自分が今いるワークスペースのパスを追加することができます。\nこれにより、ワークスペース内のパッケージを使用できるようになります。\nROSのコマンド ROSのコマンドのうち、よく使用するものを紹介します。\n  Topic関連\n$ rostopic list topicの一覧を表示する $ rostopic echo \u0026lt;topic name\u0026gt; 指定されたtopicの中身を表示する $ rostopic hz \u0026lt;topic name\u0026gt; topicの配信周波数を取得する $ rostopic info \u0026lt;topic name\u0026gt; topicの情報を表示する $ rostopic pub \u0026lt;topic name\u0026gt; \u0026lt;topic\u0026gt; topicを配信する $ rostopic type \u0026lt;topic name\u0026gt; topicの型を確認する    Node関連\n$ rosnode list nodeの一覧を表示する $ rosnode ping \u0026lt;node name\u0026gt; nodeの接続テストを行う $ rosnode info \u0026lt;node name\u0026gt; nodeの情報を表示する $ rosnode kill \u0026lt;node name\u0026gt; nodeをシャットダウンする    Package関連\n$ rospack list packageの一覧を表示する $ roscd \u0026lt;package name\u0026gt; 指定したpackage内に移動する    ROSのプログラムの書き方 それでは実際にプログラム例を見てみましょう。\n#!/usr/bin/env python3 import rospy from geometry_msgs.msg import Twist def time_control(pub, velocity, yawrate, time): # (2) vel = Twist() start_time = rospy.get_rostime().secs while(rospy.get_rostime().secs-start_time\u0026lt;time): vel.linear.x = velocity vel.angular.z = yawrate pub.publish(vel) rospy.sleep(0.1) def simple_controller(): # (1) rospy.init_node('simple_controller', anonymous=True) pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10) time_control(pub, 0.0, 0.0, 0.5) time_control(pub, 0.3, 0.0, 2.0) time_control(pub, 0.0, 0.0, 0.5) time_control(pub, -0.3, 0.0, 2.0) time_control(pub, 0.0, 0.0, 0.5) time_control(pub, 0.0, 0.5, 2.0) time_control(pub, 0.0, 0.0, 0.5) time_control(pub, 0.0, -0.5, 2.0) if __name__=='__main__': # (3) try: simple_controller() except rospy.ROSInitException: pass  (1) simple_controller関数 まずsimple_controller関数内をみていきましょう。\n以下の部分で\u0026quot;simple_controller\u0026quot;という名前のノードを定義しています。\nrospy.init_node('simple_controller', anonymous=True)  以下の部分で、このノードがPublisherであることを宣言しています。\npub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)  今回の場合は、/cmd_velトピックをTwist型で送信するPublisherを宣言しています。\n (2) time_control関数 続いて、time_control関数です。\nこの関数はpublisher、速度、角速度、時間を受け取り、速度指令をpublishします。\ndef time_control(pub, velocity, yawrate, time): vel = Twist() start_time = rospy.get_rostime().secs while(rospy.get_rostime().secs-start_time\u0026lt;time): vel.linear.x = velocity vel.angular.z = yawrate pub.publish(vel) rospy.sleep(0.1)  ここでTwist型のインスタンスを作成しています。\nvel = Twist()  while文で受け取った時間が過ぎるまでの間、受け取った速度と各速度をvelに格納し、pub.publish(vel)でpublishを行なっています。\nwhile(rospy.get_rostime().secs-start_time\u0026lt;time): vel.linear.x = velocity vel.angular.z = yawrate pub.publish(vel) rospy.sleep(0.1)   (3) グローバル変数__name__を用いたファイルの実行 (1)や(2)は関数の定義をしているだけで、実際にプログラムを実行した際には\nif __name__=='__main__':  以下の部分が実行されます。\n__name__はpythonの特殊な変数の一つで、ファイル実行時に自動で設定されます。 pythonのプログラムはコマンドで直接実行するか、importで他のプログラムから参照されるかのいずれかの方法により実行されますが、__name__はこの実行方法によって値が変わります。\n 直接実行された場合、__name__には__main__と言う文字列が代入されます。 importされた場合、__name__にはファイル名が代入されます。  この性質を利用し、ファイルが直接実行された場合のみ実行したい処理をif文で囲んでいます。\n大抵の場合、rosのプログラムでは、上に挙げたファイルのように、関数やクラスの定義と実際にプログラムを実行する部分を分けて記述します。\n 演習 【jetson・開発マシン】それぞれdockerコンテナを起動 jetsonでdockerコンテナを起動\n(開発PC):~$ ssh roomba_dev1 (jetson):~$ cd ~/group_a/roomba_hack (jetson):~/group_a/roomba_hack ./RUN-DOCKER-CONTAINER.sh (jetson)(docker):~/roomba_hack#  開発PCでdockerコンテナを起動\n(開発PC):~$ cd ~/group_a/roomba_hack (開発PC):~/group_a/roomba_hack# ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x (開発PC)(docker):~/roomba_hack#   【jetson・開発マシン】ビルドをしてパスを通す catkin_make後にdevelとbuildディレクトリが作成されることを確認しましょう。\n(開発PC)(docker):~/roomba_hack# cd catkin_ws (開発PC)(docker):~/roomba_hack/catkin_ws# rm -rf devel build (開発PC)(docker):~/roomba_hack/catkin_ws# ls (開発PC)(docker):~/roomba_hack/catkin_ws# catkin_make (開発PC)(docker):~/roomba_hack/catkin_ws# ls (開発PC)(docker):~/roomba_hack/catkin_ws# source ./devel/setup.bash   【jetson】ROSマスタ、各種ノードを起動 (jetson)(docker):~/roomba_hack# roslaunch roomba_bringup bringup.launch   ROSメッセージの可視化 【開発PC】topicの確認 Topic関連のコマンドのところのrostopic listコマンドを使用してtopic一覧を表示してみましょう\n(開発PC)(docker):~/roomba_hack# rostopic list  特定のtopicの型を確認\n(開発PC)(docker)# rostopic type /camera/color/image_raw (開発PC)(docker)# rostopic type /scan  その型が実際にどのような構成をしているのかはrosmsg info \u0026lt;topic type\u0026gt;で調べられます。\n参考\nsensor_msgs/LaserScan型 http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/LaserScan.html\nsensor_msgs/Image型 http://docs.ros.org/en/noetic/api/sensor_msgs/html/msg/Image.html\n特定のtopicの中身を確認\n(開発PC)(docker)# rostopic echo /camera/color/image_raw (開発PC)(docker)# rostopic echo /scan  rvizを用いて可視化\n(開発PC)(docker)# rviz   【開発PC】topicのpublish(配信) topic/cmd_velの情報を確認\n(開発PC)(docker)# rostopic info /cmd_vel  topic/cmd_velの型を確認\n(開発PC)(docker)# rostopic type /cmd_vel  geometry_msgs/Twist型 http://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/Twist.html\ntopic/cmd_velをpublish\n(開発PC)(docker)# rostopic pub /cmd_vel geometry_msgs/Twist \u0026quot;linear: x: 1.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0\u0026quot;  (開発PC)(docker)# rosrun navigation_tutorial simple_control.py   Try it! 時間が余った人向け try it! roomba_bringupパッケージのbringup.launchの中身を読んでみよう\nhint roscdコマンドを使うとパッケージへ簡単に移動ができます。ファイルの中身を表示するにはcatコマンドを使用します。\ntry it! 開発PCでrosnode関連のコマンドを使ってみよう\ntry it! 開発PCでrosrun rqt_graph rqt_graphを実行してnodeとtopicの関連を可視化してみよう\ntry it! 開発PCでsimple_control.pyの中身を読んでコードを変更してみよう\nhint コードを編集するときはエディタを使うことがおすすめです。新しくターミナルを開いて\n(開発PC):~$ cd group_a/roomba_hack (開発PC):~group_a/roomba_hack$ code .  でVScodeを起動することができます。\n","date":1649116800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1649116800,"objectID":"3a3eac87851dbc29e0c5e1d274ba3f31","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap2/rosbasic/","publishdate":"2022-04-05T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap2/rosbasic/","section":"course","summary":"ROSのパッケージ管理について理解しよう\n","tags":null,"title":"ROSのパッケージ・ワークスペース","type":"book"},{"authors":null,"categories":null,"content":"センサの値を読み取りロボットを動かしてみよう\nLearn ロボットセンサの基礎知識 ロボットが動作するために必要なセンサは大きく2種類に分けられる。\n1つ目が外界センサで、これはロボットが行動する環境の情報を取得するためのセンサーである。 具体的なセンサとして、\n レーザスキャナ  レーザービームを照射して物体上の点までの距離と方向を計測する。レーザビームを走査することで、センサ周囲の広い範囲を計測した点群を得られる。 レーザスキャナを用いて対象物までの距離や位置、形状を検知することをLiDAR(Light Detection And Ranging(光による検知と測距))という。   デプスカメラ  赤外線などを発光して距離情報を画像として計測する。それと同時に通常のRGB画像も取得できるものがある。    などがあげられる。\nセンサのノイズの影響を軽減するため、複数のセンサを組み合わせて利用されることもある。\n2つ目は内界センサで、これは(ロボットアームのような変形可能な)ロボットが自身の内部状態を把握し、位置や姿勢を制御するために使われるセンサーである。\n ホイールエンコーダ  回転信号を電気信号に変換するデバイス。 ロボットの位置の推定:台車に取り付けられたホイールエンコーダが回転角度や回転速度を計測し、ロボットの位置や向きを推定する。 ロボットアームの制御:ロボットアームは、ベースや関節にモーターとホイールエンコーダを備えている。ホイールエンコーダは関節の回転角度を計測し、アームの位置制御に使用される。これにより、アームは所望の位置や角度に正確に移動することができる。   IMU(Internal Measurement Unit)  ジャイロ、加速度センサ、磁気センサなどが一体となったもの。  ジャイロセンサは回転角・各速度を求めるセンサ。 加速度センサは衝撃の検出、歩行ロボットの姿勢制御などに使われる。      などが内界センサである。\n参考\n https://www.jsme.or.jp/jsme-medwiki/14:1013897#:~:text=robot%20sensor ","date":1649116800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1649116800,"objectID":"ff51cff84b07cbb120bc73f6c20e4a6b","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap2/sensing1/","publishdate":"2022-04-05T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap2/sensing1/","section":"course","summary":"センサの値を読み取りロボットを動かしてみよう\n","tags":null,"title":"ロボットシステムにおけるセンシング・アクチュエーション・通信①","type":"book"},{"authors":null,"categories":null,"content":"Learn Navigationシステム ナビゲーションの目的は、地図上の任意の目標地点へ、障害物を避けながらなるべく早く自律して移動することです。\nナビゲーションシステムは、\n 地図 目標位置 自己位置推定結果 リアルタイムのセンサ情報(LiDARスキャン情報など)  などを入力として、ロボットへの行動指令値(速度など)を出力します。\nナビゲーションでは、地図全体とロボット周辺(センサで見える範囲)の大きく2つに分けて考えることが多いです。\n地図全体を考えるグローバルパスプランでは、地図情報とゴール情報から大まかなゴールまでの経路を算出します。\nロボット周辺を考える ローカルパスプランでは、グローバルで算出した経路に沿うようにしつつ、周辺の障害物情報を避ける行動指令値を算出します。\nそれぞれの経路を考えるにあたって、経路のコストがどうなるか重要になります。 このコストを表現する方法として、コストマップが用いられることが多いです。\n  Navigationシステム概要(from ROS wiki)  Cost Map コストマップは、経路を算出するために用いることから、扱いやすいグリット上の占有格子地図という形で表現されることが多いです。\n(空を飛んだり、3次元地形を考えなくていい場合は、基本2次元で表現します。)\n経路は格子地図上で、点で扱うことが多いですが、ロボット自身はある程度の大きさを持っているので、スキャン情報で得られた点ギリギリに経路を生成すると、衝突してしまします。\nそのため、コストマップでは以下の図のようにスキャンで得られた点(図中の赤点)から、ロボットが入ってほしくない範囲にコスト(図中の青く塗りつぶされているところ)が付与するという表現をします。\n  コストマップ概要(from ROS wiki)  Global Path Planning グローバルパスプランの例として、グラフ探索を利用したダイクストラ法やA*法などで経路探索をすることがあります。\n  グローバルパスプランの例(from PythonRobotics)  Local Path Planning 局所経路計画(Local Path Planning)は、ロボット周辺の障害物を避けながら、目標値へ早く行けるような経路(ロボットの行動)を算出するモジュールです。\n代表的なアルゴリズムとしてDynamic Window Approach(DWA)というものがあります。   ローカルパスプラン概要(from ROS wiki) \nアルゴリズムの概要は以下になります。\n ロボットの行動空間から行動をサンプル サンプルした行動とロボットの運動モデルを用いて、一定時間シミュレーションをして経路を生成 生成した経路ごとに、コストマップやゴール情報からコストを算出 コスト最小の経路を選択し、ロボットの指令値とする 1~4を繰り返す  演習 Dockerfileにnavigationを追加してBuildする \n -- navigationをlaunchして、rviz上で指定した位置までナビゲーションさせてみる (開発PC)(docker) roslaunch navigation_tutorial navigation.launch   navigationをlaunchして、map座標系の位置を指定してナビゲーションさせてみる \n -- navigationのparamをチューニングする move baseのパラメータは navigation_tutorial/params の中にyaml形式で保存されています。\nlaunchファイルではloadコマンドでyamlを読み込んでいます。\n move_base base_local_planner costmap_2d  ","date":1609459200,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1609459200,"objectID":"ea25624150d5f3e46f1ef192419e7583","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap4/navigation/","publishdate":"2021-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap4/navigation/","section":"course","summary":"","tags":null,"title":"ナビゲーション","type":"book"},{"authors":null,"categories":null,"content":"Congratulations to Jian Yang and Monica Hall for winning the Best Paper Award at the 2020 Conference on Wowchemy for their paper “Learning Wowchemy”.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tempus augue non tempor egestas. Proin nisl nunc, dignissim in accumsan dapibus, auctor ullamcorper neque. Quisque at elit felis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aenean eget elementum odio. Cras interdum eget risus sit amet aliquet. In volutpat, nisl ut fringilla dignissim, arcu nisl suscipit ante, at accumsan sapien nisl eu eros.\nSed eu dui nec ligula bibendum dapibus. Nullam imperdiet auctor tortor, vel cursus mauris malesuada non. Quisque ultrices euismod dapibus. Aenean sed gravida risus. Sed nisi tortor, vulputate nec quam non, placerat porta nisl. Nunc varius lobortis urna, condimentum facilisis ipsum molestie eu. Ut molestie eleifend ligula sed dignissim. Duis ut tellus turpis. Praesent tincidunt, nunc sed congue malesuada, mauris enim maximus massa, eget interdum turpis urna et ante. Morbi sem nisl, cursus quis mollis et, interdum luctus augue. Aliquam laoreet, leo et accumsan tincidunt, libero neque aliquet lectus, a ultricies lorem mi a orci.\nMauris dapibus sem vel magna convallis laoreet. Donec in venenatis urna, vitae sodales odio. Praesent tortor diam, varius non luctus nec, bibendum vel est. Quisque id sem enim. Maecenas at est leo. Vestibulum tristique pellentesque ex, blandit placerat nunc eleifend sit amet. Fusce eget lectus bibendum, accumsan mi quis, luctus sem. Etiam vitae nulla scelerisque, eleifend odio in, euismod quam. Etiam porta ullamcorper massa, vitae gravida turpis euismod quis. Mauris sodales sem ac ultrices viverra. In placerat ultrices sapien. Suspendisse eu arcu hendrerit, luctus tortor cursus, maximus dolor. Proin et velit et quam gravida dapibus. Donec blandit justo ut consequat tristique.\n","date":1606867200,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1606867200,"objectID":"2a0ec8a990dbd78a00c4e15a09364b00","permalink":"https://matsuolab.github.io/roomba_hack_course/post/20-12-02-icml-best-paper/","publishdate":"2020-12-02T00:00:00Z","relpermalink":"/roomba_hack_course/post/20-12-02-icml-best-paper/","section":"post","summary":"Congratulations to Jian Yang and Monica Hall for winning the Best Paper Award at the 2020 Conference on Wowchemy for their paper “Learning Wowchemy”.\n","tags":null,"title":"Jian Yang and Monica Hall Win the Best Paper Award at Wowchemy 2020","type":"post"},{"authors":null,"categories":null,"content":"Congratulations to Richard Hendricks for winning first place in the Wowchemy Prize.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tempus augue non tempor egestas. Proin nisl nunc, dignissim in accumsan dapibus, auctor ullamcorper neque. Quisque at elit felis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aenean eget elementum odio. Cras interdum eget risus sit amet aliquet. In volutpat, nisl ut fringilla dignissim, arcu nisl suscipit ante, at accumsan sapien nisl eu eros.\nSed eu dui nec ligula bibendum dapibus. Nullam imperdiet auctor tortor, vel cursus mauris malesuada non. Quisque ultrices euismod dapibus. Aenean sed gravida risus. Sed nisi tortor, vulputate nec quam non, placerat porta nisl. Nunc varius lobortis urna, condimentum facilisis ipsum molestie eu. Ut molestie eleifend ligula sed dignissim. Duis ut tellus turpis. Praesent tincidunt, nunc sed congue malesuada, mauris enim maximus massa, eget interdum turpis urna et ante. Morbi sem nisl, cursus quis mollis et, interdum luctus augue. Aliquam laoreet, leo et accumsan tincidunt, libero neque aliquet lectus, a ultricies lorem mi a orci.\nMauris dapibus sem vel magna convallis laoreet. Donec in venenatis urna, vitae sodales odio. Praesent tortor diam, varius non luctus nec, bibendum vel est. Quisque id sem enim. Maecenas at est leo. Vestibulum tristique pellentesque ex, blandit placerat nunc eleifend sit amet. Fusce eget lectus bibendum, accumsan mi quis, luctus sem. Etiam vitae nulla scelerisque, eleifend odio in, euismod quam. Etiam porta ullamcorper massa, vitae gravida turpis euismod quis. Mauris sodales sem ac ultrices viverra. In placerat ultrices sapien. Suspendisse eu arcu hendrerit, luctus tortor cursus, maximus dolor. Proin et velit et quam gravida dapibus. Donec blandit justo ut consequat tristique.\n","date":1606780800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1606780800,"objectID":"be2bd15f022f0d83fe9ffd743881e70c","permalink":"https://matsuolab.github.io/roomba_hack_course/post/20-12-01-wowchemy-prize/","publishdate":"2020-12-01T00:00:00Z","relpermalink":"/roomba_hack_course/post/20-12-01-wowchemy-prize/","section":"post","summary":"Congratulations to Richard Hendricks for winning first place in the Wowchemy Prize.\n","tags":null,"title":"Richard Hendricks Wins First Place in the Wowchemy Prize","type":"post"},{"authors":null,"categories":null,"content":"","date":-62135596800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":-62135596800,"objectID":"f26b5133c34eec1aa0a09390a36c2ade","permalink":"https://matsuolab.github.io/roomba_hack_course/admin/config.yml","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/admin/config.yml","section":"","summary":"","tags":null,"title":"","type":"wowchemycms"},{"authors":null,"categories":null,"content":"","date":-62135596800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":-62135596800,"objectID":"6d99026b9e19e4fa43d5aadf147c7176","permalink":"https://matsuolab.github.io/roomba_hack_course/contact/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/contact/","section":"","summary":"","tags":null,"title":"","type":"widget_page"}]
\ No newline at end of file
+[{"authors":null,"categories":null,"content":"TRAILに関する最新情報をお届けします.\n","date":-62135596800,"expirydate":-62135596800,"kind":"term","lang":"en","lastmod":-62135596800,"objectID":"2525497d367e79493fd32b198b28f040","permalink":"https://matsuolab.github.io/roomba_hack_course/authors/admin/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/authors/admin/","section":"authors","summary":"TRAILに関する最新情報をお届けします.","tags":null,"title":"TRAIL Admin","type":"authors"},{"authors":["jumpei-arima"],"categories":null,"content":"","date":-62135596800,"expirydate":-62135596800,"kind":"term","lang":"en","lastmod":-62135596800,"objectID":"451d184f9c1e53301faf72f3ade4d5c6","permalink":"https://matsuolab.github.io/roomba_hack_course/authors/jumpei-arima/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/authors/jumpei-arima/","section":"authors","summary":"","tags":null,"title":"有馬 純平","type":"authors"},{"authors":["tatsuya-matsushima"],"categories":null,"content":"人間と共生できるような適応的なロボットの開発と,そのようなロボットを作ることにより生命性や知能を構成的に理解することに興味があります. 特に現在は,深層生成モデルを用いた環境のダイナミクスのモデリング(世界モデル)・モデルベース強化学習・メタ模倣学習に関して研究を行っています.\n","date":-62135596800,"expirydate":-62135596800,"kind":"term","lang":"en","lastmod":-62135596800,"objectID":"96eea6d6aacba886dc9c6ad4c3b6f83f","permalink":"https://matsuolab.github.io/roomba_hack_course/authors/tatsuya-matsushima/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/authors/tatsuya-matsushima/","section":"authors","summary":"人間と共生できるような適応的なロボットの開発と,そのようなロ","tags":null,"title":"松嶋 達也","type":"authors"},{"authors":["yuya-ikeda"],"categories":null,"content":"","date":-62135596800,"expirydate":-62135596800,"kind":"term","lang":"en","lastmod":-62135596800,"objectID":"4c9f99a2ef4096b81e6d0a08880581e8","permalink":"https://matsuolab.github.io/roomba_hack_course/authors/yuya-ikeda/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/authors/yuya-ikeda/","section":"authors","summary":"","tags":null,"title":"池田 悠也","type":"authors"},{"authors":null,"categories":null,"content":" 開発環境 ロボットシステムの開発環境に使われている要素の概要を理解する   ROSとは ロボット開発によく用いられるROSの概要を理解する   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"f1fb9b3e706d6a8e7af6ee8a67c187b1","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap1/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap1/","section":"course","summary":"ロボットシステムの基礎知識","tags":null,"title":"Chapter 1","type":"book"},{"authors":null,"categories":null,"content":" ROSのパッケージ・ワークスペース ROSのパッケージ管理について理解しよう   ロボットシステムにおけるセンシング・アクチュエーション・通信① センサの値を読み取りロボットを動かしてみよう   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"b03bbdc309591542a3a0cf48966f3d87","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap2/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap2/","section":"course","summary":"センシング・アクチュエーション・通信①","tags":null,"title":"Chapter 2","type":"book"},{"authors":null,"categories":null,"content":" ロボットシステムにおけるセンシング・アクチュエーション・通信② 複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう   ロボットシステムにおけるセンシング・アクチュエーション・通信③ 複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"b891a66c1b2d8231b078e52b380c46a1","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap3/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap3/","section":"course","summary":"センシング・アクチュエーション・通信②","tags":null,"title":"Chapter 3","type":"book"},{"authors":null,"categories":null,"content":" 自己位置推定   ナビゲーション   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"ef4ff386bad3919cafa8bb6a9000e1a5","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap4/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap4/","section":"course","summary":"自律移動","tags":null,"title":"Chapter 4","type":"book"},{"authors":null,"categories":null,"content":" 三次元画像処理   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"c5b6edce25a3188d94c102a439d09587","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap5/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap5/","section":"course","summary":"3次元画像認識","tags":null,"title":"Chapter 5","type":"book"},{"authors":null,"categories":null,"content":" serviceとactionlib   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"8f714aaf4a960d30c5c5c987c0a4c5d1","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap6/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap6/","section":"course","summary":"複雑なロボットシステムの実装・分散処理","tags":null,"title":"Chapter 6","type":"book"},{"authors":null,"categories":null,"content":"","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"2f5bc53e4f4c671993cb760f7d80fa51","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap7/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap7/","section":"course","summary":"最終プロジェクト準備","tags":null,"title":"Chapter 7","type":"book"},{"authors":null,"categories":null,"content":" 最終プロジェクト   ","date":1536451200,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1536451200,"objectID":"2adf7951016803e68618153c45700bf0","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap8/","publishdate":"2018-09-09T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap8/","section":"course","summary":"最終プロジェクト","tags":null,"title":"Chapter 8","type":"book"},{"authors":null,"categories":null,"content":"  -- Table of Contents  Program overview Courses in this program Meet your instructor    Python programming skills - Statistical concepts and how to apply them in practice - Gain experience with the Scikit, including data visualization with Plotly and data wrangling with Pandas -- Program overview 実ロボット(roomba)を利用した演習を通じて、ロボットシステムの仕組みから、センシング・認識・行動について研究開発に必要な最低限の知識や実装スキルを習得する。\nCourses in this program  Chapter 1 ロボットシステムの基礎知識\n  Chapter 2 センシング・アクチュエーション・通信①\n  Chapter 3 センシング・アクチュエーション・通信②\n  Chapter 4 自律移動\n  Chapter 5 3次元画像認識\n  Chapter 6 複雑なロボットシステムの実装・分散処理\n  Chapter 7 最終プロジェクト準備\n  Chapter 8 最終プロジェクト\n  Meet your instructor TRAIL Admin Are there prerequisites? There are no prerequisites for the first course.\n --  Begin the course   ","date":1611446400,"expirydate":-62135596800,"kind":"section","lang":"en","lastmod":1611446400,"objectID":"97ede4bb026d52eb5ee887238b8270ea","permalink":"https://matsuolab.github.io/roomba_hack_course/course/","publishdate":"2021-01-24T00:00:00Z","relpermalink":"/roomba_hack_course/course/","section":"course","summary":"roombaを用いた実ロボットシステム入門","tags":null,"title":"📊 ロボットシステム入門","type":"book"},{"authors":null,"categories":null,"content":"Learn ここまでトピックを使った通信を使ってロボットシステムを構築してきました. トピック通信は,メッセージをpublish・subscribeすることで通信する,相手を仮定しない非同期な通信方法でした.\nしかし,システムを構築する場合には,「相手ノードの処理の結果を呼び出し側のノードで受け取って活用したい」 といった場合もあります.\nこのような比較的複雑な通信を実現するための通信方式として,ROSではサービス(service)とアクション(actionlib)が用意されています.\nservice これまで利用してきたトピック通信は,通信の相手を仮定しない(相手がいようといまいと関係ない)ため, ロボットシステムに特有な非同期通信・処理を簡単に実現することができました.\n一方で,他のノードに対して「特定の処理の依頼をして,その結果を待ちたい」場合など,同期的・双方向な通信が必要になることがあります. 例えば,あるノードの設定を変更をして,それが成功したかどうかを知りたい場合などに使えます. サービスを使った通信は,「クライアント・サーバ」型の通信(クライアントサーバモデル, client-server model)となり, クライアントがサーバにリクエストを送ると,サーバ側で処理を行い,クライアントにレスポンスを返します.\npythonでは,rospyモジュールのrospy.Service()や rospy.ServiceProxy()を使用することで,サーバ・クライアント を簡単に実装することができます(参考).\nまた,以下のように,コマンドライン上でサービスを活用する方法も用意されています.\n# サーバを呼び出す $ rosservice call \u0026lt;ServiceName\u0026gt; \u0026lt;Arguments\u0026gt; # 存在するサービスの一覧を表示 $ rosservice list # サービスのメッセージ型を表示 $ rosservice type \u0026lt;ServiceName\u0026gt;  actionlib ここまで,トピック通信を使うことで相手を仮定しない非同期通信を,サービスを使った通信を行うことで相手のレスポンスを待つ同期的な通信を実現できることを見てきました.\nサービスによる通信では,クライアントはサーバからのレスポンスを待つため,サーバで長い時間がかかるような処理を行う(計算量が大きい,または,移動に時間がかかるなど)場合には,クライアントの処理が長い間停止してしまうという問題があります.\nそのため,処理の呼び出し側のプログラムをブロックせずに,かつ,処理の結果(や途中経過)を知れるような非同期通信が欲しくなります. この要求を満たすのが,ROSのアクション(actionlib)です.\nactionlibは,実はトピック通信の組み合わせとして構成されており,goal(命令),result(処理の結果),feedback(途中経過),status(サーバの状態),cancel(命令の取り消し)の5つのトピックからなります. このあたりの仕様は,qiitaのROS講座が詳しいので参照してください.\npythonでは,actionlibのサーバやクライアントも,\nimport actionlib  したのちに,他の通信方式と同様にactionlib.SimpleActionServerとして,簡単に作成できます(ドキュメント).\n今回の演習では,簡単のためaction serverの作成は行いません. 変わりに,移動のためのactionとして,move_baseパッケージの中で定義されているmove_baseというactionを使うことにしましょう.\n実はこのパッケージは\nroslaunch navigation_tutorial navigation.launch  を実行してmove_baseノードを起動した際に既に利用されていました.\n move_baseパッケージの詳細はドキュメントを参照してください.\n同様に,action clientもactionlib.SimpleActionClientを利用することで簡単に作成できます.\n例えば,move_baseのaction clientを実装する際には,\nimport actionlib import tf from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal from geometry_msgs.msg import Quaternion action_client = actionlib.SimpleActionClient('move_base', MoveBaseAction) action_client.wait_for_server() # action serverの準備ができるまで待つ goal = MoveBaseGoal() # goalのメッセージの定義 goal.target_pose.header.frame_id = 'map' # マップ座標系でのゴールとして設定 goal.target_pose.header.stamp = rospy.Time.now() # 現在時刻 # ゴールの姿勢を指定 goal.target_pose.pose.position.x = X goal.target_pose.pose.position.y = Y q = tf.transformations.quaternion_from_euler(0, 0, YAW) # 回転はquartanionで記述するので変換 goal.target_pose.pose.orientation = Quaternion(q[0], q[1], q[2], q[3]) action_client.send_goal(goal) # サーバにゴールを送信  のようにクライアントのsend_goalメソッドでゴールを指定できます.\nその後,\naction_client.wait_for_result(rospy.Duration(30))  とすると,結果が返ってくるまで(この場合30秒間)クライアントの処理をブロックすることができ,\nresult = action_client.wait_for_result(rospy.Duration(30))  とすることで,result変数に処理の結果を格納できます.\n演習 【jetson・開発マシン】起動準備 cd roomba_hack git fetch git checkout feature/integrate (jetson) ./RUN-DOCKER-CONTAINER.sh (開発マシン) ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x   【開発マシン】scriptベースのnavigationを実行してみる (開発マシン)(docker) roslaunch navigation_tutorial navigation.launch (開発マシン)(docker) rosrun navigation_tutorial topic_goal.py (開発マシン)(docker) rosrun navigation_tutorial action_goal.py   【開発マシン】RealSenseで検出した障害物をコストマップに追加してみよう (開発マシン)(docker) roslaunch three-dimensions_tutorial detection_pc.launch   (総合課題)障害物を避けながらnavigationする Lidarに映らない物体も画像ベースで検出しコストマップに追加することでナビゲーション時にぶつからないようにしましょう。\nヒント\n 物体検出結果に基づいて物体部分以外をマスクしたデプス画像をpublishする depth2pc.launchでそれをsubscribeし、point(cloud)に変換する 変換されたpointからmap座標系での位置を取得する costmapに反映する move_baseアクションを使ってナビゲーションを実装しよう.  するとactionがタイムアウトした場合や,KeyboardInterruptされた場合にcancel_goalメソッドを使うことでactionをキャンセルできるように拡張できるはずです.    さらに,PyTorchを使用した自作の分類器やネット上の分類器をシステムに組み込んで(例えばセグメンテーションモデルなど),よりよく動作するように改良してみましょう.\n","date":1690329600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1690329600,"objectID":"ba96fee21f24c18e95f236953fe70e74","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap6/service-actionlib/","publishdate":"2023-07-26T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap6/service-actionlib/","section":"course","summary":"","tags":null,"title":"serviceとactionlib","type":"book"},{"authors":null,"categories":null,"content":"複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう\nLearn 前回使用したsimple_control.py では,速度,角速度,時間を指定し, \u0026ldquo;速度 * 時間\u0026rdquo; あるいは \u0026ldquo;角速度 * 時間\u0026rdquo; という演算を行うことで, ロボットを意図した場所へ移動させる命令を与えていました.\nしかし,この制御の仕方には,いくつかの問題があります.\n ホイールと地面との間に滑りがあった場合,ロボットは指定した距離より小さい距離しか移動しない可能性がある. ロボット本体の問題で,指定した速度よりも実際の速度が大きいまたは小さい場合, ロボットは指定した位置には移動しない可能性がある.  これらは,動作の結果を考慮せず,はじめに指定した速度と時間にのみ従って動く,という制御の仕方のために起こります.\nこのように,あらかじめ指定された制御信号にのみ基づいて制御を行い, その結果(フィードバック情報)を考慮しない制御の仕方を フィードフォワード制御(開ループ制御)と呼びます. フィードフォワード制御は,制御対象が予測可能で外乱が少ない場合や, システムが簡潔である場合に使用されることがあります.\n一方で,センサーからのフィードバック情報を利用して制御信号を修正する制御の仕方を フィードバック制御(閉ループ制御)と呼びます. フィードバック制御は,制御対象の予測が難しく,外乱が大きい場合に有効です.\n今回は,ロボットのセンサ情報を用いるフィードバック制御 によってロボットをより柔軟に動かしてみましょう. オドメトリとLiDARという2種類のセンサの情報を用います.\nフィードバック制御を行なってみましょう.-- オドメトリのセンサ情報を用いた制御 まずは,ロボットのタイヤの回転量から計算される移動距離である(ホイール)オドメトリ(odometry)を使った制御をしてみましょう.\nオドメトリのメッセージ(/odom)の中身を見てみよう roombaのオドメトリの情報は,/odomトピックにpublishされています.\n$ rostopic echo /odom  を実行するとメッセージとしてどのような情報が流れているかがわかります. $ rostopic echo -n 1 /odom root@dynamics:~/roomba_hack# rostopic echo -n 1 /odom header: seq: 2115 stamp: secs: 1649692132 nsecs: 791056254 frame_id: \u0026quot;odom\u0026quot; child_frame_id: \u0026quot;base_footprint\u0026quot; pose: pose: position: x: -0.014664691872894764 y: -0.0010878229513764381 z: 0.0 orientation: x: 0.0 y: 0.0 z: 0.0056752621080531414 w: 0.9999838955703261 covariance: [0.08313143998384476, 0.00019857974257320166, 0.0, 0.0, 0.0, 0.004368376452475786, 0.00019857988809235394, 0.015032557770609856, 0.0, 0.0, 0.0, -0.26573312282562256, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0043683769181370735, -0.26573312282562256, 0.0, 0.0, 0.0, 6.021446704864502] twist: twist: linear: x: 0.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] ---  \nまた,\n$ rostopic type /odom  を実行すると,メッセージとして,nav_msgs/Odometry型が使われていることがわかります. $ rostopic type /odom root@dynamics:~/roomba_hack# rostopic type /odom nav_msgs/Odometry  \nnav_msgs/Odometry型のドキュメントを確認すると,このメッセージはposeとtwistで構成されていることがわかります.\n  poseは(child_frameから見た)ロボットの推定姿勢(位置と回転角)を表していて,covarianceにはその不確かさを表す共分散が記録されています.\n  一方,twistは(child_frameから見た)ロボットの速度を表していて,poseと同様にcovarianceにはその不確かさを表す共分散が記録されています.\n  なお,メッセージ型の定義は,コマンドで\n$ rosmsg info nav_msgs/Odometry  を実行しても確認できます. $ rosmsg info nav_msgs/Odometry root@dynamics:~/roomba_hack# rosmsg info nav_msgs/Odometry std_msgs/Header header uint32 seq time stamp string frame_id string child_frame_id geometry_msgs/PoseWithCovariance pose geometry_msgs/Pose pose geometry_msgs/Point position float64 x float64 y float64 z geometry_msgs/Quaternion orientation float64 x float64 y float64 z float64 w float64[36] covariance geometry_msgs/TwistWithCovariance twist geometry_msgs/Twist twist geometry_msgs/Vector3 linear float64 x float64 y float64 z geometry_msgs/Vector3 angular float64 x float64 y float64 z float64[36] covariance  \nクォータニオン(quaternion) さて,/odomのトピックでは,ロボットの回転角はクォータニオン(quaternion)で記述されています.\nクォータニオンは,日本語では四元数と呼ばれ,3次元空間上での回転角を表現する方法の一つで,4つの要素を持つベクトルで表現されます.\nクォータニオンによる3次元回転の表現は,角度を連続的にかつ簡潔に表現できるためROSではよく用いられます(その他には,オイラー角による表現や回転行列による表現があります).\nそれぞれの回転角に関する表現のメリット・デメリットを調べてみましょう(「ジンバルロック」などのキーワードで調べるとよりよく理解できると思います).\nクォータニオンからオイラー角へは,tfパッケージのtf.transformations.euler_from_quaternionを使うことで変換できます(ドキュメント).\n実装の例を見てみる それでは,オドメトリ/odomの情報を使った制御の実装の例としてnavigation_tutorialパッケージの中のsimple_control2.pyを見てみましょう(github).\nソースコードを読んでみよう 画面にウィンドウを2つ並べるなど,githubのソースコードをみながら以下の解説を読むことをお勧めします.\nsimple_control2.py #!/usr/bin/env python3 import numpy as np import rospy import tf from geometry_msgs.msg import Twist from nav_msgs.msg import Odometry class SimpleController: def __init__(self): rospy.init_node('simple_controller', anonymous=True) # Publisher self.cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10) # Subscriber odom_sub = rospy.Subscriber('/odom', Odometry, self.callback_odom) self.x = None self.y = None self.yaw = None while self.x is None: rospy.sleep(0.1) def callback_odom(self, data): self.x = data.pose.pose.position.x self.y = data.pose.pose.position.y self.yaw = self.get_yaw_from_quaternion(data.pose.pose.orientation) def go_straight(self, dis, velocity=0.3): vel = Twist() x0 = self.x y0 = self.y while(np.sqrt((self.x-x0)**2+(self.y-y0)**2)\u0026lt;dis): vel.linear.x = velocity vel.angular.z = 0.0 self.cmd_vel_pub.publish(vel) rospy.sleep(0.1) self.stop() def turn_right(self, yaw, yawrate=-0.5): vel = Twist() yaw0 = self.yaw while(abs(self.yaw-yaw0)\u0026lt;np.deg2rad(yaw)): vel.linear.x = 0.0 vel.angular.z = yawrate self.cmd_vel_pub.publish(vel) rospy.sleep(0.1) self.stop() def turn_left(self, yaw, yawrate=0.5): vel = Twist() yaw0 = self.yaw while(abs(self.yaw-yaw0)\u0026lt;np.deg2rad(yaw)): vel.linear.x = 0.0 vel.angular.z = yawrate self.cmd_vel_pub.publish(vel) rospy.sleep(0.1) self.stop() def stop(self): vel = Twist() vel.linear.x = 0.0 vel.angular.z = 0.0 self.cmd_vel_pub.publish(vel) def get_yaw_from_quaternion(self, quaternion): e = tf.transformations.euler_from_quaternion( (quaternion.x, quaternion.y, quaternion.z, quaternion.w)) return e[2] if __name__=='__main__': simple_controller = SimpleController() try: simple_controller.go_straight(1.0) simple_controller.turn_left(90) simple_controller.turn_right(90) except rospy.ROSInitException: pass   かんたんな説明 上記ソースコードの大枠のみを抜き出すと,以下のようになっています. simple_control2.pyの大枠 #!/usr/bin/env python3 # 1 import ~ # 2 class SimpleController: # 3 def __init__(self): # 4 pass def callback_odom(self, data): # 5 pass def go_straight(self, dis, velocity=0.3): pass def turn_right(self, yaw, yawrate=-0.5): pass def turn_left(self, yaw, yawrate=0.5): pass def stop(self): pass def get_yaw_from_quaternion(self, quaternion): pass if __name__=='__main__': # 6 simple_controller = SimpleController() # 7 pass  \nそれぞれについて簡潔に解説します.\n#1 shebang shebang(シバン)と呼ばれるもので,このファイルを実行する際に,どのプログラムを使って実行するかを指定する. #!/usr/bin/env python3 と書いてあるので,このファイルはpython3で実行するのだとコンピュータに教えている.\n #2 import import numpy as np import rospy import tf from geometry_msgs.msg import Twist from nav_msgs.msg import Odometry   pythonの標準の関数(printなど)だけでは機能が足りないので,別のモジュールをインポートしている. このソースコードでは,numpy, rospy, tf というモジュールをインポートしている.  rospyというのが,pythonでrosを使うためのモジュール.   また,geometry_msgs.msgというモジュールからTwistというデータ型, nav_msgs.msgというモジュールからOdometoryというデータ型をそれぞれインポートしている.   #3 class  SimpleControllerという名前のクラスを定義している. クラスはオブジェクトの設計図のようなもの.  オブジェクトとは,データとそのデータの振る舞いをまとめたもの.    classの例1 以下のようなクラスを定義したとする.\nclass Car: def __init__(self, color, speed): self.color = color self.speed = speed self.fuel = 100 def drive(self): self.fuel -= 20 print(\u0026lsquo;drove!') print(f\u0026rsquo;残りの燃料は{self.fuel}リットルです\u0026rsquo;)\ndef charge(self): self.fuel = 100 print(\u0026lsquo;charged!') print(f\u0026rsquo;残りの燃料は{self.fuel}リットルです\u0026rsquo;)\ndef info(self): print(f\u0026rsquo;色は{self.color}です') print(f\u0026rsquo;速度は{self.speed}km/hです') print(f\u0026rsquo;残りの燃料は{self.fuel}リットルです') \n以下のように使える.\nmycar = Car('red', 200) mycar.drive() #drove! #残りの燃料は80リットルです\nmycar.drive() #drove! #残りの燃料は60リットルです\nmycar.charge() #charged! #残りの燃料は100リットルです\nmycar.info() #色はredです #速度は200km/hです #残りの燃料は100リットルです \n classの例2 pythonのstring型や,int型,list型も,実はオブジェクトである.\npeople = ['Alice', 'Bob', 'Charlie'] people.append('Dave') print(people) #[\u0026lsquo;Alice\u0026rsquo;, \u0026lsquo;Bob\u0026rsquo;, \u0026lsquo;Charlie\u0026rsquo;, \u0026lsquo;Dave\u0026rsquo;] \n上の例では,list型のオブジェクトpeopleに対して,appendというメソッド(そのオブジェクトが持つ関数)を呼び出し,新しい要素を追加している.\n  #4 コンストラクタ  コンストラクタ__init__()とは,オブジェクト生成時に呼び出される関数のこと. 初期化のための関数というイメージ.   #5 メソッドの定義  メソッドとは,オブジェクトが持つ関数のこと. classの定義の中では,self.method_name(引数1, 引数2)という形で呼び出すことができる. オブジェクトの外から使用するときには,上のCarの例のように,object_name.method_name(引数1, 引数2)という形で呼び出すことができる. 定義の第一引数には,必ずselfを指定する.これは,そのオブジェクト自身を指す.  呼び出すときにはselfは省略する.     #6 ファイル実行時の処理  このif文の中の処理は,ファイルを直接実行したときにのみ実行される. __name__は特殊な変数で,ファイルを直接実行したときには'__main__'という値を持つ.  importされたときには__name__にはファイル名が入るため,このif文の中の処理は実行されない. #!/usr/bin/env python3 print(__name__)  とだけ記述したファイルを実行してみると,ふるまいが理解しやすいかもしれない.\n     より詳細な理解 simple_control2.pyについてより詳細に解説します.\nコンストラクタ def __init__(self): rospy.init_node('simple_controller', anonymous=True) # Publisher self.cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10) # Subscriber odom_sub = rospy.Subscriber('/odom', Odometry, self.callback_odom) self.x = None self.y = None self.yaw = None while self.x is None: rospy.sleep(0.1)  上にも述べた通り,__init__はコンストラクタと呼ばれ,オブジェクト生成時に自動で呼び出される関数です. 各行について順番に見ていきます.\nrospy.init_node('simple_controller', anonymous=True)   simple_controllerという名前のノードを作成しています.  self.cmd_vel_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)   rospy.Publisher()によって,上で作成したノードがpublisherとして機能することを宣言しています. このノードは,/cmd_velというトピックに対して,Twistというデータ型のメッセージを送信しています.  Twistの情報は,geometry_msgsのドキュメントより確認できます. Twistは速度3成分と角速度3成分を格納するデータ型です.   第2引数のqueue_sizeは,メッセージを送信する命令が,許容できる周期より短い場合に,メッセージをキューにためておく数を指定します.  ここでは,10個までキューにためておくことを指定しています. たまった数が10個より少ない場合には,古い順にメッセージをパブリッシュしていきます. たまった数が10個に達した場合には,最も古いメッセージを破棄します.    odom_sub = rospy.Subscriber('/odom', Odometry, self.callback_odom)   rospy.Subscriber()によって,作成したノードがsubscriberとして機能することを宣言しています. このノードは,/odomというトピックから,Odometryというデータ型のメッセージを受信しています.  Odometryの情報は,nav_msgsのドキュメントより確認できます. Odometryは,位置と姿勢,及び速度と角速度を格納するデータ型です. ここでは,Odometryには,ルンバの現在の位置や運動の様子が格納されており,それを受信しています.   rospy.Subscriber()の第三引数として,self.callback_odomというコールバック関数を指定しています.  subscriberにはコールバック関数を指定する必要があります. コールバック関数とは,subscriberがメッセージを受信したときに実行される関数のことです.  コールバック関数は,メッセージを引数として実行します.   コールバック関数の中身は,後述します.    self.x = None self.y = None self.yaw = None   アトリビュートを定義しています. クラスの定義の中でself.\u0026lt;name\u0026gt;の形式で表される変数は,そのクラスのオブジェクトが持つ\u0026quot;attribute(アトリビュート)\u0026ldquo;と呼ばれます.  アトリビュートには,そのclassの定義の中であればどこからでもアクセスできます.   ここでは,self.x, self.y, self.yawというアトリビュートを定義しています.  これらのアトリビュートは,後述するコールバック関数の中で値が更新されます. このアトリビュートには,ロボットの現在の位置や姿勢が格納されます.    while self.x is None: rospy.sleep(0.1)   self.xがNoneである間,0.1秒間隔で待機し続けます.   コールバック関数 def callback_odom(self, data): self.x = data.pose.pose.position.x self.y = data.pose.pose.position.y self.yaw = self.get_yaw_from_quaternion(data.pose.pose.orientation)   コンストラクタの項目で説明した通り,subscriberを定義する際にはコールバック関数を指定する必要があります. コールバック関数はsubscriberがメッセージを受信した際,そのメッセージを引数として実行する関数でした. 上のコールバック関数では,引数dataには,Odometry型のメッセージが格納されます.  data.pose.pose.positionという書き方によって,Odometry型の中の,位置を表すpositionという要素にアクセスしています. 同様に,data.pose.pose.orientationという書き方によって,Odometry型の中の,姿勢を表すorientationという要素にアクセスしています.   コンストラクタで定義していたアトリビュートself.x, self.y, self.yawに,メッセージから得られた位置と姿勢を格納しています.  self.yawに値を格納する時に使用している関数get_yaw_from_quaternion()については,後述します.   このコールバック関数が一度でも呼ばれると,self.xに入っているNoneの値が上書きされ,コンストラクタの中のwhile文が終了します.   get_yaw_from_quaternion関数 go_straight関数やturn_right関数とは順番が前後しますが,先にget_yaw_from_quaternion関数について説明します.\ndef get_yaw_from_quaternion(self, quaternion): e = tf.transformations.euler_from_quaternion( (quaternion.x, quaternion.y, quaternion.z, quaternion.w)) return e[2]   tfモジュールのeuler_from_quaternion関数を利用しています.  euler_from_quaternion()は,クォータニオン(4要素)を引数として,オイラー角(3要素)を返す関数です. クォータニオンについてはLearn/オドメトリのセンサ情報を用いた制御/クォータニオンの項目で説明しました.   Odometry型のメッセージのうち,姿勢を表すorientationという要素は,クォータニオンで表されているため,オイラー角で制御したい場合には,この関数を用いてオイラー角に変換する必要があります. ルンバはxy平面上を動くため,z軸周りのオイラー角さえわかれば十分です.そのため,このget_yaw_from_quaternion関数ではオイラー角のz軸成分(第2成分)のみを返しています.   go_straight関数 def go_straight(self, dis, velocity=0.3): vel = Twist() x0 = self.x y0 = self.y while(np.sqrt((self.x-x0)**2+(self.y-y0)**2)\u0026lt;dis): vel.linear.x = velocity vel.angular.z = 0.0 self.cmd_vel_pub.publish(vel) rospy.sleep(0.1) self.stop()  ロボットを直進させる関数です.順に説明します\n vel = Twist()   Twist型のオブジェクトを生成しています.  x0 = self.x y0 = self.y   self.x, self.yには,コールバック関数で更新された(直前の)ルンバの位置が格納されています.それをx0, y0に代入しています.  while(np.sqrt((self.x-x0)**2+(self.y-y0)**2)\u0026lt;dis): vel.linear.x = velocity vel.angular.z = 0.0 self.cmd_vel_pub.publish(vel) rospy.sleep(0.1)   点(self.x, self.y)と点(x0, y0)の距離が指定したdisより小さい間,while以下の処理を繰り返します.  velの並進成分のx成分に,引数で指定したvelocityの値を格納します. velの回転成分のz成分(z軸周りの角速度)に,0を格納します. コンストラクタの中で定義したpublisherであるself.cmd_vel_pubの.publish()関数を用いて,publishを行います.  引数にvelを指定しているので,確かにTwist型のメッセージをパブリッシュしています.   rospy.sleep(0.1)で0.1秒待ち,次のループに入ります.    self.stop()   while文が終了したら,ロボットを停止させます.  stop関数については,説明を省略します.   turn_right, turn_left関数についても,go_straight関数の説明と同様なので省略します.   単純に \u0026ldquo;(角)速度 * 時間\u0026rdquo; によって移動の姿勢を指定しているのではなく,オドメトリのセンサ情報を使いながら, 目標の姿勢に到達するように制御していることを再度強調しておきます.\npythonのコードの読み方についての基本的な説明は上の説明で尽きているので, 余力があれば(なくても)各自roomba_hackリポジトリ 上の気になったコードを読んでみましょう.\n演習 【jetson・開発マシン】それぞれdockerコンテナを起動 . jetsonでdockerコンテナを起動\n(開発PC):~$ ssh roomba_dev1 (jetson):~$ cd ~/group_a/roomba_hack (jetson):~/group_a/roomba_hack ./RUN-DOCKER-CONTAINER.sh (jetson)(docker):~/roomba_hack#  開発PCでdockerコンテナを起動\n(開発PC):~$ cd ~/group_a/roomba_hack (開発PC):~/group_a/roomba_hack ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x (開発PC)(docker):~/roomba_hack#   【jetson】ROSマスタ,各種ノードを起動 (jetson)(docker):~/roomba_hack# roslaunch roomba_bringup bringup.launch   ROSメッセージの可視化 【開発PC】topicの確認 /odomの型を確認\n(開発PC)(docker):~/roomba_hack# rostopic type /odom  /odomの中身を確認\n(開発PC)(docker):~/roomba_hack# rostopic echo /odom   オドメトリを使ったフィードバック制御 simple_control2.pyを実行してみよう.\n開発PCでteleopのコードを実行しましょう\n(開発PC)(docker):~/roomba_hack# roslaunch roomba_teleop teleop.launch  このプログラムを動かすときには,コントローラのYボタンを押してからBボタンを押してautoモードにしておきましょう.\n1メートルほど前に進んだあと,左に90度程度旋回し,右に90度程度旋回したら成功です.\n(開発PC)(docker):~/roomba_hack# rosrun navigation_tutorial simple_control2.py  try it! simple_control2.pyの中身を読んでコードを変更してみよう\n","date":1687392e3,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1687392e3,"objectID":"2ee7ed9f0716848dbd6ffe877377e500","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap3/sensing2/","publishdate":"2023-06-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap3/sensing2/","section":"course","summary":"複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう\n","tags":null,"title":"ロボットシステムにおけるセンシング・アクチュエーション・通信②","type":"book"},{"authors":null,"categories":null,"content":" これは 2023 年度の情報です。今後変更の可能性があります。   最終課題について 最終課題発表会の日程は8/30です。\nそれまでに各チームで以下のタスクを行うコードを準備してください。 また、チームでGitHubにリポジトリを作成し、準備したコードをアップロードしてください。\nすべてのタスクを完璧に実装しようとすると少し大変なので、まずは、ヒントを参考に「はじめの一歩」を実装するのを目指しましょう。\nルールの質問、実装のやり方などわからないことは #q-roomba で質問お願いします! また、角川でも質問対応デーを設ける予定です。\nルール 1チームずつTask1とTask2の2つのタスクを行い、その合計点を競います。\nそれぞれのタスクは図の環境で行われます。(角川にある環境です。)\n  タスクがおこなわれるフィールド図  それぞれのTaskのStaring Pointではルンバの自己位置を合わせることはできますが、一度ルンバが動き始めたら人が介入することができないことに注意してください。(リスタートを除く)\nTask1 ルール Task1では、3分の制限時間の間で、Room1のStarting Pointからルンバを自律移動させ、Area1内に落ちている5つの物体を避けながらRoom2へと移動することが目標になります。\n途中でロボットが停止した場合などは再びStarting Pointからリスタートすることができます。\nまた、Area1内に落ちている5つの物体を検出し、正しく分類することで追加ポイントを獲得することができます。\nタスク終了時にArea1内で出てきた物体の名前をターミナルに表示してください。\nまた、5つの物体は以下の8個の物体からランダムに選択されます。\n  Task1で出現する物体  物体の名前は左から順に\n chips can mini soccor ball rubic cube banana apple strawberry toy plane wood block です。  実際に使用する物体は角川に置いてあります。\n 採点基準 ゴール得点と物体検出得点それぞれ100点満点です。また、ゴール得点と物体検出得点それぞれの得点が負になることはありません。\nゴール得点内訳(100点満点)\n制限時間以内にゴール(Room2)に辿り着けたら100点、物体に衝突するたびに物体ごとに-20点、加えて、ロボットをStarting Pointからリスタートする度に-10点\n物体検出得点内訳(100点満点)\n検出結果の分類結果が正しければ物体ごとに+20点、間違っていたら-20点\n ヒント はじめの一歩\n Task1はまず、ゴールへ移動するコードを作成することを目標にしましょう (スクリプトでナビゲーションを行うためのヒント参照) 余裕があれば、他のヒントを参照して高得点を目指していきましょう  スクリプトでナビゲーションを行うためのヒント\n スクリプトでナビゲーションを行うコード(https://matsuolab.github.io/roomba_hack_course/course/chap6/service-actionlib/ の演習)を参考に、指定した位置へ移動するコードを書いてみましょう  障害物を避けながらnavigationするためのヒント\n 物体検出器を改善するためのヒントおよび https://matsuolab.github.io/roomba_hack_course/course/chap6/service-actionlib/ の総合課題を参照し、課題物体を検出し、それをコストマップに追加することで障害物を避けるコードを書いてみましょう  より正確な自己位置推定を行うためのヒント\n amclのパラメータ調整 (参考: https://matsuolab.github.io/roomba_hack_course/course/chap4/localization/ の演習)をしてみましょう gmappingを用いてより正確な地図を作成 (参考: https://matsuolab.github.io/roomba_hack_course/course/chap4/localization/ の演習)してみましょう emcl(https://github.com/ryuichiueda/emcl)等の異なる自己位置推定アルゴリズムを使ってみましょう Lidarの位置を合わせをより正確に(https://github.com/matsuolab/roomba_hack/blob/master/catkin_ws/src/roomba/roomba_description/urdf/roomba.urdf.xacro を編集)合わせてみましょう  物体検出器を改善するためのヒント\n 自作データを用いて既存モデルの学習 (参考: https://eng-memo.info/blog/yolo-original-dataset/) を行ってみましょう   Task2 ルール Task2では、3分の制限時間の間で、Room2のStarting Pointからルンバを自律移動させ、Area2にいる2人の人のうち、手を振っている人の前で停止することが目標になります。\nまた、2人の人がArea2にある2つの椅子に座っており、そのうち一方のみが手を振っていることが保証されています。\n途中でロボットが停止した場合などは再びStarting Pointからリスタートすることができます。\n 採点基準 Task2全体で150点満点です、また、Task2の得点が負になることはありません。\n時間以内にいずれかの人の前に移動が成功し、その場で停止する(人の50cm以内に到達する)+70点\n上に加え、正しい人(手を振っている人)の前に移動が成功する+80点\n加えて、ロボットをStarting Pointからリスタートする度に-10点\n ヒント はじめの一歩\n Task2はまず、人を検出して移動するコードを作成することを目標にしましょう (人を検出して移動するためのヒント参照) 余裕があれば手を振っている人の検出にチャレンジしてみましょう  人を検出して移動するためのヒント\n 人を検出する部分については三次元画像処理で扱ったYOLOv3で検出することができます 検出した結果に対応する深度画像から距離を取得することで、人の近くへ移動するコードを書くことができます (参考: https://matsuolab.github.io/roomba_hack_course/course/chap5/three-dimensions/ のdetection_distance.py) さらに余裕があれば、深度画像を点群等に変換するとより正確な移動が可能になります  手を振っている人の検出\n 学習済みのKeypoint R-CNNを用いることで人間の手や腕の位置などのキーポイントを推定することができます。  ","date":1687392e3,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1687392e3,"objectID":"b048517e8455bfa798e5d9368632cd92","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap8/final_project/","publishdate":"2023-06-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap8/final_project/","section":"course","summary":"","tags":null,"title":"最終プロジェクト","type":"book"},{"authors":null,"categories":null,"content":"ロボットシステムの開発環境に使われている要素の概要を理解する\nLearn Linuxコマンド コンピュータやロボットの研究・開発では、LinuxというOS(Operating System)がよく使用されます。\nROSはLinux(とくにUbuntuというディストリビューション)で動作することを想定されているため、ROSを使うにはある程度Linuxの知識が必要になります。 Linuxについてあまり知らないという方は、前もって調べておくと良いでしょう(参考)。 特に、Linuxコマンドについて軽く知っておくとROS自体の理解もスムーズになります。\n\u0026quot;\u0026quot;\u0026quot; ここではLinuxのコマンドを十分に説明できないので、コマンドの例を挙げることに留めます。 詳しいサイトに沿って自分で使いながら覚えていくことをお勧めします。 \u0026quot;\u0026quot;\u0026quot; # カレントディレクトリ(自分が今いるディレクトリ)を確認 pwd # ディレクトリ内のファイル・ディレクトリの表示 ls # カレントディレクトリを変更 cd \u0026lt;DirectoryName\u0026gt; # ディレクトリ作成 mkdir \u0026lt;NewDirectoryName\u0026gt; # ディレクトリ削除 rmdir \u0026lt;DirectoryName\u0026gt; # ファイル作成 touch \u0026lt;NewFileName\u0026gt; # ファイル消去 rm \u0026lt;FileName\u0026gt; # ファイルの内容を表示 cat \u0026lt;FileName\u0026gt;  エディタ プログラミング言語でコードを書くときに使用するツールをEditor(エディタ)と呼びます。\nテキストファイルを編集するツールなのでメモ帳でも同じ事ができるのですが、エディタには便利な機能が多く、効率よくコードが編集できるようになります。\n研究室のPCでは以下の二つが使用できます。\n vim  シンプルで使いやすいエディタ。 macだと標準で使える。  ターミナルでvimtutorを実行するとチュートリアルが受けられる.   Windowsだとインストールする必要がある。   vscode  visual studio code ダウンロードする必要がある さまざまな機能を追加でき、とても便利 詳しい動画    Git/GitHub  Gitとは   ファイルのバージョン管理が簡単にできるツール。\n使い方を知っておくと開発が快適になります。\n  詳しい記事(コマンドなし)\n  詳しい記事(コマンドあり)\n   GitHubとは   gitで管理しているファイルを他の人と共有できるようにするサービス。\nチーム開発で大変重宝します。\n自分が書いたコードを公開して世界中の人に使ってもらったり、他の人が公開しているコードを使わせてもらうこともできます。\n  https://github.co.jp/\n  詳しい記事\n  GitとGitHubを用いたワークフロー\n  このサイトもGitHubを使って開発しています。(このサイトのリポジトリ)\n  公開されているリポジトリの例\n https://github.com/gundam-global-challenge/gundam_robot      Docker   Dockerとは\n「データやプログラムを隔離できる」仕組み。\n  例えば、コンピュータの中でシステムAとシステムBを動かしたいとします。そして、これらは他のシステムCに依存しているものとします。\nもし、AがCのver.2にのみ対応していて、BはCのver.3にのみ対応していた場合、Bを実行できるようにCをver.3にしてしまうと、Aが実行できなくなってしまいます。\n  このような場合、dockerコンテナを複数作成し(コンテナI、コンテナIIとします)、\n コンテナIの中にはプログラムAとCのバージョン2をインストールして使い、 コンテナIIの中にはプログラムBとCのバージョン3をインストールして使用する  のようなことことができます。\n    コンテナの中の環境はPC本体の環境からは隔離されるため、安全に開発をすることができます(他の人が書いたプログラムが動かなくなるということも防げる)。\n    詳しい記事\n  : -f   ``` - Docker Image ``` # Docker image一覧 docker images # Docker Imageのダウンロード docker pull : # 削除 docker rmi  # 不要なDocker imageを消す docker image prune ``` - Docker Container ``` # Docker containerの起動 docker run   # Docker container一覧 docker ps -a # Docker containerに接続 docker exec -it  bash ``` -- ` - コンテナの名前の指定 - `--rm` - コンテナを抜けた際に自動的にコンテナを削除する - `--gpus all` - コンテナに全gpuを渡す - gpuの個数を指定する場合は all の代わりに数字(0, 1,...) - gpuを指定する場合は `--gpus '\"device=0,1\"'` - `-v ` - コンテナ内にホストのディレクトリをマウントする - `-p :` - ホストのポートをコンテナのポートにマップする - コンテナ内でwebサーバを動かす場合などに使う - `--net=host` - コンテナとホストでネットワークを共有する(IPアドレスなどが同じになる) - ROSノードをコンテナ内で動かす場合などはこれを使うと楽 - `--privileged` - コンテナからのデバイスへのアクセスを許可 - コンテナからWEBカメラにアクセスしたいときなど -- ssh   sshとは\n  sshを使用することで、開発PCからルンバに載っているjetsonというコンピュータを遠隔で操作することや、各自のPCから開発PCを遠隔で操作することができる。\n  @ -p  -i  ``` -- 演習 演習には個人PC, 開発PC, ルンバに搭載されているjetsonの3種類のコンピュータを用います。\n開発PC : robot_dev系, hsr_dev系\njetson : roomba_dev系\n【ssh】開発用PCにsshする 個人PCから開発PCにsshする\n(個人PC):~$ vim ~/.ssh/config (個人PC):~$ ssh robot_dev2  sshに成功すると\nrobot_dev2@robot-dev2:~$  などと表記が変わり、開発PCに接続できたことが確認できます。\n -- 【Linuxコマンド】グループのディレクトリを作成し移動する (開発PC):~$ mkdir 23_group_x (開発PC):~$ cd 23_group_x   【git】roomba_hackリポジトリをcloneし移動する (開発PC):~/23_group_x$ git clone https://github.com/matsuolab/roomba_hack.git (開発PC):~/23_group_x$ ls # 23_group_xディレクトリの中に新しいディレクトリができていることを確認 (開発PC):~/23_group_x$ cd roomba_hack (開発PC):~/23_group_x/roomba_hack$ ls  https://github.com/matsuolab/roomba_hack をそのままダウンロードできたことが確認できると思います。\n 【git】ブランチを確認する git branchコマンドを使ってみましょう。\n# ローカルブランチの一覧を表示 (開発PC):~/23_group_x/roomba_hack$ git branch   【docker】roomba_hackの開発環境のdockerイメージをビルドする # shellファイルの中身をcatコマンドで確認してみます。 (開発PC):~/23_group_x/roomba_hack$ cat BUILD-DOCKER-IMAGE.sh # ファイルの中身が表示される # ファイルの最後の # docker build . -f docker/${DOCKERFILE_NAME} -t ${IMAGE_NAME}:${TAG_NAME} --build-arg BASE_IMAGE=${BASE_IMAGE} # の部分でDockerイメージのビルドを実行するようです。 # shellファイルを実行してdockerイメージのビルドを行います。 (開発PC):~/23_group_x/roomba_hack$ ./BUILD-DOCKER-IMAGE.sh  「イメージ」とは、dockerコンテナを作成するための素となるもので、コンテナの設計図のようなものです。上のコマンドを実行することで、roomba_hackの開発環境のdockerイメージが作成されます(まだコンテナ自体は作っていません)。\n 【ssh】jetsonにsshする 開発用PCからルンバに載っているjetson nanoへsshします。\n(開発PC):~/23_group_x/roomba_hack$ ssh roomba_dev2 roomba_dev2@roomba-dev-jetson2:~$  先頭の表記がroomba_dev2@roomba-dev-jetson2と変わり、jetsonへ接続されたことがわかります。\njetsonでも同様にグループのディレクトリを作成し、移動し、roomba_hackリポジトリをcloneしてみましょう。\n 【ssh】VNCを使う 個人PCから開発PCにsshで接続\n(個人PC):~$ ssh robot_dev2 -L 5900:localhost:5900  手元のVNC viewerでlocalhost:5900を開く\n -- ","date":1686009600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1686009600,"objectID":"2a1ada4f20c1997bdd8592ad8bf5f9b1","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap1/%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83/","publishdate":"2023-06-06T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap1/%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83/","section":"course","summary":"ロボットシステムの開発環境に使われている要素の概要を理解する\n","tags":null,"title":"開発環境","type":"book"},{"authors":null,"categories":null,"content":"Learn  RGBDカメラを用いて三次元画像処理を行いましょう。\nRGBDカメラについて RGBDカメラとは、色(RGB)に加え、深度(Depth)情報を取得できるカメラのことです。 周りの環境を三次元の空間として認識することで、ロボットはより複雑にふるまうことができます。 比較的安価でよく利用されるRGBDカメラとして、Intel社製のRealSenseやMicrosoft社製のXtion(エクシオン)などがあります。\nRealSense 今回はRGBDカメラとしてRealSenseD435を使用します。\nROSで用いる際には標準のラッパーを使用します。\nroslaunch realsense2_camera rs_camera.launch  を実行すると、2種類のトピック\n/camera/color/image_raw (RGB画像)\n/camera/depth/image_raw (デプス画像)\nが利用できるようになります。\nこれらのトピックはいずれもsensor_msgs/Image型です。\nRealSenseはRGB画像モジュールとデプス画像モジュールが物理的に離れています。 このため、これら2つのトピックはいずれも画像データではあるものの、ピクセルの位置関係が対応しておらず、そのまま画像処理に利用することはできません。\nRealSenseを使用するためのlaunchファイル(rs_camera.launch)を起動する際に \u0026ldquo;align_depth\u0026quot;パラメータを\u0026quot;true\u0026quot;に指定することで、デプス画像をRGB画像のピクセルに対応するように変換した /camera/aligned_depth_to_color/image_rawトピックが使用できるようになります。\nただし、roomba_bringupパッケージのbringup.launchファイルの\n \u0026lt;include file=\u0026quot;$(find realsense2_camera)/launch/rs_camera.launch\u0026quot; if=\u0026quot;$(arg realsense)\u0026quot;\u0026gt; \u0026lt;arg name=\u0026quot;align_depth\u0026quot; value=\u0026quot;true\u0026quot;/\u0026gt; \u0026lt;/include\u0026gt;  の箇所がこの操作に対応していため、今回は特別な操作をせずとも /camera/aligned_depth_to_color/image_rawトピックを使用できます。\n物体検出 まずは3次元情報を扱わず、RGB画像/camera/color/image_rawのみを用いて画像検出を行ってみましょう。\n以下は、 three-dementions_tutorialパッケージの object_detection.py です。\n /camera/color/image_rawをsubscribeし、 物体検出アルゴリズムであるYOLOv8に入力し、 物体検出の結果をbounding boxとして描画し、 /detection_resultとしてpublish  の処理を行っています。\n#!/usr/bin/env python3 import copy from typing import List import cv2 import rospy from cv_bridge import CvBridge from sensor_msgs.msg import Image from ultralytics import YOLO from ultralytics.engine.results import Results class ObjectDetection: def __init__(self): rospy.init_node('object_detection', anonymous=True) # Publisher self.detection_result_pub = rospy.Publisher('/detection_result', Image, queue_size=10) # Subscriber rospy.Subscriber('/camera/color/image_raw', Image, self.callback_rgb) self.bridge = CvBridge() self.rgb_image = None self.model = YOLO('yolov8n.pt') def callback_rgb(self, data): cv_array = self.bridge.imgmsg_to_cv2(data, 'bgr8') self.rgb_image = cv_array def process(self): while not rospy.is_shutdown(): if self.rgb_image is None: continue results: List[Results] = self.model.predict(self.rgb_image) # plot bounding box tmp_image = copy.deepcopy(self.rgb_image) for result in results: boxes = result.boxes.cpu().numpy() names = result.names for xyxy, conf, cls in zip(boxes.xyxy, boxes.conf, boxes.cls): if conf \u0026lt; 0.5: continue x1, y1, x2, y2 = map(int, xyxy[:4]) cls_pred = cls tmp_image = cv2.rectangle(tmp_image, (x1, y1), (x2, y2), (0, 255, 0), 3) tmp_image = cv2.putText(tmp_image, names[cls_pred], (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) # publish image detection_result = self.bridge.cv2_to_imgmsg(tmp_image, \u0026quot;bgr8\u0026quot;) self.detection_result_pub.publish(detection_result) if __name__ == '__main__': od = ObjectDetection() try: od.process() except rospy.ROSInitException: pass  画像データを物体検出モデルに入力として渡すためには、その画像データの型がnp.ndarray型である必要があります。 そのため、コールバック関数で、受け取ったsensor_msgs/Image型の画像データをnp.ndarray型に変換しています。\ncv_array = self.bridge.imgmsg_to_cv2(data, 'bgr8')  の部分がこの処理に対応します。\nsubscriberを宣言するときにコールバック関数を指定して、 subscribeしたデータをこの関数に渡すという基本的な処理の流れは、 scanなど他のトピックを扱うsubscriberと同じです。\nここで、YOLOの推論部分をコールバック関数内で行っていないことに注意しましょう。 一見、新しいデータが入ってくるときのみに推論を回すことは合理的に見えますが、 センサの入力に対してコールバック関数内の処理が重いと処理するデータが最新のものからどんどん遅れてしまいます。 コールバック関数はセンサデータの最低限の処理にとどめ、重い処理は分けて書くことを意識しましょう。\nまた、ここでは既存の物体検出モジュールを使用しましたが、PyTorchなどで自作したモデルも同様の枠組みで利用することができます。\n三次元画像処理 次に、RGB画像とデプス画像を統合し、検出した物体までの距離を測定してみましょう。\n/camera/color/image_raw (RGB画像) /camera/aligned_depth_to_color/image_raw (整列されたDepth画像)\nはピクセル同士が対応するように処理されてはいるものの、 パブリッシュされた時刻は独立しているため、 併せて使用するには時刻の同期を行う必要があります。\n画像の時刻同期にはmessage_filtersがよく使われます。\nmessage_filters.ApproximateTimeSynchronizerを使い、以下のようにSubscriberを作成します。\n#!/usr/bin/env python3 import copy from typing import List import cv2 import message_filters import rospy from cv_bridge import CvBridge from sensor_msgs.msg import Image class DetectionDistance: def __init__(self): rospy.init_node('detection_distance', anonymous=True) # Publisher self.detection_result_pub = rospy.Publisher('/detection_result', Image, queue_size=10) # Subscriber rgb_sub = message_filters.Subscriber('/camera/color/image_raw', Image) depth_sub = message_filters.Subscriber('/camera/aligned_depth_to_color/image_raw', Image) message_filters.ApproximateTimeSynchronizer([rgb_sub, depth_sub], 10, 1.0).registerCallback(self.callback_rgbd) self.bridge = CvBridge() self.rgb_image, self.depth_image = None, None def callback_rgbd(self, data1, data2): cv_array = self.bridge.imgmsg_to_cv2(data1, 'bgr8') cv_array = cv2.cvtColor(cv_array, cv2.COLOR_BGR2RGB) self.rgb_image = cv_array cv_array = self.bridge.imgmsg_to_cv2(data2, 'passthrough') self.depth_image = cv_array # 後略  この例では、\n/camera/color/image_rawと\n/camera/aligned_depth_to_color/image_rawの\nトピックを同時(1.0秒までのずれを許容する)に受け取った場合のみ、 2つの画像データをコールバック関数callback_rgbdに渡します。\nそれでは、three-dementions_tutorial パッケージのdetection_distance.py を見てみましょう。\n物体を検出し、その物体までの距離を測定するスクリプトです。\n#!/usr/bin/env python3 import copy from typing import List import cv2 import message_filters import rospy from cv_bridge import CvBridge from sensor_msgs.msg import Image from ultralytics import YOLO from ultralytics.engine.results import Results class DetectionDistance: def __init__(self): rospy.init_node('detection_distance', anonymous=True) # Publisher self.detection_result_pub = rospy.Publisher('/detection_result', Image, queue_size=10) # Subscriber rgb_sub = message_filters.Subscriber('/camera/color/image_raw', Image) depth_sub = message_filters.Subscriber('/camera/aligned_depth_to_color/image_raw', Image) message_filters.ApproximateTimeSynchronizer([rgb_sub, depth_sub], 10, 1.0).registerCallback(self.callback_rgbd) self.bridge = CvBridge() self.rgb_image, self.depth_image = None, None self.model = YOLO('yolov8n.pt') def callback_rgbd(self, data1, data2): cv_array = self.bridge.imgmsg_to_cv2(data1, 'bgr8') cv_array = cv2.cvtColor(cv_array, cv2.COLOR_BGR2RGB) self.rgb_image = cv_array cv_array = self.bridge.imgmsg_to_cv2(data2, 'passthrough') self.depth_image = cv_array def process(self): while not rospy.is_shutdown(): if self.rgb_image is None: continue # inference tmp_image = copy.copy(self.rgb_image) results: List[Results] = self.model.predict(self.rgb_image, verbose=False) # plot bouding box for result in results: boxes = result.boxes.cpu().numpy() names = result.names if len(boxes.xyxy) == 0: continue x1, y1, x2, y2 = map(int, boxes.xyxy[0][:4]) cls_pred = boxes.cls[0] tmp_image = cv2.rectangle(tmp_image, (x1, y1), (x2, y2), (0, 255, 0), 3) tmp_image = cv2.putText(tmp_image, names[cls_pred], (x1, y1), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) cx, cy = (x1+x2)//2, (y1+y2)//2 print(names[cls_pred], self.depth_image[cy][cx]/1000, \u0026quot;m\u0026quot;) # publish image tmp_image = cv2.cvtColor(tmp_image, cv2.COLOR_RGB2BGR) detection_result = self.bridge.cv2_to_imgmsg(tmp_image, \u0026quot;bgr8\u0026quot;) self.detection_result_pub.publish(detection_result) if __name__ == '__main__': dd = DetectionDistance() try: dd.process() except rospy.ROSInitException: pass  基本的には物体検出のスクリプトと同じですが、\ncx, cy = (x1+x2)//2, (y1+y2)//2 print(names[cls_pred], self.depth_image[cy][cx]/1000, \u0026quot;m\u0026quot;)  でbounding boxの中心座標を変換し、対応する距離をメートル単位で表示しています。\n整列されたデプス画像を用いているため、RGB画像に基づき算出した座標をそのまま指定できます。\n点群の作成 上の例ではRGB画像とDepth画像を用いて、物体位置の代表となる点とロボット位置の関係を扱うことができました。\nしかし、代表となる点以外の深度データも三次元空間に直接マッピングできると、 物体の大きさや形状といった情報も扱うことができ、 環境情報をより直感的・統一的に扱うことができるように思われます。\nそこでDepth画像から点群と呼ばれるデータを作成することを考えます。\n点群とは三次元座標値 (X, Y, Z) で構成された点の集まりのことです。各点の情報として、三次元座標値に加え色の情報 (R, G, B) が加わることもあります。 デプス画像はカメラの内部パラメータを用いることによって点群データに変換することができます。(参考)\n今回は、デプス画像を点群データに変換するためのROSの外部パッケージである depth_image_proc を使用して点群を作成します。\n外部パッケージは~/catkin_ws/src等のワークスペースに配置し、ビルドしパスを通すことで簡単に使用できます。\ndepth_image_procのwikiを参考に以下のようなlaunchファイルを作成しました。\n\u0026lt;?xml version=\u0026quot;1.0\u0026quot;?\u0026gt; \u0026lt;launch\u0026gt; \u0026lt;node pkg=\u0026quot;nodelet\u0026quot; type=\u0026quot;nodelet\u0026quot; name=\u0026quot;nodelet_manager\u0026quot; args=\u0026quot;manager\u0026quot; /\u0026gt; \u0026lt;node pkg=\u0026quot;nodelet\u0026quot; type=\u0026quot;nodelet\u0026quot; name=\u0026quot;nodelet1\u0026quot; args=\u0026quot;load depth_image_proc/point_cloud_xyz nodelet_manager\u0026quot;\u0026gt; \u0026lt;remap from=\u0026quot;camera_info\u0026quot; to=\u0026quot;/camera/color/camera_info\u0026quot;/\u0026gt; \u0026lt;remap from=\u0026quot;image_rect\u0026quot; to=\u0026quot;/camera/aligned_depth_to_color/image_raw\u0026quot;/\u0026gt; \u0026lt;remap from=\u0026quot;points\u0026quot; to=\u0026quot;/camera/depth/points\u0026quot;/\u0026gt; \u0026lt;/node\u0026gt; \u0026lt;/launch\u0026gt;  このlaunchファイルを実行すると\n/camera/color/camera_infoと\n/camera/aligned_depth_to_color/image_rawを\nsubscribeし、\n/camera/depth/pointsをpublishするノードが作成されます。\n/camera/color/camera_infoは sensor_msgs/CameraInfo型のトピックです。 カメラパラメータやフレームid、タイムスタンプなどの情報を保持しており、点群の変換に利用されます。\n/camera/aligned_depth_to_color/image_rawはRGB画像に合わせて整列されたDepth画像であるため、 /camera/depth/camera_infoではなく\n/camera/color/camera_infoを指定しています。\n(開発PC)(docker)# roslaunch three-dimensions_tutorial depth2pc.launch  を実行し、/camera/depth/pointsトピックをrvizで可視化をすると三次元空間に点群データが表示されているのが確認できます。\n演習 (開発PC, jetson)起動準備 (jetson)$ ./RUN-DOCKER-CONTAINER.sh (jetson)(docker)# roslaunch roomba_bringup bringup.launch (開発PC)$ ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x   (開発PC)RealSenseのトピックの可視化 (開発PC)(docker)# rviz  rviz上で\n /camera/color/image_raw /camera/depth/image_raw /camera/aligned_depth_to_color/image_raw  を可視化して違いを確認してみましょう。\n (開発PC)物体検出を行う (開発PC)(docker)# cd catkin_ws; catkin_make; source devel/setup.bash (開発PC)(docker)# rosrun three-dimensions_tutorial object_detection.py # rvizで`/detection_result`を表示し結果を確認してみよう。 (開発PC)(docker)# rosrun three-dimensions_tutorial detection_distance.py   (開発PC)外部パッケージを使用 (開発PC)(docker)# cd ~/external_catkin_ws/src (開発PC)(docker)# git clone https://github.com/ros-perception/image_pipeline (開発PC)(docker)# cd ../; catkin build; source devel/setup.bash (開発PC)(docker)# cd ~/roomba_hack/catkin_ws; source devel/setup.bash (開発PC)(docker)# roslaunch three-dimensions_tutorial depth2pc.launch (開発PC)(docker)# roslaunch navigation_tutorial navigation.launch  rvizで/camera/depth/pointsトピックを追加して確認してみましょう。\n 余裕がある人向け 物体を検出し、特定の物体の手前まで移動するスクリプトを作ってみましょう。\nヒント\n 物体検出結果に基づいて物体部分以外をマスクしたデプス画像をpublishする depth2pc.launchでそれをsubscribeし、point(cloud)に変換する 変換されたpointからmap座標系での位置を取得する navigation_tutorial/scripts/set_goal.py (map座標系で指定した位置・姿勢までナビゲーションするスクリプト)などを参考に、その位置へとナビゲーションする  PyTorchを使用した自作の分類器やネット上の分類器をシステムに組み込んでみましょう。\nLidarに映らない物体も画像ベースで検出しコストマップに追加することでナビゲーション時にぶつからないようにしましょう。\n","date":1642809600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1642809600,"objectID":"6efadb7100ad083ad0bc43c5fd3c3b1a","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap5/three-dimensions/","publishdate":"2022-01-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap5/three-dimensions/","section":"course","summary":"","tags":null,"title":"三次元画像処理","type":"book"},{"authors":null,"categories":null,"content":"Learn 前回の演習では、オドメトリを用いてロボットを制御しました。\nルンバが用いているホイールオドメトリは、ホイールの回転量を足し合わせることで算出しています。 長い距離を動かしたり、長時間動かしているとセンサの僅かな誤差の積み重ねで徐々にずれが大きくなってしまいます。\nそこで今回は、オドメトリ情報だけでなく、地図とLiDARスキャン情報も同時に使いながら、ロボット自身の尤もらしい位置を推定していきましょう。\nROSにおける座標系の扱い 座標系を扱うモチベーション ロボット頭部の距離画像カメラ(デプスカメラ)を用いて、2メートル前にりんごを見つけたとします。\nこの時、ロボットのハンドを2メートル前に動かしても、りんごに触れられるとは限りません。\nロボットには体積があり、各センサやアクチュエータ(ここではカメラとハンド)は距離的に離れているためです。\nセンサから得た情報を用いてロボットが何らかの動作を行うには、得たセンサの情報や動作の指令がどの座標系を前提としているのかを意識する必要があり、また、異なる座標系の変換を適切に行う必要があります。\n tf tfは、\n ロボット座標系 センサの座標系 ロボットの関節の座標系 部屋の座標系 物体の座標系 地図の座標系  など多くの座標系同士を繋げ、ロボットシステム上で座標系の管理をしてくれるROSのモジュールです。\n$ rosrun rqt_tf_tree rqt_tf_tree  を実行すると、各座標系の間の関係を可視化することができます。\ntfは、座標系の関係をツリー構造で管理します。親の座標系が複数あることは許されません。\n今回自己位置推定を行うにあたり用いる座標系の関係は以下のようになります。\n  tfツリーをrqtで可視化  ここで、odom座標系は、オドメトリの算出を始めた位置(起動した位置)を原点とした座標系で、 ホイールオドメトリの値から、ロボットの基準となるbase_footprint座標系を繋げています。 base_footprint座標系の下には、ルンバロボットの構成要素であるセンサ類やホイールなどの座標系が子として繋がっています。\n一番親にいるmap座標系は、地図の原点を基準とした座標系ですが、 この座標系におけるロボットの座標系(base_footprint)を繋げること、 つまり、ロボットが地図上のどこにいるのかを決めることが、 自己位置推定の目的になります。\n今回の場合、base_footprintの親には既にodomがいるため、 map座標系とodom座標系を繋げることで、全体をひとつのツリーとして管理することができます。\n自己位置推定 地図が事前に与えられているという前提のもと、 その地図上のどこにロボットがいるのか、 LiDARやOdometryなどのセンサ情報を用いて推定することを自己位置推定といいます。\nここでは、自己位置推定法の一般的な手法の一つであるMCL(Monte Carlo Localization)について説明します。 MCLは、モンテカルロ法(Monte Carlo method)と呼ばれる確率的な手法を用いて、ロボットの位置推定を行います。\n以下のような手順で動作します。\n パーティクルの生成\nロボットの位置を表す候補となるパーティクル(粒子)を生成します。 パーティクルは、ロボットの姿勢(位置と角度)の候補を表す状態ベクトルです。 パーティクルの重み付け\nセンサデータやロボットの制御入力(移動量など)との一致の度合いに応じて、各パーティクルに重みを割り当てます。 センサデータには、LiDARのスキャンデータやカメラの画像データなどが使われます。 リサンプリング\n重み付けされたパーティクルから、新たなパーティクルを生成します。 重みが大きいパーティクルほど、新しいセットに多く含まれる可能性が高くなります。 これにより、より確からしい位置候補が残るようになります。 パーティクルの移動\nロボットが移動すると、パーティクルも同様に移動します。 パーティクルの移動には、ロボットの制御入力やノイズモデルが使用されます。 ロボットの制御入力と実際の移動距離には誤差が含まれるため、 この時パーティクルの分布は広がります。 パーティクルの更新\nロボットがセンサデータを取得すると、各パーティクルの重みが再計算されます。 予測されたセンサーデータと実際のセンサーデータの一致度に基づいて重みが更新されます。 推定された位置の計算\n更新された重みに基づいて、推定されたロボットの位置を計算します。 一般に、重み付き平均や最も重みの大きいパーティクルの位置が使用されます。  MCLは、センサデータのノイズや環境の不確実性に対して柔軟に対応できるため、自己位置推定の際によく使用されます。 ただし、パーティクル数が十分でない場合や、環境の動きが大きい場合には、正確な位置推定が難しくなる可能性があります。 また、MCLは計算コストが高いため、リアルタイムの応用には制約があります。\nMCLのパフォーマンスを向上させるためには、 パーティクル数やリサンプリングの戦略の選択、センサモデルの精度向上など、 いくつかの改良手法があります。\nまた、他の自己位置推定手法との組み合わせや統合が行われることもあります。\nroomba_hackでは、amcl(Adaptive Monte Carlo Localization)パッケージと emcl(MCL with Expansion resetting)パッケージが用意されています。\n  Monte Carlo Localization(Particle Filter) Dieter Fox et al. 1999, using sonar. http://www.doc.ic.ac.uk/~ajd/Robotics/RoboticsResources/montecarlolocalization.gif  [詳しい文献]\n \u0026ldquo;Probabilistic Robotics\u0026rdquo; by Sebastian Thrun, Wolfram Burgard, Dieter Fox  邦訳:\u0026ldquo;確率ロボティクス\u0026rdquo; 上田隆一    launchファイルとrosparam 自己位置推定では、初期位置がどこか、レーザーのスペックや、パーティクルの数など数十個のパラメータを保持します。\nこれらをプログラム内部で記述するのではなく、launchファイル内で指定することが可能です。 rosでは、rosparamという形でパラメータを管理することが可能です。\n以下に、今回用いるamcl.launch を示します。 launchファイルはxml形式で記述され、paramを指定すること以外にも、 launchファイル実行時に引数で指定可能なargや、トピック名などのリマップをすることも可能です。\nlaunchの詳しい書き方は、rosのドキュメントを参照してください。\n\u0026lt;?xml version=\u0026quot;1.0\u0026quot;?\u0026gt; \u0026lt;launch\u0026gt; \u0026lt;arg name=\u0026quot;use_map_topic\u0026quot; default=\u0026quot;true\u0026quot;/\u0026gt; \u0026lt;arg name=\u0026quot;odom_topic\u0026quot; default=\u0026quot;/odom\u0026quot; /\u0026gt; \u0026lt;arg name=\u0026quot;scan_topic\u0026quot; default=\u0026quot;/scan\u0026quot; /\u0026gt; \u0026lt;node pkg=\u0026quot;amcl\u0026quot; type=\u0026quot;amcl\u0026quot; name=\u0026quot;amcl\u0026quot; output=\u0026quot;screen\u0026quot;\u0026gt; \u0026lt;remap from=\u0026quot;scan\u0026quot; to=\u0026quot;$(arg scan_topic)\u0026quot;/\u0026gt; \u0026lt;remap from=\u0026quot;odom\u0026quot; to=\u0026quot;$(arg odom_topic)\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;use_map_topic\u0026quot; value=\u0026quot;$(arg use_map_topic)\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_pose_x\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_pose_y\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_pose_a\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_cov_xx\u0026quot; value=\u0026quot;0.1*0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_cov_yy\u0026quot; value=\u0026quot;0.1*0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;initial_cov_aa\u0026quot; value=\u0026quot;0.3*3.14\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;gui_publish_rate\u0026quot; value=\u0026quot;10.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_max_beams\u0026quot; value=\u0026quot;2.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_min_range\u0026quot; value=\u0026quot;0.15\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_max_range\u0026quot; value=\u0026quot;12.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_z_hit\u0026quot; value=\u0026quot;0.8\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_z_short\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_z_max\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_z_rand\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_sigma_hit\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_lambda_short\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_model_type\u0026quot; value=\u0026quot;likelihood_field\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;laser_likelihood_max_dist\u0026quot; value=\u0026quot;2.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;min_particles\u0026quot; value=\u0026quot;100\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;max_particles\u0026quot; value=\u0026quot;1000\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;kld_err\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;kld_z\u0026quot; value=\u0026quot;0.0\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;update_min_d\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;update_min_a\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;resample_interval\u0026quot; value=\u0026quot;1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;transform_tolerance\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;recovery_alpha_slow\u0026quot; value=\u0026quot;0.001\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;recovery_alpha_fast\u0026quot; value=\u0026quot;0.1\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_frame_id\u0026quot; value=\u0026quot;odom\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_model_type\u0026quot; value=\u0026quot;diff\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha1\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha2\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha3\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha4\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;param name=\u0026quot;odom_alpha5\u0026quot; value=\u0026quot;0.2\u0026quot;/\u0026gt; \u0026lt;/node\u0026gt; \u0026lt;/launch\u0026gt;  演習 【jetson・開発マシン】それぞれdockerコンテナを起動 jetsonでdockerコンテナを起動\n(開発PC):~$ ssh roomba_dev1 (jetson):~$ cd ~/group_a/roomba_hack (jetson)::~/group_a/roomba_hack$ git pull (jetson):~/group_a/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh (jetson)(docker):~/roomba_hack# roslaunch roomba_bringup bringup.launch  開発PCでdockerコンテナを起動\n(開発PC):~$ cd ~/group_a/roomba_hack (開発PC):~/group_a/roomba_hack$ git pull (開発PC):~/group_a/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x   gmappingで地図作成 (開発PC)(docker) roslaunch navigation_tutorial gmapping.launch  地図の保存。map.pgm(画像データ)とmap.yaml(地図情報)が保存される。\n(開発PC)(docker) rosrun map_server map_saver  ~/roomba_hack/catkin_ws/src/navigation_tutorial/map の下に保存する。\n amclをlaunchして、自己位置推定する localizationノードと地図サーバーを同時に起動。\n(開発PC)(docker) roslaunch navigation_tutorial localization.launch (開発PC)(docker) roslaunch roomba_teleop teleop.launch (開発PC)(docker) rviz -d /root/roomba_hack/catkin_ws/src/navigation_tutorial/configs/navigation.rviz   初期位置の指定(rvizの2D Pose Estimate) コントローラで移動させてみて自己位置を確認 rqt_tf_treeを見てみる   amclのparamをチューニングする launchファイルの中身を見てみて、値を変えてみる。\n各パラメータの意味はamclのページを参照。\n例えば、・・・\n initial_cov_** を大きくしてみて、パーティクルがちゃんと収束するかみてみる。 particleの数(min_particles、max_particles)を変えてみて挙動をみてみる。   launchファイルの拡張 localization.launchファイルに以下を追加してteleop.launchとrvizが同時に起動するようにしてみよう。\n\u0026lt;!-- teleop.launchを起動--\u0026gt; \u0026lt;include file=\u0026quot;$(find roomba_teleop)/launch/teleop.launch\u0026quot;\u0026gt; \u0026lt;/include\u0026gt; \u0026lt;!-- rvizを起動--\u0026gt; \u0026lt;node pkg=\u0026quot;rviz\u0026quot; type=\u0026quot;rviz\u0026quot; name=\u0026quot;navigation_rviz\u0026quot; args=\u0026quot;-d $(find navigation_tutorial)/configs/navigation.rviz\u0026quot;/\u0026gt;  ","date":1642809600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1642809600,"objectID":"29d802971ebbc674f8dd6b80aeddda91","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap4/localization/","publishdate":"2022-01-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap4/localization/","section":"course","summary":"","tags":null,"title":"自己位置推定","type":"book"},{"authors":null,"categories":null,"content":"複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう\nLearn LiDARのスキャンデータを使って,障害物を回避してみよう 次に,LiDARでスキャンしたデータを使って,障害物を回避するようなプログラムを作ってみましょう.\nLiDARスキャンのメッセージ(/scan)の中身を見てみよう LiDARは,Light Detection And Rangingの略で,レーザ光を使って離れた場所にある物体形状や距離を測定するためのセンサです. 近年では,自動車の自動運転にも用いられることの多いセンサの一つです.\nroombaに搭載されたLiDARセンサ(rplidar)の値は,/scanのトピックに流れていて,rostopic echo /scanをしてみるとメッセージとしてどんな情報が流れているかわかります.\n大きなデータなので今回はテキストに掲載するのは省略しますが,rostopic type /scanをしてみると,メッセージとして,sensor_msgs/LaserScan型が使われていることがわかります. rostopic type /scan root@dynamics:~/roomba_hack# rostopic type /scan sensor_msgs/LaserScan  \nsensor_msgs/LaserScan型の定義を確認してみましょう. メッセージ型の定義は,ドキュメントのほか,rosmsg info sensor_msgs/LaserScanすることでもコマンドから確認できます. rosmsg info sensor_msgs/LaserScan root@dynamics:~/roomba_hack# rosmsg info sensor_msgs/LaserScan std_msgs/Header header uint32 seq time stamp string frame_id float32 angle_min float32 angle_max float32 angle_increment float32 time_increment float32 scan_time float32 range_min float32 range_max float32[] ranges float32[] intensities  \nangle_minにはスキャンの開始角度,angle_maxにはスキャンの終了角度がラジアンで記録されています. angle_incrementは,計測した間隔がラジアンで記録されています. range_maxにはスキャンの間で検出された最大の距離,range_minには最小の距離がメートルで記録されています.\nrvizでLiDARスキャンの値を可視化してみよう rvizでLiDARのスキャン結果を可視化してみましょう.\nLaserScanをAddして,topicに/scanを設定すると,以下のように,ロボットを中心にLiDARによって計測された障害物が赤く表示されます.\n  LiDARスキャンをrvizで可視化  LiDARを使って障害物を回避しよう それでは,LiDARスキャン/scanの情報を使った制御の実装の例としてnavigation_tutorialパッケージの中のavoidance.pyをみてみましょう.\navoidance.py #!/usr/bin/env python3 import numpy as np import rospy from geometry_msgs.msg import Twist from sensor_msgs.msg import LaserScan class Avoidance: def __init__(self): rospy.init_node('avoidance', anonymous=True) # Publisher self.cmd_vel_pub = rospy.Publisher('/planner/cmd_vel', Twist, queue_size=10) # Subscriber scan_sub = rospy.Subscriber('/scan', LaserScan, self.callback_scan) self.min_range = None def callback_scan(self, data): fov = np.deg2rad(60) min_range = data.range_max min_idx = -1 angle = data.angle_min for idx, r in enumerate(data.ranges): angle += data.angle_increment if -fov\u0026lt;angle\u0026lt;fov: if r\u0026lt;min_range: min_range = r min_idx = idx if min_idx \u0026lt; len(data.ranges)/2.0: self.direction = \u0026quot;RIGHT\u0026quot; else: self.direction = \u0026quot;LEFT\u0026quot; self.min_range = min_range def process(self): r = rospy.Rate(10) while not rospy.is_shutdown(): vel = Twist() if self.min_range is not None: if self.min_range \u0026gt;= 0.4: vel.linear.x = 0.2 vel.angular.z = 0.0 else: vel.linear.x = 0.0 if self.direction == \u0026quot;RIGHT\u0026quot;: vel.angular.z = 0.5 elif self.direction == \u0026quot;LEFT\u0026quot;: vel.angular.z = -0.5 self.cmd_vel_pub.publish(vel) r.sleep() if __name__=='__main__': avoidance = Avoidance() try: avoidance.process() except rospy.ROSInitException: pass   このプログラムでは,LiDARを使って進行方向に存在する障害物を見つけ,それを回避しながら進むようにロボットを制御しています.具体的には,\n ロボットの進行方向に物体がなかったら直進 ロボットの右側に障害物があったら左回転 ロボットの左側に障害物があったら右回転  することで障害物を回避(ぶつかる前に方向転換)しています.\nでは,プログラムの中身を見ていきます.\n/odomを使った制御の場合と同様に,ノードを定義する際に,コマンドを送るパブリッシャと,LiDARスキャンのデータを読み取るサブスクライバを作成します.\nclass Avoidance: def __init__(self): rospy.init_node('avoidance', anonymous=True) # Publisher self.cmd_vel_pub = rospy.Publisher('/planner/cmd_vel', Twist, queue_size=10) # Subscriber scan_sub = rospy.Subscriber('/scan', LaserScan, self.callback_scan) self.min_range = None  /scanのコールバックは,\ndef callback_scan(self, data): fov = np.deg2rad(60) min_range = data.range_max min_idx = -1 angle = data.angle_min for idx, r in enumerate(data.ranges): angle += data.angle_increment if -fov\u0026lt;angle\u0026lt;fov: if r\u0026lt;min_range: min_range = r min_idx = idx if min_idx \u0026lt; len(data.ranges)/2.0: self.direction = \u0026quot;RIGHT\u0026quot; else: self.direction = \u0026quot;LEFT\u0026quot; self.min_range = min_range  となっており,正面から左右60度の範囲内で最も短い距離をself.min_rangeに格納し,それが右側にあるのか左側にあるのかをself.directionに格納しています..\nこのプログラムを実行するとprocessメソッドが(0.1秒おきに)常に実行されます.\ndef process(self): r = rospy.Rate(10) while not rospy.is_shutdown(): vel = Twist() if self.min_range is not None: if self.min_range \u0026gt;= 0.4: vel.linear.x = 0.2 vel.angular.z = 0.0 else: vel.linear.x = 0.0 if self.direction == \u0026quot;RIGHT\u0026quot;: vel.angular.z = 0.5 elif self.direction == \u0026quot;LEFT\u0026quot;: vel.angular.z = -0.5 self.cmd_vel_pub.publish(vel) r.sleep()  processメソッド内部では,格納されたself.min_rangeが0.4(メートル)より大きい場合は,ロボットの前に何もないと判断して直進,小さい場合は,self.directionの値を見て,RIGHTであれば右に障害物があると判断して左回転,LEFTであれば左に障害物があると判断して右回転するようなプログラムになっています.\nそれでは,実際にLiDARを使って障害物を回避するプログラムを実行してみましょう.\n演習 【開発PC】topicの確認 /scanの型を確認\n(開発PC)(docker):~/roomba_hack# rostopic type /scan  /scanの中身を確認\n(開発PC)(docker):~/roomba_hack# rostopic echo /scan   LiDARスキャンを使ったフィードバック制御 avoidance.pyを実行してみよう.\nこのプログラムを動かすときには,コントローラのYボタンを押してからBボタンを押してautoモードにしておきましょう.\n今回はせっかくなので,launchfileから起動してみましょう. このlaunchfileは,navigation_tutorialパッケージの中のlaunchフォルダの中にあるavoidance.launchに記述されています(github).\n(開発PC)(docker):~/roomba_hack# roslaunch navigation_tutorial avoidance.launch  ロボットの進行方向に障害物があるときに,それを避けるように方向転換したら成功です.\ntry it! avoidance.pyの中身を読んでコードを変更してみよう\n","date":1687392e3,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1687392e3,"objectID":"4115bb19a4232d2372987cbff0c843fc","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap3/sensing3/","publishdate":"2023-06-22T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap3/sensing3/","section":"course","summary":"複数のセンサを組み合わせてよりかしこくロボットを動かしてみよう\n","tags":null,"title":"ロボットシステムにおけるセンシング・アクチュエーション・通信③","type":"book"},{"authors":null,"categories":null,"content":"ロボット開発によく用いられるROSの概要を理解する\nLearn ROSの概要 ROS(Robot Operating System)は、ロボット・アプリケーション開発に便利な機能を提供するフレームワークです。 フレームワークとは、プログラミング言語を特定の目的に特化させて使うためのツールのことです。 具体的には以下にあげる機能を提供しています。\n  メッセージ通信\n プロセス間、コンピュータ間の通信ライブラリが提供されています。    デバイスドライバ\n  ROSに対応しているセンサやアクチュエータを搭載したロボットであれば、違うロボットであってもほぼ同じソースコードを使用して動かすことができます。\nroombaを動かすために書いたソースコードをそのまま使用してHSRを動かす、といったことができます。\n  https://github.com/ros-drivers\n  http://wiki.ros.org/Sensors\n    ライブラリ\n ロボットを動作させるソフトウェア(ナビゲーション、マニピュレーション)の基本機能の大半が提供されています。    視覚化ツール\n ロボットの内部状態やセンサ出力を2次元、3次元で視覚化するRvizや3次元動力学シミュレータのGazeboなどが提供されています。    パッケージ管理\n 多種多様なプログラミング言語(python, C++, \u0026hellip;)、依存関係で記述されたプログラム同士を統合的に使用することが可能です。 これにより、経路計画など処理が重いプロセスはC++でコードを書き、画像認識など機械学習系のプロセスはpythonでコードを書く、といったこともできるようになります。    ROSのメッセージ通信 ロボットを動かす際には、多くのプログラムを並列して実行し、それぞれがデータをやりとりする必要があります。 ROSはそのようなプログラム間の通信に必要な機能を提供しています。\n  ノード(node)\n ROSでは、一つのプログラム単位を「ノード(node)」と呼びます。 基本的には、一つのファイルが一つのノードに対応しています。 各ノードは次に述べるtopic、service、actionlibの三つの通信方法を使って、他のノードとデータのやり取りを行います。    トピック(topic)\n  ROSでの、最も基本的なデータ通信の経路を「トピック(topic)」と呼びます。\n  ノードはメッセージをトピックへ向けて配信(Publish)し、また購読する(Subscribe)ことで他のノードと情報を共有することができます。\n  配信を行うノードをPublisher、購読を行うノードをSubscriberと呼びます。ノードはこのどちらかに二分することができるというわけではなく、実際には一つのノードがpublisherであり、subscriberでもあるという状況がほとんどです。\n  トピックには名前が付けられており、同じトピックに複数のノードがデータを送ったり、複数のノードが同じデータを受け取ることができます。\n  メッセージ(message)\nトピックへ配信したり、購読したりするときのROSのデータ型のことを「メッセージ(message)」と呼びます。 メッセージの型はmsgファイルに記述されており、使用するプログラミング言語に依存しないデータ形式になっています。\n以下に、物体やロボットの位置を表す時によく用いるgeomemtry_msgs/PoseStamped型のmsgファイルを示します。 位置情報の時間や座標フレームの情報が含まれるheaderと座標位置を表すposeで定義されています。\nstd_msgs/Header header uint32 seq time stamp string frame_id geometry_msgs/Pose pose geometry_msgs/Point position float64 x float64 y float64 z geometry_msgs/Quaternion orientation float64 x float64 y float64 z float64 w  各行の左側にはデータ型が、右側には変数名が記述されています。\n    Topic通信のイメージ  \n    サービス(service)\n  「サービス(service)」も、ノードが他のノードと通信するための手段の一つです。topicより少し複雑な通信の仕方を提供します.\n  サービスには、サービスを提供するノード(service server)とサービスを要求するノード(service client)があります。\n  サービスは以下のような流れで使用されます。\n clientがserverに引数を渡す。 引数を受け取ったserverが何らかのプログラムを実行する。 serverは行為の結果を返り値としてclientに返す。 clientはその返り値に応じて後の挙動を変える。      サービスにおいて送受信されるデータの型は.srvファイルに記述されています。\n  メッセージと同様使用言語に依存しないデータ形式ですが、メッセージと異なるのは、引数と戻り値の二つの形式を定義する必要があるところです。\n  以下に、srvの例としてstd_srvs/SetBoolを示します。\nこのように引数と戻り値の間に---を入れて定義します。\nbool data --- bool success string message      アクション(actionlib)\n アクションもノード間通信の一つの手段です。serviceよりもさらに複雑な通信ができます。 サービスは動作の終了時にのみserverからclientに結果を返すのに対し、アクションは動作の途中経過をclientに渡すことができます。 ここでは詳しい説明を省略します。    ROSマスタ(ROS master)\n  「ROSマスタ(ROS master)」は、ノード、トピックおよびサービスの名前登録を行い、それぞれのノードが他のノードから見えるようにする役割を担っています。\n  通信するノード名とトピック名およびサービス名の対応が決定した後、ノード同士が「peer-to-peer」で通信します。\n  ROSマスタを起動するには「roscore」というコマンドを実行します(が、RoombaやHSRをつかうときにはこのコマンドが自動で実行されることが多いため、あまり意識する機会はないかもしれません)。\n      ROS通信  -- ROSと連動するソフトウェア ROSは以下のようなソフトウェアと連動して使うためのパッケージを提供しています。簡単な説明にとどめるので、詳しい使い方は必要になった際に調べてください。\n  OpenCV\n豊富な機能を持つ2D画像処理用のライブラリです。 カメラで撮影した画像を処理する際に使用します。\n  PCL(Point Cloud Library)\n 3次元点群処理のライブラリ。 HSRやRoombaにはRGBDカメラが搭載されています。DはDepthという意味で、画像の各ピクセルに距離情報を対応させたDepth画像を取得することができます。\nこのような三次元の点群の情報を処理する際にPCLを使うと便利です。     OpenSLAM\n 地図を効果的に使うことで、より安定したロボットのナビゲーションを行うことができます。 移動ロボットの自己位置推定と地図生成を同時に行うSLAM(Simultaneous Localization and Mapping)という手法は、それだけで一つの研究分野になる程奥深い分野で、活発に研究が行われています。 OpenSLAMは、SLAMのソースコードを公開するためのプラットフォームを提供しており、様々なSLAMの手法を実装しています。    これ以外にも多くのツールがROSと連動しています。\n可視化ツール ロボット内部の大量のデータが正しく処理されているか知りたい場合、変数の中身の数値などを直接みるのは大変です。直感的にわかりづらいためミスも増えます。\n可視化をすることで、開発やデバッグがより効率よく進められます。\n  rqt\nrqtはROSのGUIフレームワークで、様々なツールを提供しています。\nノードの状態を可視化するrqt_graph(下図1)、メッセージの値を時系列に沿ってプロットするrqt_plot(下図2)などがあります。\n  図1 rqt_graph wikipediaより引用    図2 rqt_plot wikipediaより引用      RViz\nロボットの三次元モデルや座標系、測定した三次元点群などを可視化するツールです。\n三次元空間の情報以外に、カメラに写っている画像なども表示できます。\n    gazebo\nオープンソースのロボット用三次元動力学シミュレータ。\n説明は割愛します。\n  演習 roombaドライバを起動し、動作することを確認する   jetsonにアクセスする\n(開発PC):~$ ssh roomba_dev1 (jetson):~$    dockerコンテナを起動する\n (jetson):~$ cd ~/23_group_x/roomba_hack (jetson):~/23_group_x/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh # このファイルを実行することでdockerコンテナを作成し、コンテナの中に入る。 root@roomba-dev-jetson:~/roomba_hack# # 上のように表示されればコンテナ内部に入れています。  今後docker内部であることは(docker)と表記します。\n  roomba driverなどを起動するlaunchファイルを実行する\nこのタイミングでルンバの電源が入っているかを確認しておきましょう。\n(jetson)(docker):~/roomba_hack# roslaunch roomba_bringup bringup.launch  起動に成功すればルンバからピッと短い音が鳴り、ターミナルには赤い文字が出続けるはずです。\n   開発PCでdockerコンテナを起動する   開発PCでdockerコンテナを起動する\n(開発PC):~$ cd ~/23_group_x/roomba_hack (開発PC):~/23_group_x/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x # xにはroomba_devの後につく数字を入れてください。   先ほどjetson内でdockerコンテナを起動しましたが、今回は開発PC内でコンテナを起動します。 このとき引数にjetsonのIPアドレスを入れることで、jetson内のROSマスタ(前述)に、開発PCからアクセスできるようにしています。    パッケージのビルド\n(開発PC)(docker):~/roomba_hack# cd catkin_ws (開発PC)(docker):~/roomba_hack/catkin_ws# ls # catkin_ws内に存在するディレクトリを確認する。 (開発PC)(docker):~/roomba_hack/catkin_ws# catkin_make # いろいろな出力が生成される。 (開発PC)(docker):~/roomba_hack/catkin_ws# ls # 再度catkin_ws内に存在するディレクトリを確認する。  ここで、buildとdevelというディレクトリが生成されていると、うまくいっています。\n build\nC++のコードを用いる際に、コンパイルされたファイルが生成されるディレクトリ。pythonを使っているときにはほとんど意識しない。 devel\n様々なファイルを含んでいるが、特にsetupファイルが重要。\nこのファイルを実行することで、現在いるワークスペースに含まれるコードを使用するようにROSの環境が設定される。    setupファイルを実行する\n(開発PC)(docker):~/roomba_hack/catkin_ws# source devel/setup.bash # setupファイルを実行     コントローラーを使ってロボットを動かす   コントローラーを起動\nコントローラーが開発PCに接続されていることを確認してください。\n(開発PC)(docker):~/roomba_hack/catkin_ws# roslaunch roomba_teleop teleop.launch    コントローラのモード\n 移動・停止 自動・マニュアル ドッキング・アンドッキング    コントローラによる操縦\n 移動ロック解除 L1を押している時のみ移動コマンドが動作します。 左ジョイスティック 縦方向で前進速度(手前に倒すとバック)、横方向は回転速度に対応しています。 左矢印 それぞれ、一定に低速度で前進・後退・回転します。       Roombaの情報を取得する   開発PCの新しいターミナルでdockerコンテナに入る\nbringup.launch及びteleop.launchを実行したターミナルは実行中のプログラムに占領されているので、開発PCで新しくターミナルを開いてコンテナの中に入ります。\nすでに開発PCで起動されているコンテナに入る場合は、\n(開発PC):~/23_group_x/roomba_hack$ docker exec -it roomba_hack bash # docker exec -it \u0026lt;コンテナ名\u0026gt; bash で起動中のコンテナに入ることができる。  または\n(開発PC):~/23_group_x/roomba_hack$ ./RUN-DOCKER-CONTAINER.sh  のいずれかのコマンドで入ることができます。\n  Roombaの情報を取得する\nさまざまなコマンドを使ってRoombaの情報を取得してみましょう。\n(開発PC)(docker):~/roomba_hack# rosnode list # ノードの一覧を表示する (開発PC)(docker):~/roomba_hack# rostopic list # トピックの一覧を表示する (開発PC)(docker):~/roomba_hack# rostopic echo /cmd_vel # /cmd_velというトピックの中身を表示する # teleop.launchを実行している状態でコントローラーを操作すると、/cmd_velの中身が変化することがわかる。 (開発PC)(docker):~/roomba_hack# rqt_graph # ノードとトピックの関係を表示 (開発PC)(docker):~/roomba_hack# rviz # rvizを起動     プロセスの終了・dockerコンテナから出る   プロセスの終了\n一部のプログラムは終了するまで処理を続けるため、明示的に終了させる必要があります。\n多くのプログラムはCtrl+Cで終了します。\n  dockerコンテナ・ターミナルから出る\nコマンドライン上で\nexit  を実行することで、\n コンテナの中にいる場合はコンテナ外にでる。 sshしている場合はsshを終了する。 ターミナルを使用している場合はターミナルを終了する。  ことができます。また、Ctrl+Dでも同様のことができます。\n  ","date":1686009600,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1686009600,"objectID":"20855653654f6e28e5120898d90797bd","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap1/ros/","publishdate":"2023-06-06T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap1/ros/","section":"course","summary":"ロボット開発によく用いられるROSの概要を理解する\n","tags":null,"title":"ROSとは","type":"book"},{"authors":null,"categories":null,"content":"ROSのパッケージ管理について理解しよう\nLearn ROSのパッケージ ROSでは、特定の機能やタスクを実現するためのコードやファイルをまとめてパッケージとして管理します。 各パッケージは独立して開発・保守され、他のパッケージと組み合わせて使用することができます。また、各パッケージが独立していることで、一度開発した機能を他のプロジェクトで再利用することも容易になります。\n例として、navigation_tutorialパッケージのファイル構成を示します。\nnavigation_tutorial ├── CMakeLists.txt ├── launch │ ├── amcl.launch │ ├── avoidance.launch │ ├── gmapping.launch │ ├── go_straight.launch │ ├── localization.launch │ ├── map_server.launch │ ├── move_base.launch │ └── navigation.launch ├── package.xml ├── params │ ├── base_global_planner_params.yaml │ ├── base_local_planner_params.yaml │ ├── costmap_common_params.yaml │ ├── dwa_local_planner_params.yaml │ ├── global_costmap_params.yaml │ ├── local_costmap_params.yaml │ └── move_base_params.yaml ├── scripts │ ├── avoidance.py │ ├── simple_control2.py │ └── simple_control.py └── src ├── avoidance.cpp └── go_straight.cpp  scriptsディレクトリ、srcディレクトリの役割 一般的に、scriptsディレクトリ内にpythonのプログラムが、srcディレクトリ内にC++のプログラムが配置されます。\n作成したプログラムはrosrunコマンドで実行することができます。\n$ rosrun \u0026lt;package name\u0026gt; \u0026lt;file name\u0026gt; # pythonファイルの場合 $ rosrun navigation_tutorial simple_control2.py # C++ファイルの場合 $ rosrun navigation_tutorial go_straight  実行時にパッケージ名を指定するので、現在どこのディレクトリにいるかに関係なく実行が可能です。\n launchディレクトリの役割 launchディレクトリにはlaunchファイルが配置されています。\nlaunchファイルは複数のROSノードを一括で起動するための設定が書かれたファイルです。複数のノードを起動したい時に、必要なファイルを一つ一つコマンドラインで実行していくことは大変ですが、launchファイルを用いることで一括で起動することができます。\nlaunchファイルはroslaunchコマンドで実行することができます。\n$ roslaunch \u0026lt;package name\u0026gt; \u0026lt;launch file name\u0026gt; # 例 $ roslaunch navigation_tutorial move_base.launch  .pyファイルや.cppファイルと同様、実行時にパッケージを指定するので、現在どこのディレクトリにいるかに関係なく実行が可能です。\n ROSのワークスペース 新しいROSのパッケージの開発は、一般にワークスペースと呼ばれる作業スペースのもとで行われます。\nワークスペースは、パッケージのビルドや実行に必要なファイルをまとめて管理するためのディレクトリです。catkin_makeコマンドを実行することで作成することができます。ワークスペースのディレクトリ名は任意ですが、よくcatkin_wsという名前が使われます。\nroomba_hackリポジトリ下にあるcatkin_wsのファイル構成を示します。\ncatkin_ws ├── build ├── devel └── src ├── CMakeLists.txt ├── navigation_tutorial │ ├── CMakeLists.txt │ ├── launch │ ├── package.xml │ ├── params │ ├── scripts │ └── src └── roomba ├── roomba_bringup │ ├── CMakeLists.txt │ ├── config │ ├── launch │ └── package.xml ├── roomba_description │ ├── CMakeLists.txt │ ├── config │ ├── launch │ ├── meshes │ ├── package.xml │ └── urdf ├── roomba_gazebo │ ├── CMakeLists.txt │ ├── launch │ └── package.xml └── roomba_teleop ├── CMakeLists.txt ├── include ├── launch ├── package.xml └── src  catkin_ws内でよく使用されるcatkin_makeコマンドについて説明します。\n$ cd catkin_ws # catkin_wsに移動 $ catkin_make  のように使用します。この時、以下の手順が実行されています。\n ワークスペースのsrcディレクトリ内に配置されたROSパッケージを検出し、ビルド対象として認識する。 各パッケージの依存関係を解決し、ビルドが必要な場合はコンパイルを行う。 ビルドが完了したパッケージをdevelディレクトリに配置する。  また、catkin_makeコマンドの実行後に\n$ source devel/setup.bash  を実行することで環境変数に自分が今いるワークスペースのパスを追加することができます。\nこれにより、ワークスペース内のパッケージを使用できるようになります。\nROSのコマンド ROSのコマンドのうち、よく使用するものを紹介します。\n  Topic関連\n$ rostopic list topicの一覧を表示する $ rostopic echo \u0026lt;topic name\u0026gt; 指定されたtopicの中身を表示する $ rostopic hz \u0026lt;topic name\u0026gt; topicの配信周波数を取得する $ rostopic info \u0026lt;topic name\u0026gt; topicの情報を表示する $ rostopic pub \u0026lt;topic name\u0026gt; \u0026lt;topic\u0026gt; topicを配信する $ rostopic type \u0026lt;topic name\u0026gt; topicの型を確認する    Node関連\n$ rosnode list nodeの一覧を表示する $ rosnode ping \u0026lt;node name\u0026gt; nodeの接続テストを行う $ rosnode info \u0026lt;node name\u0026gt; nodeの情報を表示する $ rosnode kill \u0026lt;node name\u0026gt; nodeをシャットダウンする    Package関連\n$ rospack list packageの一覧を表示する $ roscd \u0026lt;package name\u0026gt; 指定したpackage内に移動する    ROSのプログラムの書き方 それでは実際にプログラム例を見てみましょう。\n#!/usr/bin/env python3 import rospy from geometry_msgs.msg import Twist def time_control(pub, velocity, yawrate, time): # (2) vel = Twist() start_time = rospy.get_rostime().secs while(rospy.get_rostime().secs-start_time\u0026lt;time): vel.linear.x = velocity vel.angular.z = yawrate pub.publish(vel) rospy.sleep(0.1) def simple_controller(): # (1) rospy.init_node('simple_controller', anonymous=True) pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10) time_control(pub, 0.0, 0.0, 0.5) time_control(pub, 0.3, 0.0, 2.0) time_control(pub, 0.0, 0.0, 0.5) time_control(pub, -0.3, 0.0, 2.0) time_control(pub, 0.0, 0.0, 0.5) time_control(pub, 0.0, 0.5, 2.0) time_control(pub, 0.0, 0.0, 0.5) time_control(pub, 0.0, -0.5, 2.0) if __name__=='__main__': # (3) try: simple_controller() except rospy.ROSInitException: pass  (1) simple_controller関数 まずsimple_controller関数内をみていきましょう。\n以下の部分で\u0026quot;simple_controller\u0026quot;という名前のノードを定義しています。\nrospy.init_node('simple_controller', anonymous=True)  以下の部分で、このノードがPublisherであることを宣言しています。\npub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)  今回の場合は、/cmd_velトピックをTwist型で送信するPublisherを宣言しています。\n (2) time_control関数 続いて、time_control関数です。\nこの関数はpublisher、速度、角速度、時間を受け取り、速度指令をpublishします。\ndef time_control(pub, velocity, yawrate, time): vel = Twist() start_time = rospy.get_rostime().secs while(rospy.get_rostime().secs-start_time\u0026lt;time): vel.linear.x = velocity vel.angular.z = yawrate pub.publish(vel) rospy.sleep(0.1)  ここでTwist型のインスタンスを作成しています。\nvel = Twist()  while文で受け取った時間が過ぎるまでの間、受け取った速度と各速度をvelに格納し、pub.publish(vel)でpublishを行なっています。\nwhile(rospy.get_rostime().secs-start_time\u0026lt;time): vel.linear.x = velocity vel.angular.z = yawrate pub.publish(vel) rospy.sleep(0.1)   (3) グローバル変数__name__を用いたファイルの実行 (1)や(2)は関数の定義をしているだけで、実際にプログラムを実行した際には\nif __name__=='__main__':  以下の部分が実行されます。\n__name__はpythonの特殊な変数の一つで、ファイル実行時に自動で設定されます。 pythonのプログラムはコマンドで直接実行するか、importで他のプログラムから参照されるかのいずれかの方法により実行されますが、__name__はこの実行方法によって値が変わります。\n 直接実行された場合、__name__には__main__と言う文字列が代入されます。 importされた場合、__name__にはファイル名が代入されます。  この性質を利用し、ファイルが直接実行された場合のみ実行したい処理をif文で囲んでいます。\n大抵の場合、rosのプログラムでは、上に挙げたファイルのように、関数やクラスの定義と実際にプログラムを実行する部分を分けて記述します。\n 演習 【jetson・開発マシン】それぞれdockerコンテナを起動 jetsonでdockerコンテナを起動\n(開発PC):~$ ssh roomba_dev1 (jetson):~$ cd ~/group_a/roomba_hack (jetson):~/group_a/roomba_hack ./RUN-DOCKER-CONTAINER.sh (jetson)(docker):~/roomba_hack#  開発PCでdockerコンテナを起動\n(開発PC):~$ cd ~/group_a/roomba_hack (開発PC):~/group_a/roomba_hack# ./RUN-DOCKER-CONTAINER.sh 192.168.10.7x (開発PC)(docker):~/roomba_hack#   【jetson・開発マシン】ビルドをしてパスを通す catkin_make後にdevelとbuildディレクトリが作成されることを確認しましょう。\n(開発PC)(docker):~/roomba_hack# cd catkin_ws (開発PC)(docker):~/roomba_hack/catkin_ws# rm -rf devel build (開発PC)(docker):~/roomba_hack/catkin_ws# ls (開発PC)(docker):~/roomba_hack/catkin_ws# catkin_make (開発PC)(docker):~/roomba_hack/catkin_ws# ls (開発PC)(docker):~/roomba_hack/catkin_ws# source ./devel/setup.bash   【jetson】ROSマスタ、各種ノードを起動 (jetson)(docker):~/roomba_hack# roslaunch roomba_bringup bringup.launch   ROSメッセージの可視化 【開発PC】topicの確認 Topic関連のコマンドのところのrostopic listコマンドを使用してtopic一覧を表示してみましょう\n(開発PC)(docker):~/roomba_hack# rostopic list  特定のtopicの型を確認\n(開発PC)(docker)# rostopic type /camera/color/image_raw (開発PC)(docker)# rostopic type /scan  その型が実際にどのような構成をしているのかはrosmsg info \u0026lt;topic type\u0026gt;で調べられます。\n参考\nsensor_msgs/LaserScan型 http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/LaserScan.html\nsensor_msgs/Image型 http://docs.ros.org/en/noetic/api/sensor_msgs/html/msg/Image.html\n特定のtopicの中身を確認\n(開発PC)(docker)# rostopic echo /camera/color/image_raw (開発PC)(docker)# rostopic echo /scan  rvizを用いて可視化\n(開発PC)(docker)# rviz   【開発PC】topicのpublish(配信) topic/cmd_velの情報を確認\n(開発PC)(docker)# rostopic info /cmd_vel  topic/cmd_velの型を確認\n(開発PC)(docker)# rostopic type /cmd_vel  geometry_msgs/Twist型 http://docs.ros.org/en/noetic/api/geometry_msgs/html/msg/Twist.html\ntopic/cmd_velをpublish\n(開発PC)(docker)# rostopic pub /cmd_vel geometry_msgs/Twist \u0026quot;linear: x: 1.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0\u0026quot;  (開発PC)(docker)# rosrun navigation_tutorial simple_control.py   Try it! 時間が余った人向け try it! roomba_bringupパッケージのbringup.launchの中身を読んでみよう\nhint roscdコマンドを使うとパッケージへ簡単に移動ができます。ファイルの中身を表示するにはcatコマンドを使用します。\ntry it! 開発PCでrosnode関連のコマンドを使ってみよう\ntry it! 開発PCでrosrun rqt_graph rqt_graphを実行してnodeとtopicの関連を可視化してみよう\ntry it! 開発PCでsimple_control.pyの中身を読んでコードを変更してみよう\nhint コードを編集するときはエディタを使うことがおすすめです。新しくターミナルを開いて\n(開発PC):~$ cd group_a/roomba_hack (開発PC):~group_a/roomba_hack$ code .  でVScodeを起動することができます。\n","date":1649116800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1649116800,"objectID":"3a3eac87851dbc29e0c5e1d274ba3f31","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap2/rosbasic/","publishdate":"2022-04-05T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap2/rosbasic/","section":"course","summary":"ROSのパッケージ管理について理解しよう\n","tags":null,"title":"ROSのパッケージ・ワークスペース","type":"book"},{"authors":null,"categories":null,"content":"センサの値を読み取りロボットを動かしてみよう\nLearn ロボットセンサの基礎知識 ロボットが動作するために必要なセンサは大きく2種類に分けられる。\n1つ目が外界センサで、これはロボットが行動する環境の情報を取得するためのセンサーである。 具体的なセンサとして、\n レーザスキャナ  レーザービームを照射して物体上の点までの距離と方向を計測する。レーザビームを走査することで、センサ周囲の広い範囲を計測した点群を得られる。 レーザスキャナを用いて対象物までの距離や位置、形状を検知することをLiDAR(Light Detection And Ranging(光による検知と測距))という。   デプスカメラ  赤外線などを発光して距離情報を画像として計測する。それと同時に通常のRGB画像も取得できるものがある。    などがあげられる。\nセンサのノイズの影響を軽減するため、複数のセンサを組み合わせて利用されることもある。\n2つ目は内界センサで、これは(ロボットアームのような変形可能な)ロボットが自身の内部状態を把握し、位置や姿勢を制御するために使われるセンサーである。\n ホイールエンコーダ  回転信号を電気信号に変換するデバイス。 ロボットの位置の推定:台車に取り付けられたホイールエンコーダが回転角度や回転速度を計測し、ロボットの位置や向きを推定する。 ロボットアームの制御:ロボットアームは、ベースや関節にモーターとホイールエンコーダを備えている。ホイールエンコーダは関節の回転角度を計測し、アームの位置制御に使用される。これにより、アームは所望の位置や角度に正確に移動することができる。   IMU(Internal Measurement Unit)  ジャイロ、加速度センサ、磁気センサなどが一体となったもの。  ジャイロセンサは回転角・各速度を求めるセンサ。 加速度センサは衝撃の検出、歩行ロボットの姿勢制御などに使われる。      などが内界センサである。\n参考\n https://www.jsme.or.jp/jsme-medwiki/14:1013897#:~:text=robot%20sensor ","date":1649116800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1649116800,"objectID":"ff51cff84b07cbb120bc73f6c20e4a6b","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap2/sensing1/","publishdate":"2022-04-05T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap2/sensing1/","section":"course","summary":"センサの値を読み取りロボットを動かしてみよう\n","tags":null,"title":"ロボットシステムにおけるセンシング・アクチュエーション・通信①","type":"book"},{"authors":null,"categories":null,"content":"Learn Navigationシステム ナビゲーションの目的は、地図上の任意の目標地点へ、障害物を避けながらなるべく早く自律して移動することです。\nナビゲーションシステムは、\n 地図 目標位置 自己位置推定結果 リアルタイムのセンサ情報(LiDARスキャン情報など)  などを入力として、ロボットへの行動指令値(速度など)を出力します。\nナビゲーションでは、地図全体とロボット周辺(センサで見える範囲)の大きく2つに分けて考えることが多いです。\n地図全体を考えるグローバルパスプランでは、地図情報とゴール情報から大まかなゴールまでの経路を算出します。\nロボット周辺を考える ローカルパスプランでは、グローバルで算出した経路に沿うようにしつつ、周辺の障害物情報を避ける行動指令値を算出します。\nそれぞれの経路を考えるにあたって、経路のコストがどうなるか重要になります。 このコストを表現する方法として、コストマップが用いられることが多いです。\n  Navigationシステム概要(from ROS wiki)  Cost Map コストマップは、経路を算出するために用いることから、扱いやすいグリット上の占有格子地図という形で表現されることが多いです。\n(空を飛んだり、3次元地形を考えなくていい場合は、基本2次元で表現します。)\n経路は格子地図上で、点で扱うことが多いですが、ロボット自身はある程度の大きさを持っているので、スキャン情報で得られた点ギリギリに経路を生成すると、衝突してしまします。\nそのため、コストマップでは以下の図のようにスキャンで得られた点(図中の赤点)から、ロボットが入ってほしくない範囲にコスト(図中の青く塗りつぶされているところ)が付与するという表現をします。\n  コストマップ概要(from ROS wiki)  Global Path Planning グローバルパスプランの例として、グラフ探索を利用したダイクストラ法やA*法などで経路探索をすることがあります。\n  グローバルパスプランの例(from PythonRobotics)  Local Path Planning 局所経路計画(Local Path Planning)は、ロボット周辺の障害物を避けながら、目標値へ早く行けるような経路(ロボットの行動)を算出するモジュールです。\n代表的なアルゴリズムとしてDynamic Window Approach(DWA)というものがあります。   ローカルパスプラン概要(from ROS wiki) \nアルゴリズムの概要は以下になります。\n ロボットの行動空間から行動をサンプル サンプルした行動とロボットの運動モデルを用いて、一定時間シミュレーションをして経路を生成 生成した経路ごとに、コストマップやゴール情報からコストを算出 コスト最小の経路を選択し、ロボットの指令値とする 1~4を繰り返す  演習 Dockerfileにnavigationを追加してBuildする \n -- navigationをlaunchして、rviz上で指定した位置までナビゲーションさせてみる (開発PC)(docker) roslaunch navigation_tutorial navigation.launch   navigationをlaunchして、map座標系の位置を指定してナビゲーションさせてみる \n -- navigationのparamをチューニングする move baseのパラメータは navigation_tutorial/params の中にyaml形式で保存されています。\nlaunchファイルではloadコマンドでyamlを読み込んでいます。\n move_base base_local_planner costmap_2d  ","date":1609459200,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1609459200,"objectID":"ea25624150d5f3e46f1ef192419e7583","permalink":"https://matsuolab.github.io/roomba_hack_course/course/chap4/navigation/","publishdate":"2021-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/course/chap4/navigation/","section":"course","summary":"","tags":null,"title":"ナビゲーション","type":"book"},{"authors":null,"categories":null,"content":"Congratulations to Jian Yang and Monica Hall for winning the Best Paper Award at the 2020 Conference on Wowchemy for their paper “Learning Wowchemy”.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tempus augue non tempor egestas. Proin nisl nunc, dignissim in accumsan dapibus, auctor ullamcorper neque. Quisque at elit felis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aenean eget elementum odio. Cras interdum eget risus sit amet aliquet. In volutpat, nisl ut fringilla dignissim, arcu nisl suscipit ante, at accumsan sapien nisl eu eros.\nSed eu dui nec ligula bibendum dapibus. Nullam imperdiet auctor tortor, vel cursus mauris malesuada non. Quisque ultrices euismod dapibus. Aenean sed gravida risus. Sed nisi tortor, vulputate nec quam non, placerat porta nisl. Nunc varius lobortis urna, condimentum facilisis ipsum molestie eu. Ut molestie eleifend ligula sed dignissim. Duis ut tellus turpis. Praesent tincidunt, nunc sed congue malesuada, mauris enim maximus massa, eget interdum turpis urna et ante. Morbi sem nisl, cursus quis mollis et, interdum luctus augue. Aliquam laoreet, leo et accumsan tincidunt, libero neque aliquet lectus, a ultricies lorem mi a orci.\nMauris dapibus sem vel magna convallis laoreet. Donec in venenatis urna, vitae sodales odio. Praesent tortor diam, varius non luctus nec, bibendum vel est. Quisque id sem enim. Maecenas at est leo. Vestibulum tristique pellentesque ex, blandit placerat nunc eleifend sit amet. Fusce eget lectus bibendum, accumsan mi quis, luctus sem. Etiam vitae nulla scelerisque, eleifend odio in, euismod quam. Etiam porta ullamcorper massa, vitae gravida turpis euismod quis. Mauris sodales sem ac ultrices viverra. In placerat ultrices sapien. Suspendisse eu arcu hendrerit, luctus tortor cursus, maximus dolor. Proin et velit et quam gravida dapibus. Donec blandit justo ut consequat tristique.\n","date":1606867200,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1606867200,"objectID":"2a0ec8a990dbd78a00c4e15a09364b00","permalink":"https://matsuolab.github.io/roomba_hack_course/post/20-12-02-icml-best-paper/","publishdate":"2020-12-02T00:00:00Z","relpermalink":"/roomba_hack_course/post/20-12-02-icml-best-paper/","section":"post","summary":"Congratulations to Jian Yang and Monica Hall for winning the Best Paper Award at the 2020 Conference on Wowchemy for their paper “Learning Wowchemy”.\n","tags":null,"title":"Jian Yang and Monica Hall Win the Best Paper Award at Wowchemy 2020","type":"post"},{"authors":null,"categories":null,"content":"Congratulations to Richard Hendricks for winning first place in the Wowchemy Prize.\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tempus augue non tempor egestas. Proin nisl nunc, dignissim in accumsan dapibus, auctor ullamcorper neque. Quisque at elit felis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aenean eget elementum odio. Cras interdum eget risus sit amet aliquet. In volutpat, nisl ut fringilla dignissim, arcu nisl suscipit ante, at accumsan sapien nisl eu eros.\nSed eu dui nec ligula bibendum dapibus. Nullam imperdiet auctor tortor, vel cursus mauris malesuada non. Quisque ultrices euismod dapibus. Aenean sed gravida risus. Sed nisi tortor, vulputate nec quam non, placerat porta nisl. Nunc varius lobortis urna, condimentum facilisis ipsum molestie eu. Ut molestie eleifend ligula sed dignissim. Duis ut tellus turpis. Praesent tincidunt, nunc sed congue malesuada, mauris enim maximus massa, eget interdum turpis urna et ante. Morbi sem nisl, cursus quis mollis et, interdum luctus augue. Aliquam laoreet, leo et accumsan tincidunt, libero neque aliquet lectus, a ultricies lorem mi a orci.\nMauris dapibus sem vel magna convallis laoreet. Donec in venenatis urna, vitae sodales odio. Praesent tortor diam, varius non luctus nec, bibendum vel est. Quisque id sem enim. Maecenas at est leo. Vestibulum tristique pellentesque ex, blandit placerat nunc eleifend sit amet. Fusce eget lectus bibendum, accumsan mi quis, luctus sem. Etiam vitae nulla scelerisque, eleifend odio in, euismod quam. Etiam porta ullamcorper massa, vitae gravida turpis euismod quis. Mauris sodales sem ac ultrices viverra. In placerat ultrices sapien. Suspendisse eu arcu hendrerit, luctus tortor cursus, maximus dolor. Proin et velit et quam gravida dapibus. Donec blandit justo ut consequat tristique.\n","date":1606780800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":1606780800,"objectID":"be2bd15f022f0d83fe9ffd743881e70c","permalink":"https://matsuolab.github.io/roomba_hack_course/post/20-12-01-wowchemy-prize/","publishdate":"2020-12-01T00:00:00Z","relpermalink":"/roomba_hack_course/post/20-12-01-wowchemy-prize/","section":"post","summary":"Congratulations to Richard Hendricks for winning first place in the Wowchemy Prize.\n","tags":null,"title":"Richard Hendricks Wins First Place in the Wowchemy Prize","type":"post"},{"authors":null,"categories":null,"content":"","date":-62135596800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":-62135596800,"objectID":"f26b5133c34eec1aa0a09390a36c2ade","permalink":"https://matsuolab.github.io/roomba_hack_course/admin/config.yml","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/admin/config.yml","section":"","summary":"","tags":null,"title":"","type":"wowchemycms"},{"authors":null,"categories":null,"content":"","date":-62135596800,"expirydate":-62135596800,"kind":"page","lang":"en","lastmod":-62135596800,"objectID":"6d99026b9e19e4fa43d5aadf147c7176","permalink":"https://matsuolab.github.io/roomba_hack_course/contact/","publishdate":"0001-01-01T00:00:00Z","relpermalink":"/roomba_hack_course/contact/","section":"","summary":"","tags":null,"title":"","type":"widget_page"}]
\ No newline at end of file
diff --git a/index.xml b/index.xml
index d6be282..32a8e26 100644
--- a/index.xml
+++ b/index.xml
@@ -1398,7 +1398,6 @@ subscribeし、<br>
 <details class="spoiler " id="spoiler-4">
 <summary>(開発PC)物体検出を行う</summary>
 <p><pre><code class="language-bash">(開発PC)(docker)# cd catkin_ws; catkin_make; source devel/setup.bash
-(開発PC)(docker)# roscd three-dimensions_tutorial; cd yolov3/weights; ./download_weights.sh
 (開発PC)(docker)# rosrun three-dimensions_tutorial object_detection.py
 # rvizで`/detection_result`を表示し結果を確認してみよう。
 (開発PC)(docker)# rosrun three-dimensions_tutorial detection_distance.py