您现在的位置是:首页 >学无止境 >ros2教程【5】:话题通信(C++和python示例)网站首页学无止境
ros2教程【5】:话题通信(C++和python示例)
简介ros2教程【5】:话题通信(C++和python示例)
目录
一、C++话题示例:
1.发布消息
1.2 工作空间+构建功能包+新建节点文件
这个是依托于小海龟的示例做的演示
首先还是
新建一个工作空间chapt3_ws,
在下面新建src源码库
在源码下面构建功能包,
在功能包的src文件夹下面新建turtle_circle.cpp文件
这些都是标准动作,具体看
链接: 构建一个功能包
mkdir -p ~/chapt3_ws/src
cd ~/chapt3_ws/src
ros2 pkg create demo_cpp_topic --build-type ament_cmake --dependencies rclcpp geometry_msgs turtlesim --license Apache-2.0
touch turtle_circle.cpp
在turtle_circle.cpp中输入:
代码内容:
// 包含 ROS 2 的核心库,用于创建节点、发布者、定时器等
#include "rclcpp/rclcpp.hpp"
// 包含 geometry_msgs::msg::Twist 消息类型,用于发布速度指令
#include "geometry_msgs/msg/twist.hpp"
// 包含标准库中的时间相关功能,用于定时器
#include <chrono>
using namespace std::chrono_literals; // 使用 std::chrono 的字面量语法,如 1000ms
// 定义一个名为 TurtleCircleNode 的类,继承自 rclcpp::Node
class TurtleCircleNode : public rclcpp::Node
{
private:
// 发布者对象,用于发布速度指令
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_;
// 定时器对象,用于周期性调用回调函数
rclcpp::TimerBase::SharedPtr timer_;
public:
// 构造函数,初始化节点
explicit TurtleCircleNode(const std::string &node_name) : Node(node_name)
{
// 创建一个发布者,发布速度指令到 "/turtle1/cmd_vel" 话题,队列大小为 10
publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("turtle1/cmd_vel", 10);
// 创建一个定时器,每 1 秒调用一次 timer_callback 函数
timer_ = this->create_wall_timer(1000ms, std::bind(&TurtleCircleNode::timer_callback, this));
}
// 定时器回调函数,用于发布速度指令
void timer_callback()
{
// 创建一个 Twist 消息对象
auto msg = geometry_msgs::msg::Twist();
// 设置线速度为 1.0 m/s
msg.linear.x = 1.0;
// 设置角速度为 0.5 rad/s
msg.angular.z = 0.5;
// 发布速度指令
publisher_->publish(msg);
}
};
int main(int argc, char **argv)
{
// 初始化 ROS 2 环境,设置日志系统、参数服务器等基础设施
rclcpp::init(argc, argv);
// 创建一个名为 "turtle_circle" 的 TurtleCircleNode 节点
auto node = std::make_shared<TurtleCircleNode>("turtle_circle");
// 运行节点的事件循环,处理定时器回调等事件
rclcpp::spin(node);
// 关闭 ROS 2 环境,清理资源
rclcpp::shutdown();
// 程序正常退出
return 0;
}
1.2:CMakeList的处理
CMakeList.txt
add_executable(turtle_cirtle src/turtle_circle.cpp)
ament_target_dependencies(turtle_cirtle rclcpp geometry_msgs turtlesim)
install(TARGETS turtle_cirtle
DESTINATION lib/${PROJECT_NAME}
)
1.3:构建+测试
终端1:
cd ..
colcon build
source install/setup.bash
ros2 run demo_cpp_topic turtle_circle
新建终端2:
ros2 run turtlesim turtlesim_node
这个时候就会出现一个小乌龟在画圆。
2.订阅消息:
2.1新建文件turtle_control.cpp
cd ~/chapt3_ws/src/demo_cpp_topic/src
touch turtle_control.cpp
输入代码:
#include "rclcpp/rclcpp.hpp" // 包含 ROS 2 的核心库
#include "geometry_msgs/msg/twist.hpp" // 包含 Twist 消息类型,用于发布速度指令
#include "turtlesim/msg/pose.hpp" // 包含 Pose 消息类型,用于接收海龟的位置信息
using namespace std::chrono_literals; // 使用 std::chrono 的字面量语法
// 定义一个名为 TurtleControl 的类,用于控制海龟移动到目标位置
class TurtleControl : public rclcpp::Node
{
private:
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_; // 发布者对象,用于发布速度指令
rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr Subscriber_; // 订阅者对象,用于接收海龟的位置信息
double target_x_{1.0}; // 目标 x 坐标
double target_y_{1.0}; // 目标 y 坐标
double k_{1.0}; // 控制增益,用于调节速度
double max_speed_{3.0}; // 最大线速度限制
public:
// 构造函数,初始化节点
explicit TurtleControl(const std::string &node_name) : Node(node_name)
{
// 创建一个发布者,发布速度指令到 "/turtle1/cmd_vel" 话题
publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("turtle1/cmd_vel", 10);
// 创建一个订阅者,订阅 "/turtle1/pose" 话题,接收海龟的位置信息
Subscriber_ = this->create_subscription<turtlesim::msg::Pose>("/turtle1/pose", 10, std::bind(&TurtleControl::on_pose_reveived, this, std::placeholders::_1));
}
// 回调函数,处理接收到的海龟位置信息
void on_pose_reveived(const turtlesim::msg::Pose::SharedPtr pose)
{
// 获取当前海龟的位置
auto current_x = pose->x;
auto current_y = pose->y;
// 打印当前海龟的位置信息
RCLCPP_INFO(get_logger(), "当前:x = %f,y = %f", current_x, current_y);
// 计算当前海龟位置与目标位置之间的距离差
auto distance = std::sqrt(
(target_x_ - current_x) * (target_x_ - current_x) +
(target_y_ - current_y) * (target_y_ - current_y));
// 计算当前海龟位置与目标位置之间的角度差
auto angle = std::atan2((target_y_ - current_y), (target_x_ - current_x)) - pose->theta;
// 创建一个 Twist 消息对象,用于发布速度指令
auto msg = geometry_msgs::msg::Twist();
// 如果距离大于阈值(0.1),则进行控制
if (distance > 0.1)
{
// 如果角度差大于阈值(0.2),则调整角度
if (fabs(angle) > 0.2)
{
msg.angular.z = angle; // 设置角速度
}
else
{
// 否则,设置线速度
msg.linear.x = k_ * distance;
}
}
// 限制线速度的最大值
if (msg.linear.x > max_speed_)
{
msg.linear.x = max_speed_;
}
// 发布速度指令
publisher_->publish(msg);
}
};
int main(int args, char **argv)
{
// 初始化 ROS 2 环境
rclcpp::init(args, argv);
// 创建一个名为 "turtle_control" 的 TurtleControl 节点
auto node = std::make_shared<TurtleControl>("turtle_control");
// 运行节点的事件循环
rclcpp::spin(node);
// 关闭 ROS 2 环境
rclcpp::shutdown();
// 程序正常退出
return 0;
}
2.2:CMakeList的处理
CMakeList.txt
add_executable(turtle_cirtle src/turtle_circle.cpp)
add_executable(turtle_control src/turtle_control.cpp)
ament_target_dependencies(turtle_cirtle rclcpp geometry_msgs turtlesim)
ament_target_dependencies(turtle_control rclcpp geometry_msgs turtlesim)
install(TARGETS turtle_cirtle turtle_control
DESTINATION lib/${PROJECT_NAME}
)
2.3:构建+测试
终端1:
cd ~/chapt3_ws/src
colcon build
source install/setup.bash
ros2 run demo_cpp_topic turtle_control
新建终端2:
ros2 run turtlesim turtlesim_node
这个时候就会出现一个小乌龟在跑步了。
二、python话题示例:
1.命名空间
新建一个chapt3_ws的命名空间,然后在下面新建src源码库,在chapt3_ws命名空间文件夹层面运行colcon build
cd ~/chapt3_ws/src
ros2 pkg create demo_python_topic --build-type ament_python --dependencies rclpy example_interfaces --license Apache-2.0
touch novel_pub_node.py
2.写入代码(发布消息)
touch novel_pub_node.py
看注释即可
import rclpy
from rclpy.node import Node
from example_interfaces.msg import String
class NovelPubNode(Node):
def __init__(self,node_name):
#调用父类方法,节点初始化工作
super().__init__(node_name)
self.get_logger().info(f'{node_name},启动!')
self.i = 0 # 初始化变量 i
self.novel_pub()
def novel_pub(self):
self.novel_publisher = self.create_publisher(String,'novel',10)#创建发布者,分别对应数据类型,话题名,qos
#ros2创建一个定时器,时间间隔5秒,回调函数和qos
self.create_timer(2.0,self.novel_pub_timer)
def novel_pub_timer(self):
#创建发布数据类型
self.msg = String()
self.i += 1 # 自增操作
self.msg.data = f'你好!{self.i}'
#发布数据
self.novel_publisher.publish(self.msg)
def main():
rclpy.init()
node = NovelPubNode('novel_pub')
rclpy.spin(node)
rclpy.shutdown()
3.接收文字并朗读(订阅消息)
3.1:准备工作 (安装语音包)
//安装pip功能包
sudo apt install python3-pip -y
//安装语音朗读包
pip3 install espeakng
sudo apt install espeak-ng
cd ~/chapt3/src/demo_python_topic/demo_python_topic
touch novel_sub_node.py
novel_sub_node.py代码编辑:
import espeakng # 导入espeakng库,用于文本转语音
import rclpy # 导入rclpy库,ROS 2的Python客户端库
from rclpy.node import Node # 从rclpy.node导入Node类,用于创建ROS 2节点
from example_interfaces.msg import String # 从example_interfaces.msg导入String消息类型
from queue import Queue # 从queue模块导入Queue类,用于创建线程安全的队列
import threading # 导入threading模块,用于创建和管理线程
import time # 导入time模块,用于执行时间相关的操作
class NovelSubNode(Node):
def __init__(self, node_name):
super().__init__(node_name) # 调用父类Node的构造函数,初始化节点
# 新建一个队列,用于存储从话题订阅接收到的文本信息
self.novel_queue_ = Queue()
self.get_logger().info(f'{node_name},启动!') # 打印节点启动日志
# 新建一个话题订阅,订阅名为'novel'的String类型话题,回调函数为sub_callback,队列长度为10
self.novel_sub = self.create_subscription(String, 'novel', self.sub_callback, 10)
# 绑定一个线程,用于处理队列中的文本信息并朗读
self.novel_thread = threading.Thread(target=self.speaker_thread)
# 启动线程,使其开始执行speaker_thread方法
self.novel_thread.start()
# 回调函数,当订阅的话题有新消息时,此方法会被调用
def sub_callback(self, msg):
# 把话题订阅到的信息(文本)存到队列里面,用于解决解读文本和发送文本速度不一致的问题
self.novel_queue_.put(msg.data)
def speaker_thread(self):
# 新建朗读对象,用于将文本转换为语音
speaker = espeakng.Speaker()
# 设置朗读语言为中文
speaker.voice = 'zh'
# 判断rclpy环境是否正常,即ROS 2节点是否仍在运行
while rclpy.ok():
# 如果队列中有文本信息,则取出并朗读
if self.novel_queue_.qsize() > 0:
text = self.novel_queue_.get() # 从队列中获取文本信息
self.get_logger().info(f'启动朗读:{text}') # 打印即将朗读的文本信息
speaker.say(text) # 使用espeakng朗读文本
speaker.wait() # 等待朗读完成
else:
time.sleep(1) # 如果队列为空,则等待1秒后再检查队列,避免频繁检查浪费资源
def main():
rclpy.init() # 初始化rclpy环境
node = NovelSubNode('novel_sub') # 创建NovelSubNode节点实例
rclpy.spin(node) # 让节点保持运行状态,等待接收和处理消息
rclpy.shutdown() # 关闭rclpy环境,清理资源
4.编辑package.xml
<depend>rclpy</depend>
<depend>example_interfaces</depend>
5.编辑setup.py
entry_points={
'console_scripts': [
'novel_pub_python = demo_python.novel_pub_node:main',
'novel_sub_python = demo_python.novel_sub_node:main',
],
},
6.构建+测试
终端1:
cd ~/chapt3/chapt3_ws
colcon build
source install/setup.bash
ros2 run demo_python_topic novel_pub_node
终端2:
cd ~/chapt3/chapt3_ws
source install/setup.bash
ros2 run demo_python_topic novel_pub_node
这个时候就听见有个机器人在读数了
三.命令行关于话题的各种查询
ros2 topic list 列出话题列表
ros2 topic echo /novel(话题名) //输出话题内容
ros2 topic hz /novel(话题名) //输出话题频率
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。