2. 服务调用实现远程控制

任务说明

本案例实现一个最小可复现的ROS2服务调用远程控制闭环:服务端节点模拟机器人底盘,接收速度指令并更新内部状态;客户端节点通过服务请求发送线速度与角速度,实现远程控制。两个节点通过ROS2服务通信,不依赖真实硬件。

环境依赖

  • Ubuntu 22.04 + ROS2 Humble(或更高版本)
  • 已创建ROS2工作空间(如ros2_ws/src)

实现步骤

在ros2_ws/src下创建功能包robot_control,包内包含两个Python节点:robot_server.py(服务端)与robot_client.py(客户端)。服务端定义服务接口SetVelocity,客户端通过命令行参数发送目标速度。

关键完整代码

1. 服务接口定义(srv/SetVelocity.srv)

float32 linear_x
float32 angular_z
---
bool success
string message

2. 服务端节点(robot_server.py)

import rclpy
from rclpy.node import Node
from robot_control.srv import SetVelocity

class RobotServer(Node):
    def __init__(self):
        super().__init__('robot_server')
        self.srv = self.create_service(SetVelocity, 'set_velocity', self.callback)
        self.current_linear = 0.0
        self.current_angular = 0.0
        self.get_logger().info('服务端已启动,等待速度指令...')

    def callback(self, request, response):
        # 模拟底盘限速:线速度最大1.0 m/s,角速度最大2.0 rad/s
        if abs(request.linear_x) > 1.0 or abs(request.angular_z) > 2.0:
            response.success = False
            response.message = '速度超出限制(线速度≤1.0,角速度≤2.0)'
            return response

        self.current_linear = request.linear_x
        self.current_angular = request.angular_z
        response.success = True
        response.message = f'已更新速度:线速度={self.current_linear:.2f} m/s,角速度={self.current_angular:.2f} rad/s'
        self.get_logger().info(response.message)
        return response

def main(args=None):
    rclpy.init(args=args)
    node = RobotServer()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

3. 客户端节点(robot_client.py)

import sys
import rclpy
from rclpy.node import Node
from robot_control.srv import SetVelocity

class RobotClient(Node):
    def __init__(self):
        super().__init__('robot_client')
        self.cli = self.create_client(SetVelocity, 'set_velocity')
        while not self.cli.wait_for_service(timeout_sec=1.0):
            self.get_logger().info('等待服务端上线...')
        self.req = SetVelocity.Request()

    def send_request(self, linear_x, angular_z):
        self.req.linear_x = linear_x
        self.req.angular_z = angular_z
        future = self.cli.call_async(self.req)
        rclpy.spin_until_future_complete(self, future)
        return future.result()

def main(args=None):
    rclpy.init(args=args)
    if len(sys.argv) != 3:
        print('用法: ros2 run robot_control robot_client <线速度> <角速度>')
        print('示例: ros2 run robot_control robot_client 0.5 0.2')
        rclpy.shutdown()
        return

    node = RobotClient()
    try:
        linear = float(sys.argv[1])
        angular = float(sys.argv[2])
        response = node.send_request(linear, angular)
        if response.success:
            print(f'[成功] {response.message}')
        else:
            print(f'[失败] {response.message}')
    except ValueError:
        print('错误:速度参数必须为数字')
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()

编译与运行

# 在ros2_ws目录下
colcon build --packages-select robot_control
source install/setup.bash

# 终端1:启动服务端
ros2 run robot_control robot_server

# 终端2:启动客户端(发送速度指令)
ros2 run robot_control robot_client 0.5 0.2

验证方法与判定标准

  • 正常控制:客户端发送0.5 0.2,服务端日志显示“已更新速度:线速度=0.50 m/s,角速度=0.20 rad/s”,客户端输出[成功],即通过。
  • 超限保护:客户端发送2.0 0.0(线速度超限),服务端拒绝更新,客户端输出[失败] 速度超出限制,即通过。
  • 服务发现:先启动客户端再启动服务端,客户端等待约1秒后自动连接并成功发送请求,即通过。

预期输出(确定性结果)

当输入为0.5 0.2时,服务端日志与客户端输出严格为:

服务端: [INFO] 已更新速度:线速度=0.50 m/s,角速度=0.20 rad/s
客户端: [成功] 已更新速度:线速度=0.50 m/s,角速度=0.20 rad/s
客户端节点 robot_client.py 发送速度指令 (linear_x, angular_z) 例: 0.5, 0.2 服务端节点 robot_server.py 接收并校验速度 更新内部状态 限速: 线≤1.0, 角≤2.0 服务请求 set_velocity 服务响应 success + message 验证路径: 启动服务端 → 启动客户端并传参 → 观察服务端日志与客户端返回