ROS 节点初始化步骤、topic/service创建及使用

目录

1、节点初始化步骤

2、service 创建及使用

3、topic创建及使用

4、框架总结


这是一个总结复盘的记录

1、节点初始化步骤

在 mian 函数中使用 ros::init 初始化节点,注册节点名,这里注册的节点名为 "test_node" :

#include <ros/ros.h>
int main(int argc, char ** argv)
{
    ros::init(argc, argv, "test_node"); //解析传入的ROS参数,定义节点名称
    ros::NodeHandle nh; // 创建句柄对象
    。。。
}

一个 cpp 文件只能注册一个 ROS 节点,但作为该节点的句柄却可以设置多个,不同的句柄通过不同的命名空间来区别。在这里节点句柄有两个作用:

  1. 第一个是在roscpp程序里管理内部节点的启动和关闭
  2. 第二个是它提供了一个额外的命名空间解析层,可以使编写子组件变得更容易。

节点句柄允许让我们自定义命名空间,命名空间分为 全局命名空间、局部命名空间、私有命名空间。理解命名空间有助于我们理解话题名(message)或者服务名(service)该如何调用。

假设创建的包名为 test,该包中的某个节点的上层命名空间为 <node_namespace> ,不过默认是没有这层命名空间的,除非你在代码中刻意设置了这个上层命名空间。为了说明问题,下面的例子中都会带上这层命名空间。

  • 对于局部变量,定义如下:
ros::NodeHandle nh1("namespace1");  // 局部命名空间:<node_namespace>/namespace1

这个句柄的完整命名为:<node_namespace>/namespace1 ,也就是 test/namespace1

  • 对于全局命名空间 即在名称前面加一个斜线 "/" ,定义如下:
ros::NodeHandle nh3("/namespace2");  // 全局命名空间:/namespace2

 其不受外部命名空间的影响,这个句柄的完整命名为:/namespace2

  • 对于私有命名空间 即在名称前面加一个波浪线 "~" ,定义如下:
ros::NodeHandle nh3("~namespace3");  // 私有命名空间:<node_namespace>/<node_name>/namespace4

其实际的命名为:<node_namespace>/<node_name>/namespace3

也就是:test/test_node/namespace3

这里注意 <node_namespace><node_name> 的区别,前面有说。私有名称就是节点私有,因此要加上节点名,其他的只需要通过包名直接定位就行了

  • 另外还可以设置具有父子关系的句柄
ros::NodeHandle nh2(nh1, "namespace4");  // 设置节点的父句柄:<node_namespace>/namespace1/namespace4

这个句柄的完整命名为:<node_namespace>/namespace1/namespace4  

也就是:test/namespace1/namespace4  

启动节点使用如下命令:

rosrun <package_name> <node_name>

对于本例包名为 test,节点名为 test_node,启动节点的命令就是:

rosrun test test_node

2、service 创建及使用

service 数据类型文件建立在 srv 文件夹中,后缀名为 .srv, 分为 request 与 response 两部分,用三条短横线 --- 隔开,假设我创建了名为 mySrvInfo.srv 的 service 数据。 这里以创建服务端(server)为例。

service 通过前面定义的句柄 nh 来创建,类型是 ros::ServiceServer。第一个参数是这个服务的名字,这个命名空间也是分局部全局私有的。第二个参数是回调函数的函数名。

ros::ServiceServer my_service = nh.advertiseService("/my_service", my_serviceCallback);

这里的设置是全局命名空间,调用时直接使用  /my_service 即可,前面不需要加其他命名空间。

其他的话一般的完整名称为:<node_namespace>/<node_name>/<nh_name>/<service_name>

具体的名称是具体定义时的设置而定,比如前面定义句柄是并没有给句柄命名,则忽略 <nh_name>,如果不是私有命名空间,则忽略<node_name>,其他类似,后面说的 topic 的命名也是一样的。

service是有回调函数的,service的回调函数有返回值,类型为 bool

bool my_serviceCallback(test::mySrvInfo::Request  & req,
                        test::mySrvInfo::Response & res)
{
    ...
    // you should return true or false
}

在定义 srv 文件时,req 和 res 部分会用 --- 隔开,程序在编译时会自动生成对应的结构体的头文件放在 devel/include/<package_name> 文件夹下,使用时 req 和 res 部分是要分开声明的,其命名空间为:

  • <package_name>::<srv_file_name>::Request 
  • <package_name>::<srv_file_name>::Respone

service 服务端可以通过其他的客服端节点进行调用,或者使用命令行的形式:

rosservice call /my_service "<req参数>"

3、topic创建及使用

topic 也是通过句柄 nh 创建的,topic 的消息类型文件建立在 msg 文件夹中,后缀名为 .msg,假设我创建了名为 myMsgInfo.msg 的 message 数据。这里记录下如何订阅(subscribe)消息的。

第一个参数是 topic 的名字,同样可以全局/局部/私有命名

第二个参数是一个消息接收队列,可以规定队列的长度,相当于数据缓冲的作用,如果接受数据大于缓冲队列的长度,则会丢弃最早缓冲的数据

第三个参数是回调函数的名字,函数名随便起,符合语法要求就行。

ros::Subscriber my_subscribe = nh.subscribe("/my_topic", 10, elevatorSetCallback);

topic 的回调函数没有返回值,传入的参数就是消息类型的结构体指针,回调函数就是对接收到的消息(由发布节点发布的)进行处理。

void myTopicCallback(const test::myMsgInfo::ConstPtr & msg)
{
    ...
}

程序在编译时会自动生成消息文件对应的结构体的头文件放在 devel/include/<package_name> 文件夹下,使用时调用消息类型对应的常量指针,其命名空间为:

  • <package_name>::<msg_file_name>::ConstPtr

比如这里就是 test::myMsgInfo::ConstPtr

topic 可以由其他的节点进行调用,或者使用命令行的形式,发布消息供节点订阅的命令为:

rostopic pub -1 /my_topic test/myMsgInfo "<msg参数>"

-1 代表发布一次,不加这个参数就是一直发布,后面跟 topic 名字,这里用的全局命名空间,就是 /my_topic,话题名后跟消息类型,具体为 <package_name>/<msg_name>,这里就是test/myMsgInfo,然后最后是具体的参数值,要跟msg文件里定义的参数类型相对应。

最后在代码后面加上 ros::spin(); 函数实现阻塞回调。

4、框架总结

// service 回调函数
bool my_serviceCallback(test::mySrvInfo::Request  & req,
                        test::mySrvInfo::Response & res)
{
    ...
    // you should return true or false
}
// topic订阅回调函数
void myTopicCallback(const test::myMsgInfo::ConstPtr & msg)
{
    ...
}

// 主函数
int main(int argc, char ** argv)
{
  // init node
  ros::init(argc, argv, "test_node");
  // nh init
  ros::NodeHandle nh;

  // srvice server
  ros::ServiceServer my_service = nh.advertiseService("/my_service", my_serviceCallback);

  // topic subscriber
  ros::Subscriber my_subscribe = nh.subscribe("/my_topic", 10, elevatorSetCallback);

  ROS_INFO("start");

  ros::spin();

  ROS_INFO("closed");
  return 0;
}

猜你喜欢

转载自blog.csdn.net/Flag_ing/article/details/126043624