您正在阅读的是旧版本但仍受支持的 ROS 2 文档。 Jazzy.
设置机器人模拟(高级)
目标 使用避障节点扩展机器人模拟。
辅导水平: 高级
时间 20 分钟
背景介绍
在本教程中,您将扩展本教程第一部分创建的软件包: 设置机器人模拟(基础).目的是实现一个 ROS 2 节点,利用机器人的距离传感器避开障碍物。本教程的重点是将机器人设备与 webots_ros2_driver
接口。
先决条件
这是教程第一部分的继续: 设置机器人模拟(基础).必须从第一部分开始,设置自定义软件包和必要的文件。
本教程与 2023.1.0 版本的 webots_ros2
和 Webots R2023b,以及即将推出的版本。
任务
1 更新 my_robot.urdf
正如 设置机器人模拟(基础), webots_ros2_driver
包含可将大多数 Webots 设备与 ROS 2 直接连接的插件。这些插件可以使用 设备
标签。机器人 参照
属性应与 Webots 设备 名字
参数。所有现有接口和相应参数的列表如下所示 设备参考页面.对于 URDF 文件中未配置的可用设备,将自动创建接口,并使用 ROS 参数的默认值(例如:"......")。 更新 费率
, 主题 名字
和 画框 名字
).
在 my_robot.urdf
将全部内容替换为
<?xml version="1.0" ?>;
机器人 名称"我的机器人";>;
<webots>;
设备 参考文献="ds0"; type="距离传感器";>;
<ros>;
<主题名称>;/left_sensor</topicName>;
<始终打开>;真</alwaysOn>;
</ros>;
</device>;
设备 参考文献="ds1"; type="距离传感器";>;
<ros>;
<主题名称>;/右传感器</topicName>;
<始终打开>;真</alwaysOn>;
</ros>;
</device>;
<plugin type="my_package.my_robot_driver.MyRobotDriver"; />;
</webots>;
</机器人>;
<?xml version="1.0" ?>;
机器人 名称"我的机器人";>;
<webots>;
设备 参考文献="ds0"; type="距离传感器";>;
<ros>;
<主题名称>;/left_sensor</topicName>;
<始终打开>;真</alwaysOn>;
</ros>;
</device>;
设备 参考文献="ds1"; type="距离传感器";>;
<ros>;
<主题名称>;/右传感器</topicName>;
<始终打开>;真</alwaysOn>;
</ros>;
</device>;
<plugin type="my_robot_driver::MyRobotDriver"; />;
</webots>;
</机器人>;
除了您的自定义插件外, webots_ros2_driver
将解析 设备
标记指向 距离传感器 节点,并使用 <ros>;
标签来启用传感器并命名其主题。
2 创建 ROS 节点以避开障碍物
机器人将使用一个标准的 ROS 节点来检测墙壁,并发送电机指令以避开墙壁。在 my_package/my_package/
文件夹,创建一个名为 obstacle_avoider.py
使用此代码:
舶来品 rclpy
从 rclpy.node 舶来品 节点
从 传感器_msgs.msg 舶来品 范围
从 几何_msgs.msg 舶来品 扭转
最大范围 = 0.15
类 障碍规避器(节点):
捍卫 启动(自我):
棒极了().启动('obstacle_avoider';)
自我.__出版商 = 自我.创建出版商(扭转, 'cmd_vel';, 1)
自我.创建订阅(范围, 'left_sensor', 自我.__左传感器回调, 1)
自我.创建订阅(范围, 'right_sensor', 自我.__右传感器回调, 1)
捍卫 __左传感器回调(自我, 信息):
自我.__左传感器值 = 信息.范围
捍卫 __右传感器回调(自我, 信息):
自我.右传感器值 = 信息.范围
命令消息 = 扭转()
命令消息.线形.x = 0.1
如果 自我.__左传感器值 <; 0.9 * 最大范围 或 自我.右传感器值 <; 0.9 * 最大范围:
命令消息.角度.z = -2.0
自我.__出版商.发布(命令消息)
捍卫 主要(参数=无):
rclpy.启动(参数=参数)
避免者 = 障碍规避器()
rclpy.后旋(避免者)
# 明确销毁节点
# (可选,否则将自动完成
# 当垃圾回收器销毁节点对象时)
避免者.destroy_node()
rclpy.关闭()
如果 姓名____ == '__main__';:
主要()
该节点将为命令创建一个发布者,并在此订阅传感器主题:
自我.__出版商 = 自我.创建出版商(扭转, 'cmd_vel';, 1)
自我.创建订阅(范围, 'left_sensor', 自我.__左传感器回调, 1)
自我.创建订阅(范围, 'right_sensor', 自我.__右传感器回调, 1)
从左侧传感器接收到的测量值将被复制到成员字段中:
捍卫 __左传感器回调(自我, 信息):
自我.__左传感器值 = 信息.范围
最后,将向 /cmd_vel
当接收到来自正确传感器的测量值时,就会出现该主题。传感器 命令消息
将至少以 线性.x
以便在没有检测到障碍物时让机器人移动。如果两个传感器中的任何一个检测到障碍物、 命令消息
也会以 angular.z
以使机器人右转。
捍卫 __右传感器回调(自我, 信息):
自我.右传感器值 = 信息.范围
命令消息 = 扭转()
命令消息.线形.x = 0.1
如果 自我.__左传感器值 <; 0.9 * 最大范围 或 自我.右传感器值 <; 0.9 * 最大范围:
命令消息.角度.z = -2.0
自我.__出版商.发布(命令消息)
机器人将使用一个标准的 ROS 节点来检测墙壁,并发送电机指令以避开墙壁。在 my_package/include/my_package
文件夹中,创建一个名为 ObstacleAvoider.hpp
使用此代码:
#include <内存>;
#include "geometry_msgs/msg/twist.hpp";
#include "rclcpp/rclcpp.hpp";
#include "sensor_msgs/msg/range.hpp";
类 障碍规避器 : 公 rclcpp::节点 {
公:
不含糊 障碍规避器();
私人:
空白 左传感器回调(缢 传感器::信息::范围::SharedPtr 信息);
空白 右传感器回调(缢 传感器::信息::范围::SharedPtr 信息);
rclcpp::出版商<;几何参数::信息::扭转>::SharedPtr 出版商_;
rclcpp::订阅<;传感器::信息::范围>::SharedPtr 左传感器子传感器;
rclcpp::订阅<;传感器::信息::范围>::SharedPtr 右传感器子系统;
双人 左传感器值{0.0};
双人 右传感器值{0.0};
};
在 my_package/src
文件夹中,创建一个名为 ObstacleAvoider.cpp
使用此代码:
#include "my_package/ObstacleAvoider.hpp";
#define MAX_RANGE 0.15
障碍规避器::障碍规避器() : 节点("obstacle_avoider";) {
出版商_ = 创建出版商<;几何参数::信息::扭转>;("/cmd_vel";, 1);
左传感器子传感器 = 创建订阅<;传感器::信息::范围>;(
"/left_sensor";, 1,
标准::约束(及样品;障碍规避器::左传感器回调, 此,
标准::占位符::_1));
右传感器子系统 = 创建订阅<;传感器::信息::范围>;(
"/right_sensor";, 1,
标准::约束(及样品;障碍规避器::右传感器回调, 此,
标准::占位符::_1));
}
空白 障碍规避器::左传感器回调(
缢 传感器::信息::范围::SharedPtr 信息) {
左传感器值 = 信息->;范围;
}
空白 障碍规避器::右传感器回调(
缢 传感器::信息::范围::SharedPtr 信息) {
右传感器值 = 信息->;范围;
汽车 命令消息 = 标准::make_unique<;几何参数::信息::扭转>;();
命令消息->;线形.x = 0.1;
如果 (左传感器值 <; 0.9 * 最大范围 ||
右传感器值 <; 0.9 * 最大范围) {
命令消息->;角度.z = -2.0;
}
出版商_->;发布(标准::行动(命令消息));
}
int 主要(int 参数, 烧焦 *参数[]) {
rclcpp::启动(参数, 参数);
汽车 避免者 = 标准::共享<;障碍规避器>;();
rclcpp::后旋(避免者);
rclcpp::关闭();
返回 0;
}
该节点将为命令创建一个发布者,并在此订阅传感器主题:
出版商_ = 创建出版商<;几何参数::信息::扭转>;("/cmd_vel";, 1);
左传感器子传感器 = 创建订阅<;传感器::信息::范围>;(
"/left_sensor";, 1,
标准::约束(及样品;障碍规避器::左传感器回调, 此,
标准::占位符::_1));
右传感器子系统 = 创建订阅<;传感器::信息::范围>;(
"/right_sensor";, 1,
标准::约束(及样品;障碍规避器::右传感器回调, 此,
标准::占位符::_1));
从左侧传感器接收到的测量值将被复制到成员字段中:
空白 ObstacleAvoider::leftSensorCallback(
缢 传感器::信息::范围::SharedPtr 信息) {
左传感器值 = 信息->;范围;
}
最后,将向 /cmd_vel
当接收到来自正确传感器的测量值时,就会出现该主题。传感器 命令消息
将至少以 线性.x
以便在没有检测到障碍物时让机器人移动。如果两个传感器中的任何一个检测到障碍物、 命令消息
也会以 angular.z
以使机器人右转。
空白 ObstacleAvoider::rightSensorCallback(
缢 传感器::信息::范围::SharedPtr 信息) {
右传感器值 = 信息->;范围;
汽车 命令消息 = 标准::make_unique<;几何参数::信息::扭转>;();
命令消息->;线形.x = 0.1;
如果 (左传感器值 <; 0.9 * 最大范围 ||
右传感器值 <; 0.9 * 最大范围) {
命令消息->;角度.z = -2.0;
}
出版商_->;发布(标准::行动(命令消息));
}
3 更新其他文件
您必须修改这两个文件才能启动新节点。
编辑 setup.py
并取代 控制台脚本
用:
'console_scripts';: [
'my_robot_driver = my_package.my_robot_driver:main';,
'obstacle_avoider=my_package.obstacle_avoider:main';
],
这将为 避障器
节点。
编辑 CMakeLists.txt
并添加编译和安装 避障器
:
cmake_minimum_required(版本 3.5)
项目(我的包)
如果(不是 cmake_cxx_standard)
设置(cmake_cxx_standard 14)
endif()
# 除了软件包的特定依赖项,我们还需要 `pluginlib` 和 `webots_ros2_driver` 。
查找软件包(ament_cmake 要求)
查找软件包(rclcpp 要求)
查找软件包(std_msgs 要求)
查找软件包(几何参数 要求)
查找软件包(插件库 要求)
查找软件包(webots_ros2_driver 要求)
# 导出插件配置文件
pluginlib_export_plugin_description_file(webots_ros2_driver my_robot_driver.xml)
# 避开障碍
包含目录(
包括
)
添加可执行(避障器
src/ObstacleAvoider.cpp
)
ament_target_dependencies(避障器
rclcpp
几何参数
传感器
)
安装(目标
避障器
目的地 lib/${项目名称}
)
安装(
目录 包括
目的地 包括
)
# MyRobotDriver 库
add_library(
${项目名称}
共享
src/MyRobotDriver.cpp
)
目标包含目录(
${项目名称}
私人
包括
)
ament_target_dependencies(
${项目名称}
插件库
rclcpp
webots_ros2_driver
)
安装(目标
${项目名称}
存档 目的地 lib
图书馆 目的地 lib
运行时间 目的地 箱柜
)
# 安装其他目录。
安装(目录
启动
资源
世界
目的地 分享/${项目名称}/
)
ament_export_include_directories(
包括
)
AEMENT_EXTORT_LIBRARY(
${项目名称}
)
ament_package()
转到文件 robot_launch.py
并替换为
舶来品 os
舶来品 启动
从 launch_ros.actions 舶来品 节点
从 启动 舶来品 启动说明
从 ament_index_python.packages 舶来品 获取软件包共享目录
从 webots_ros2_driver.webots_launcher 舶来品 WebotsLauncher
从 webots_ros2_driver.webots_controller 舶来品 WebotsController
捍卫 生成发射描述():
软件包目录 = 获取软件包共享目录('my_package';)
机器人描述路径 = os.路.加入(软件包目录, '资源';, 'my_robot.urdf')
网点 = WebotsLauncher(
世界=os.路.加入(软件包目录, '世界';, 'my_world.wbt')
)
我的机器人驱动程序 = WebotsController(
机器人名称='我的机器人';,
参数=[
{'robot_description';: 机器人描述路径},
]
)
避障器 = 节点(
包装='my_package';,
可执行='obstacle_avoider';,
)
返回 启动说明([
网点,
我的机器人驱动程序,
避障器,
启动.行动.注册事件处理程序(
事件处理程序=启动.事件处理程序.进程结束时(
target_action=网点,
on_exit=[启动.行动.发射事件(事件=启动.活动.关闭())],
)
)
])
这将创建一个 避障器
节点,该节点将被包含在 启动说明
.
4 测试避障代码
从 ROS 2 工作区的终端启动模拟:
在 ROS 2 工作区的终端运行
Colcon build
source install/local_setup.bash
ros2 launch my_package robot_launch.py
在 WSL ROS 2 工作区的终端运行
Colcon build
export WEBOTS_HOME=/mnt/c/Program\ Files/Webots
source install/local_setup.bash
ros2 launch my_package robot_launch.py
请务必使用 /mnt
前缀,以便从 WSL 访问 Windows 文件系统。
如果尚未指定 Webots 安装文件夹,请在主机终端(而非虚拟机)中指定该文件夹(例如:"............")。 /Applications/Webots.app
),并使用以下命令启动服务器:
export WEBOTS_HOME=/Applications/Webots.app
python3 local_simulation_server.py
请注意,一旦 ROS 2 节点结束,服务器将继续运行。每次启动新的模拟时,都不需要重新启动服务器。在 ROS 2 工作区的 Linux 虚拟机终端上,用以下命令构建并启动自定义软件包:
cd ~/ros2_ws
Colcon build
source install/local_setup.bash
ros2 launch my_package robot_launch.py
您的机器人应向前行驶,在撞墙之前应顺时针转动。您可以按 Ctrl+F10
或访问 查看
菜单 可选 效果图
和 显示 距离传感器 射线
来显示机器人距离传感器的测距范围。

摘要
在本教程中,您将使用避障器 ROS 2 节点扩展基本模拟,该节点可根据机器人的距离传感器值发布速度指令。
下一步工作
您可能需要改进插件或创建新节点,以改变机器人的行为。您还可以执行重置处理程序,以便在从 Webots 界面重置模拟时自动重启 ROS 节点: