又和大家见面了!今天跟大家聊聊Webots是如何跟ROS联合仿真的,话不多说,直接进入正题~
1.开餐前的准备
关于仿真环境和机器人咱就不再重复建模了,还用上次的小车,具体参见文章《Webots建模指南2 – 机器人建模》,只不过在它的基础上又加了一个激光雷达。
哦!瞧瞧这绿的让人发慌的车轮子,瞧瞧这可怜的Kinect,为了给激光雷达留位置只能悬空在那儿~ 怎么能就这么几个箱子呢,忍不了,必须给他多加几个!
完事儿以后,我们在controller
节点选择ros
控制器,保存备用。 ros
控制器是官方自带的标准控制,可以在任何一个机器人中使用,并且将Webots中的所有功能封装成了服务或主题,以便将这些信息传递给其他ROS节点,它的作用相当于Gazebo仿真时的gazebo_ros_control
插件。它是被预编译过的控制器,通常情况下能满足我们大多数需求,只有在特殊需求下我们才会去使用自定义的ros控制器,这个问题以后再讨论。 再说回标准的ROS控制器,它在ROS通信网络上声明的服务或话题如以下格式:[robot_unique_name]/[device_name]/[service/topic_name]
robot_unique_name
:是为了避免同一机器人在不同实例之间产生冲突,同一种机器人的多机协作时显得至关重要!device_name
:对应设备的名称;service/topic_name
:对应已经定义好的service/topic名称,即各种操作所对应的名称;
此外,ROS控制器还有一个特别关键的参数,Webots的模型树下,机器人的controllerArgs
节点的值需要根据情况配置一个值,这个值的格式为:--name=<robot_unique_name>
,即预定义robot_unique_name
这个参数,以用于service/topic的命名空间设置,这将直接影响我们是否能够实现ros节点与Webots控制器的成功通信。
2.餐前开胃菜(如何实现通信)
关于通信的实现,实际上有两种方案:①根据对应语言的API函数编写控制器,控制器调用ros库,进而将数据封装成话题/服务发到ros通信网络上,这相当于自定义ros控制器;②使用官方已经封装好的服务/话题; 方案①是我比较喜欢的,这个过程更接近实际机器人产品的开发,当然,操作也更麻烦;方案②当然是简单咯,但更受约束~ 既然官方已经帮我们搞定了Webots的通信过程,那接下来我们就看看怎样在ROS中去操作机器人吧! 官方实际上已经给出了一个功能包webots_ros,该功能包在本地的$WEBOTS_HOME/projects/languages/ros/
目录下。当然,还有第三种方法: sudo apt-get install ros-melodic-webots-ros*
不过我个人喜欢第一种方案,直接将官方的git包clone到工作空间。
接下来,我们分析一下webots_ros功能包的结构:
.
├── CMakeLists.txt
├── launch
│ ├── catch_the_bird.launch
│ ├── complete_test.launch
│ ├── e_puck_line.launch
│ ├── keyboard_teleop.launch
│ ├── panoramic_view_recorder.launch
│ ├── pioneer3at.launch
│ ├── robot_information_parser.launch
│ └── webots.launch
├── msg
│ ├── BoolStamped.msg
│ ├── Float64Stamped.msg
│ ├── Int32Stamped.msg
│ ├── Int8Stamped.msg
│ ├── RadarTarget.msg
│ ├── RecognitionObject.msg
│ └── StringStamped.msg
├── package.xml
├── README.md
├── src
│ ├── catch_the_bird.cpp
│ ├── complete_test.cpp
│ ├── e_puck_line.cpp
│ ├── keyboard_teleop.cpp
│ ├── panoramic_view_recorder.cpp
│ ├── pioneer3at.cpp
│ ├── pr2_beer.cpp
│ ├── robot_information_parser.cpp
│ └── webots_launcher.py
└── srv
├── automobile_get_dimensions.srv
├── camera_get_focus_info.srv
├── camera_get_info.srv
├── camera_get_zoom_info.srv
├── display_draw_line.srv
├── skin_set_bone_position.srv
├── speaker_is_sound_playing.srv
├── ...
└── ...
4 directories, 128 files
可以看到,跟普通的ros功能包也没什么大区别,我们的关注点主要在launch
和src
文件夹,另外两个文件夹存放的一些定义好的服务和消息文件,我们通过API文档查询即可。 src
文件夹中,有几个例程文件,可以通过launch
文件夹中的几个*.launch
文件启动,但启动前要注意:需要将src
下webots_launcher.py
程序的执行权限设置为允许作为程序执行文件。如果环境变量没有问题,那么在终端键入webots
可以直接启动Webots软件,它的启动逻辑是这样的:webots_launcher.py
通过运行终端命令来启动和加载Webots及其仿真环境,而webots_launcher.py
是功能包的一部分,因此可以被*.launch
文件所启动,最终实现通过*.launch
间接启动Webots的目的。
抛开几个例程不看,我们把关注点放在robot_information_parser.cpp
和complete_test.cpp
上,前者给我们提供了一个很好的程序模板,后面我们也将使用这套模板来实现自己的机器人在Webots中的控制;而后者是一个测试文档,为什么我要提它?因为它基本囊括了Webots控制函数在ros中的实现方法,如果看不懂API参考文档中的ros接口的使用时,我们可以在这里找到想要的答案!
3.控制逻辑是怎样的?
还记得之前的文章中是怎样实现机器人的驱动的吗?我们来一起回顾一下:
# 导入需要的库
from controller import Robot, Motor, DistanceSensor
# 实例化机器人
robot = Robot()
# 获取基本仿真步长
timestep = int(robot.getBasicTimeStep())
# 绑定执行器及传感器设备
# motor = robot.getMotor('motorname')
# ds = robot.getDistanceSensor('dsname')
# 使能传感器设备
# ds.enable(timestep)
# Main loop:
while robot.step(timestep) != -1:
# 控制过程
# ds.getValue()
这套逻辑虽然简单,但大多数的机器人控制程序都是这样子(包括实际机器人),祥哥个人的理解,实例化机器人、绑定执行器及传感器设备,还有使能传感器设备等操作,实际上可以概况为很多机器人产品的ros功能包中的robot_driver
部分。 写ros-webots控制程序时,逻辑也是这样子的,只不过是多了些节点的订阅与发布,把直接调用的函数变成了服务的调用与话题的订阅。
至此,我们已经捋清了整个过程,篇幅有限,我们下次再来一起实现这个联合过程! 读万卷书也要行万里路,我是罗伯特祥,下次见!
参考文献: