一.话题消息自定义和使用
以一个例题为例演示
使用 ROS 话题(Topic) 机制实现消息发布与订阅
要求:编写代码实现 ROS 中消息的发布与订阅: 创建一个发布者,每隔 100ms 依次发送斐波拉契数列的数字到话题/fibonacci 中;创建一个订阅者,订阅该话题,输出订阅结果。如,订阅者依次输出: 1 1 2 3 5 8 ··
1.如何自定义话题消息
·定义msg文件
#发送到话题内的数字
int32 number
在工作空间homeworkws/src内的homeworkpkg功能包内创建文件夹msg,并在msg内创建文件number.msg文件,在把上面的文件内容复制进文件。
·在package.xml中添加动态生成message的功能包依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

·在CMakeLists.txt添加编译选项
1.在findpacksge(…..)结尾添加messagegeneration
2.在写有关于message内容下面添加
add_message_files(
FILES
number1.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
·在catkinpackage(….)结尾添加messageruntime
·回到工作空间的根目录下编译生成相应的头文件
cd homework_ws
catkin_make
2.创建发布者和订阅者代码实现(C++)
在工作空间homeworkws/src内的homeworkpkg功能包src内创建文件编写代码
发布者
cd homework_ws/src/homework_pkg/src
touch number_publisher.cpp
vim number_publisher.cpp
i(进入编辑模式)
写代码
esc键
:wq!(强制保存退出)
具体代码
#include<ros/ros.h>
#include"homework_pkg/number1.h"
int f(int count) {
if (count == 1 || count == 2) {
return 1;
}
else {
return f(count - 2) + f(count - 1);
}
}
int main(int argc, char** argv) {
setlocale(LC_ALL,"");
//ROS节点初始化
ros::init(argc, argv, "number_publisher");
//创建节点句柄
ros::NodeHandle n;
//创建一个publisher,发布名为/fibonacci的topic,消息类型为std_msgs::Int32,队>列长度为10
ros::Publisher fibonacci_pub = n.advertise<homework_pkg::number1>("/fibonacci", 100);
//设置循环频率
ros::Rate loop_rate(10);
int count = 1;
while (ros::ok()) {
//初始化geometry_sgs::Twist类型消息
homework_pkg::number1 msg;
msg.number = f(count);
//发布消息
fibonacci_pub.publish(msg);
ROS_INFO("%d",msg.number);
//按循环频率延时
loop_rate.sleep();
count++;
}
return 0;
}
订阅者
touch number_subscriber.cpp
vim number_subscriber.cpp
i(进入编辑模式)
写代码
esc键
:wq!(强制保存退出)
具体代码
#include<ros/ros.h>
#include"homework_pkg/number1.h"
void poseCallback(const homework_pkg::number1::ConstPtr& msg)
{
//将接受到的消息打印出来
ROS_INFO("%d", msg->number);
}
int main(int argc, char** argv)
{
//初始化ROS节点
ros::init(argc, argv, "number_subscriber");
//创建节点句柄
ros::NodeHandle n;
//创建一个Subscriber,订阅名为/fibonacci的topic,注册回调函数poseCallback
ros::Subscriber number_sub = n.subscribe("/fibonacci", 1000, poseCallback);
//循环等待回调函数
ros::spin();
return 0;
}
3.编译并运行
编译规则设置
编译前先要在功能包homework_pkg内的CMakeList.txt内配置编译规则
·设置需要编译的代码和生成的可执行文件
·设置链接库
add_executable(number_publisher src/number_publisher.cpp)
target_link_libraries(number_publisher ${catkin_LIBRARIES})
add_executable(number_subscriber src/number_subscriber.cpp)
target_link_libraries(number_subscriber ${catkin_LIBRARIES})
addexecutable(numberpublisher src/numberpublisher.cpp),src/numberpublisher.cpp是指的前面编写代码的文件,numberpublisher是设置src/numberpublisher.cpp编译后形成的可执行文件的名字
targetlinklibraries(numberpublisher ${catkinLIBRARIES})是将可执行文件与对应的链接库链接。
将这两句话写在homework_pkg功能包内的CMakeList.txt内的有关build的内容的最下面
回到homework_ws工作空间根目录下编译
cd homework_ws
catkin_make
运行
开三个终端,分别执行命令
roscore
rosrun homework_pkg number_publisher
rosrun homework_pkg number_subscriber
效果

二.服务数据自定义与使用
以一个例题为例演示
使用 ROS 服务(Service)机制实现同步请求与答复
要求:编写代码实现 ROS 中的服务请求与答复: 创建服务端,注册 Service,当服务端收到客户端 Service 请求(携带整型参数 a.b) 后,服务端返回 a.b 的和给客户端,客户端输出结果。如,客户端给服务端 Service 发送参数 3,9,服务端返回 12,客户端输出: 12。
1.如何自定义服务数据
·定义srv文件
#客户端请求时发送的两个数据
int32 num1
int32 num2
---
#服务端响应时发送的两数据
int32 sum
在工作空间homeworkws/src内的homeworkpkg功能包内创建文件夹srv,并在srv内创建文件number.srv文件,在把上面的文件内容复制进文件。
·在package.xml中添加动态生成message的功能包依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

·在CMakeLists.txt添加编译选项
1.在findpacksge(…..)结尾添加messagegeneration
2.在写有关于service内容下面添加
add_service_files(
FILES
number.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)
·在catkinpackage(….)结尾添加messageruntime
·回到工作空间的根目录下编译生成相应的头文件
cd homework_ws
catkin_make
2.创建服务端和客户端代码实现(C++)
在工作空间homeworkws/src内的homeworkpkg功能包src内创建文件编写代码
客户端
cd homework_ws/src/homework_pkg/src
touch number_client.cpp
vim number_client.cpp
i(进入编辑模式)
写代码
esc键
:wq!(强制保存退出)
具体代码
#include "ros/ros.h"
#include "homework_pkg/number.h"
int main(int argc, char **argv)
{
// 初始化 ROS 节点
ros::init(argc, argv, "xiaomei");
// 创建 ROS 句柄
ros::NodeHandle n;
// 创建 客户端 对象
ros::ServiceClient client = n.serviceClient<homework_pkg::number>("number");
// 提交请求并处理响应
homework_pkg::number a;
//提交请求
a.request.num1 = 3;
a.request.num2 = 9;
//处理响应
bool flag = client.call(a);
if (flag)
{
ROS_INFO(" 响应成功!两个数的和为:%d", a.response.sum);
}
else
{
ROS_INFO("响应失败");
}
// ros::spin()循环等待回调函数
ros::spin();
ret
服务端
touch number_server.cpp
vim number_server.cpp
i(进入编辑模式)
写代码
esc键
:wq!(强制保存退出)
具体代码
#include "ros/ros.h"
#include "homework_pkg/number.h"
// 回调函数:bool 返回值由于标志是否处理成功
bool Callback(homework_pkg::number::Request& req,
homework_pkg::number::Response& resp)
{
//1处理请求
int num1 = req.num1;
int num2 = req.num2;
ROS_INFO("服务器接收到的请求数据为:num1 = %d, num2 = %d", num1, num2);
//2组织响应
int sum = num1 + num2;
resp.sum = sum;
ROS_INFO("两个数的和:sum = %d", sum);
return true;
}
int main(int argc, char **argv)
{
// 初始化 ROS 节点
ros::init(argc, argv, "number_Server");
// 创建 ROS 句柄
ros::NodeHandle n;
// 创建 服务 对象
ros::ServiceServer server = n.advertiseService("number", Callback);
ROS_INFO("服务启动....");
// 回调函数处理请求并产生响应
// 由于请求有多个,需要调用 ros::spin()
3.编译并运行
编译规则设置
编译前先要在功能包homework_pkg内的CMakeList.txt内配置编译规则
·设置需要编译的代码和生成的可执行文件
·设置链接库
add_executable(number_client src/number_client.cpp)
target_link_libraries(number_client ${catkin_LIBRARIES})
add_executable(number_server src/number_server.cpp)
target_link_libraries(number_server ${catkin_LIBRARIES})
addexecutable(numberclient src/numberclient.cpp),src/numberclient.cpp是指的前面编写代码的文件,numberclient是设置src/numberclient.cpp编译后形成的可执行文件的名字
targetlinklibraries(numberclient ${catkinLIBRARIES})是将可执行文件与对应的链接库链接。
将这两句话写在homework_pkg功能包内的CMakeList.txt内的有关build的内容的最下面
回到homework_ws工作空间根目录下编译
cd homework_ws
catkin_make
运行
开三个终端,分别执行命令
roscore
rosrun homework_pkg number_client
rosrun homework_pkg number_server
效果

三.动作编程自定义
以一个例题演示
使用 ROS 动作(Action)机制实现目标请求、进度与完成结果的反馈。
要求: 编写代码实现 ROS 中动作请求与进度反馈,创建服务端,注册 Action,客户端发送action 请求检测 40 个零件, 服务端接收后,每隔 1s 测一个零件 (每检测一个打印一次),:实时给客户端返回检测进度
(客户端打印进度百分比),并在检测完毕时告知客户端目标完成。如,服务端实时打印: 检测 1个零件 检测2 个零件 …检测 40 个零件 检测完成客户端实时打印: 2.5% 5% … 100% 检测完成
1.自定义动作消息
·定义action文件
cd homework_ws/src/homework_pkg
mkdir action
cd action
touch checkaction.action
int32 requestnumber
---
int32 resultnumber
---
int32 feedbacknumber
·在package.xml中添加功能包依赖
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
·在CMakeLists.txt中添加编译选项
findpackage(catkin REQUIRED actionlibmsgs actionlib)
addactionfiles(DIRECTORY action FILES checkaction.action)
generatemessages(DEPENDENCIES actionlibmsgs)(这最后一句是把内容追加到之前话题服务部分已经添加了的语句后面。)之后再回到工作空间根目录下编译。
2.实现动作服务器(C++)
初始化ROS节点
创建动作服务器实例
启动服务器,等待动作请求
在回调函数中完成动作服务功能的处理,并反馈进度信息
动作完成,发送结束信息
在src下创建代码文件checkactionserver.cpp和checkactionclient.cpp
具体代码
checkaction_server.cpp
#include "ros/ros.h"
#include "actionlib/server/simple_action_server.h"
#include "homework_pkg/checkactionAction.h"
typedef actionlib::SimpleActionServer<homework_pkg::checkactionAction> Server;
// 收到action的goal后调用该回调函数
void execute(const homework_pkg::checkactionGoalConstPtr &goal, Server *as)
{
ros::Rate r(1);
homework_pkg::checkactionFeedback feedback;
ROS_INFO("开始检测 %d 个零件.", goal->requestnumber);
// 假设检测的进度,并且按照1Hz的频率发布进度feedback
for (int i = 1; i <= 40; i++)
{
feedback.feedbacknumber = i;
as->publishFeedback(feedback);
r.sleep();
}
// 当action完成后,向客户端返回结果
ROS_INFO("已经检测完 %d 个零件", goal->requestnumber);
as->setSucceeded();
}
int main(int argc, char** argv)
{
setlocale(LC_ALL, "");
ros::init(argc, argv, "check_server");
ros::NodeHandle hNode;
// 定义一个服务器
Server server(hNode, "check", boost::bind(&execute, _1, &server), false);
// 服务器开始运行
server.start();
ros::spin();
return 0;
}
checkaction_client.cpp
#include <actionlib/client/simple_action_client.h>
#include "homework_pkg/checkactionAction.h"
typedef actionlib::SimpleActionClient<homework_pkg::checkactionAction> Client;
// 当action完成后会调用该回调函数一次
void doneCb(const actionlib::SimpleClientGoalState& state,
const homework_pkg::checkactionResultConstPtr& result)
{
ROS_INFO("检测完成!");
ros::shutdown();
}
// 当action激活后会调用该回调函数一次
void activeCb()
{
ROS_INFO("开始检测了");
}
// 收到feedback后调用该回调函数
void feedbackCb(const homework_pkg::checkactionFeedbackConstPtr& feedback)
{
double t = feedback->feedbacknumber;
double n = t / 40;
ROS_INFO(" 检测进度 : %.2f%% ", n*100);
}
int main(int argc, char** argv)
{
setlocale(LC_ALL, "");
ros::init(argc, argv, "check_client");
// 定义一个客户端
Client client("check", true);
// 等待服务器端
ROS_INFO("等待服务器响应.");
client.waitForServer();
ROS_INFO("服务启动, 发送目标");
// 创建一个action的goal
homework_pkg::checkactionGoal goal;
goal.requestnumber = 40;
// 发送action的goal给服务器端,并且设置回调函数
client.sendGoal(goal, &doneCb, &activeCb, &feedbackCb);
ros::spin();
return 0;
}
3.编译并运行
编译规则设置
add_executable(checkaction_server src / checkaction_server.cpp)
target_link_libraries(checkaction_server ${ catkin_LIBRARIES })
add_dependencies(checkaction_server ${ ${PROJECT_NAME}_EXPORTED_TARGETS })
add_executable(checkaction_client src / checkaction_client.cpp)
target_link_libraries(checkaction_client ${ catkin_LIBRARIES })
add_dependencies(checkaction_client ${ ${PROJECT_NAME}_EXPORTED_TARGETS })
编译
cd homework_ws
catkin_make
运行
roscore
rosrun homework_pkg checkaction_server
rosrun homework_pkg checkaction_client
效果
