上一节已经给大家介绍了ROS这个多功能的操作系统的概念,和在机器人开发中可以基于我们的帮助。那么这一篇我们继续来讲ROS当中最重要的几个概念 - topic service msg srv 和 OOP(基于对象编程)。正是因为ROS给我们封装了和linux内核和进程之间的通讯方式,所以我们在不同的功能之间开发,需要遵从ROS的开发守则。
上一节除了安装ros-noetic-full-desktop之外,有些老程序猿应该想知道有没有特定的IDE。答案也是肯定的。一个好的IDE可以让项目事半功倍。官网也介绍了许多 (IDEs - ROS Wiki) 我在这里就不一一赘述了,值得一提的是:目前RoboWare,也是我认为最好的一款,和Ubuntu20.04 LTS是不兼容的,安装完打开就闪退了。所以建议大家可以安装VS extension ROS.
1. ROS node 和 package
ROS核心开发理念之一就是它的低耦合性,我们可以把机器人系统拆分成不同的功能模块,比如下面:
- 机器人视觉
-- 相机图像获取
-- 图像采样和处理
- 机器人运动规划
-- 路径规划控制(插值精度调控,自动避障,奇异点规避...)
- 机器人控制
-- 机器人状态
-- 机器人PID控制
-- 编码器数据采集
-- 伺服电机控制
如果都编辑在一个项目(文件)中,既难以分工合作,运行起来其中的小错误又容易导致系统崩溃。所以ROS的解决方案是:(图中方框)把功能模块安装节点的概念来进行开发,每个节点之间可以通过封装的TCP/IP来进行通讯。
创建节点的方式很简单:
$ cd ~/catkin_ws/src/
$ catkin_create_pkg <name package> # insert the name of your package
创建完毕后:
$ mkdir -p script
$ cd script/
即可在script路径下创建自己的节点,后面我将用python,创建下面这个例子。(也可以使用CPP, 但是py是解释性语言,如果使用CPP的话,需要编译,所以需要在package.xml和CMakelist中进行改写,然后重新catkin_make)
2. ROS topic这个例子使用topic (/number 从这个写法可以看出,使用网络通讯架构的感觉),来进行nodes之间的通讯,两个node分别为numberpublisher1.py 和 numbercounter.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import Int64
if __name__ == "__main__":
rospy.init_node("number_publisher1")
pub = rospy.Publisher("/number",Int64, queue_size=10)
#publish_freq = rospy.get_param("/number_publish_frequency")
#rate = rospy.Rate(publish_freq)
rate = rospy.Rate(11)
while not rospy.is_shutdown():
msg = Int64()
msg.data = 2
pub.publish(msg)
rate.sleep()
#!/usr/bin/env python
import rospy
from std_msgs.msg import Int64
from std_srvs.srv import SetBool
counter = 0
pub = None
def callback_function(msg):
global counter
counter += msg.data
new_msg = Int64()
new_msg.data = counter
pub.publish(new_msg)
def callback_reset_counter(req):
if req.data:
global counter
counter = 0
return True, "Counter has been successfully reset"
return False, "Counter has not been reset"
if __name__ == "__main__":
rospy.init_node("number_counter")
sub = rospy.Subscriber("/number", Int64, callback_function)
pub = rospy.Publisher("/number_counter",Int64, queue_size=10)
reset_service = rospy.Service("/reset_counter", SetBool, callback_reset_counter)
rospy.spin()
这里引入publisher 和 subscriber的概念:publisher是信息的发布方,而subscriber是信息的接收方,P将信息通过topic的形式发布出去,S通过订阅topic的形式接收到信息,但是这里的信息传输是异步的(Async)。意思是P发出信息后,并不需要和S建立连接,也不需要得到S的反馈是否收到信息。比如上面的例子,P不断发出信息(信息类型Int64)2, S收到信息之后把数字做累加。同时S有作为其它节点(rostopic)的publisher,将累加的数据发布出去
另外通过topic发送指令还有下面特点:
1. 信息传输是单向的,即P->S(/P)->S, S无法反馈给P是否接收到信息
所以topic只能用来实现功能,比如写log,无法用来实现电机位置写入,或者led闪烁,急停,等需要反馈是否成功的应用
2.对于同一个topic,P和S之间的关系可任意(只需要名字不同,或者使用anonymous option),即可以一个P发布给多个S,多个P发布给一个S,多个P发布给多个S
发现这一篇有点多了,那我把service,msg和srv,launch和parameter放在后面来讲把.