4_Publisher的编程实现
我们前面讲解了如何创建工作空间和功能包,但是我们都仅仅只创建了一个空的工作空间和功能包,什么都没有实现
我们想要进一步为功能包添加功能,就不可避免的需要添加Publisher和Subscriber.
下面我们就将讲解Publisher和Subscriber的编程实现,我们同样,还是以小海龟仿真器为例,只不过这次我们将通过编程来实现让小海龟动起来,而非通过命令行工具
0.明确任务
我们前面所讲的Publisher和Subscriber本质上都是节点,因此我们下面要实现一个能够发布message的节点,即Publisher
我们之前讲到的话题模型具体在小海龟仿真器中具体的表现如下
Publisher就是我们之前启动的teleop_key节点,而Subscriber就是turtlesim节点
Topic就是/tirtle1/cmd_vel,其中的message我们前面见过,具体包含了线速度和角速度
我们想要编写自己的Publisher,那么首先Publisher需要向ROS Master注册,然后还要向/turtle1/cmd_vel发布指定格式的Message
1.创建功能包
因为节点是以功能包形式存在的,因此我们下面创建自己的功能包,随后我们在其中加入
首先必须要的三个依赖是roscpp, rospy, std_msgs
这里我们由于需要和turtlesim进行数据传输,因此我们还需要依赖turtlesim的接口
此外我们还需要发布指定格式的message,即geometry_msgs,所以我们还需要依赖这个接口
jack@ubuntu:~/myFirstRosProject/src$ catkin_create_pkg myturtle_velocity_publishernode_cpp roscpp rospy std_msgs geometry_msgs turtlesim
2.C++实现
在动手开始写之前,我们要明白我们用C++实现一个Publisher需要干的事.
上面说到Publisher本质上还是一个节点,而节点都是以功能包的形式出现的,因此我们实际上就是编写一个功能包,而这个功能包只有一个Publisher,因此我们编写完之后需要去编译,而编译则要求我们修改CMakeLists中的内容
因此我们使用C++实现一个Publisher分两三,第一步编写代码,第二步修改CmakeLists中的内容,第三步进行编译,最后验证下我们自己写的Node能不能用
1.编写代码
-
打开当前目录的工作空间
这里用Vscode进行编写,具体如何使用VScode搭建ROS开发环境未来我会写一篇笔记记录jack@ubuntu:~/myFirstRosProject/src$ code myturtle_velocity_publishernode_cpp/
左边打开的文件夹就是我们正在编写的Node的工作空间,接下来我们在
src
新建一个c++文件
-
在当前Node的src目录下新建cpp代码文件,进行编写
-
快乐的进行编写
注释里解释了每一句的意思
我们编写一个ROS节点的流程如下:
-
初始化ros节点 & 在ROS Master中注册节点信息
对应这一句ros::init(argc,argv,"my_velocity_publisher_cpp")
其中argc和argv是系统变量,表示用户运行时传入的参数个数,argv是所有传入参数的列表,"my_velocity_publisher_cpp"是我们自定义的节点名,
需要注意的是,ros中节点的命名规则是小写字母/下划线/数字的组合,不建议带大写字母
这个接口既初始化节点,又进行了注册 -
创建发布者
对应这句话
ros::Publisher turtleVelocityPublihser_cpp=n.advertise<geometry_msgs::Twist> ("/turtle1/cmd_vel",10)
其中我们要指出发布的消息类型,发布的话题名和消息的队列长度 -
发布数据
对应这句话turtleVelocityPublihser_cpp.publish(velocity_msg)
-
向命令行输出信息
对应这句话
ROS_INFO("Publish the velocity [%0.2f m/s, %0.2f rad/s]\n\t\t Info from Jack",velocity_msg.linear.x,velocity_msg.angular.z);
/* * This is a node created by Jack Wang to practice ROS publisher */ // Include head files #include <ros/ros.h> #include <geometry_msgs/Twist.h> // Receive system variable // argc is the number of system variable, ** argv is the list of all the system variables int main(int argc, char ** argv){ // Initialize ROS node // argc,argv,name of the node ros::init(argc,argv,"my_velocity_publisher_cpp"); // Create node handle ros::NodeHandle n; // Create a ros publisher which publishes topic named /turtle1/cmd_vel // and the type of message it sends is geometry_msgs::Twist // max length of the message quene is 10 ros::Publisher turtleVelocityPublihser_cpp=n.advertise<geometry_msgs::Twist> ("/turtle1/cmd_vel",10); // Set loop frequency to 10 ros::Rate loop_rate(10); // Things to do in while loop while (ros::ok()) { // Initialize geometry_msgs::Twist message geometry_msgs::Twist velocity_msg; // Set the message velocity_msg.angular.z=0.2; // Set the angular velocity to 0.2 rad/s velocity_msg.linear.x=0.5; // Set the linear velocity to 0.5 m/s // Publish the message turtleVelocityPublihser_cpp.publish(velocity_msg); // Print info on the terminal ROS_INFO("Publish the velocity [%0.2f m/s, %0.2f rad/s]\n\t\t Info from Jack", velocity_msg.linear.x,velocity_msg.angular.z ); // Delay as setting frequency loop_rate.sleep(); } return 0; }
-
2.修改Cmakelists中的内容
前面讲过CmakeLists使用了简单的语言描述了该如何对一个项目进行编译
我们这里主要是设置catkin编译这个包的时候会编译我们刚刚添加的文件
所以主要有两点,第一指定编译我们刚才新增的文件,第二指定该文件的依赖库
-
打开该node的CmakeLists
-
在指定位置添加如下两行
add_executable(myVelocityPublisherNode src/myVelocityPublisherNode.cpp) target_link_libraries(myVelocityPublisherNode ${catkin_LIBRARIES})
第一句表明需要编译的文件和编译后的可执行文件名
第二句表明需要添加的库文件
3.编译
接下来我们回到工作空间的根目录,使用catkin_make进行编译
jack@ubuntu:~/myFirstRosProject$ catkin_make
4.运行编写的node
上面我们自己创建了一个功能包,这个功能包中只有一个node
接下来我们将运行所编写的node
具体分为两步,第一步添加环境变量,使得Tab
键能够补全自己编写的包,第二步就是运行
-
添加环境变量
注意这个环境变量仅在当前运行过下面命令的命令行生效jack@ubuntu:~/myFirstRosProject$ source ./devel/setup.bash
-
运行自己的包
和运行小海龟仿真器一样,我们首先启动ros master,然后在使用rosrun命令运行自己的包即可
注
通过上面的操作,我们最终得知一个node实际上就是一个可执行文件,我们运行这个node实际上就是运行这个可执行文件
而就像上面turtlesim_node和我们的my_velocity_publisher_cpp之间会有数据(即geometry_msgs::Twist)的传输,因此节点之间数据的传输可以类比于进程之间的通信
此外,我们运行的my_velocity_publisher_cpp最终的可执行文件的位置在./devel/lib/包名
这个文件夹下
3.Python实现
Python由于是脚本语言,因此不需要像上面使用C++来编写node一样修改CmakeLists的内容然后编译
但是我们具体在代码中两者实际上是很像的,并且编写运行流程也很像,只是直接添加环境变量并运行即可
1.编写代码
代码的流程和上面C++版本的代码流程是一样的,同样我们需要创建包,一般来说,我们正常开发一个软件包的时候,可能会有很多节点
而有的节点是C++写得,有些节点是Python写得,这个时候我们会在创建的包下再新建一个script文件夹,所有用Python写得节点都放在里面
而所有的C++写得节点放在src中,写好之后修改当前包的CmakeLists文件即可
-
打开文件夹并创建Python文件
jack@ubuntu:~/myFirstRosProject/src/myturtle_velocity_publishernode_cpp$ code scripts/myVelocityPublisherNode.py
-
快乐的编写代码
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Importing libraries import rospy from geometry_msgs.msg import Twist # Encapsulate the function def myVelocityPublisher(): # Initialize ros node rospy.init_node("my_velocity_publisher_py",anonymous=True) # Create a publisher turtle_vel_pub = rospy.Publisher("/turtle1/cmd_vel",Twist,queue_size=10) # Set loop rate rate = rospy.Rate(10) while not rospy.is_shutdown(): velocity_msgs=Twist() velocity_msgs.angular.z=0.2 velocity_msgs.linear.x=0.5 turtle_vel_pub.publish(velocity_msgs) rospy.loginfo("Publish the velocity [%0.2f m/s, %0.2f rad/s]\n\t\t Info from Jack",\ velocity_msgs.linear.x,velocity_msgs.angular.z) rate.sleep() # Main program if __name__=="__main__": try: myVelocityPublisher() except rospy.ROSInitException: print("my_velocity_publisher_py is terminated !")
2.运行代码
我们完成编辑后调用Python解释器运行即可,或者赋予可执行权限后运行
-
赋予可执行权限
使用如下命令添加可执行权限
jack@ubuntu:~/myFirstRosProject/src/myturtle_velocity_publishernode_cpp/scripts$ chmod u+x myVelocityPublisherNode.py
-
添加环境变量
和C++编译后的结果一样,我们先启动ros master,然后在rosrun节点即可,但是需要注意的是,我们直接写的脚本也需要添加环境变量,这样才能用Tab补全jack@ubuntu:~$ source myFirstRosProject/devel/setup.bash
-
rosrun运行即可
jack@ubuntu:~$ rosrun myturtle_velocity_publishernode_cpp myVelocityPublisherNode.py