ROS话题、服务、动作基本操作

本文介绍了ROS中的消息传递机制,包括发布者和订阅者如何通过话题交换数据,如何自定义消息类型。此外,还详细阐述了服务的概念,包括服务端和客户端的交互,并展示了如何创建和使用自定义服务。最后,讨论了动作的概念,包括行为服务器和行为客户端,以及如何定义和使用自定义动作。
摘要由CSDN通过智能技术生成

一.话题消息自定义以及使用

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   #第三个终端运行


  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值