(二)ROS话题通信
- 1:发布话题
- 2:订阅话题
- 3:自定义消息数据类型
- 4:命令行
ROS话题通信:支持一对多的异步通信,发布者发布消息到话题上,订阅者从话题上订阅消息。
1:发布话题
Python
1.初始化ROS节点:命名(唯一)
rospy.init_node("名字")
2.创建发布者
pub = rospy.Publisher("发布的话题名",发布的话题的消息类型,队列长度)
3.创建消息对象(可以处理消息内容)
msg = 消息类型()
4.将消息发布出去:
pub.publish(msg)
例如
#! /usr/bin/env python
import rospy
from std_msgs.msg import String
if __name__ == "__main__":
#2.初始化 ROS 节点:命名(唯一)
rospy.init_node("talker_p")
#3.实例化 发布者 对象
pub = rospy.Publisher("chatter",String,queue_size=10)
#4.组织被发布的数据,并编写逻辑发布数据
msg = String() #创建 msg 对象
# 设置循环频率
rate = rospy.Rate(1)
while not rospy.is_shutdown():
#代码
c++
创建节点句柄:
ros::NodeHandle nh;
创建一个发布者:<话题的消息数据类型> <话题名> <消息缓存长度>
ros::Publisher pub = nh.advertise<std_msgs::String>("topic_name", queue size);
创建消息数据: 功能包::数据类型 实例
std_msgs::String str ;
消息赋值:
str.data = "hello world" ;
将消息发布出去:
pub.publish(str) ;
例如:
ros::NodeHandle n
ros::Publisher voltage_publisher;
voltage_publisher = n.advertise<std_msgs::Float32>("PowerVoltage", 10);
std_msgs::Float32 voltage_msgs;
voltage_msgs.data = Power_voltage;
voltage_publisher.publish(voltage_msgs);
2:订阅话题
python
1.初始化 ROS 节点:命名(唯一)
rospy.init_node("节点名")
2.实例化 订阅者 对象
sub = rospy.Subscriber("订阅的消息名",消息类型,定义的回调函数名,消息队列长度)
3.定义处理回调函数
def 函数名(订阅的消息对象在回调函数中的名字)
例如
#! /usr/bin/env python
import rospy
from std_msgs.msg import String
#回调函数
def doMsg(msg):
rospy.loginfo("I heard:%s",msg.data)
if __name__ == "__main__":
#2.初始化 ROS 节点:命名(唯一)
rospy.init_node("listener_p")
#3.实例化 订阅者 对象
sub = rospy.Subscriber("chatter",String,doMsg,queue_size=10)
#4.处理订阅的消息(回调函数)
#5.设置循环调用回调函数
#如果写了消息订阅函数,那一定要写rospy.spin()
#不然是得不到另一边发出的消息或者信息
#rospy.spin()以下的代码都不会执行,一直等待订阅回调函数
rospy.spin()
c++
订阅话题:
(1)创建一个订阅者
(2)创建一个回调函数 用来处理订阅话题里面的信息
//订阅的话题有新的消息就会进入回调函数
void callback(const std_msgs::StringConstPtr& str)
{
}
// ("话题名"消息队列长度 , 自定义回调函数的名字)
ros::Subscriber sub = nh.subscribe("my_topic", queue size, callback);
例如:
ros::Subscriber Cmd_Vel_Sub;
Cmd_Vel_Sub = n.subscribe(cmd_vel, 100, &turn_on_robot::Cmd_Vel_Callback);
//1:发布和订阅的话题是基于这个消息类型的
//2:指针是格式
//3:相当于用akm_ctl暂时在回调函数里面代替
//ackermann_msgs::AckermannDriveStamped
//从而直接用akm_ctl.xxx来代替订阅的话题里面的数据类型
void turn_on_robot::Akm_cmd_Vel_Callback(const ackermann_msgs::AckermannDriveStamped &akm_ctl)
{
}
如果用一个节点要订阅和发布多个话题时,把发布话题的函数,写在了订阅回调中即可。订阅多个话题时,ros会一直卡在rospy.spin()等待每个回调函数的响应。
如果要ROS中订阅多个话题并汇聚回调信息处理
import message_filters
#定义回调函数
def call_back(data, image)
#---------------------订阅----------------------
sub1 = message_filters.Subscriber('/topic1' , msg)
#message_filters.Subscriber 跟rospy.Subscriber 一样需要传入的函数
sub2 = message_filters.Subscriber('/topic2' , msg)
#---------------------集合-----------------------
ts = message_filters.TimeSynchronizer([sub1,sub2],1)
ts.registerCallback(call_back)#回调函数名
rospy.spin()
#---------------------END----------------------
#注意
global point_data # 将点云变为全局变量发出
3:自定义消息数据类型
进入功能包:
(1)自定义msg文件:
创建mgs文件夹,创建.msg文件, 写float32 linex 类似的
(2)功能包下的文件 CMakeLists.txt:中添加编译规则 :
find_package (catkin REQUIRED COMPONENTS message_generation)
//用在catkin_package前面
add_message_files( FILES 定义的.msg )
generate_messages( DEPENDENCIES std_msgs )
//
catkin_package{CATKIN_DEPENDS message_runtime}
//要在一个节点内,引用生成的自定义消息类型,需添加链接库
add_dependencies(节点名 ${PROJECT_NAME}_generate_messages_cpp)
//节点中去引用自定义消息类型
add_executable(可执行文件 src/文件名.cpp)
target_link_libraries(可执行文件 ${catkin_LIBRARIES})
(3)在package.xml文件中,添加描述规则:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
(4)编译后
在工作空间下的devel中的include中有.h文件
在cpp中include这个.h文件就可以调用自定义的消息数据类型
(即"功能包的名字/.h文件")
4:命令行
命令行话题指令:
rostopic echo :打印某个话题信息 //数据
rostopic hz : 查看话题发布频率
rostopic info :打印话题的发布者订阅者和消息类型
rostopic type :查看话题的数据类型
rostopic list :列举出所有的话题
rostopic pub 话题 消息结构(tab键补全) :往话题输入信息
循环发送:rostopic pub -r 频率 话题 消息结构
rostopic bw : 发布话题占用的带宽
rostopic find :从数据类型寻找话题
消息数据类型:
rosmsg list 列出消息数据类型
rosmsg show 消息数据类型的内容(结构体)
rostopic type 话题名 |rosmsg show