ROS2 通讯:节点(发布-订阅)

更多示例:https://github.com/LbwCode/ros2_all
鱼香ROS
一键安装: wget http://fishros.com/install -O fishros && . fishros

测试是否安装成功
你好世界
ros2 run demo_nodes_cpp talker
ros2 run demo_nodes_cpp listener

工作空间:
src 代码
install 安装
build 编译
log 日志

创建src文件夹,在里面创建功能包
创建功能包 创建类型 C++ 或 python 文件名称自己取
ros2 pkg create --build-type ament_cmake 文件名
ros2 pkg create --build-type ament_python 文件名

src/功能包文件夹/放你的代码

在setup.py配置调用的代码

例子:
ce_hello2 自己起名(最终调用的名字)
ce_python 功能包名(文件夹名)
ce_1 代码文件名

entry_points={
    'console_scripts': [
     'ce_hello1  = ce_python.ce_1:main',
     'ce_hello2  = ce_python.ce_2:main',
    ],
},

项目编译
colcon build
或者编译某个功能包
colcon build --packages-select lbw_cpp1
若默认环境冲突可强行指定cmake编译,注意需要将原本编译的删除,否则也会失败。会调用默认环境。

colcon build --packages-select ros2_param_cpp1 --cmake-args "-DPYTHON_EXECUTABLE=/usr/bin/python3"

刷新环境变量,让其生效
source ~/你的文件夹名/install/local_setup.sh

若想永久设置需要改环境变量文件的内容
显示隐藏文件 CTRL + H
环境变量
打开.bashrc
路径换成你自己的
echo “source /home/lbw/ros2_all/install/setup.bash” >> ~/.bashrc && source ~/.bashrc

运行命令:ros2 run 你的功能包名 你自己起的名

查看小工具 带界面
rqt_graph

代码示例

字符串(发布-订阅)

发布者

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
@说明: ROS2话题示例-发布“Hello World”话题
"""

import rclpy                                     # ROS2 Python接口库
from rclpy.node import Node                      # ROS2 节点类
from std_msgs.msg import String                  # 字符串消息类型

"""
创建一个发布者节点
"""
class PublisherNode(Node):
    
    def __init__(self, name):
        super().__init__(name)                                    # ROS2节点父类初始化
        self.pub = self.create_publisher(String, "chatter", 10)   # 创建发布者对象(消息类型、话题名、队列长度)
        self.timer = self.create_timer(0.5, self.timer_callback)  # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
        
    def timer_callback(self):                                     # 创建定时器周期执行的回调函数
        msg = String()                                            # 创建一个String类型的消息对象
        msg.data = 'Hello World 你好 世界'                         # 填充消息对象中的消息数据
        self.pub.publish(msg)                                     # 发布话题消息
        self.get_logger().info('发送: "%s"' % msg.data)           # 输出日志信息,提示已经完成话题发布
        
def main(args=None):                                 # ROS2节点主入口main函数
    rclpy.init(args=args)                            # ROS2 Python接口初始化
    node = PublisherNode("topic_helloworld_pub")     # 创建ROS2节点对象并进行初始化 (不能使用中文)
    rclpy.spin(node)                                 # 循环等待ROS2退出
    node.destroy_node()                              # 销毁节点对象
    rclpy.shutdown()                                 # 关闭ROS2 Python接口

"""
         'ce_hello1  = ce_python.ce_1:main',
         'ce_hello2  = ce_python.ce_2:main',
    source ~/dev_ws/install/local_setup.sh
    source /home/lbw/jiao_xue_ros2/install/local_setup.sh
    """

订阅者

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
@说明: ROS2话题示例-订阅“Hello World”话题消息
"""

import rclpy                                     # ROS2 Python接口库
from rclpy.node   import Node                    # ROS2 节点类
from std_msgs.msg import String                  # ROS2标准定义的String消息

"""
创建一个订阅者节点
"""
class SubscriberNode(Node):
    
    def __init__(self, name):
        super().__init__(name)                                    # ROS2节点父类初始化
        self.sub = self.create_subscription(\
            String, "chatter", self.listener_callback, 10)        # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)

    def listener_callback(self, msg):                             # 创建回调函数,执行收到话题消息后对数据的处理
        self.get_logger().info('收到: "%s"' % msg.data)            # 输出日志信息,提示订阅收到的话题消息
        
def main(args=None):                                 # ROS2节点主入口main函数
    rclpy.init(args=args)                            # ROS2 Python接口初始化
    node = SubscriberNode("topic_helloworld_sub")    # 创建ROS2节点对象并进行初始化 (不能使用中文)
    rclpy.spin(node)                                 # 循环等待ROS2退出
    node.destroy_node()                              # 销毁节点对象
    rclpy.shutdown()                                 # 关闭ROS2 Python接口

图像类型(发布-订阅)

发布者

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
 
@说明: ROS2话题示例-发布图像话题
"""

import rclpy                        # ROS2 Python接口库
from rclpy.node import Node         # ROS2 节点类
from sensor_msgs.msg import Image   # 图像消息类型
from cv_bridge import CvBridge      # ROS与OpenCV图像转换类
import cv2                          # Opencv图像处理库

"""
创建一个发布者节点
ros2 run ce_python ce_img1
"""
class ImagePublisher(Node):

    def __init__(self, name):
        super().__init__(name)                                           # ROS2节点父类初始化
        self.publisher_ = self.create_publisher(Image, 'image_raw', 10)  # 创建发布者对象(消息类型、话题名、队列长度)
        self.timer = self.create_timer(0.04, self.timer_callback)         # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
        self.path_ce1 = "/home/lbw/ros2_ce1/src/ros2_lbw_1/ros2_lbw_1/1.mp4"
        self.cap = cv2.VideoCapture(self.path_ce1)                             # 创建一个视频采集对象,驱动相机采集图像(相机设备号)
        self.cv_bridge = CvBridge()                                      # 创建一个图像转换对象,用于稍后将OpenCV的图像转换成ROS的图像消息

    def timer_callback(self):
        ret, frame = self.cap.read()                                     # 一帧一帧读取图像
        
        if ret == True:                                                  # 如果图像读取成功
            self.publisher_.publish(
                self.cv_bridge.cv2_to_imgmsg(frame, 'bgr8'))             # 发布图像消息
        else:
            self.cap = cv2.VideoCapture(self.path_ce1) 

        self.get_logger().info('Publishing video frame')                 # 输出日志信息,提示已经完成图像话题发布

def main(args=None):                                 # ROS2节点主入口main函数
    rclpy.init(args=args)                            # ROS2 Python接口初始化
    node = ImagePublisher("topic_webcam_pub")        # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                                 # 循环等待ROS2退出
    node.destroy_node()                              # 销毁节点对象
    rclpy.shutdown()                                 # 关闭ROS2 Python接口



订阅者

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
 
@说明: ROS2话题示例-订阅图像话题
"""

import rclpy                            # ROS2 Python接口库
from rclpy.node import Node             # ROS2 节点类
from sensor_msgs.msg import Image       # 图像消息类型
from cv_bridge import CvBridge          # ROS与OpenCV图像转换类
import cv2                              # Opencv图像处理库
import numpy as np                      # Python数值计算库


"""
创建一个订阅者节点
"""
class ImageSubscriber(Node):
    def __init__(self, name):
        super().__init__(name)                                  # ROS2节点父类初始化
        self.sub = self.create_subscription(
            Image, 'image_raw', self.listener_callback, 10)     # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
        self.cv_bridge = CvBridge()                             # 创建一个图像转换对象,用于OpenCV图像与ROS的图像消息的互相转换

    def object_detect(self, image):
        # 获取图像尺寸
        height, width = image.shape[:2]

        # 计算中心点坐标
        center_x = width // 2
        center_y = height // 2

        # 绘制中心点(红色实心圆,半径5px,线宽-1表示填充)
        cv2.circle(image, (center_x, center_y), 30, (0, 0, 255), 2)


        cv2.imshow("object", image)                             # 使用OpenCV显示处理后的图像效果
        cv2.waitKey(1)

    def listener_callback(self, data):
        self.get_logger().info('Receiving video frame')         # 输出日志信息,提示已进入回调函数
        image = self.cv_bridge.imgmsg_to_cv2(data, 'bgr8')      # 将ROS的图像消息转化成OpenCV图像
        self.object_detect(image)                               # 


def main(args=None):                                        # ROS2节点主入口main函数
    rclpy.init(args=args)                                   # ROS2 Python接口初始化
    node = ImageSubscriber("topic_webcam_sub")              # 创建ROS2节点对象并进行初始化
    rclpy.spin(node)                                        # 循环等待ROS2退出
    node.destroy_node()                                     # 销毁节点对象
    rclpy.shutdown()                                        # 关闭ROS2 Python接口


"""
'lbw_ce1 = ros2_lbw_1.ce1:main',
            'lbw_ce2 = ros2_lbw_1.ce2:main',
            'lbw_img_ce1 = ros2_lbw_1.img_ce1:main',
            'lbw_img_ce2 = ros2_lbw_1.img_ce2:main',

source /home/lbw/ros2_ce1/install/local_setup.sh 
colcon build
ros2 run ros2_lbw_1 lbw_ce1
ros2 run ros2_lbw_1 lbw_ce2

ros2 run ros2_lbw_1 lbw_img_ce1
ros2 run ros2_lbw_1 lbw_img_ce2

"""

C++ 字符串(发布-订阅)

发布者
img_cpp_ce1.cpp 文件

/***
@说明: ROS2话题示例-发布图像话题
***/

#include <chrono>
#include <functional>
#include <memory>
#include <string>

#include "rclcpp/rclcpp.hpp"          // ROS2 C++接口库
#include "std_msgs/msg/string.hpp"    // 字符串消息类型

using namespace std::chrono_literals;


class PublisherNode : public rclcpp::Node
{
    public:
        PublisherNode()
        : Node("topic_helloworld_pub") // ROS2节点父类初始化
        {
            // 创建发布者对象(消息类型、话题名、队列长度)
            publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10); 
            // 创建一个定时器,定时执行回调函数
            timer_ = this->create_wall_timer(
                500ms, std::bind(&PublisherNode::timer_callback, this));            
        }

    private:
        // 创建定时器周期执行的回调函数
        void timer_callback()                                                       
        {
          // 创建一个String类型的消息对象
          auto msg = std_msgs::msg::String();   
          // 填充消息对象中的消息数据                                    
          msg.data = "Hello World";
          // 发布话题消息                                                 
          RCLCPP_INFO(this->get_logger(), "发送: '%s'", msg.data.c_str()); 
          // 输出日志信息,提示已经完成话题发布   
          publisher_->publish(msg);                                                
        }
        
        rclcpp::TimerBase::SharedPtr timer_;                             // 定时器指针
        rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;  // 发布者指针
};

// ROS2节点主入口main函数
int main(int argc, char * argv[])                      
{
    // ROS2 C++接口初始化
    rclcpp::init(argc, argv);                
    
    // 创建ROS2节点对象并进行初始化          
    rclcpp::spin(std::make_shared<PublisherNode>());   
    
    // 关闭ROS2 C++接口
    rclcpp::shutdown();                                

    return 0;
}


订阅者
img_cpp_ce2.cpp 文件

/***
@说明: ROS2话题示例-订阅“Hello World”话题消息
***/

#include <memory>

#include "rclcpp/rclcpp.hpp"                  // ROS2 C++接口库
#include "std_msgs/msg/string.hpp"            // 字符串消息类型
using std::placeholders::_1;

class SubscriberNode : public rclcpp::Node
{
    public:
        SubscriberNode()
        : Node("topic_helloworld_sub")        // ROS2节点父类初始化
        {
            subscription_ = this->create_subscription<std_msgs::msg::String>(       
                "chatter", 10, std::bind(&SubscriberNode::topic_callback, this, _1));   // 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
        }

    private:
        void topic_callback(const std_msgs::msg::String::SharedPtr msg) const                  // 创建回调函数,执行收到话题消息后对数据的处理
        {
            RCLCPP_INFO(this->get_logger(), "接收: '%s'", msg->data.c_str());       // 输出日志信息,提示订阅收到的话题消息
        }
        
        rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;         // 订阅者指针
};

// ROS2节点主入口main函数
int main(int argc, char * argv[])                         
{
    // ROS2 C++接口初始化
    rclcpp::init(argc, argv);                 
    
    // 创建ROS2节点对象并进行初始化            
    rclcpp::spin(std::make_shared<SubscriberNode>());     
    
    // 关闭ROS2 C++接口
    rclcpp::shutdown();                                  
    
    return 0;
}
/*

colcon build
colcon build --packages-select lbw_cpp1

source /home/lbw/ros2_ce_cpp/install/setup.bash

ros2 run lbw_cpp1 cpp_ce1
ros2 run lbw_cpp1 cpp_ce2
*/

CMakeLists.txt 文件

cmake_minimum_required(VERSION 3.8)
project(lbw_cpp1)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(cpp_ce1 src/img_cpp_ce1.cpp)
ament_target_dependencies(cpp_ce1 rclcpp std_msgs)

add_executable(cpp_ce2 src/img_cpp_ce2.cpp)
ament_target_dependencies(cpp_ce2 rclcpp std_msgs)

install(TARGETS
  cpp_ce1
  cpp_ce2
  DESTINATION lib/${PROJECT_NAME})
  
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

默执_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值