文章目录
1 基于 topic 通信
1.1 自定义message(数据类型)
Message的定义类似C语言中的结构体。Message不仅仅是我们平时理解的一条一条的消息,而且更是ROS中topic的格式规范。或者可以理解msg是一个“类”,那么我们每次发布的内容可以理解为“对象”,这么对比来理解可能更加容易。
我们实际通常不会把Message概念分的那么清,通常说Message既指的是类,也是指它的对象。而msg文件则相当于类的定义了。
rosmsg命令 | 作用 |
---|---|
rosmsg list |
列出系统上所有的msg |
rosmsg show msg_name |
显示某个msg的内容 |
1.1.1 新建功能包
首先进入src目录下创建功能包,
cd catkin_ws/src/
catkin_create_pkg pkg_a rospy roscpp std_msgs geometry_msgs message_generation message_runtime
依赖:rospy roscpp,是函数接口; std_msgs geometry_msgs,是消息嵌套用到的功能包;
message_generation message_runtime,是编译和运行所需的依赖,在这里添加,则可以省去后面修改package.xml的步骤。
1.1.2 新建msg文件
Message的定义位于功能包下的msg目录中(service则位于srv目录中)。
jw@G5-5500:~/catkin_ws$ roscd pkg_a/
jw@G5-5500:~/catkin_ws/src/pkg_a$ ls
CMakeLists.txt include package.xml src
jw@G5-5500:~/catkin_ws/src/pkg_a$ mkdir msg && cd msg
jw@G5-5500:~/catkin_ws/src/pkg_a/msg$ gedit my_message.msg
msg的定义,类似结构体。
自定义的my_message.msg文件的内容如下:
- 顶行不能为空
- 中间用空格隔开而不是tab
geometry_msgs/Vector3 v2
geometry_msgs/Vector3 v1
string name
int32 num
geometry_msgs是ros的封装好的类型库,如对几何数据类型中的加速度,geometry_msgs/Accel,其msg内容为
jw@G5-5500:/opt/ros/melodic/share/geometry_msgs/msg$ cat Accel.msg
# This expresses acceleration in free space broken into its linear and angular parts.
Vector3 linear
Vector3 angular
注意,这里的Vector3
之所以没有带geometry_msgs/
,是因为Vector3
本身就位于包geometry_msgs的msg目录下。
再看Vector3的消息定义
jw@G5-5500:/opt/ros/melodic/share/geometry_msgs/msg$ cat Vector3.msg
# This represents a vector in free space.
# It is only meant to represent a direction. Therefore, it does not
# make sense to apply a translation to it (e.g., when applying a
# generic rigid transformation to a Vector3, tf2 will only apply the
# rotation). If you want your data to be translatable too, use the
# geometry_msgs/Point message instead.
float64 x
float64 y
1.1.3 修改package.xml与CMakeLists.txt
我们要确保msg文件被转换成为C++,Python和其他语言的源代码:
查看package.xml, 确保它包含一下两条语句:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
如果没有,添加进去。 注意,在构建的时候,我们只需要"message_generation"。然而,在运行的时候,我们只需要"message_runtime"。
经过之前的catkin_create_pkg,已经自动把那两条语句添加进了package.xml,检查一下:
jw@G5-5500:~/catkin_ws/src/pkg_a$ ls
CMakeLists.txt include msg package.xml src
jw@G5-5500:~/catkin_ws/src/pkg_a$ gedit package.xml
再修改CMakeLists.txt,包括4个点:
jw@G5-5500:~/catkin_ws/src/pkg_a$ gedit CMakeLists.txt
- 1,find_package,删除message_runtime
find_package(catkin REQUIRED COMPONENTS
geometry_msgs
message_generation
# message_runtime ,删除或注释掉
roscpp
rospy
std_msgs
)
- 2,catkin_package,取消注释并修改
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES pkg_a
CATKIN_DEPENDS geometry_msgs message_runtime roscpp rospy std_msgs # 去掉了message_generation
# DEPENDS system_lib
)
- 3,add_message_files;取消注释并修改
add_message_files(
FILES
my_message.msg
)
- 4,generate_messages;取消注释并修改,添加依赖的msg包
generate_messages(
DEPENDENCIES
std_msgs
geometry_msgs
)
1.1.4 编译
然后进入workplace编译一下,
cd catkin_ws/
catkin_make
编译成功,并且也能查找得到:
jw@G5-5500:~/catkin_ws$ catkin_find_pkg pkg_a
src/pkg_a
jw@G5-5500:~/catkin_ws$ rosmsg list | grep my
pkg_a/my_message
jw@G5-5500:~/catkin_ws$ rosmsg show my_message
[pkg_a/my_message]:
geometry_msgs/Vector3 v2
float64 x
float64 y
float64 z
geometry_msgs/Vector3 v1
float64 x
float64 y
float64 z
string name
int32 num
1.2 cpp
1.2.1 订阅与发布
roscd pkg_a/src/
gedit puber.cpp
gedit suber.cpp
//puber.cpp
#include "ros/ros.h"
#include "pkg_a/my_message.h"
#include "sstream"
int main(int argc, char **argv)
{
std::stringstream ss;
pkg_a::my_message msg;
ros::init(argc, argv, "puber");
ros::NodeHandle h;
ROS_INFO("Im %s", ros::this_node::getName().c_str());
ros::Publisher puber = h.advertise<pkg_a::my_message>("my_topic", 10);
ros::Rate loop_rate(1);
ss << "A B";
while (ros::ok())
{
ss >> msg.name;
ss >> msg.num;
ROS_INFO("I Send [name:%s, num:%d]", msg.name.c_str(), msg.num);
puber.publish(msg);
loop_rate.sleep();
}
return 0;
}
//suber.cpp
#include "ros/ros.h"
#include "pkg_a/my_message.h"
void infoCallback(const pkg_a::my_message::ConstPtr& msg)
// 回调函数名(常量 包名::消息::常量指针 变量名), 用于接收消息,且防止被修改
{
ROS_INFO("Name Changed [%s %d]", msg->name.c_str(), msg->num);
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "suber");
ros::NodeHandle n;
ROS_INFO("Im %s", ros::this_node::getName().c_str());
ros::Subscriber sub = n.subscribe("my_topic", 1000, infoCallback);
// 实例化订阅器,且订阅"my_topic"这个话题,并调用回调函数infoCallback
ros::spin();
// 有回调函数记得加上这句话
return 0;
}
1.2.2 修改CmakeLists.txt
除了创建消息的时候,我们修改了功能包pkg_a的CMakeList.xt关于话题使用的消息的配置,现在是节点配置,${PROJECT_NAME}_node直接换成节点名称,其他按照模板填写:
# add_executable(${PROJECT_NAME}_node src/pkg_a_node.cpp)
add_executable(puber src/puber.cpp)
add_executable(suber src/suber.cpp)
# target_link_libraries(${PROJECT_NAME}_node ${catkin_LIBRARIES} )
target_link_libraries(puber ${catkin_LIBRARIES})
target_link_libraries(suber ${catkin_LIBRARIES})
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
1.2.3 编译运行
cd ~/catkin_ws
catkin_make
- Master
roscore
- suber
jw@G5-5500:~/catkin_ws$ rosrun pkg_a suber
[ INFO] [1618652950.893544133]: Im /suber
[ INFO] [1618652951.900597800]: Name Changed [A 0]
- puber
jw@G5-5500:~/catkin_ws$ rosrun pkg_a puber
[ INFO] [1618652937.898728459]: Im /puber
[ INFO] [1618652937.899726548]: I Send [name:A, num:0]
1.3 python
1.3.1 puber.py
# !/usr/bin/env python
# -*- coding: <encoding name> -*- : # -*- coding: utf-8 -*-
import rospy
from pkg_a.msg import my_message # 从pkg_a的msg目录下导入my_message.msg
def talker():
pub = rospy.Publisher('my_topic', my_message, queue_size=10) # 实例化一个发布者
rospy.init_node('pyPuber', anonymous=True) # 初始化节点信息
rospy.loginfo("Im %s"%rospy.get_name()) # 等同ROS_INFO
rate = rospy.Rate(10)
a = my_message() # 实例化消息
while not rospy.is_shutdown():
a.name = "PY"
a.num = 0
rospy.loginfo("I sended :%s %d"%(a.name, a.num)) # 等同ROS_INFO
pub.publish(a)
rate.sleep()
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
pass
1.3.2 suber.py
#!/usr/bin/env python
# -*- coding: <encoding name> -*- : # -*- coding: utf-8 -*-
import rospy
from pkg_a.msg import my_message # 从pkg_a的msg目录下导入my_message.msg
def callback(data):
rospy.loginfo("I receieved :%s %d"%(data.name, data.num))
def listener():
rospy.init_node('pySuber', anonymous=True)
rospy.loginfo("Im %s"%rospy.get_name()) # 等同ROS_INFO
rospy.Subscriber("my_topic", my_message, callback)
rospy.spin()
if __name__ == '__main__':
listener()
1.3.3 编译运行
在1.1的基础上,使用自定义的message;
python不需要编译,只需要将.py文件修改为可执行文件即可:
chmod +x puber.py suber.py
运行:
roscore
rosrun pkg_a suber.py
rosrun pkg_a puber.py