一.话题消息自定义以及使用
1.话题基本概念
发布者(Publisher):发布者是一个ROS节点,负责将特定类型的消息发布到指定的话题(topic)上,将数 据封装为消息然后发布到话题上
订阅者(Subscriber):订阅者也是一个ROS节点,用于订阅某个话题上的消息,一但有消息发布到话题 上,订阅者就会接收处理这些消息
话题(topic):话题是消息传输的通道,它是发布者和订阅者消息传递的通道,发布者将消息发布到话题 上,订阅者则从对应的话题上接收消息
消息(message):定义了任意的数据类型和字段,可以包含任意数量和类型的数据,通常以.msg的形式定 义,并且可以根据需要自行定义
2.有关话题命令行
rostopic list #列出当前系统中的所有主题。
rostopic info <topic-name> #查看特定主题的详细信息,包括消息类型、发布者和订阅者等。
rostopic echo <topic-name> #显示特定主题上发布的消息内容。
rostopic pub <topic-name> <message-type> <arguments> #手动发布消息到指定的主题上。
rostopic hz <topic-name> #显示特定主题的消息发布频率。
3.自定义话题消息
(1)定义msg文件,以Person.msg文件为例,其中包含消息的数据类型
在工作空间catkin_ws/src/learning_topic下创建msg文件夹,在其中创建Person.msg文件,内容为
string name
uint8 sex
uint8 age
uint8 unknown = 0
uint8 male = 1
uint8 female = 2
(2)在package.xml中添加功能包依赖
<build_depend>message_generation</build_depend> #编译依赖
<exec_depend>message_runtime</exec_depend> #执行依赖
(3)在CMakeList.txt中添加编译选项
在find_package( …… )中添加message_generation
在find_package( …… )之后添加以下两行代码
add_message_files(FILES Person.msg)
generate_messages(DEPENDENCIES std_msgs)
在catkin_package(…… )中添加依赖包message_runtime
完成后返回工作目录编译catkin_make,随后可在devel/include/learning_topic中看到.h头文件
4.创建发布者和订阅者
可在外部编译器编写完成后拷入~/catkin_ws/src/learning_topic/src中
(1)发布者(代码取自www.guyue.com)
/***********************************************************************
Copyright 2020 GuYueHome (www.guyuehome.com).
***********************************************************************/
/**
* 该例程将发布/person_info话题,自定义消息类型learning_topic::Person
*/
#include <ros/ros.h>
#include "learning_topic/Person.h"
int main(int argc, char **argv)
{
// ROS节点初始化
ros::init(argc, argv, "person_publisher");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10
ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);
// 设置循环的频率
ros::Rate loop_rate(1);
int count = 0;
while (ros::ok())
{
// 初始化learning_topic::Person类型的消息
learning_topic::Person person_msg;
person_msg.name = "Tom";
person_msg.age = 18;
person_msg.sex = learning_topic::Person::male;
// 发布消息
person_info_pub.publish(person_msg);
ROS_INFO("Publish Person Info: name:%s age:%d sex:%d",
person_msg.name.c_str(), person_msg.age, person_msg.sex);
// 按照循环频率延时
loop_rate.sleep();
}
return 0;
}
(2)订阅者(代码取自www.guyuehome.com)
/***********************************************************************
Copyright 2020 GuYueHome (www.guyuehome.com).
***********************************************************************/
/**
* 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
*/
#include <ros/ros.h>
#include "learning_topic/Person.h"
// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
// 将接收到的消息打印出来
ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d",
msg->name.c_str(), msg->age, msg->sex);
}
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "person_subscriber");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);
// 循环等待回调函数
ros::spin();
return 0;
}
5.配置CMakeList.txt中的编译规则
在CMakeList.txt末尾添加
add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp) #添加依赖库
add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp) #添加依赖库
6.运行及结果
roscore #第一个终端运行
rosrun learning_topic person_publisher #第二个终端运行
rosrun learning_topic person_subscriber #第三个终端运行
二.服务数据自定义及使用
1.服务基本概念
服务端(Server):服务端是一个ROS节点,负责提供特定服务的实现。它定义了一组输入参数和输出结果,并且当有客户端请求时,会执行相应的功能并返回结果。
客户端(Client):客户端也是一个ROS节点,用于向服务端发送服务请求。它指定所需的输入参数,然后等待服务端的响应并接收返回的结果
服务(Service):服务是一个具有特定功能的通信接口。它由一个请求消息和一个响应消息组成,客户端通过发送请求消息到服务端,然后服务端处理请求并返回响应消息。
服务类型(Service Type):服务类型定义了请求消息和响应消息的数据结构。它通常以.srv文件的形式定义,其中包含请求消息和响应消息的字段类型和名称
2.有关服务命令行
rosservice list #列出当前系统中的所有服务。
rosservice info <service-name> #显示特定服务的详细信息,包括服务类型、服务端节点、客户端节点等。
rosservice call <service-name> <arguments> #向特定服务发送请求并接收响应结果。
rosservice type <service-name> #显示特定服务的服务类型。
3.自定义服务数据
(1)定义srv文件,以AddTwoInts.srv为例
在工作空间catkin_ws/src/my_package下创建srv文件夹,在其中创建AddTwoInts.srv文件,内容为
int64 a
int64 b
---
int64 sum
(2)在package.xml中添加功能包依赖
<build_depend>message_generation</build_depend> 编译依赖
<exec_depend>message_runtime</exec_depend> 执行依赖
(3)在CMakeList.txt中添加编译选项
在find_package( …… )中添加message_generation
在find_package( …… )之后添加以下两行代码
add_service_files(FILES AddTwoInts.srv)
generate_messages(DEPENDENCIES std_msgs)
在catkin_package(…… )中添加依赖包message_runtime
完成后返回工作目录编译catkin_make,随后可在devel/include/my_package中看到.h头文件
4.创建客户端和服务端
可在外部编译器编写完成后拷入~/catkin_ws/src/my_package/src中
(1)客户端
#include "ros/ros.h"
#include "my_package/AddTwoInts.h"
int main(int argc, char** argv)
{
ros::init(argc, argv, "add_two_ints_client");
ros::NodeHandle nh;
ros::ServiceClient client = nh.serviceClient<my_package::AddTwoInts>("add_two_ints");
my_package::AddTwoInts srv;
srv.request.a = 3;
srv.request.b = 9;
if (client.call(srv))
{
ROS_INFO("计算结果:%ld", (long int)srv.response.sum);
}
else
{
ROS_ERROR("无法调用服务");
return 1;
}
return 0;
}
(2)服务端
#include "ros/ros.h"
#include "my_package/AddTwoInts.h"
bool add(my_package::AddTwoInts::Request& req, my_package::AddTwoInts::Response& res)
{
int64_t sum = req.a + req.b;
res.sum = sum;
ROS_INFO("收到客户端请求:a = %ld, b = %ld", (long int)req.a, (long int)req.b);
ROS_INFO("计算结果:%ld", (long int)sum);
return true;
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "add_two_ints_server");
ros::NodeHandle nh;
ros::ServiceServer service = nh.advertiseService("add_two_ints", add);
ROS_INFO("等待客户端请求...");
ros::spin();
return 0;
}
5.配置CMakeList.txt中的编译规则
在CMakeList.txt中添加
add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server ${PROJECT_NAME}_gencpp)
add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client ${PROJECT_NAME}_gencpp)
6.运行及结果
roscore #第一个终端运行
rosrun my_package add_two_ints_client #第二个终端运行
rosrun My_package add_two-ints_server #第三个终端运行
三.动作自定义及使用
1.动作基本概念
行为服务器(Action Server):行为服务器是一个ROS节点,负责执行特定任务,并接收来自行为客户端的请求。行为服务器定义了任务的目标、反馈和结果,根据客户端的请求执行相应的操作,并返回反馈和最终结果。
行为客户端(Action Client):行为客户端也是一个ROS节点,用于向行为服务器发送请求并接收任务的反馈和结果。行为客户端指定所需的目标,并通过与行为服务器进行交互来监视任务的进度和获取执行结果。
动作(Action):动作是一个任务或操作的抽象表示,包含了目标、反馈和结果。它由一个目标消息、一个反馈消息和一个结果消息组成,行为客户端发送目标消息到行为服务器,然后行为服务器根据任务的执行情况生成反馈消息,并将最终结果返回给行为客户端。
动作类型(Action Type):动作类型定义了动作的目标、反馈和结果消息的数据结构。动作类型通常以.action文件的形式定义,其中包含目标、反馈和结果消息的字段类型和名称。
2.有关动作的命令行
rosaction list #列出当前系统中的所有动作。
rosaction info <action-name> #显示特定动作的详细信息,包括动作类型、行为服务器节点、行为客户端节点等。
rosaction send_goal <action-name> <arguments> #向特定动作发送目标请求。
rosaction cancel_goal <action-name> <goal-id> #取消特定动作的目标请求。
rosaction show <action-name> #显示特定动作的动作类型。
3.自定义action文件
(1)定义action文件,以checkaction.action为例
在工作空间catkin_ws/src/homework_pkg下创建action文件夹,在其中创建checkaction.action文件,内容为
int32 requestnumber
---
int32 resultnumber
---
int32 feedbacknumber
(2)在package.xml中添加功能包依赖,添加完成后应该有以下内容
<buildtool_depend>catkin</buildtool_depend>
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<build_depend>roscpp</build_depend>
<build_export_depend>actionlib</build_export_depend>
<build_export_depend>actionlib_msgs</build_export_depend>
<build_export_depend>roscpp</build_export_depend>
<build_depend>message_generation</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>message_runtime</exec_depend>
(3)在CMakeList.txt中添加编译选项
find_package(...)中添加 actionlib 和 actionlib_msgs 项
找到add_action_files(...),在其中添加自定义的 .action 文件到 FILES 中
找到generate_messages(...),在其中添加消息类型 actionlib_msgs 到 DEPENDENCIES中
catkin_package在exec执行的依赖项 CATKIN_DEPENDS 后添加 actionlib 和 actionlib_msgs
添加结果应该如下
find_package(catkin REQUIRED COMPONENTS
actionlib_msgs
roscpp
actionlib
)
add_action_files(
FILES
demo.action
)
generate_messages(
DEPENDENCIES
actionlib_msgs
)
4.代码
(1)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;
void execute(const homework_pkg::checkactionGoalConstPtr &goal, Server *as)
{
ros::Rate r(1);
homework_pkg::checkactionFeedback feedback;
ROS_INFO("开始检测 %d 个零件.", goal->requestnumber);
for (i = 1; i <= 40; i++)
{
feedback.feedbacknumber = i;
as->publishFeedback(feedback);
r.sleep();
}
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;
}
(2)checkaction_client.cpp
#include <actionlib/client/simple_action_client.h>
#include "homework_pkg/checkactionAction.h"
typedef actionlib::SimpleActionClient<homework_pkg::checkactionAction> Client;
void doneCb(const actionlib::SimpleClientGoalState& state,
const homework_pkg::checkactionResultConstPtr& result)
{
ROS_INFO("检测完成!");
ros::shutdown();
}
void activeCb()
{
ROS_INFO("开始检测了");
}
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("服务启动, 发送目标");
homework_pkg::checkactionGoal goal;
goal.requestnumber = 40;
client.sendGoal(goal, &doneCb, &activeCb, &feedbackCb);
ros::spin();
return 0;
}
5.配置CMakeList.txt中的编译规则
在CMakeList.txt中添加
add_executable(DoDishes_server src / DoDishes_server.cpp)
target_link_libraries(DoDishes_server ${ catkin_LIBRARIES })
add_dependencies(DoDishes_server ${ ${PROJECT_NAME}_EXPORTED_TARGETS })
add_executable(DoDishes_client src / DoDishes_client.cpp)
target_link_libraries(DoDishes_client ${ catkin_LIBRARIES })
add_dependencies(DoDishes_client ${ ${PROJECT_NAME}_EXPORTED_TARGETS })
6.运行及结果
roscore #第一个终端运行
rosrun learning_topic person_publisher #第二个终端运行
rosrun learning_topic person_subscriber #第三个终端运行