<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>URDF on M1KE BL0G</title><link>https://www.m1ke.org/tags/urdf/</link><description>Recent content in URDF on M1KE BL0G</description><generator>Hugo -- gohugo.io</generator><language>ja-jp</language><copyright>mike</copyright><lastBuildDate>Tue, 25 Aug 2026 12:00:00 +0900</lastBuildDate><atom:link href="https://www.m1ke.org/tags/urdf/index.xml" rel="self" type="application/rss+xml"/><item><title>ROS2の基本</title><link>https://www.m1ke.org/p/ros2%E3%81%AE%E5%9F%BA%E6%9C%AC/</link><pubDate>Tue, 25 Aug 2026 12:00:00 +0900</pubDate><guid>https://www.m1ke.org/p/ros2%E3%81%AE%E5%9F%BA%E6%9C%AC/</guid><description>&lt;img src="https://www.m1ke.org/p/ros2%E3%81%AE%E5%9F%BA%E6%9C%AC/ros2.png" alt="Featured image of post ROS2の基本" /&gt;&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;業務でROS2を使う必要が出てきた&lt;/li&gt;
&lt;li&gt;自分向けにROS2の基本的な考え方と用語を整理する&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ros2とは"&gt;ROS2とは&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Robot Operating System 2の略だが、OS（オペレーティングシステム）そのものではない&lt;/li&gt;
&lt;li&gt;ロボット向けのソフトウェアを作るためのミドルウェア兼フレームワーク&lt;/li&gt;
&lt;li&gt;「センサーで取得した値を処理して、モーターに指令を送る」といった、ロボットの各機能をバラバラのプログラム（後述のノード）として作り、それらを通信でつなげて1つのロボットとして動かす、という考え方が中心にある&lt;/li&gt;
&lt;li&gt;前身のROS1に対して、以下のような改善が入っている
&lt;ul&gt;
&lt;li&gt;通信の基盤にDDSという業界標準の仕組みを採用し、単一障害点（後述）をなくした&lt;/li&gt;
&lt;li&gt;リアルタイム性や、Windows・組み込み環境への対応が強化された&lt;/li&gt;
&lt;li&gt;セキュリティ機能が追加された&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ディストリビューションとインストール"&gt;ディストリビューションとインストール&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ROS2は、毎年1回コードネームつきのバージョン（ディストリビューション）がリリースされる&lt;/li&gt;
&lt;li&gt;全てのバージョンがLTS（長期サポート）というわけではなく、偶数年リリースのものがLTS（サポート期間5年）、奇数年リリースのものは短期サポート（サポート期間約1年半）になっている&lt;/li&gt;
&lt;li&gt;主なLTS版
&lt;ul&gt;
&lt;li&gt;Humble Hawksbill（2022年、Ubuntu 22.04向け）&lt;/li&gt;
&lt;li&gt;Jazzy Jalisco（2024年、Ubuntu 24.04向け）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;迷ったら、その時点の最新LTS版を、対応するUbuntuのバージョンに入れるのが基本&lt;/li&gt;
&lt;li&gt;ROS1と違い、ROS2はOSのバージョンとの結びつきが強いため、Ubuntuのバージョンを先に決めてから、対応するROS2のバージョンを選ぶことになる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="全体アーキテクチャ"&gt;全体アーキテクチャ&lt;/h2&gt;
&lt;h3 id="ddsdata-distribution-service"&gt;DDS（Data Distribution Service）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ROS2の通信を裏で支えているミドルウェアの規格&lt;/li&gt;
&lt;li&gt;「マスターレス」という特徴があり、ROS1にあった&lt;code&gt;roscore&lt;/code&gt;のような、通信を仲介する中央サーバーが存在しない&lt;/li&gt;
&lt;li&gt;各ノードが自律的にお互いを見つけて（Discoveryと呼ぶ）、直接データをやり取りする&lt;/li&gt;
&lt;li&gt;中央サーバーがないため、その1台が落ちるとシステム全体が止まる、という単一障害点の問題が起きにくい&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ノードnode"&gt;ノード（Node）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ROS2における処理の最小単位&lt;/li&gt;
&lt;li&gt;1つのノードが、1つのプログラム（プロセス）に対応する&lt;/li&gt;
&lt;li&gt;例えば「カメラ画像を取得するノード」「物体検出をするノード」「モーターを制御するノード」のように、機能ごとにノードを分けて作る&lt;/li&gt;
&lt;li&gt;ノード同士は、後述のTopic・Service・Actionという3つの方式で通信する&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ノード間の通信"&gt;ノード間の通信&lt;/h2&gt;
&lt;p&gt;ノード同士の通信には、目的に応じて3つの方式がある。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方式&lt;/th&gt;
&lt;th&gt;通信の形&lt;/th&gt;
&lt;th&gt;用途の例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Topic&lt;/td&gt;
&lt;td&gt;非同期のpub/sub&lt;/td&gt;
&lt;td&gt;センサーの値を流し続ける&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service&lt;/td&gt;
&lt;td&gt;同期的なリクエスト/レスポンス&lt;/td&gt;
&lt;td&gt;一度だけの問い合わせ・設定変更&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action&lt;/td&gt;
&lt;td&gt;非同期＋進捗報告＋キャンセル可能&lt;/td&gt;
&lt;td&gt;時間のかかる動作（アーム移動など）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="topicトピック"&gt;Topic（トピック）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;特定の名前（トピック名）に対して、データを送り続ける側（Publisher）と、受け取り続ける側（Subscriber）が非同期に繋がる仕組み&lt;/li&gt;
&lt;li&gt;PublisherとSubscriberはお互いの存在を意識しなくてよく、同じトピック名にさえ繋がれば、何対何でも通信できる&lt;/li&gt;
&lt;li&gt;センサーの値のように、継続的に流れてくるデータに向いている&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="serviceサービス"&gt;Service（サービス）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;関数呼び出しのように、リクエストを送って、その場でレスポンスを待つ同期的な通信&lt;/li&gt;
&lt;li&gt;「今の設定値を教えて」「このパラメータを変更して」のような、1回のやり取りで完結する処理に向いている&lt;/li&gt;
&lt;li&gt;レスポンスが返るまで基本的に待つことになるので、時間のかかる処理には向かない&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="actionアクション"&gt;Action（アクション）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Serviceに、進捗報告（フィードバック）とキャンセル機能を足したような仕組み&lt;/li&gt;
&lt;li&gt;「アームを目標位置まで動かす」のように、時間がかかり、途中経過を知りたかったり、途中で中断したくなったりする処理に向いている&lt;/li&gt;
&lt;li&gt;内部的にはTopicとServiceの組み合わせで実現されている&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="メッセージ型インターフェース"&gt;メッセージ型・インターフェース&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Topic・Service・Actionでやり取りするデータの型（インターフェース）は、それぞれ&lt;code&gt;.msg&lt;/code&gt;・&lt;code&gt;.srv&lt;/code&gt;・&lt;code&gt;.action&lt;/code&gt;という専用のファイル形式で定義される&lt;/li&gt;
&lt;li&gt;標準で用意されている型は、目的別のパッケージにまとまっている
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;std_msgs&lt;/code&gt;: Int32・String・Boolなど、基本的な型&lt;/li&gt;
&lt;li&gt;&lt;code&gt;geometry_msgs&lt;/code&gt;: Twist（速度）・Pose（位置・姿勢）など、幾何情報の型&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sensor_msgs&lt;/code&gt;: Image・LaserScan・PointCloud2など、センサーデータの型&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;標準の型で足りない場合は、自分のパッケージ内に&lt;code&gt;.msg&lt;/code&gt;ファイルなどを作って独自の型を定義できる&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 interface show &amp;lt;型名&amp;gt;&lt;/code&gt;で、その型がどんなフィールドを持つか確認できる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cmd_vel速度指令"&gt;cmd_vel（速度指令）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ロボットを動かすための速度指令に、事実上の標準として使われているトピック名&lt;/li&gt;
&lt;li&gt;&lt;code&gt;geometry_msgs/Twist&lt;/code&gt;（または&lt;code&gt;TwistStamped&lt;/code&gt;）という、並進速度（linear）と回転速度（angular）を持つメッセージ型を使う&lt;/li&gt;
&lt;li&gt;Nav2の出力や、キーボード操作用のteleopノードなど、様々なノードがこの&lt;code&gt;/cmd_vel&lt;/code&gt;にpublishし、ロボット側のモーター制御ノードがそれをsubscribeして実際に動かす、という流れが一般的&lt;/li&gt;
&lt;li&gt;複数のノードが同時に&lt;code&gt;/cmd_vel&lt;/code&gt;を出そうとすると競合するため、優先順位をつけて1つの指令にまとめる&lt;code&gt;twist_mux&lt;/code&gt;のようなノードがよく使われる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="qosquality-of-service"&gt;QoS（Quality of Service）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DDSが持つ、通信の品質を細かく設定できる仕組み&lt;/li&gt;
&lt;li&gt;ROS1にはなかった、ROS2特有の重要な概念&lt;/li&gt;
&lt;li&gt;代表的な設定項目
&lt;ul&gt;
&lt;li&gt;Reliability: 通信の信頼性。多少データが欠けてもいいから速さを優先する（Best Effort）か、確実に届ける（Reliable）か&lt;/li&gt;
&lt;li&gt;Durability: 新しく繋がったSubscriberに対して、過去のデータも渡すかどうか&lt;/li&gt;
&lt;li&gt;History: 直近何件分のデータを保持しておくか&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;PublisherとSubscriberでQoSの設定が噛み合っていないと、通信自体が繋がらないことがあるため、初心者がハマりやすいポイントの1つ&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="開発の基本的な流れ"&gt;開発の基本的な流れ&lt;/h2&gt;
&lt;h3 id="ワークスペース"&gt;ワークスペース&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ROS2のプロジェクト全体を置く作業ディレクトリ&lt;/li&gt;
&lt;li&gt;中に&lt;code&gt;src/&lt;/code&gt;ディレクトリを作り、その下に複数のパッケージ（後述）を置く構成が基本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;colcon build&lt;/code&gt;というビルドツールで、ワークスペース内の全パッケージをまとめてビルドする&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="パッケージ"&gt;パッケージ&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ROS2における、機能をまとめる単位（1つ以上のノードやライブラリをまとめたもの）&lt;/li&gt;
&lt;li&gt;各パッケージには&lt;code&gt;package.xml&lt;/code&gt;（パッケージの情報・依存関係を書くファイル）が必須&lt;/li&gt;
&lt;li&gt;ビルド方式によって、以下のように使うファイルが変わる
&lt;ul&gt;
&lt;li&gt;C++の場合: &lt;code&gt;CMakeLists.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Pythonの場合: &lt;code&gt;setup.py&lt;/code&gt;（&lt;code&gt;ament_python&lt;/code&gt;というビルドタイプを使う）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="rosdepとワークスペースのsource"&gt;rosdepとワークスペースのsource&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;パッケージが依存する外部ライブラリを、OSのパッケージマネージャ経由で解決してくれるツールが&lt;code&gt;rosdep&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ワークスペースの&lt;code&gt;src/&lt;/code&gt;以下にある全パッケージの依存関係を、&lt;code&gt;rosdep install --from-paths src -y --ignore-src&lt;/code&gt;のようなコマンドでまとめて解決できる&lt;/li&gt;
&lt;li&gt;&lt;code&gt;colcon build&lt;/code&gt;でビルドした後は、&lt;code&gt;source install/setup.bash&lt;/code&gt;を実行しないと、ビルドしたパッケージがシェルから認識されない&lt;/li&gt;
&lt;li&gt;「ビルドは通ったのに&lt;code&gt;ros2 run&lt;/code&gt;できない」というのは、大抵このsourceのし忘れが原因で、初心者が最初につまずきやすいポイントの1つ&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="基本的なコマンド"&gt;基本的なコマンド&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ros2 run &amp;lt;パッケージ名&amp;gt; &amp;lt;実行ファイル名&amp;gt;&lt;/code&gt;: 1つのノードを実行する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 launch &amp;lt;パッケージ名&amp;gt; &amp;lt;launchファイル名&amp;gt;&lt;/code&gt;: 複数のノードをまとめて起動する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 node list&lt;/code&gt;: 今動いているノードの一覧を見る&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 topic list&lt;/code&gt; / &lt;code&gt;ros2 topic echo &amp;lt;トピック名&amp;gt;&lt;/code&gt;: トピックの一覧確認・流れているデータの確認&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 service list&lt;/code&gt;: サービスの一覧を見る&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rviz2&lt;/code&gt;: センサーの値やロボットの状態を可視化するツール&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rqt&lt;/code&gt;: ノードの構成やログをGUIで確認できるツール&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="最小限のコード例rclpy"&gt;最小限のコード例（rclpy）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ここまでは概念の説明だったので、実際に最小限のノードを書いてみる&lt;/li&gt;
&lt;li&gt;以下は、Pythonのクライアントライブラリ&lt;code&gt;rclpy&lt;/code&gt;を使った、シンプルなPublisherの例&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;rclpy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;rclpy.node&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;std_msgs.msg&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MinimalPublisher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;minimal_publisher&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publisher_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_publisher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;topic&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timer_callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;timer_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello World: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publisher_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Publishing: &amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;rclpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MinimalPublisher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;rclpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;destroy_node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;rclpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;ポイント
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Node&lt;/code&gt;を継承したクラスを作るのが基本形&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create_publisher&lt;/code&gt;でPublisherを作り、&lt;code&gt;create_timer&lt;/code&gt;で一定間隔ごとに&lt;code&gt;timer_callback&lt;/code&gt;を呼ぶ&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rclpy.spin(node)&lt;/code&gt;で、ノードを動かし続ける（この中で、後述のExecutorがコールバックを回している）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Subscriberの場合は&lt;code&gt;create_subscription&lt;/code&gt;を使い、メッセージが届くたびに呼ばれるコールバック関数を登録する形になる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="executor"&gt;Executor&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ノード内の複数のコールバック（タイマー・Subscriberなど）を、実際にどう実行するかを管理する仕組み&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rclpy.spin(node)&lt;/code&gt;を呼ぶと、内部でデフォルトのExecutor（SingleThreadedExecutor）が動き、登録されたコールバックを順番に処理する&lt;/li&gt;
&lt;li&gt;シングルスレッドのExecutorだと、1つのコールバックの処理が終わるまで次のコールバックは実行されない
&lt;ul&gt;
&lt;li&gt;時間のかかる処理を1つのコールバックに書いてしまうと、他のコールバック（他のトピックの受信など）がその間ブロックされてしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;複数のコールバックを並行して処理したい場合は、MultiThreadedExecutorを使う&lt;/li&gt;
&lt;li&gt;「トピックが来ているはずなのに反応が遅い/来ない」という不具合の原因が、実はExecutorのブロッキングだった、というのはよくあるハマりどころ&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="実行時の設定と制御"&gt;実行時の設定と制御&lt;/h2&gt;
&lt;h3 id="パラメータparameters"&gt;パラメータ（Parameters）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ノードに渡す設定値のこと。プログラムを書き換えずに、動作を外から調整できる仕組み&lt;/li&gt;
&lt;li&gt;例えば「カメラのフレームレート」「しきい値」のような値を、パラメータとして持たせることが多い&lt;/li&gt;
&lt;li&gt;コマンドでの確認・操作
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ros2 param list&lt;/code&gt;: あるノードが持つパラメータの一覧を見る&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 param get &amp;lt;ノード名&amp;gt; &amp;lt;パラメータ名&amp;gt;&lt;/code&gt;: 値を取得する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 param set &amp;lt;ノード名&amp;gt; &amp;lt;パラメータ名&amp;gt; &amp;lt;値&amp;gt;&lt;/code&gt;: 値を変更する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;実際の運用では、パラメータを1つずつ&lt;code&gt;set&lt;/code&gt;するのではなく、後述のLaunchファイルからYAMLファイルでまとめて渡すのが一般的&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="namespaceremapping"&gt;Namespace・Remapping&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Namespaceは、ノード名やトピック名の前につける「名前空間」で、同じパッケージ・同じノードを複数のインスタンスとして区別して動かすときに使う
&lt;ul&gt;
&lt;li&gt;例: &lt;code&gt;robot1/cmd_vel&lt;/code&gt;、&lt;code&gt;robot2/cmd_vel&lt;/code&gt;のように、ロボットごとに名前空間を分ける&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Remappingは、ノードが元々使っているトピック名・サービス名などを、実行時に別の名前に付け替える仕組み
&lt;ul&gt;
&lt;li&gt;例: &lt;code&gt;ros2 run pkg node --ros-args -r cmd_vel:=robot1/cmd_vel&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;どちらも、コードを一切変更せずに、起動時のオプションやLaunchファイルの引数だけで名前を切り替えられるのがポイント&lt;/li&gt;
&lt;li&gt;複数台のロボットを同時に動かす場合や、他人が作ったパッケージをそのまま自分のシステムに組み込む場合に、ほぼ必須になる知識&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="launchファイル"&gt;Launchファイル&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;複数のノードを、パラメータ付きでまとめて起動するための設定ファイル&lt;/li&gt;
&lt;li&gt;Python・XML・YAMLのいずれかで書ける（最近はPythonで書くことが多い）&lt;/li&gt;
&lt;li&gt;実際のロボットは複数のノードが同時に動いて成り立っているため、1つずつ&lt;code&gt;ros2 run&lt;/code&gt;するのではなく、launchファイルでまとめて立ち上げるのが基本的な運用になる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ライフサイクルノードmanaged-nodes"&gt;ライフサイクルノード（Managed Nodes）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;通常のノードは起動したらすぐ全機能が有効になるが、ライフサイクルノードは明示的な状態管理を持つ&lt;/li&gt;
&lt;li&gt;主な状態
&lt;ul&gt;
&lt;li&gt;Unconfigured: 起動直後、まだ設定されていない状態&lt;/li&gt;
&lt;li&gt;Inactive: 設定は終わっているが、まだ動いていない状態&lt;/li&gt;
&lt;li&gt;Active: 実際に処理を行っている状態&lt;/li&gt;
&lt;li&gt;Finalized: 終了処理が終わった状態&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;状態遷移は、&lt;code&gt;configure&lt;/code&gt;・&lt;code&gt;activate&lt;/code&gt;・&lt;code&gt;deactivate&lt;/code&gt;・&lt;code&gt;cleanup&lt;/code&gt;・&lt;code&gt;shutdown&lt;/code&gt;といったサービス呼び出しで制御する&lt;/li&gt;
&lt;li&gt;Nav2は、内部の各サーバー（Planner、Controllerなど）がこのライフサイクルノードとして作られていて、&lt;code&gt;lifecycle_manager&lt;/code&gt;というノードが、それらの状態をまとめて管理・遷移させている&lt;/li&gt;
&lt;li&gt;Nav2起動時に「なぜか動かない」場合、実はライフサイクルが&lt;code&gt;Active&lt;/code&gt;まで遷移していないだけ、ということもよくある&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ロボット特有の周辺技術"&gt;ロボット特有の周辺技術&lt;/h2&gt;
&lt;h3 id="rosbag2"&gt;rosbag2&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;トピックに流れているデータを記録・再生するツール（ROS1の&lt;code&gt;rosbag&lt;/code&gt;のROS2版）&lt;/li&gt;
&lt;li&gt;実機やシミュレーションで流れたデータをファイルに記録しておき、後から同じデータを何度でも再生できる&lt;/li&gt;
&lt;li&gt;実機を毎回動かさなくても、記録済みのデータでノードの開発・デバッグを進められるのが大きな利点&lt;/li&gt;
&lt;li&gt;主なコマンド
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ros2 bag record -a&lt;/code&gt;: 全トピックを記録する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 bag record &amp;lt;topic1&amp;gt; &amp;lt;topic2&amp;gt;&lt;/code&gt;: 指定したトピックだけ記録する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 bag play &amp;lt;bagファイル&amp;gt;&lt;/code&gt;: 記録したデータを再生する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 bag info &amp;lt;bagファイル&amp;gt;&lt;/code&gt;: 記録内容（トピック・件数・期間など）を確認する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;保存形式は&lt;code&gt;sqlite3&lt;/code&gt;か&lt;code&gt;mcap&lt;/code&gt;が使われる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="urdfunified-robot-description-format"&gt;URDF（Unified Robot Description Format）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ロボットの物理的な構造（リンクとジョイント）を記述するXML形式&lt;/li&gt;
&lt;li&gt;リンク（Link）は剛体の部品（腕の一部、車体など）、ジョイント（Joint）はリンク同士をつなぐ関節（回転・スライドなど）を表す&lt;/li&gt;
&lt;li&gt;各リンクの見た目（形状・色）や、衝突判定用の形状、質量・慣性などの物理情報もここに書ける&lt;/li&gt;
&lt;li&gt;&lt;code&gt;robot_state_publisher&lt;/code&gt;というノードが、URDFとジョイントの角度情報から、後述のTF2の座標変換を自動的に計算・配信してくれる&lt;/li&gt;
&lt;li&gt;RVizでロボットの3Dモデルを表示したり、Gazeboでシミュレーションしたりする際の元データにもなる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="tf2座標変換"&gt;TF2（座標変換）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ロボットに関わる複数の座標系（フレーム）の関係を、時系列で管理する仕組み&lt;/li&gt;
&lt;li&gt;例えば「地図（map）」「ロボット本体（base_link）」「カメラ（camera_link）」は、それぞれ別の座標系を持っていて、TF2はこれらの間の相対位置・姿勢を管理する&lt;/li&gt;
&lt;li&gt;各フレームは木構造（ツリー）でつながっていて、&lt;code&gt;map -&amp;gt; odom -&amp;gt; base_link -&amp;gt; camera_link&lt;/code&gt;のように親子関係を辿ることで、任意の2点間の座標変換を計算できる&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ros2 run rqt_tf_tree rqt_tf_tree&lt;/code&gt;で、今のフレーム構成をツリー図として確認できる&lt;/li&gt;
&lt;li&gt;Nav2のようなナビゲーションスタックは、TF2が正しく配信されていることが前提になっているため、ロボット開発で最初につまずきやすいポイントの1つ&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="nav2navigation2"&gt;Nav2（Navigation2）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ROS2上で自律移動（ナビゲーション）を実現するための標準的なソフトウェアスタック&lt;/li&gt;
&lt;li&gt;地図上で目的地を指定すると、経路計画と障害物回避をしながら自律的に移動できるようにする&lt;/li&gt;
&lt;li&gt;主な構成要素
&lt;ul&gt;
&lt;li&gt;Global Planner: 地図全体を使って、大まかな経路を計画する&lt;/li&gt;
&lt;li&gt;Controller（Local Planner）: 直近の障害物やロボットの動特性を考慮して、実際の速度指令（前述の&lt;code&gt;cmd_vel&lt;/code&gt;）を計算する&lt;/li&gt;
&lt;li&gt;Costmap: 障害物や走行コストを表現する地図。全体用のGlobal Costmapと、直近用のLocal Costmapの2種類がある&lt;/li&gt;
&lt;li&gt;Behavior Tree: 経路計画→移動→リカバリー動作といった、Nav2全体の動作フローを制御する仕組み&lt;/li&gt;
&lt;li&gt;Recovery Behaviors: 経路が見つからない・スタックしたときに行う復帰動作（その場回転、後退など）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Nav2自体は「自分が今どこにいるか」は推定しないため、AMCLなどの自己位置推定と組み合わせて使うのが前提&lt;/li&gt;
&lt;li&gt;launchファイルで各構成要素をまとめて立ち上げ、パラメータYAMLで細かい挙動をチューニングするのが基本的な使い方&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="gazeboシミュレータ"&gt;Gazebo（シミュレータ）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ロボットの物理シミュレーションを行うためのソフトウェア&lt;/li&gt;
&lt;li&gt;前述のURDFで記述したロボットモデルを仮想空間に配置し、重力・摩擦・衝突などを考慮した動きをシミュレートできる&lt;/li&gt;
&lt;li&gt;実機がまだない、あるいは実機を壊すリスクがある動作を試したいときに、ROS2のノードをそのまま実機と同じように動かして検証できる&lt;/li&gt;
&lt;li&gt;カメラ・LiDARなどのセンサーのシミュレーションデータも出力できるので、Nav2のようなセンサー入力が前提のスタックのテストにもよく使われる&lt;/li&gt;
&lt;li&gt;最近は従来のGazebo Classicから、後継のGazebo（旧Ignition Gazebo）への移行が進んでいる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ROS2は、ロボットの各機能をノードという単位に分けて、通信でつなげて動かすためのフレームワーク&lt;/li&gt;
&lt;li&gt;インストールは、その時点の最新LTSディストリビューションを、対応するUbuntuのバージョンに入れるのが基本&lt;/li&gt;
&lt;li&gt;通信の基盤はDDSで、ROS1にあった中央サーバー（単一障害点）がない&lt;/li&gt;
&lt;li&gt;ノード間の通信は、Topic（非同期の流し込み）・Service（同期的な問い合わせ）・Action（進捗報告つきの長時間処理）の3種類を目的に応じて使い分ける。QoSはその通信の品質を細かく設定する仕組み&lt;/li&gt;
&lt;li&gt;実際のコードは、&lt;code&gt;Node&lt;/code&gt;を継承したクラスにPublisher/Subscriberやコールバックを持たせる形で書き、&lt;code&gt;rclpy.spin&lt;/code&gt;が内部のExecutorでコールバックを回す&lt;/li&gt;
&lt;li&gt;開発は、ワークスペースの中にパッケージを作り、rosdepで依存関係を解決してcolconでビルドし、setup.bashをsourceして使う、という流れが基本&lt;/li&gt;
&lt;li&gt;動作の調整はパラメータで行い、Namespace・Remappingで名前を実行時に切り替えられる。複数ノードとパラメータをまとめて起動するのがLaunchファイルの役割&lt;/li&gt;
&lt;li&gt;rosbag2でデータを記録・再生できるので、実機を毎回動かさなくても開発・デバッグを進められる&lt;/li&gt;
&lt;li&gt;URDFでロボットの構造を記述し、そこからTF2が座標変換を計算・配信する。この2つはNav2などの前提になる基礎的な仕組み&lt;/li&gt;
&lt;li&gt;速度指令は&lt;code&gt;cmd_vel&lt;/code&gt;という標準的なトピック名でやり取りされ、ライフサイクルノードで状態管理されたNav2が、その&lt;code&gt;cmd_vel&lt;/code&gt;を出す自律移動用の標準スタック&lt;/li&gt;
&lt;li&gt;Gazeboを使えば、実機なしでもロボットのシミュレーションができる&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>