前言
本系统的系统是Ubuntu20.04的ros-noetic,本文讲述了通过创建 ROS 节点(服务器和客户端)的完整流程,使用 Action 实现从 1 到 N 的累加和,并在计算过程中向客户端发送进度反馈。
整体概述
- 创建功能包:用来包含 Action 文件及其相关依赖。
- 定义 Action 文件:在.action文件中定义目标、结果和反馈。
- 修改配置文件:编辑CMakeLists.txt 和package.xml 以包含 Action 文件和依赖。
- 编写服务端和客户端:服务端实现累加逻辑并发布进度反馈,客户端发送请求并处理响应。
- 运行:通过 ROS Master、服务端和客户端来测试整个流程
过程
1. 创建功能包
首先我们需要在catkin_ws/src目录下创建一个新的功能包,命名为demo_action,它将包含Action相关的代码和文件。如果没有catkin_ws的文件的话,就自己创建一下吧。
mkdir -p catkin_ws/src
catkin_make
cd ~/catkin_ws/src
catkin_create_pkg demo_action actionlib actionlib_msgs roscpp rospy std_msgs
2.创建Action文件
cd demo_action
mkdir action
在action目录下创建名为AddInts.action的文件:
touch AddInts.action
打开AddInts.action文件,添加以下内容:
#目标值
int32 num
---
#最终结果
int32 result
---
#连续反馈
float64 progress_bar
这个文件中定义了请求的目标值num,最终的响应结果result,以及来纳许反馈的进度progress_bar。
3.编辑配置文件
修改CMakeLists.txt,在功能包的根目录下,编辑CMakeLists.txt文件。
cd ~/catkin_ws/src/demo_action
sudo nano CMakeLists.txt
找到find_package部分,添加actionlib和actionlib_msgs,修改完如下:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
actionlib
actionlib_msgs
)
再在下面添加add_action_files,指向.action文件。
add_action_files(
FILES
AddInts.action
)
生成消息和动作文件。
generate_messages(
DEPENDENCIES
std_msgs
actionlib_msgs
)
在catkin_pachage部分声明依赖。
catkin_package(
CATKIN_DEPENDS roscpp rospy std_msgs actionlib actionlib_msgs
)
添加完如下图。
4.修改package.xml文件
在package.xml中,添加以下依赖项声明。
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
添加完如下图。
5.案例测试
现在使用一个简单的测试程序来测试一下是否能够正常使用。
在demo_action/src目录中创建add_ints_server.py和add_ints_client.py文件:
cd ~/catkin_ws/src/demo_action/src
touch add_ints_server.py
sudo nano add_ints_server.py
将server的代码复制粘贴进去:
#!/usr/bin/env python
import rospy
import actionlib
from demo_action.msg import AddIntsAction, AddIntsResult, AddIntsFeedback
class AddIntsActionServer:
def __init__(self):
self.server = actionlib.SimpleActionServer('add_ints', AddIntsAction, self.execute, False)
self.server.start()
def execute(self, goal):
result = AddIntsResult()
feedback = AddIntsFeedback()
total_sum = 0
for i in range(1, goal.num + 1):
total_sum += i
feedback.progress_bar = float(i) / goal.num
self.server.publish_feedback(feedback)
rospy.sleep(0.5) # Simulate a delay
result.result = total_sum
self.server.set_succeeded(result)
if __name__ == '__main__':
rospy.init_node('add_ints_server')
server = AddIntsActionServer()
rospy.spin()
然后是client的代码。
#!/usr/bin/env python
import rospy
import actionlib
from demo_action.msg import AddIntsAction, AddIntsGoal
def feedback_cb(feedback):
rospy.loginfo('Progress: %f' % feedback.progress_bar)
def add_ints_client():
client = actionlib.SimpleActionClient('add_ints', AddIntsAction)
client.wait_for_server()
goal = AddIntsGoal(num=10)
client.send_goal(goal, feedback_cb=feedback_cb)
client.wait_for_result()
return client.get_result()
if __name__ == '__main__':
rospy.init_node('add_ints_client')
result = add_ints_client()
rospy.loginfo('Result: %d' % result.result)
要确保这两个文件可执行:
chmod +x add_ints_server.py add_ints_client.py
返回catkin_ws根目录,编译功能包:
cd ~/catkin_ws
catkin_make
然后启动。
source devel/setup.bash
启动Action服务器和客户端。
roscore
启动 Action 服务器:
rosrun demo_action add_ints_server.py
启动 Action 客户端:
rosrun demo_action add_ints_client.py
这样就可以测试服务器和客户端是否能正常通信了。
6. 实际测试
过程和上述的是一样的,我在这里就直接放入代码了。
server.py
#! /usr/bin/env python
import rospy
import actionlib
from demo01_action.msg import *
"""
需求:
创建两个ROS 节点,服务器和客户端,
客户端可以向服务器发送目标数据N(一个整型数据)服务器会计算 1 到 N 之间所有整数的和,
这是一个循环累加的过程,返回给客户端,这是基于请求响应模式的,
又已知服务器从接收到请求到产生响应是一个耗时操作,每累加一次耗时0.1s,
为了良好的用户体验,需要服务器在计算过程中,
每累加一次,就给客户端响应一次百分比格式的执行进度,使用 action实现。
流程:
1.导包
2.初始化 ROS 节点
3.使用类封装,然后创建对象
4.创建服务器对象
5.处理请求数据产生响应结果,中间还要连续反馈
6.spin
"""
class MyActionServer:
def __init__(self):
#SimpleActionServer(name, ActionSpec, execute_cb=None, auto_start=True)
self.server = actionlib.SimpleActionServer("addInts",AddIntsAction,self.cb,False)
self.server.start()
rospy.loginfo("服务端启动")
def cb(self,goal):
rospy.loginfo("服务端处理请求:")
#1.解析目标值
num = goal.num
#2.循环累加,连续反馈
rate = rospy.Rate(10)
sum = 0
for i in range(1,num + 1):
# 累加
sum = sum + i
# 计算进度并连续反馈
feedBack = i / num
rospy.loginfo("当前进度:%.2f",feedBack)
feedBack_obj = AddIntsFeedback()
feedBack_obj.progress_bar = feedBack
self.server.publish_feedback(feedBack_obj)
rate.sleep()
#3.响应最终结果
result = AddIntsResult()
result.result = sum
self.server.set_succeeded(result)
rospy.loginfo("响应结果:%d",sum)
if __name__ == "__main__":
rospy.init_node("action_server_p")
server = MyActionServer()
rospy.spin()
client.py
#! /usr/bin/env python
import rospy
import actionlib
from demo01_action.msg import *
"""
需求:
创建两个ROS 节点,服务器和客户端,
客户端可以向服务器发送目标数据N(一个整型数据)服务器会计算 1 到 N 之间所有整数的和,
这是一个循环累加的过程,返回给客户端,这是基于请求响应模式的,
又已知服务器从接收到请求到产生响应是一个耗时操作,每累加一次耗时0.1s,
为了良好的用户体验,需要服务器在计算过程中,
每累加一次,就给客户端响应一次百分比格式的执行进度,使用 action实现。
流程:
1.导包
2.初始化 ROS 节点
3.创建 action Client 对象
4.等待服务
5.组织目标对象并发送
6.编写回调, 激活、连续反馈、最终响应
7.spin
"""
def done_cb(state,result):
if state == actionlib.GoalStatus.SUCCEEDED:
rospy.loginfo("响应结果:%d",result.result)
def active_cb():
rospy.loginfo("服务被激活....")
def fb_cb(fb):
rospy.loginfo("当前进度:%.2f",fb.progress_bar)
if __name__ == "__main__":
# 2.初始化 ROS 节点
rospy.init_node("action_client_p")
# 3.创建 action Client 对象
client = actionlib.SimpleActionClient("addInts",AddIntsAction)
# 4.等待服务
client.wait_for_server()
# 5.组织目标对象并发送
goal_obj = AddIntsGoal()
goal_obj.num = 10
client.send_goal(goal_obj,done_cb,active_cb,fb_cb)
# 6.编写回调, 激活、连续反馈、最终响应
# 7.spin
rospy.spin()
如果运行成功的话,就会如下图的显示。
结语
在本文中,创建了一个基于 ROS 的 Action 通信示例,包括一个服务器和一个客户端。服务器能够接收来自客户端的整数输入,并计算从 1 到 N 的和。服务器在计算过程中实时反馈执行进度,客户端则展示了这一进度和最终结果。
在以后中,可以在此基础上扩展功能,进一步探索 ROS 的其他特性,比如消息过滤、服务调用等,提升机器人系统的智能和灵活性。