📝 【ROS2入門⑤】カスタムインターフェース定義 - 独自の.msg/.srv/.actionファイルをつくる
シリーズ この記事は「ROS2入門シリーズ」の一部です。 シリーズ目次を見る
Publisher/Subscriber を実装済みの状態から、カスタム .msg/.srv/.action を定義し rosidl 生成型を import するまでを示します。正本は Custom ROS 2 interfaces です。2026年5月時点、Ubuntu 22.04 + ROS 2 Humble を想定しています。
なぜカスタムインターフェースが必要なのか?
ROS 2 には std_msgs/String や geometry_msgs/Twist など標準インターフェースがあります。Interface packages で一覧を確認し、上書きできる型がないか先に確かめてから .msg を書くと手戻りが減ります。
- 複数フィールドをまとめたい: robot_id, battery_level, error_code をひとつのメッセージに集約
- サービスのRequest/Responseを自分で設計したい
- アクションのGoal / Result / Feedbackの内容を自由に定義したい
標準だけでは載せきれないロボット固有情報は、プロジェクト専用のインターフェース用パッケージにまとめると、後からノードを増やしても依存を追いやすくなることがあります。
ROS2 のインターフェース種類とは?.msg/.srv/.action
| 種類 | 拡張子 | 用途 | 通信パターン |
|---|---|---|---|
| Message | .msg | トピックのデータ構造 | 一方向(Pub→Sub) |
| Service | .srv | Request/Response定義 | 同期に近い Request/Response が中心(実装側はディストリで非同期APIも) |
| Action | .action | Goal/Result/Feedback定義 | 長時間処理と Feedback を扱いやすい |
インターフェース専用パッケージの作成手順
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake my_interfaces
mkdir -p ~/ros2_ws/src/my_interfaces/msg
mkdir -p ~/ros2_ws/src/my_interfaces/srv
mkdir -p ~/ros2_ws/src/my_interfaces/action 作成後のディレクトリ構成:
my_interfaces/
├── msg/
│ └── RobotStatus.msg
├── srv/
│ └── SetSpeed.srv
├── action/
│ └── MoveTo.action
├── CMakeLists.txt
└── package.xml .msg ファイルでカスタムメッセージを定義する方法
# msg/RobotStatus.msg
string robot_id # ロボットの識別子
float32 battery_level # バッテリー残量(%)
bool is_moving # 移動中か否か
int32 error_code # エラーコード(0=正常) # 以降はコメントです。フィールドは 型名 フィールド名 の順で記述します。
使用できる型
| 型 | 説明 | 使用例 |
|---|---|---|
| bool | 真偽値 | is_moving, is_ready |
| int8/16/32/64 | 符号付き整数 | error_code, count |
| uint8/16/32/64 | 符号なし整数 | sequence_id, flags |
| float32/64 | 浮動小数点数 | battery_level, speed |
| string | 文字列 | robot_id, status_msg |
| float32[] | 可変長配列 | distances, scan_data |
| float32[3] | 固定長配列 | position, rgb_color |
.srv ファイルでカスタムサービスを定義する方法
# srv/SetSpeed.srv
float32 linear_speed # 直進速度 [m/s]
float32 angular_speed # 旋回速度 [rad/s]
---
bool success # 成功/失敗
string message # ステータスメッセージ ---(3本のハイフン)で Request と Response を区切ります。上がリクエスト、下がレスポンスです。
.action ファイルでカスタムアクションを定義する方法
# action/MoveTo.action
# ─── Goal(目標指示) ───
float32 target_x
float32 target_y
float32 tolerance # 到達判定の許容誤差 [m]
---
# ─── Result(最終結果) ───
bool success
float32 elapsed_time # 所要時間 [s]
---
# ─── Feedback(途中経過) ───
float32 current_x
float32 current_y
float32 remaining_distance アクションファイルは --- が 2箇所 あり、Goal / Result / Feedback の3セクションに分かれます。
CMakeLists.txt と package.xml の設定方法
CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(my_interfaces)
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/RobotStatus.msg"
"srv/SetSpeed.srv"
"action/MoveTo.action"
)
ament_package() package.xml
<?xml version="1.0"?>
<package format="3">
<name>my_interfaces</name>
<version>0.0.1</version>
<description>Custom ROS2 interfaces</description>
<maintainer email="[email protected]">Your Name</maintainer>
<license>Apache-2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
</package> インターフェースパッケージのビルドと確認手順
cd ~/ros2_ws
colcon build --packages-select my_interfaces
source install/setup.bash 生成されたインターフェースを確認します。
# インターフェース一覧
ros2 interface list | grep my_interfaces
# my_interfaces/msg/RobotStatus
# my_interfaces/srv/SetSpeed
# my_interfaces/action/MoveTo
# 詳細確認
ros2 interface show my_interfaces/msg/RobotStatus
# デフォルト値を確認
ros2 interface proto my_interfaces/msg/RobotStatus
# robot_id: ''
# battery_level: 0.0
# is_moving: false
# error_code: 0 Python ノードでカスタムインターフェースを使う方法
利用側パッケージのpackage.xmlに依存を追加
<depend>my_interfaces</depend> カスタムメッセージのPublisher
# status_publisher.py
import rclpy
from rclpy.node import Node
from my_interfaces.msg import RobotStatus # カスタムメッセージをインポート
class StatusPublisher(Node):
def __init__(self):
super().__init__('status_publisher')
self.pub = self.create_publisher(RobotStatus, 'robot_status', 10)
self.timer = self.create_timer(1.0, self.publish_status)
def publish_status(self):
msg = RobotStatus()
msg.robot_id = 'robot_001'
msg.battery_level = 85.5
msg.is_moving = True
msg.error_code = 0
self.pub.publish(msg)
self.get_logger().info(f'Battery: {msg.battery_level:.1f}%')
def main(args=None):
rclpy.init(args=args)
rclpy.spin(StatusPublisher())
rclpy.shutdown() カスタムサービスのサーバーノード
# speed_service.py
import rclpy
from rclpy.node import Node
from my_interfaces.srv import SetSpeed # カスタムサービスをインポート
class SpeedServer(Node):
def __init__(self):
super().__init__('speed_server')
self.srv = self.create_service(
SetSpeed, 'set_speed', self.callback)
def callback(self, request, response):
if abs(request.linear_speed) > 1.0:
response.success = False
response.message = 'linear_speed must be <= 1.0 m/s'
return response
self.get_logger().info(
f'Speed set: linear={request.linear_speed} angular={request.angular_speed}')
response.success = True
response.message = 'OK'
return response
def main(args=None):
rclpy.init(args=args)
rclpy.spin(SpeedServer())
rclpy.shutdown()
カスタムインターフェース定義の注意点
カスタムインターフェースを使うノードをビルドする前に、必ず my_interfaces を先にビルドして source してください。順序を間違えると ImportError になります。
colcon build --packages-select my_interfaces
source install/setup.bash
colcon build --packages-select my_robot_node .msg / .srv / .action のファイル名は RobotStatus.msg のように UpperCamelCase で命名します。スネークケース(robot_status.msg)は慣習に反するため避けてください。
検証環境とつまずきやすい点
- my_interfaces をビルド → source install/setup.bash → 利用側パッケージ の順。逆順だと生成物が無く ImportError に落ちやすい
- フィールド名や型を変えたら再利用側との齟齬防止のためインターフェース側のビルドをやり直すのが無難
標準インターフェースを確認する CLI コマンド
独自定義の前に `ros2 interface list` / `ros2 interface show` で既存型を確認してください。
# インターフェースパッケージ一覧
ros2 interface packages
# よく使うパッケージと型
# std_msgs → String, Float32, Int32, Bool, Header
# geometry_msgs → Twist, Pose, Point, Quaternion, Vector3
# sensor_msgs → Image, LaserScan, Imu, JointState
# nav_msgs → Odometry, Path, OccupancyGrid
# 詳細確認
ros2 interface show geometry_msgs/msg/Twist カスタムインターフェース定義のまとめ
- .msg はトピック用データ構造、区切り不要
- .srv はRequest/Responseを --- で1箇所区切り
- .action はGoal/Result/Feedbackを --- で2箇所区切り
- ament_cmake パッケージに集約し rosidl_generate_interfaces() でビルド
- 利用側の package.xml に <depend>my_interfaces</depend> を追加
- 次回(シリーズ⑥)では、サービス/アクションを Python で繋ぐ流れへ進む想定です