跨机器调用 ROS 服务时,服务端和客户端可以放在不同 package,甚至不同机器上运行;但 .srv 类型定义应来自同一个共享接口包。不要在服务端包和客户端包里各自复制一份同名 .srv,否则后续字段变更、生成代码和类型一致性都很难维护。
下面示例拆成三个包:
robot_interfaces:只定义 AddTwoInts.srv
robot_server_package:服务端节点
robot_client_package:客户端节点
一、创建共享 srv 包
1 2 3
| cd ~/catkin_ws/src catkin_create_pkg robot_interfaces message_generation mkdir -p robot_interfaces/srv
|
创建 robot_interfaces/srv/AddTwoInts.srv:
1 2 3 4
| int64 a int64 b --- int64 sum
|
robot_interfaces/CMakeLists.txt 关键配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| find_package(catkin REQUIRED COMPONENTS message_generation )
add_service_files( FILES AddTwoInts.srv )
generate_messages()
catkin_package( CATKIN_DEPENDS message_runtime )
|
robot_interfaces/package.xml 至少包含:
1 2 3
| <buildtool_depend>catkin</buildtool_depend> <build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>
|
编译后,服务端机器和客户端机器都要能找到这个接口包。如果两台机器各自有工作空间,需要保证两边的 robot_interfaces 内容一致,并分别执行 catkin_make 和 source devel/setup.bash。
二、服务端包
1 2 3
| cd ~/catkin_ws/src catkin_create_pkg robot_server_package rospy robot_interfaces mkdir -p robot_server_package/scripts
|
robot_server_package/CMakeLists.txt 关键配置:
1 2 3 4 5 6 7 8
| find_package(catkin REQUIRED COMPONENTS rospy robot_interfaces )
catkin_package( CATKIN_DEPENDS rospy robot_interfaces )
|
创建 robot_server_package/scripts/add_two_ints_server.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
import rospy from robot_interfaces.srv import AddTwoInts, AddTwoIntsResponse
def handle_add_two_ints(req): rospy.loginfo("Returning [%s + %s = %s]", req.a, req.b, req.a + req.b) return AddTwoIntsResponse(req.a + req.b)
def main(): rospy.init_node("add_two_ints_server") rospy.Service("add_two_ints", AddTwoInts, handle_add_two_ints) rospy.loginfo("Ready to add two ints.") rospy.spin()
if __name__ == "__main__": main()
|
三、客户端包
1 2 3
| cd ~/catkin_ws/src catkin_create_pkg robot_client_package rospy robot_interfaces mkdir -p robot_client_package/scripts
|
robot_client_package/CMakeLists.txt 关键配置:
1 2 3 4 5 6 7 8
| find_package(catkin REQUIRED COMPONENTS rospy robot_interfaces )
catkin_package( CATKIN_DEPENDS rospy robot_interfaces )
|
创建 robot_client_package/scripts/add_two_ints_client.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
import sys import rospy from robot_interfaces.srv import AddTwoInts
def add_two_ints_client(x, y): rospy.wait_for_service("add_two_ints") try: add_two_ints = rospy.ServiceProxy("add_two_ints", AddTwoInts) resp = add_two_ints(x, y) return resp.sum except rospy.ServiceException as exc: rospy.logerr("Service call failed: %s", exc) raise
if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: %s [x y]" % sys.argv[0]) sys.exit(1)
x = int(sys.argv[1]) y = int(sys.argv[2]) print("Requesting %s + %s" % (x, y)) print("%s + %s = %s" % (x, y, add_two_ints_client(x, y)))
|
四、跨机器环境变量
假设服务端机器 IP 是 192.168.1.2,客户端机器 IP 是 192.168.1.3,并且 ROS master 运行在服务端。
服务端:
1 2
| export ROS_MASTER_URI=http://192.168.1.2:11311 export ROS_IP=192.168.1.2
|
客户端:
1 2
| export ROS_MASTER_URI=http://192.168.1.2:11311 export ROS_IP=192.168.1.3
|
ROS_IP 应写本机可被对方访问的 IP 地址。如果要使用主机名,则配置 /etc/hosts 或 DNS 后使用 ROS_HOSTNAME,不要把主机名写进 ROS_IP。
五、运行服务和客户端
两台机器都先编译并加载各自工作空间:
1 2 3
| cd ~/catkin_ws catkin_make source devel/setup.bash
|
服务端:
1 2 3
| roscore chmod +x ~/catkin_ws/src/robot_server_package/scripts/add_two_ints_server.py rosrun robot_server_package add_two_ints_server.py
|
客户端:
1 2
| chmod +x ~/catkin_ws/src/robot_client_package/scripts/add_two_ints_client.py rosrun robot_client_package add_two_ints_client.py 2 3
|
如果配置正确,服务端会打印请求处理日志,客户端会输出 2 + 3 = 5。跨机器调试时,优先检查两边是否互相能 ping 通、端口是否被防火墙拦截、ROS_MASTER_URI 是否一致,以及 robot_interfaces 是否在两边都已经编译并 source。