设置机器人模拟(基础)
目标 设置机器人仿真,并通过 ROS 2 进行控制。
辅导水平: 高级
时间 30 分钟
背景介绍
在本教程中,您将使用 Webots 机器人模拟器设置并运行一个非常简单的 ROS 2 模拟场景。
"(《世界人权宣言》) webots_ros2
软件包提供了 ROS 2 和 Webots 之间的接口。它包括多个子软件包,但在本教程中,您将只使用 webots_ros2_driver
子软件包来实现控制模拟机器人的 Python 或 C++ 插件。其他一些子软件包包含不同机器人的演示,如 TurtleBot3。它们在 Webots ROS 2 示例 page.
先决条件
建议先了解初级课程中涉及的 ROS 基本原理。 教程.特别是 使用 turtlesim、ros2 和 rqt, 了解主题, 创建工作区, 创建软件包 和 创建启动文件 是有用的先决条件。
本教程中的 Linux 和 ROS 命令可在标准 Linux 终端中运行。以下页面 安装(Ubuntu) 解释了如何安装 webots_ros2
软件包。
本教程中的 Linux 和 ROS 命令必须在 WSL(Windows Subsystem for Linux)环境中运行。以下页面 安装(视窗) 解释了如何安装 webots_ros2
软件包。
本教程中的 Linux 和 ROS 命令必须在预先配置好的 Linux 虚拟机(VM)中运行。以下页面 安装(macOS) 解释了如何安装 webots_ros2
软件包。
本教程与 2023.1.0 版本的 webots_ros2
和 Webots R2023b,以及即将推出的版本。
任务
1 创建软件包结构
让我们在自定义 ROS 2 软件包中组织代码。创建一个名为 我的包
从 来源
文件夹。将终端的当前目录更改为 ros2_ws/src
并运行:
ros2 pkg create --build-type ament_python --license Apache-2.0 --node-name my_robot_driver my_package --ependencies rclpy geometry_msgs webots_ros2_driver
"(《世界人权宣言》) --节点名 我的机器人驱动程序
选项将创建一个 my_robot_driver.py
中的 Python 模板插件 我的包
子文件夹,稍后您将对其进行修改。文件夹 --依赖 rclpy 几何参数 webots_ros2_driver
选项指定 my_robot_driver.py
插件中的 package.xml
锉刀
让我们添加一个 启动
和一个 世界
文件夹内的 我的包
文件夹。
cd my_package
mkdir launch
mkdir worlds
最终的文件夹结构如下:
源代码
└──我的包装
├── 发射/
├── my_package/
│ ├── __init__.py
│ └── my_robot_driver.py
资源
│ └── my_package
├── 测试/
│ ├── test_copyright.py
│ ├── test_flake8.py
│ └── test_pep257.py
├──世界/
├── package.xml
├── setup.cfg
└──setup.py
ros2 pkg create --build-type ament_cmake --license Apache-2.0 --node-name MyRobotDriver my_package --dependencies rclcpp geometry_msgs webots_ros2_driver pluginlib
"(《世界人权宣言》) --节点名 我的机器人驾驶员
选项将创建一个 MyRobotDriver.cpp
模板的 C++ 插件 my_package/src
子文件夹,稍后您将对其进行修改。文件夹 --依赖 rclcpp 几何参数 webots_ros2_driver 插件库
选项指定 我的机器人驾驶员
插件中的 package.xml
锉刀
让我们添加一个 启动
, a 世界
和一个 资源
文件夹内的 我的包
文件夹。
cd my_package
mkdir launch
mkdir worlds
mkdir resource
必须创建两个附加文件:"...... 我的机器人驾驶员
和 my_robot_driver.xml
pluginlib 说明文件。
touch my_robot_driver.xml
touch include/my_package/MyRobotDriver.hpp
最终的文件夹结构如下:
源代码
└──我的包装
├── include/
│ └── my_package/
│ └──MyRobotDriver.hpp
├── 发射/
资源
├── src/
│ └───MyRobotDriver.cpp
├──世界/
├── CMakeList.txt
├── my_robot_driver.xml
└──package.xml
2 设置模拟世界
您需要一个包含机器人的世界文件来启动模拟。
下载 此 世界 文件
并将其移入 my_package/worlds/
.
这实际上是一个相当简单的文本文件,你可以用文本编辑器将其可视化。其中已经包含了一个简单的机器人 my_world.wbt
世界文件。
备注
如果您想了解如何在 Webots 中创建自己的机器人模型,可以查看以下内容 教程.
3 编辑 我的机器人驱动程序
插头
"(《世界人权宣言》) webots_ros2_driver
子软件包会自动为大多数传感器创建 ROS 2 接口。关于现有设备接口以及如何配置这些接口的更多详情,请参阅教程的第二部分: 设置机器人模拟(高级).在本任务中,您将通过创建自己的自定义插件来扩展该接口。自定义插件是一个 ROS 节点,相当于机器人控制器。您可以用它访问 Webots 机器人应用程序接口 并创建您自己的主题和服务来控制您的机器人。
备注
本教程的目的是展示一个使用最少依赖项的基本示例。不过,您可以通过使用另一个 webots_ros2
名为 webots_ros2_control
,引入了一个新的依赖关系。这个子软件包创建了一个与 ros2_control
该软件包有助于控制差动轮式机器人。
开放 my_package/my_package/my_robot_driver.py
在您喜欢的编辑器中,将其内容替换为以下内容:
舶来品 rclpy
从 几何_msgs.msg 舶来品 扭转
轮距的一半 = 0.045
WHEEL_RADIUS = 0.025
类 我的机器人驾驶员:
捍卫 启动(自我, 网络节点, 属性):
自我.机器人 = 网络节点.机器人
自我.__左发动机 = 自我.机器人.获取设备('左轮电机';)
自我.__右电机 = 自我.机器人.获取设备('右轮电机';)
自我.__左发动机.设置位置(浮动('inf';))
自我.__左发动机.设置速度(0)
自我.__右电机.设置位置(浮动('inf';))
自我.__右电机.设置速度(0)
自我.__目标扭转 = 扭转()
rclpy.启动(参数=无)
自我.节点 = rclpy.创建节点('my_robot_driver')
自我.节点.创建订阅(扭转, 'cmd_vel';, 自我.__cmd_vel_callback, 1)
捍卫 __cmd_vel_callback(自我, 绹):
自我.__目标扭转 = 绹
捍卫 步骤(自我):
rclpy.自旋一次(自我.节点, 超时秒数=0)
前进速度 = 自我.__目标扭转.线形.x
角速度 = 自我.__目标扭转.角度.z
左发动机指令 = (前进速度 - 角速度 * 轮距的一半) / WHEEL_RADIUS
右电机指令 = (前进速度 + 角速度 * 轮距的一半) / WHEEL_RADIUS
自我.__左发动机.设置速度(左发动机指令)
自我.__右电机.设置速度(右电机指令)
正如您所看到的 我的机器人驾驶员
类实现了三种方法。
第一个方法名为 init(self、 ...)
实际上是 ROS 节点与 Python __init__(self、 ...)
构造函数。构造函数 启动
方法总是需要两个参数:
"(《世界人权宣言》)
网络节点
参数包含 Webots 实例的引用。"(《世界人权宣言》)
属性
参数是由 URDF 文件中给出的 XML 标记创建的字典 (4 创建 my_robot.urdf 文件) 并允许您向控制器传递参数。
模拟出的机器人实例 self.__robot
可用于访问 Webots 机器人应用程序接口.然后,它会获取两个电机实例,并用目标位置和目标速度对其进行初始化。最后创建一个 ROS 节点,并为名为 /cmd_vel
将处理 扭转
留言
捍卫 启动(自我, 网络节点, 属性):
自我.机器人 = 网络节点.机器人
自我.__左发动机 = 自我.机器人.获取设备('左轮电机';)
自我.__右电机 = 自我.机器人.获取设备('右轮电机';)
自我.__左发动机.设置位置(浮动('inf';))
自我.__左发动机.设置速度(0)
自我.__右电机.设置位置(浮动('inf';))
自我.__右电机.设置速度(0)
自我.__目标扭转 = 扭转()
rclpy.启动(参数=无)
自我.节点 = rclpy.创建节点('my_robot_driver')
自我.节点.创建订阅(扭转, 'cmd_vel';, 自我.__cmd_vel_callback, 1)
然后是执行 __cmd_vel_callback(self、 扭曲)
回调私人方法,每个 扭转
上收到的信息 /cmd_vel
主题,并将其保存在 self.__target_twist
成员变量。
捍卫 __cmd_vel_callback(自我, 绹):
自我.__目标扭转 = 绹
最后 step(self)
方法在模拟的每个时间步长都会被调用。调用 rclpy.spin_once()
以保证 ROS 节点顺利运行。在每个时间步长,该方法将检索所需的 前进速度
和 角速度
从 self.__target_twist
.由于电机是用角速度控制的,因此该方法会将 前进速度
和 角速度
转换成每个轮子的单独指令。这种转换取决于机器人的结构,更具体地说,取决于轮子的半径和它们之间的距离。
捍卫 步骤(自我):
rclpy.自旋一次(自我.节点, 超时秒数=0)
前进速度 = 自我.__目标扭转.线形.x
角速度 = 自我.__目标扭转.角度.z
左发动机指令 = (前进速度 - 角速度 * 轮距的一半) / WHEEL_RADIUS
右电机指令 = (前进速度 + 角速度 * 轮距的一半) / WHEEL_RADIUS
自我.__左发动机.设置速度(左发动机指令)
自我.__右电机.设置速度(右电机指令)
开放 my_package/include/my_package/MyRobotDriver.hpp
在您喜欢的编辑器中,将其内容替换为以下内容:
#ifndef WEBOTS_ROS2_PLUGIN_EXAMPLE_HPP
#define WEBOTS_ROS2_PLUGIN_EXAMPLE_HPP
#include "rclcpp/macros.hpp";
#include "webots_ros2_driver/PluginInterface.hpp";
#include "webots_ros2_driver/WebotsNode.hpp";
#include "geometry_msgs/msg/twist.hpp";
#include "rclcpp/rclcpp.hpp";
命名空间 我的机器人驱动程序 {
类 我的机器人驾驶员 : 公 webots_ros2_driver::插件接口 {
公:
空白 步骤() 否决;
空白 启动(webots_ros2_driver::网络节点 *网站,
标准::无序图<;标准::字符串, 标准::字符串>; 及样品;参数) 否决;
私人:
rclcpp::订阅<;几何参数::信息::扭转>::SharedPtr
cmd_vel_subscription_;
几何参数::信息::扭转 cmd_vel_msg;
WbDeviceTag 右发动机;
WbDeviceTag 左发动机;
};
} // 命名空间 my_robot_driver
#endif
班级 我的机器人驾驶员
的定义,它继承自 webots_ros2_driver::PluginInterface
类。插件必须覆盖 步骤(...)
和 启动(...)
功能。更多详情见 MyRobotDriver.cpp
文件。插件内部将使用的几个辅助方法、回调和成员变量都被声明为私有。
然后,打开 my_package/src/MyRobotDriver.cpp
在您喜欢的编辑器中,将其内容替换为以下内容:
#include "my_package/MyRobotDriver.hpp";
#include "rclcpp/rclcpp.hpp";
#include <cstdio>;
#include 功能强大;
#include <webots/motor.h>;
#include <webots/robot.h>;
#define HALF_DISTANCE_BETWEEN_WHEELS 0.045
#define WHEEL_RADIUS 0.025
命名空间 我的机器人驱动程序 {
空白 我的机器人驱动程序::初始化(
webots_ros2_driver::网络节点 *网站,
标准::无序图<;标准::字符串, 标准::字符串>; 及样品;参数) {
右发动机 = wb_robot_get_device("右轮电机";);
左发动机 = wb_robot_get_device("左轮电机";);
wb_motor_set_position(左发动机, 无限);
wb_motor_set_velocity(左发动机, 0.0);
wb_motor_set_position(右发动机, 无限);
wb_motor_set_velocity(右发动机, 0.0);
cmd_vel_subscription_ = 网站->;创建订阅<;几何参数::信息::扭转>;(
"/cmd_vel";, rclcpp::传感器数据QoS().可靠(),
[此](缢 几何参数::信息::扭转::SharedPtr 信息){
此->;cmd_vel_msg.线形 = 信息->;线形;
此->;cmd_vel_msg.角度 = 信息->;角度;
}
);
}
空白 我的机器人驱动程序::步骤() {
汽车 前进速度 = cmd_vel_msg.线形.x;
汽车 角速度 = cmd_vel_msg.角度.z;
汽车 左发动机指令 =
(前进速度 - 角速度 * 轮距的一半) /
WHEEL_RADIUS;
汽车 右电机指令 =
(前进速度 + 角速度 * 轮距的一半) /
WHEEL_RADIUS;
wb_motor_set_velocity(左发动机, 左发动机指令);
wb_motor_set_velocity(右发动机, 右电机指令);
}
} // 命名空间 my_robot_driver
#include "pluginlib/class_list_macros.hpp";
pluginlib_export_class(我的机器人驱动程序::我的机器人驾驶员,
webots_ros2_driver::插件接口)
"(《世界人权宣言》) 我的机器人驱动程序::初始化
方法会在插件通过 webots_ros2_driver
软件包。它需要两个参数:
的指针
网络节点
定义为webots_ros2_driver
可以访问 ROS 2 节点功能。"(《世界人权宣言》)
参数
参数是一个无序的字符串映射,由 URDF 文件 (4 创建 my_robot.urdf 文件) 并允许向控制器传递参数。本示例中未使用该参数。
它通过设置机器人电机、设定其位置和速度以及订阅 /cmd_vel
主题。
空白 我的机器人驱动程序::初始化(
webots_ros2_driver::网络节点 *网站,
标准::无序图<;标准::字符串, 标准::字符串>; 及样品;参数) {
右发动机 = wb_robot_get_device("右轮电机";);
左发动机 = wb_robot_get_device("左轮电机";);
wb_motor_set_position(左发动机, 无限);
wb_motor_set_velocity(左发动机, 0.0);
wb_motor_set_position(右发动机, 无限);
wb_motor_set_velocity(右发动机, 0.0);
cmd_vel_subscription_ = 网站->;创建订阅<;几何参数::信息::扭转>;(
"/cmd_vel";, rclcpp::传感器数据QoS().可靠(),
[此](缢 几何参数::信息::扭转::SharedPtr 信息){
此->;cmd_vel_msg.线形 = 信息->;线形;
此->;cmd_vel_msg.角度 = 信息->;角度;
}
);
}
订阅回调被声明为一个 lambda 函数,每收到一条 Twist 消息,就会调用这个函数。 /cmd_vel
主题,并将其保存在 cmd_vel_msg
成员变量。
[此](缢 几何参数::信息::扭转::SharedPtr 信息){
此->;cmd_vel_msg.线形 = 信息->;线形;
此->;cmd_vel_msg.角度 = 信息->;角度;
}
"(《世界人权宣言》) step()
方法在模拟的每个时间步长都会被调用。在每个时间步长,该方法都会检索所需的 前进速度
和 角速度
从 cmd_vel_msg
.由于电机是用角速度控制的,因此该方法会将 前进速度
和 角速度
转换成每个轮子的单独指令。这种转换取决于机器人的结构,更具体地说,取决于轮子的半径和它们之间的距离。
空白 我的机器人驱动程序::步骤() {
汽车 前进速度 = cmd_vel_msg.线形.x;
汽车 角速度 = cmd_vel_msg.角度.z;
汽车 左发动机指令 =
(前进速度 - 角速度 * 轮距的一半) /
WHEEL_RADIUS;
汽车 右电机指令 =
(前进速度 + 角速度 * 轮距的一半) /
WHEEL_RADIUS;
wb_motor_set_velocity(左发动机, 左发动机指令);
wb_motor_set_velocity(右发动机, 右电机指令);
}
文件的最后几行定义了 我的机器人驱动程序
命名空间,并包含一个导出 我的机器人驾驶员
类作为插件使用 pluginlib_export_class
宏。这样,Webots ROS2 驱动程序就能在运行时加载该插件。
#include "pluginlib/class_list_macros.hpp";
pluginlib_export_class(我的机器人驱动程序::我的机器人驾驶员,
webots_ros2_driver::插件接口)
备注
虽然插件是用 C++ 实现的,但必须使用 C API 才能与 Webots 控制器库交互。
4 创建 my_robot.urdf
文件
现在,您必须创建一个 URDF 文件,以声明 我的机器人驾驶员
插件。这将允许 webots_ros2_driver
ROS 节点,以启动插件并将其连接到目标机器人。
在 my_package/resource
文件夹中创建一个名为 my_robot.urdf
有这些内容:
<?xml version="1.0" ?>;
机器人 名称"我的机器人";>;
<webots>;
<plugin type="my_package.my_robot_driver.MyRobotDriver"; />;
</webots>;
</机器人>;
"(《世界人权宣言》) 类型
属性指定了文件分层结构给出的类的路径。
webots_ros2_driver
负责根据指定的软件包和模块加载类。
<?xml version="1.0" ?>;
机器人 名称"我的机器人";>;
<webots>;
<plugin type="my_robot_driver::MyRobotDriver"; />;
</webots>;
</机器人>;
"(《世界人权宣言》) 类型
属性指定了要加载的命名空间和类名。
插件库
负责根据指定信息加载类。
备注
这个简单的 URDF 文件不包含任何有关机器人的链接或关节信息,因为本教程不需要这些信息。不过,URDF 文件通常包含更多信息,详见 URDF 教程。
备注
在这里,插件不接受任何输入参数,但可以通过包含参数名称的标签来实现。
<plugin type="my_package.my_robot_driver.MyRobotDriver";>;
<参数名称>;someValue</参数名称>;
/插件>;
<plugin type="my_robot_driver::MyRobotDriver";>;
<参数名称>;someValue</参数名称>;
/插件>;
即用于向现有 Webots 设备插件传递参数(参见 设置机器人模拟(高级)).
5 创建启动文件
让我们创建一个启动文件,只需一条命令就能轻松启动模拟和 ROS 控制器。在 my_package/launch
文件夹中新建一个名为 robot_launch.py
使用此代码:
舶来品 os
舶来品 启动
从 启动 舶来品 启动说明
从 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';: 机器人描述路径},
]
)
返回 启动说明([
网点,
我的机器人驱动程序,
启动.行动.注册事件处理程序(
事件处理程序=启动.事件处理程序.进程结束时(
target_action=网点,
on_exit=[启动.行动.发射事件(事件=启动.活动.关闭())],
)
)
])
"(《世界人权宣言》) WebotsLauncher
对象是一个自定义操作,可用于启动 Webots 模拟实例。您必须在构造函数中指定模拟器将打开哪个世界文件。
网点 = WebotsLauncher(
世界=os.路.加入(软件包目录, '世界';, 'my_world.wbt')
)
然后,创建与模拟机器人交互的 ROS 节点。该节点名为 WebotsController
位于 webots_ros2_driver
包装
该节点可通过基于 IPC 和共享内存的定制协议与模拟机器人进行通信。
节点(在 WSL 中)可以通过 TCP 连接与模拟机器人(在本地 Windows 的 Webots 中)进行通信。
节点(在 docker 容器中)可以通过 TCP 连接与模拟机器人(在本地 macOS 的 Webots 中)进行通信。
在您的案例中,您只需运行该节点的一个实例,因为模拟中只有一个机器人。但如果模拟中有更多机器人,就必须为每个机器人运行一个节点实例。节点 机器人名称
参数用于定义驱动程序应连接的机器人名称。该 机器人描述
参数持有指向 URDF 文件的路径,该文件引用了 我的机器人驾驶员
插件。您可以看到 WebotsController
节点作为连接控制器插件和目标机器人的接口。
我的机器人驱动程序 = WebotsController(
机器人名称='我的机器人';,
参数=[
{'robot_description';: 机器人描述路径},
]
)
之后,这两个节点将被设置为在 启动说明
构造函数:
返回 启动说明([
网点,
我的机器人驱动程序,
最后,还添加了一个可选部分,以便在 Webots 终止时(如从图形用户界面关闭时)关闭所有节点。
启动.行动.注册事件处理程序(
事件处理程序=启动.事件处理程序.进程结束时(
target_action=网点,
on_exit=[启动.行动.发射事件(事件=启动.活动.关闭())],
)
)
备注
更多详情 WebotsController
和 WebotsLauncher
可以发现 在节点参考页面上.
6 编辑附加文件
在启动启动文件之前,您必须修改 setup.py
文件,以包含您添加的额外文件。打开 my_package/setup.py
并将其内容替换为
从 设置工具 舶来品 查找软件包, 设置
包名 = 'my_package';
数据文件 = []
数据文件.追加(('share/ament_index/resource_index/packages';, ['资源/'; + 包名]))
数据文件.追加(('分享/'; + 包名 + '/launch';, ['launch/robot_launch.py';]))
数据文件.追加(('分享/'; + 包名 + '/worlds';, ['worlds/my_world.wbt';]))
数据文件.追加(('分享/'; + 包名 + '/resource';, ['resource/my_robot.urdf';]))
数据文件.追加(('分享/'; + 包名, ['package.xml';]))
设置(
名字=包名,
版本='0.0.0',
套餐=查找软件包(排除=['测试';]),
数据文件=数据文件,
安装要求=['setuptools';],
zip_safe=正确,
维护者='用户';,
维护者电子邮件='[email protected]';,
描述='TODO:软件包描述';,
许可='TODO:许可证声明';,
测试要求=['pytest';],
入口={
'console_scripts';: [
'my_robot_driver = my_package.my_robot_driver:main';,
],
},
)
这将设置软件包并添加 数据文件
可变新添加的文件: my_world.wbt
, my_robot.urdf
和 robot_launch.py
.
在启动启动文件之前,您必须修改 CMakeLists.txt
和 my_robot_driver.xml
文件
CMakeLists.txt
定义了插件的编译规则。my_robot_driver.xml
是插件库找到 Webots ROS 2 插件的必要条件。
开放 my_package/my_robot_driver.xml
并将其内容替换为
图书馆 path="my_package";>;
<!-- `type` 属性是对插件类的引用。-->;
<!--`base_class_type`属性总是`webots_ros2_driver::PluginInterface`。-->;
类别 type="my_robot_driver::MyRobotDriver"; base_class_type="webots_ros2_driver::PluginInterface";>;
<描述>;
这 是 a Webots ROS 2 插头 范例
</description>;
</class>;
</library>;
开放 my_package/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)
# MyRobotDriver 库
add_library(
${项目名称}
共享
src/MyRobotDriver.cpp
)
目标包含目录(
${项目名称}
私人
包括
)
ament_target_dependencies(
${项目名称}
插件库
rclcpp
webots_ros2_driver
)
安装(目标
${项目名称}
存档 目的地 lib
图书馆 目的地 lib
运行时间 目的地 箱柜
)
# 安装其他目录。
安装(目录
启动
资源
世界
目的地 分享/${项目名称}/
)
ament_export_include_directories(
包括
)
AEMENT_EXTORT_LIBRARY(
${项目名称}
)
ament_package()
CMakeLists.txt 导出插件配置文件,其中包含 pluginlib_export_plugin_description_file()
定义了 C++ 插件的共享库 src/MyRobotDriver.cpp
并使用 ament_target_dependencies()
.
然后,该文件会安装程序库、目录 启动
, 资源
和 世界
到 共享/我的软件包
目录。最后,它会使用 ament_export_include_directories()
和 ament_export_libraries()
分别声明软件包,并使用 ament_package()
.
7 测试代码
在 ROS 2 工作区的终端运行
Colcon build
source install/local_setup.bash
ros2 launch my_package robot_launch.py
这将启动模拟。如果 Webots 尚未安装,则将在首次运行时自动安装。
在 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 尚未安装,则将在首次运行时自动安装。
在 macOS 上,必须在主机上启动本地服务器,才能从虚拟机启动 Webots。可下载本地服务器 上的 webots-server 存储库.
在主机终端(而非虚拟机)中指定 Webots 安装文件夹(例如:"......")。 /Applications/Webots.app
),并使用以下命令启动服务器:
export WEBOTS_HOME=/Applications/Webots.app
python3 local_simulation_server.py
在 ROS 2 工作区的 Linux 虚拟机终端中,使用以下命令构建并启动自定义软件包:
Colcon build
source install/local_setup.bash
ros2 launch my_package robot_launch.py
备注
如果您想手动安装 Webots,可以下载 这里.
然后,打开第二个终端,发送一条命令:
ros2 topic pub /cmd_vel geometry_msgs/Twist "linear: { x: 0.1 }";
机器人现在向前移动。

此时,机器人可以盲目地听从您的电机指令。但是,当你命令它向前移动时,它最终会撞到墙上。

关闭 Webots 窗口,同时关闭从启动器启动的 ROS 节点。同时关闭主题命令 Ctrl+C
在第二个终端。
摘要
在本教程中,您使用 Webots 建立了一个逼真的机器人模拟,并实施了一个自定义插件来控制机器人的电机。
下一步工作
为了改进模拟效果,机器人的传感器可以用来探测障碍物并避开它们。教程的第二部分展示了如何实现这种行为: