ROS2学习笔记-话题

二、话题

1.概述

使用话题进行通信时,我们需要创建发布者订阅者以及消息接口

打开终端,输入

ros2 interface list

此时,就会显示出ros2中已经定义好的接口,简单起见,我们现在只使用其中标准消息接口下的的String

在这里插入图片描述

可以看到,String位于std_msgs包下,所以待会要导入这个包。

2. Python版本话题

2.1 创建发布者

(1)创建功能包

在src下,输入

ros2 pkg create topic_demo_py --build-type ament_python  --dependencies rclpy std_msgs --license Apache-2.0

创建这个功能包相比之前讲节点时,仅仅多加了一个std_msgs依赖,事实上,在创建功能包时,你可以不写依赖,不过为了项目结构更清晰,建议加上,如果忘记写了,之后也可以在package.xml文件里加上,如图

在这里插入图片描述

(2)编写代码

src/topic_demo_py/topic_demo_py/下创建py文件,然后输入代码

import rclpy
from rclpy.node import Node
from std_msgs.msg import String#导入消息接口
class PubNode(Node):#自定义一个节点
    def __init__(self,node_name):
        super().__init__(node_name)
        #创建一个定时器,用于定时发布消息,的一个参数是间隔时间,单位是秒
        #第二个参数是一个回调函数,用与周期性执行操作
        self.timer = self.create_timer(1,self.timer_callback)
        #创建发布者,第一个参数是信息接口,第二个参数是话题名字,第三个参数很久之后才会细讲,目前填个10先
        self.publisher = self.create_publisher(String,'py_topic',10)
    def timer_callback(self):
        msg = String()#创建消息接口
        msg.data = '你好'
        self.publisher.publish(msg)#发布消息
def main():
    rclpy.init()
    node = PubNode('xxx')
    rclpy.spin(node)
    rclpy.shutdown()        

这个节点会每隔一秒钟在这个话题中发一个你好

再附一张截图

在这里插入图片描述

2.2 创建订阅者

再创建一个py文件,输入代码

import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class SubNode(Node):
    def __init__(self,node_name):
        super().__init__(node_name)
        #创建订阅者,第一个参数是消息接口,第二个参数是话题名字,第三个参数是回调函数,
        #回调函数接收一个参数,即接收到的消息,第四个参数很久之后才会细讲,目前填个10先
        self.subscriber = self.create_subscription(String,'py_topic',self.sub_callback,10)

    def sub_callback(self,msg):
        self.get_logger().info(msg.data)#打印接收到的消息

def main():
    rclpy.init()
    node = SubNode('yyy')
    rclpy.spin(node)
    rclpy.shutdown()
2.3 运行

接下来,注册节点,然后编译,运行,这些在之前讲节点时就已经讲过,就不再赘述了,不过还是附一张截图。

在这里插入图片描述

我们打开两个终端,一个运行发布者节点,另一个运行订阅者节点,注意两个终端都应该source一下,把可执行文件临时加入环境变量,方便运行。结果如图。

在这里插入图片描述

确实是每隔一秒钟接收到一次消息。

3. C++版本话题

换个编程语言,思路也是完全一样的,仅仅语法不同罢了。

(1)创建功能包

命令行输入

ros2 pkg create topic_demo_cpp  --dependencies rclcpp std_msgs --license Apache-2.0

(2)编写代码

创建cpp文件,然后输入代码,创建发布者的节点。

#include <iostream>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std;
class PubNode:public rclcpp::Node{
private:
    //定义发布者
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher;
    //定时器,用于周期性向话题发布消息
    rclcpp::TimerBase::SharedPtr timer;
public:
    PubNode():Node("pubnode"){
        //创建发布者,泛型类型指定为string,方法第一个参数为话题名字,第二个参数很久之后才会细讲,填10先
        publisher = this->create_publisher<std_msgs::msg::String>("topic_cpp",10);
        //创建定时器,第一个参数为间隔时间,第二个参数是一个回调函数,无参无返回值
        timer = this->create_wall_timer(1000ms,[&](){
            //创建消息
            auto msg = std_msgs::msg::String();
            //写入内容
            msg.data = "pjp";
            //发布消息
            publisher->publish(msg);
        });
    }
};

int main(int argc,char** argv){
    rclcpp::init(argc,argv);
    auto node = make_shared<PubNode>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

再创建cpp文件,然后输入代码,创建订阅者的节点。

#include <iostream>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std;

class SubNode:public rclcpp::Node{
private:
    //定义订阅者
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription;
public:
    SubNode():Node("subnode"){
        //创建订阅者,指定泛型类型为string,方法第一个参数是话题名称,第二个参数填个10先
        //第三个参数是一个函数,这里用lambda,这个函数接受一个参数,即收到的消息
        subscription = this->create_subscription<std_msgs::msg::String>("topic_cpp",10,
        [&](std_msgs::msg::String msg){
            string data= msg.data;
            //打印INFO级别日志,这个c_str()函数用于将字符串转换为C风格的字符串
            RCLCPP_INFO(get_logger(),"%s",data.c_str());
        });
    }
};

int main(int argc,char** argv){
    rclcpp::init(argc,argv);
    auto node = make_shared<SubNode>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

接下来就是编写CMakelists.txt文件

add_executable(pubnode src/publisher.cpp)
add_executable(subnode src/subscriber.cpp)
ament_target_dependencies(pubnode rclcpp std_msgs)
ament_target_dependencies(subnode rclcpp std_msgs)
install(TARGETS pubnode subnode DESTINATION lib/${PROJECT_NAME})

还是放一张截图

在这里插入图片描述

(4)编译运行

通过上张截图,可以看到我们已经在src下创建了好几个功能包了,用colcon build命令,会在当前目录下生成buildinstalllog这些目录,这也是为什么我们在用这个命令时要切到与src同级目录,这样会使结构清晰。

我们暂时用不到其他功能包,colcon build会编译所有功能包,因此可以用--packages-select来选择编译指定功能包。

colcon build --packages-select topic_demo_cpp

4. 自定义消息接口

为了实现特定的功能,ros2提供的消息接口不够用,此时我们可以自定义消息接口,大致步骤可分为:创建功能包、编写msg文件、编写package.xml文件、编写CMakeLists.txt文件。

4.1创建功能包

就像创建包含节点的功能包一样,不过我们需要包含rosidl_default_generators依赖,自定义的消息可以依赖于其他消息,为了演示,我这里还添加了sensor_msgs/msg/Image依赖,这是ros2提供的消息接口。

你可能会问rosidl中的idl是什么,不太好记住啊,idl是Interface Definition Language(接口定义语言)的缩写。

ros2 pkg create my_msg --dependencies rosidl_default_generators sensor_msgs --licence Apache-2.0
4.2编写msg文件

在这个功能包中我们用不到includesrc文件夹,删除就好,然后我们创建msg文件夹,并且在里面创建msg文件,如图

在这里插入图片描述

在ros2中,支持的原始类型有:int8int16int32int64uint8uint16uint32uint64float32float64stringbyte以及对应的数组类型,如int8[]

简单起见,我们整点简单的,在A.msg里面输入

sensor_msgs/Image image #格式为:类型 名称
string str

我们定义了一个sensor_msgs包下的Image,还定义了一个字符串,注意,本来Image的路径应该是sensor_msgs/msg/Image,但是这里写的是sensor_msgs/Image,不用管为什么,记住就好了。

4.3编写package.xml

打开package.xml,在里面添加

<member_of_group>rosidl_interface_packages</member_of_group>

以标识这是一个包含接口的功能包。

为防止其他东西漏写,放出一张截图
在这里插入图片描述

4.4编写cmake

打开CMakeLists.txt,添加如下代码

rosidl_generate_interfaces(
  ${PROJECT_NAME}
  "msg/A.msg"
  DEPENDENCIES sensor_msgs
)

这个函数是ros2提供的,它能将我们编写的.msg文件转化成代码,默认转成C++和python,也可以指定成其他语言,目前只讲最基本的用法。第一个参数是项目名称,用${PROJECT_NAME}就好,第二个参数是.msg文件,填相对路径,如果有多个.msg文件,用空格隔开,然后是依赖的其他消息接口,同样,如果依赖了多个消息接口,也是用空格隔开。

4.4编译运行

像之前一样,命令行输入

colcon build --packages-select my_msg

这样我们的消息接口就创建好了,我们可以查看一下,输入

source install/setup.bash
ros2 interface show my_msg/msg/A

结果如图

在这里插入图片描述

可以看到,里面还显示出了seneor_msgs/Image的详细信息。

4.5使用

自定义消息接口的使用,和系统提供的,也是完全一样了,下面给出两张截图,相信都能看懂,就不再赘述了。

在这里插入图片描述

在这里插入图片描述

### ROS2 C++ Topic Communication Learning Materials and Tutorials For those interested in exploring how to use topics for communication within the ROS2 framework using C++, several resources provide comprehensive guidance on this subject. The official documentation of ROS 2 offers an extensive tutorial that covers setting up publishers and subscribers with detailed explanations and code examples written in C++. This resource is invaluable as it not only explains the theory but also provides practical steps necessary to establish topic-based communications between nodes[^1]. Additionally, a popular choice among developers includes tutorials available from Robot Ignite Academy which presents step-by-step instructions alongside video demonstrations specifically tailored towards understanding message passing through topics via C++ interfaces in ROS2 environments. These sessions are designed to cater both beginners who need foundational knowledge about ROS concepts along with more advanced users looking into optimizing their applications' performance when dealing with real-time data exchange over networks. Another noteworthy mention goes out to Open Robotics’ own set of educational materials where one can find well-documented samples illustrating various aspects related to working with messages across different programming languages including C++; these documents often include best practices recommendations ensuring efficient utilization while adhering closely to community standards established around ROS ecosystem development processes. ```cpp // Example Code Snippet Demonstrating Basic Publisher Node Implementation Using rclcpp Library In C++ #include "rclcpp/rclcpp.hpp" using namespace std::chrono_literals; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto node = rclcpp::Node::make_shared("example_publisher"); auto publisher = node->create_publisher<std_msgs::msg::String>("topic", 10); auto msg = std_msgs::msg::String(); msg.data = "Hello, world!"; rclcpp::WallRate loop_rate(1s); // Publish every second while (rclcpp::ok()) { RCLCPP_INFO(node->get_logger(), "Publishing: '%s'", msg.data.c_str()); publisher->publish(msg); rclcpp::spin_some(node); loop_rate.sleep(); } rclcpp::shutdown(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值