ROS系统(三)编程基础
一、创建功能包。
请查看第一章的前三个知识点。
ROS系统(二)常用工具及命令https://blog.csdn.net/qq_48361010/article/details/146033554?spm=1001.2014.3001.5501
二、发布/订阅节点(C++)
前期准备:在工作站中新建src文件夹,进入src中新建包,然后回到工作站的目录下编译包会在功能包的目录下自动生成package.xml
和 CMakeLists.txt
。然后进入包的目录下创建src文件夹,进入该目录下,创建c++文件。
1. 编写C++文件。
test_talker.cpp
#include "ros/ros.h" // ROS 头文件
#include "std_msgs/String.h" // ROS 标准消息类型 String
#include <sstream> // C++ 字符串流,用于拼接字符串int main(int argc, char **argv) {// 初始化 ROS 节点,节点名称为 "talker"ros::init(argc, argv, "talker");// 创建节点句柄ros::NodeHandle n;// 创建一个发布者 (Publisher)// 话题名称为 "chatter",消息类型为 std_msgs::String,消息队列大小为 1000ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);// 设定循环频率为 10Hz,即每 0.1 秒运行一次ros::Rate loop_rate(10);int count = 0; // 计数变量,记录消息发送次数// 进入循环,持续发布消息while (ros::ok()) { std_msgs::String msg; // 定义 ROS 消息std::stringstream ss; // 创建字符串流ss << "hello world " << count; // 拼接消息内容msg.data = ss.str(); // 赋值给消息的 data 字段// 在终端输出消息内容 (ROS_INFO 相当于 printf 或 cout)ROS_INFO("%s", msg.data.c_str());// 发布消息到 "chatter" 话题chatter_pub.publish(msg);// 处理 ROS 事件(虽然当前节点没有订阅者,但养成调用的习惯)ros::spinOnce();// 休眠以维持 10Hz 运行频率loop_rate.sleep();count++; // 计数器递增}return 0; // 结束程序
}
test_listener.cpp
#include "ros/ros.h" // ROS 头文件
#include "std_msgs/String.h" // 用于发布/订阅字符串消息的标准消息类型// 订阅者回调函数,接收并打印从 "chatter" 主题接收到的消息
void chatterCallback(const std_msgs::String::ConstPtr& msg) {ROS_INFO("I heard: [%s]", msg->data.c_str()); // 使用 c_str() 转换 std::string 为 C 字符串
}int main(int argc, char **argv) {ros::init(argc, argv, "listener"); // 初始化 ROS 节点,命名为 "listener"ros::NodeHandle n; // 创建节点句柄// 创建一个订阅者,订阅 "chatter" 主题,队列大小为 1000,回调函数为 chatterCallbackros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);ros::spin(); // 进入循环,等待回调函数处理消息return 0;
}
2. 修改编译规则。
返回到功能包目录下,修改CMakelist.txt文件来构建编译规则,该文件内容知识点请查看下面文章。
ROS系统(一)安装及基础知识https://blog.csdn.net/qq_48361010/article/details/146005869?sharetype=blogdetail&sharerId=146005869&sharerefer=PC&sharesource=qq_48361010&spm=1011.2480.3001.8118将功能包目录下的CMakelist.txt修改为以下内容。
cmake_minimum_required(VERSION 3.0.2) # 1. 指定最低 CMake 版本
project(my_ros_tutorials) # 2. 定义项目名称,与 package.xml 一致find_package(catkin REQUIRED COMPONENTS # 3. 查找 ROS 依赖组件roscpp # C++ 版 ROS 运行库rospy # python 版 ROS 运行库std_msgs # 使用的消息类型
)catkin_package() # 4. 声明 catkin 包include_directories( # 5. 指定头文件路径include ${catkin_INCLUDE_DIRS}
)add_executable(talker src/test_talker.cpp) # 6. (要生成的可执行文件 源文件)
target_link_libraries(talker # 7. 连接 ROS 相关库${catkin_LIBRARIES}
)add_executable(listener src/test_listener.cpp) # 6. (要生成的可执行文件 源文件)
target_link_libraries(listener # 7. 连接 ROS 相关库${catkin_LIBRARIES}
)
3. 编译生成可执行文件。
在工作站目录下,使用命令:catkin_make。如果编译成功,就会在
~/catkin_ws/devel/lib/包名/
目录下生成talker、listener
可执行文件。
4. 运行节点。
使用命令:roscore开始主节点,然后使用命令:rosrun 包名 talker
,如果运行完成后出现下面内容,说明编写成功!
三、发布/订阅节点(Python)
编写python代码时,由于不需要被编译,但是为了保持项目的整洁性,应该遵循 ROS 的目录结构,并放在功能包(package)目录下,创建scripts目录来存放python代码。
1. 编写py代码。
talker.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import Stringdef talker():pub = rospy.Publisher("chatter", String, queue_size=10)rospy.init_node("talker", anonymous=True)rate = rospy.Rate(10) # 10Hzwhile not rospy.is_shutdown():hello_str = "hello world %s" % rospy.get_time()rospy.loginfo(hello_str)pub.publish(hello_str)rate.sleep()if __name__ == '__main__':try:talker()except rospy.ROSInterruptException:pass
listener.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import String # std_msgs.msg 的 String 需要大写def callback(data):rospy.loginfo(rospy.get_caller_id() + " I heard: %s", data.data) # 修正 loginfo 语法错误def listener():rospy.init_node("listener", anonymous=False) # 修正 init_node 语法错误rospy.Subscriber("chatter", String, callback) # 修正 String 类型拼写rospy.spin()if __name__ == "__main__": # 修正 if __name__ 语句错误listener()
2. 运行节点。
需要先开启主节点,然后使用命令rosrun 包名 talker.py来运行节点。这里C++和python都可以互通,比如运行C++写的talker,再运行python写的listener.py,都是可用互相通信的。
在 ROS中,C++ 和 Python 节点是可以互通的。这是因为 ROS 中的消息传递是基于发布-订阅(publish-subscribe)模型的,只要
talker
和listener
节点使用相同的消息类型和主题(topic),它们就可以互相通信。而 ROS 提供了多语言支持,使得使用不同语言编写的节点能够通过共享消息格式进行通信。