服务通信的目的
1, 前面我们讲了什么是消息通信和服务通信, 这两个通信机制, 其实已经可以满足大部分的使用场景需求, 那为什么要新增一个动作编程机制呢? 比如有这样一个场景, 比如加热一杯水, 我可以设置这杯水的最终加热温度, 同时, 我需要在加热过程中, 每隔一段时间, 知道现在加热到多少度了. 我也可以随时取消加热, 当加热完成后, 我也需要知道这杯水的最终加热温度, 一般来说最终温度和设置的目标温度是不想等的. 那么ROS为了方便这个场景, 新增了一个action通信机制.
2, 动作通信,可以用最简单的一张图来表示:
Goal: 目标任务
Cancel: 取消指令
State: 任务状态
Result: 最终执行结果
FeedBack: 每隔一段时间反馈任务数据.
代码实现
创建自定义动作
1, 在src同级目录, 创建action文件夹.
2, 在action文件夹中新建heatWater.action件.并粘贴以下代码
#目标水温
float32 tempTarget #目标
---
#最终水温
float32 tempFinal #最终结果
---
#当前水温
float32 tempNow #中间过程
3, 修改CMakeLists.txt文件.
find_package处添加:
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
message_generation #自定义消息类型依赖 自定义重点
actionlib_msgs #动作编程重点
actionlib #动作编程重点
)
add_action_files处添加:
add_action_files(
FILES
heatWater.action
)
generate_messages处添加:
generate_messages(
DEPENDENCIES
std_msgs #自定义重点
actionlib_msgs #动作重点
)
catkin_package处添加:
catkin_package(
INCLUDE_DIRS include
LIBRARIES ros_test
CATKIN_DEPENDS roscpp std_msgs message_runtime #自定义重点
DEPENDS system_lib
)
4, 在package.xml的<-package>节点中添加, 用于自动生成动作.
<build_depend>actionlib</build_depend>
<exec_depend>actionlib</exec_depend>
<!--自定义动作类型,功能包依赖-->
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib_msgs</exec_depend>
5, 在vscode的终端中使用catkin_make
,生成heatWater动作.生成完成后, 可以在devel/include/ros_test
文件夹下, 查看生成的heatWaterAction.h
, heatWaterActionFeedback.h
, heatWaterActionGoal.h
, heatWaterActionResult.h
, heatWaterFeedback.h
, heatWaterGoal.h
, heatWaterResult.h
.
创建Server和Client
1, 在src文件夹下创建, heatWaterClient.cpp
和heatWaterServer.cpp
两个文件.
2, 在heatWaterClient.cpp
(client)文件中粘贴以下代码:
#include <actionlib/client/simple_action_client.h>
#include "ros_test/heatWaterAction.h"
typedef actionlib::SimpleActionClient<ros_test::heatWaterAction> Client;
int main(int argc, char** argv)
{
setlocale(LC_ALL, "");
// ROS节点初始化
ros::init(argc, argv, "heatWaterClient");
//定义一个动作的客户端
Client client("heatWater");
//等待烧水服务
ROS_INFO("等待烧水服务...");
client.waitForServer();
ROS_INFO("烧水服务已启用...");
ros_test::heatWaterGoal goal;
goal.tempTarget = 100;
client.sendGoal(goal,[](const actionlib::SimpleClientGoalState& state,
const ros_test::heatWaterResultConstPtr& result)->void{ //done
ROS_INFO("水烧开了,开水温度[%f]",result->tempFinal);
}
,
[]()->void{ //activte
ROS_INFO("开始烧水...");
}
,
[](const ros_test::heatWaterFeedbackConstPtr &feedback)->void{ //feedback
ROS_INFO("当前水温[%f]℃",feedback->tempNow);
}
);
ros::spin();
return 0;
}
3, 在heatWaterServer.cpp
(server)文件中粘贴以下代码:
#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "ros_test/heatWaterAction.h"
typedef actionlib::SimpleActionServer<ros_test::heatWaterAction> Server;
void execute(const ros_test::heatWaterGoalConstPtr& goal, Server* server)
{
ros::Rate r(10000);
ros_test::heatWaterFeedback feedback;
ros_test::heatWaterResult result;
ROS_INFO("目标水温[%f]℃.", goal->tempTarget);
float TTemp = goal->tempTarget;
float NTemp = 0;
float deltaTemp = 5.5;
// 假设烧水的温度增长为线性
while(1)
{
sleep(1);
NTemp += deltaTemp;
feedback.tempNow = NTemp;
if(NTemp > TTemp)
{
result.tempFinal = NTemp;
break;
}
server->publishFeedback(feedback);
}
// 当action完成后,向客户端返回结果
ROS_INFO("水烧开了..");
server->setSucceeded(result);
}
int main(int argc, char** argv)
{
setlocale(LC_ALL, "");
ros::init(argc, argv, "heatWaterServer");
ros::NodeHandle n;
// 定义一个服务器
Server server(n, "heatWater", boost::bind(&execute, _1, &server), false);
// 服务器开始运行
server.start();
ros::spin();
return 0;
}
4, 具体释义, 请查看注释.
5, 修改CMakeLists.txt文件,(末尾添加)
add_executable(heatWaterClient src/heatWaterClient.cpp)
target_link_libraries(heatWaterClient ${catkin_LIBRARIES})
add_dependencies(heatWaterClient ${PROJECT_NAME}_gen_cpp) #包含自定义消息类型 自定义重点
add_executable(heatWaterServer src/heatWaterServer.cpp)
target_link_libraries(heatWaterServer ${catkin_LIBRARIES})
add_dependencies(heatWaterServer ${PROJECT_NAME}_gen_cpp) #包含自定义消息类型 自定义重点
编译和运行
在vscode的终端中使用catkin_make
,编译整个工程, 使用快捷键Ctrl + Shift + T , 弹出系统终端, 启动roscore, 运行heatWaterClient和heatWaterServer节点, 测试结果:
获取源码
公-众-号搜索 ”机器人小站"。后台回复 190918R即可.