
创建纯 Python 包(生成 setup.py 结构):
ros2 pkg create –build-type ament_python 包名
创建 C++/接口包(生成 CMakeLists.txt 结构):
ros2 pkg create –build-type ament_cmake 包名
1. Service 的作用
ROS2 中,Service 适合处理“一次请求,一次响应”的任务。
/robot/set_mode 的本质是:
客户端请求机器人切换模式,服务端判断是否合法,然后返回成功或失败。
它不像 Topic 那样持续发布数据,而是更像一次函数调用。
2. 自定义 Service 接口
文件位置:robot_interfaces/srv/SetMode.srv
string mode
—
bool success
string message
|
字段 |
所属部分 |
作用 |
|
mode |
Request |
客户端请求切换的模式 |
|
success |
Response |
服务端返回是否成功 |
|
message |
Response |
服务端返回说明信息 |
这里的 — 用来分隔请求部分和响应部分。
3. 接口包 package.xml 配置
robot_interfaces 是专门放自定义接口的包。
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
|
配置 |
含义 |
|
rosidl_default_generators |
编译时生成接口代码 |
|
rosidl_default_runtime |
运行时使用生成好的接口 |
|
rosidl_interface_packages |
声明这是一个接口包 |
4. 接口包 CMakeLists.txt 配置
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"srv/SetMode.srv"
)
这段代码告诉 ROS2:
请把 SetMode.srv 生成为 Python/C++ 可以调用的 Service 接口。
5. 功能包依赖接口包
在 robot_base/package.xml 中加入:
<depend>robot_interfaces</depend>
含义是:
robot_base 需要使用 robot_interfaces 中定义的自定义 Service。
没有这个依赖,robot_base 就无法导入 SetMode。
6. 服务节点入口注册
在 robot_base/setup.py 中注册节点:
entry_points={
'console_scripts': [
'mode_service_node = robot_base.mode_service_node:main',
],
},
这表示之后可以通过 ROS2 运行 mode_service_node 这个节点。
7. /robot/set_mode 核心代码
文件位置:robot_base/robot_base/mode_service_node.py
import rclpy
from rclpy.node import Node
from robot_interfaces.srv import SetMode
class ModeServiceNode(Node):
def __init__(self):
super().__init__('mode_service_node')
self.current_mode = 'stop'
self.valid_modes = {'stand', 'walk', 'stop', 'estop'}
self.declare_parameter('max_linear_speed', 0.3)
self.declare_parameter('max_angular_speed', 0.8)
self.declare_parameter('safe_distance', 0.5)
self.service = self.create_service(
SetMode,
'/robot/set_mode',
self.handle_set_mode
)
self.get_logger().info('/robot/set_mode service is ready')
def handle_set_mode(self, request, response):
mode = request.mode
if mode not in self.valid_modes:
response.success = False
response.message = f'Invalid mode: {mode}'
self.get_logger().warn(response.message)
return response
self.current_mode = mode
response.success = True
response.message = f'Successfully switched to {mode} mode'
self.get_logger().info(response.message)
return response
def main(args=None):
rclpy.init(args=args)
node = ModeServiceNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
8. 代码逻辑理解
这段节点代码可以拆成四层:
|
层级 |
作用 |
|
初始化节点 |
创建 mode_service_node |
|
声明参数 |
配置最大速度和安全距离 |
|
创建 Service |
暴露 /robot/set_mode |
|
处理请求 |
判断模式是否合法并返回结果 |
核心判断逻辑是:
if mode not in self.valid_modes:
response.success = False
response.message = f'Invalid mode: {mode}'
return response
这表示:
如果客户端传入的模式不在允许列表中,就拒绝切换。
9. 支持的机器人模式
self.valid_modes = {'stand', 'walk', 'stop', 'estop'}
|
模式 |
含义 |
|
stand |
站立或准备状态 |
|
walk |
行走状态 |
|
stop |
普通停止 |
|
estop |
紧急停止 |
其中 estop 是 emergency stop,表示紧急停止。真实机器人中,它的安全优先级通常最高。
10. 参数代码理解
self.declare_parameter('max_linear_speed', 0.3)
self.declare_parameter('max_angular_speed', 0.8)
self.declare_parameter('safe_distance', 0.5)
这三个参数分别表示:
|
参数 |
作用 |
|
max_linear_speed |
限制最大前进速度 |
|
max_angular_speed |
限制最大旋转速度 |
|
safe_distance |
设置安全距离阈值 |
这些值做成参数的好处是:
后续调试不同机器人或不同场地时,不需要修改代码,只需要调整参数。
11. 验证方式
|
验证目标 |
使用内容 |
|
查看接口定义 |
ros2 interface show robot_interfaces/srv/SetMode |
|
启动服务节点 |
ros2 run robot_base mode_service_node |
|
查看服务列表 |
ros2 service list |
|
调用合法模式 |
ros2 service call /robot/set_mode robot_interfaces/srv/SetMode "{mode: 'walk'}" |
|
调用非法模式 |
ros2 service call /robot/set_mode robot_interfaces/srv/SetMode "{mode: 'run'}" |
|
查看参数 |
ros2 param list |
|
获取参数值 |
ros2 param get /mode_service_node max_linear_speed |
12. Launch 文件的作用
ROS2 的 launch 文件可以理解为“节点启动脚本”。
当项目只有一个节点时,可以手动启动;但当系统里有多个节点,比如模式切换、状态发布、速度控制、视觉感知、FSM 状态机时,一个个开终端会很麻烦。
所以 launch 主要解决:
|
问题 |
作用 |
|
多节点启动麻烦 |
一条命令统一启动 |
|
参数配置分散 |
启动时统一传入参数 |
|
Demo 展示不方便 |
面试时可以快速启动系统 |
Launch 负责“怎么启动系统”,节点代码负责“系统启动后做什么”。
13. 当前 launch 文件代码
文件位置:
src/robot_base/launch/mode_service_launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
mode_service_node = Node(
package='robot_base',
executable='mode_service_node',
name='mode_service_node',
output='screen',
parameters=[
{
'max_linear_speed': 0.3,
'max_angular_speed': 0.8,
'safe_distance': 0.5,
}
]
)
return LaunchDescription([
mode_service_node
])
这段代码的核心作用是:启动 robot_base 包中的 mode_service_node 节点,并传入运行参数。
|
配置 |
含义 |
|
package='robot_base' |
节点来自 robot_base 包 |
|
executable='mode_service_node' |
启动注册过的节点入口 |
|
name='mode_service_node' |
设置节点名称 |
|
output='screen' |
日志输出到终端 |
|
parameters=[…] |
启动时传入 ROS2 参数 |
14. setup.py 中安装 launch 文件
为了让 ROS2 能通过 ros2 launch 找到 launch 文件,需要在 setup.py 中安装它。
需要在文件顶部加入:
import os
from glob import glob
然后在 data_files 中加入:
(
os.path.join('share', package_name, 'launch'),
glob('launch/*.py')
),
把 launch 目录下的 .py 文件安装到 ROS2 包的共享目录中。
如果没有这一步,可能会出现 launch 文件找不到的问题。
15. 验证结果
常用验证命令:
|
目的 |
命令 |
|
启动 launch |
ros2 launch robot_base mode_service_launch.py |
|
查看节点 |
ros2 node list |
|
查看服务 |
ros2 service list |
|
查看参数 |
ros2 param get /mode_service_node safe_distance |
|
调用服务 |
ros2 service call /robot/set_mode robot_interfaces/srv/SetMode "{mode: 'walk'}" |




