又和大家见面了!今天跟大家聊聊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功能包也没什么大区别,我们的关注点主要在launchsrc文件夹,另外两个文件夹存放的一些定义好的服务和消息文件,我们通过API文档查询即可。 src文件夹中,有几个例程文件,可以通过launch文件夹中的几个*.launch文件启动,但启动前要注意:需要将srcwebots_launcher.py程序的执行权限设置为允许作为程序执行文件。如果环境变量没有问题,那么在终端键入webots可以直接启动Webots软件,它的启动逻辑是这样的:webots_launcher.py通过运行终端命令来启动和加载Webots及其仿真环境,而webots_launcher.py是功能包的一部分,因此可以被*.launch文件所启动,最终实现通过*.launch间接启动Webots的目的。 抛开几个例程不看,我们把关注点放在robot_information_parser.cppcomplete_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控制程序时,逻辑也是这样子的,只不过是多了些节点的订阅与发布,把直接调用的函数变成了服务的调用与话题的订阅。 至此,我们已经捋清了整个过程,篇幅有限,我们下次再来一起实现这个联合过程! 读万卷书也要行万里路,我是罗伯特祥,下次见!
参考文献: