前回 は、ROS2 のユーザノードのトピックの実装をやってみました。
今回は、ROS2 の中身を見ていきたいと思います。
それでは、やっていきます。
参考文献
環境構築については、こちらを参考にしました。Python よりも C++ の解説が多かったです。ROS2 について、しっかりと詳しく解説されていた印象です。
こちらは、逆に、Python が多く、ROS2 について、基本的なことは解説されていますが、応用的なところとして、AI と関連付けての説明に重点が置かれていた印象です。
はじめに
ROS の公式ページです。ただ、インストール手順や、その他の情報は、ROS の各ディストリビューションごとのページに書かれているので、使用するディストリビューションに応じた公式のドキュメントを参照する必要があります。このページで紹介されているディストリビューションは、Ubuntu と同じように 5年間の長期サポートをしている、最新の Jazzy Jalisco(以下、Jazzy)と、最初に長期サポートを行った Humble Hawksbill(以下、Humble)でした。現状、長期サポートしているのは、この 2つのディストリビューションのようです。
今回は、Humble を使っていこうと思います。Humble が対応している Ubuntu のバージョンは、22.04 のみであり、現在、私の PC の Virtual Box に入ってる Ubuntu が 22.04 だからです。ちなみに、Jazzy の方は、Ubuntu のバージョンは、24.04 のみ対応しています。ちなみに、書籍(改訂新版 ROS 2ではじめよう 次世代ロボットプログラミング〜ロボットアプリケーション開発のための基礎から実践まで)では、Jazzy のインストール方法が解説されています。
Humble のドキュメントは以下です。
参考文献の書籍(改訂新版 ROS 2ではじめよう 次世代ロボットプログラミング〜ロボットアプリケーション開発のための基礎から実践まで)のサポートサイトです。
この書籍のサンプルコードは、以下の GitHub で公開されています。
また、前回 使用した、書籍のサンプルコードのもとにもなっている、公式のサンプルコードは、以下です。
それでは、やっていきます。
ROS2のアーキテクチャ
ROS2 のアーキテクチャについて、書籍 改訂新版 ROS 2ではじめよう 次世代ロボットプログラミング〜ロボットアプリケーション開発のための基礎から実践まで で書かれていた図が分かりやすかったので、それをもとに以下の図を作成しました。

この図を参考にソースコードを見ていきます(確認した内容を図に追記しました)。
サンプルコードが参照しているROS2のソースコード
Python のインタラクティブモードで調べてみます。
Python が参照できるライブラリに、ROS2 のパスが追加されています。
$ python3 Python 3.10.12 (main, Feb 4 2025, 14:57:36) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> sys.path ['', '/opt/ros/humble/lib/python3.10/site-packages', '/opt/ros/humble/local/lib/python3.10/dist-packages', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/home/daisuke/.local/lib/python3.10/site-packages', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']
/opt/ros/humble/lib/ を見ると、前回実装した publisher_member_function が既に含まれていたことが分かりました。ROS2 のインストールで入ったのだと思います。実際に動かしてみると、前回確認した内容と同じ結果でした。
$ ll /opt/ros/humble/lib/examples_rclpy_minimal_publisher/ 合計 56K drwxr-xr-x 2 root root 4.0K 5月 31 22:25 ./ drwxr-xr-x 76 root root 36K 5月 31 22:26 ../ -rwxr-xr-x 1 root root 1.1K 4月 30 03:56 publisher_local_function* -rwxr-xr-x 1 root root 1.1K 4月 30 03:56 publisher_member_function* -rwxr-xr-x 1 root root 1.1K 4月 30 03:56 publisher_old_school* $ cd svn_/ros2/ros2_ws/ $ source install/setup.bash $ ros2 run examples_rclpy_minimal_publisher publisher_member_function [INFO] [1749366199.345828166] [minimal_publisher]: Publishing: "Hello World: 0" [INFO] [1749366199.771954251] [minimal_publisher]: Publishing: "Hello World: 1" [INFO] [1749366200.269907259] [minimal_publisher]: Publishing: "Hello World: 2" [INFO] [1749366200.769315738] [minimal_publisher]: Publishing: "Hello World: 3" [INFO] [1749366201.292444344] [minimal_publisher]: Publishing: "Hello World: 4" ^CTraceback (most recent call last): File "/opt/ros/humble/lib/examples_rclpy_minimal_publisher/publisher_member_function", line 33, in <module> sys.exit(load_entry_point('examples-rclpy-minimal-publisher==0.15.3', 'console_scripts', 'publisher_member_function')()) File "/opt/ros/humble/lib/python3.10/site-packages/examples_rclpy_minimal_publisher/publisher_member_function.py", line 43, in main rclpy.spin(minimal_publisher) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/__init__.py", line 226, in spin executor.spin_once() File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 751, in spin_once self._spin_once_impl(timeout_sec) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 740, in _spin_once_impl handler, entity, node = self.wait_for_ready_callbacks(timeout_sec=timeout_sec) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 723, in wait_for_ready_callbacks return next(self._cb_iter) File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 620, in _wait_for_ready_callbacks wait_set.wait(timeout_nsec) KeyboardInterrupt [ros2run]: Interrupt
次は、前回実装したライブラリのソースコードを探します。from rclpy.node import Node というようにインポートしてるので、rclpy というディレクトリに、node.py というファイルがあり、Node というクラスが定義されているはずです(たぶん)。
rclpy
Nodeクラス
実際に、パスが通ってるところで、検索してみます。4件ヒットして、4件目が探してるファイルだと思います。4件目のファイルを開いてみると、Nodeクラスが定義されているだけのファイルでしたが、約2000行ありました。このファイルで間違いなさそうです。このファイルは、上の図(ROS2アーキテクチャ)の rclpy のブロックに該当すると思います。
$ find /opt/ros/humble/ -name 'node.py' /opt/ros/humble/lib/python3.10/site-packages/launch_ros/actions/node.py /opt/ros/humble/lib/python3.10/site-packages/ros2node/command/node.py /opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/lifecycle/node.py /opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/node.py
2000行を見るのも大変なので、node.py の中を def で検索すると、81件ヒットしました。その中で、前回実装したときに使用した「create_publisherメソッド」を見て、作成されたインスタンスの publishメソッドを見ていきたいと思います。
以下が、create_publisherメソッドです。
全部理解するのは難しいですが、Publisherクラスのインスタンスを作って、それを返しているようです。
def create_publisher( self, msg_type, topic: str, qos_profile: Union[QoSProfile, int], *, callback_group: Optional[CallbackGroup] = None, event_callbacks: Optional[PublisherEventCallbacks] = None, qos_overriding_options: Optional[QoSOverridingOptions] = None, publisher_class: Type[Publisher] = Publisher, ) -> Publisher: """ Create a new publisher. :param msg_type: The type of ROS messages the publisher will publish. :param topic: The name of the topic the publisher will publish to. :param qos_profile: A QoSProfile or a history depth to apply to the publisher. In the case that a history depth is provided, the QoS history is set to KEEP_LAST, the QoS history depth is set to the value of the parameter, and all other QoS settings are set to their default values. :param callback_group: The callback group for the publisher's event handlers. If ``None``, then the node's default callback group is used. :param event_callbacks: User-defined callbacks for middleware events. :return: The new publisher. """ qos_profile = self._validate_qos_or_depth_parameter(qos_profile) callback_group = callback_group or self.default_callback_group failed = False try: final_topic = self.resolve_topic_name(topic) except RuntimeError: # if it's name validation error, raise a more appropriate exception. try: self._validate_topic_or_service_name(topic) except InvalidTopicNameException as ex: raise ex from None # else reraise the previous exception raise if qos_overriding_options is None: qos_overriding_options = QoSOverridingOptions([]) _declare_qos_parameters( Publisher, self, final_topic, qos_profile, qos_overriding_options) # this line imports the typesupport for the message module if not already done failed = False check_is_valid_msg_type(msg_type) try: with self.handle: publisher_object = _rclpy.Publisher( self.handle, msg_type, topic, qos_profile.get_c_qos_profile()) except ValueError: failed = True if failed: self._validate_topic_or_service_name(topic) try: publisher = publisher_class( publisher_object, msg_type, topic, qos_profile, event_callbacks=event_callbacks or PublisherEventCallbacks(), callback_group=callback_group) except Exception: publisher_object.destroy_when_not_in_use() raise self._publishers.append(publisher) self._wake_executor() for event_callback in publisher.event_handlers: self.add_waitable(event_callback) return publisher
Publisherクラス
node.py と同じディレクトリの rclpyディレクトリに publisher.py があり、そこに Publisherクラスが定義されていました。
publisher.py は、120行ぐらいのファイルでした。Publisherクラスの publishメソッドに注目します。__publisher というオブジェクトの publishメソッドを実行しています。これをたどっていくと、同じ rclpyディレクトリの implディレクトリの implementation_singleton.py だということが分かります。
def publish(self, msg: Union[MsgType, bytes]) -> None: """ Send a message to the topic for the publisher. :param msg: The ROS message to publish. :raises: TypeError if the type of the passed message isn't an instance of the provided type when the publisher was constructed. """ with self.handle: if isinstance(msg, self.msg_type): self.__publisher.publish(msg) elif isinstance(msg, bytes): self.__publisher.publish_raw(msg) else: raise TypeError('Expected {}, got {}'.format(self.msg_type, type(msg)))
implementation_singleton.py
implementation_singleton.py は、とても小さいファイルです。以下のように 3行だけでした。ChatGPT によると、これは、クライアントライブラリの rcl を呼び出している _rclpy_pybind11.so を読んでいるとのことです。
from rpyutils import import_c_library package = 'rclpy' rclpy_implementation = import_c_library('._rclpy_pybind11', package)
rcl
また、ChatGPT に、さらに聞いてみると、rcl のソースは以下にあるようです。
rcl_publish()
対象のソースコードは、rcl/src/rcl/publisher.c にありました。おそらく、publishメソッドの変換先は、以下の rcl_publish() のようです。最終的には、rmw の rmw_publish関数を呼び出しています。
rcl_ret_t rcl_publish( const rcl_publisher_t * publisher, const void * ros_message, rmw_publisher_allocation_t * allocation) { RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_PUBLISHER_INVALID); RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR); if (!rcl_publisher_is_valid(publisher)) { return RCL_RET_PUBLISHER_INVALID; // error already set } RCL_CHECK_ARGUMENT_FOR_NULL(ros_message, RCL_RET_INVALID_ARGUMENT); TRACEPOINT(rcl_publish, (const void *)publisher, (const void *)ros_message); if (rmw_publish(publisher->impl->rmw_handle, ros_message, allocation) != RMW_RET_OK) { RCL_SET_ERROR_MSG(rmw_get_error_string().str); return RCL_RET_ERROR; } return RCL_RET_OK; }
rmw
rmw のソースコードは、同じく ros2 のリポジトリにありました。
rmw_publish()
rmw_publish関数は、ヘッダファイルに API だけが書かれていました。ChatGPT によると、使用している RMW の実装によるとのことです。
DDS
次に、DDS について調べます。DDS(Data Distribution Service)とは、ROS2 で作られたのではなく、既に、世の中で使われている通信のミドルウェアです。ROS2 では、DDS を採用することにより、ROS2 で新たに実装する量を削減したのだと思います。
ROS2 では、使用する DDS の実装を選ぶことができます。もちろん、自分で実装することも出来ます。では、現在使われている DDS は何なのかを調べてみます。
ちょっと出力されるログが多いので、最後のところだけ貼ります。
RMW MIDDLEWARE のところを見ると、rmw_fastrtps_cpp と出力されています。これは、通称 Fast DDS と呼ばれているもので、ROS2 のデフォルトで使われる DDS であり、現在使っている DDS ということになります。
$ ros2 doctor --report PLATFORM INFORMATION system : Linux platform info : Linux-6.8.0-57-generic-x86_64-with-glibc2.35 release : 6.8.0-57-generic processor : x86_64 QOS COMPATIBILITY LIST compatibility status : No publisher/subscriber pairs found RMW MIDDLEWARE middleware name : rmw_fastrtps_cpp ROS 2 INFORMATION distribution name : humble distribution type : ros2 distribution status : active release platforms : {'rhel': ['8'], 'ubuntu': ['jammy']} TOPIC LIST topic : none publisher count : 0 subscriber count : 0
使用する DDS を変更する 1つの方法としては、環境変数の RMW_IMPLEMENTATION に使用したい DDS を指定することです。とはいえ、特に今は困ってないので、デフォルトの DDS である Fast DDS(rmw_fastrtps_cpp)を使っていきます。
今回は以上になります。
おわりに
今回は、ROS2 のソースコードを見ていきました。なんとなく、全体像は見えた気がしました。今後は、ROS2 で強化されたセキュリティについて、情報収集していきたいと思います。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。