Skip to content

Webots 仿真重置处理器

目标: 在机器人仿真中扩展重置处理器,以便在按下 Webots 重置按钮时重启节点。

教程级别: 进阶

预计时间: 10 分钟

在本教程中,你将学习如何在使用 Webots 的机器人仿真中实现重置处理器。

Webots 的重置按钮会将世界恢复到初始状态并重启控制器。

这个功能很方便,可以快速重置仿真。但在 ROS 2 的上下文中,机器人控制器不会自动重新启动,导致仿真停止。

重置处理器允许你在 Webots 中按下重置按钮时重启特定节点或执行额外操作。

这在需要重置仿真状态或重启特定组件、而又不想完全重启整个 ROS 系统的场景中非常实用。

在继续本教程之前,请确保你已完成以下内容:

简单情况下的重置处理器(仅控制器)

Section titled “简单情况下的重置处理器(仅控制器)”

在你的包的 launch 文件中,添加 respawn 参数。

def generate_launch_description():
robot_driver = WebotsController(
robot_name='my_robot',
parameters=[
{'robot_description': robot_description_path}
],
# 每次重置仿真时,控制器会自动重新启动
respawn=True
)
# 启动 Webots
webots = WebotsLauncher(world=PathJoinSubstitution([package_dir, 'worlds', world]))
return LaunchDescription([
webots,
robot_driver
])

重置时,Webots 会终止所有驱动节点。

因此,要在重置后重新启动它们,需要将驱动节点的 respawn 属性设置为 True。

这样可以确保驱动节点在重置后正常运行。

多节点的重置处理器(无需关闭)

Section titled “多节点的重置处理器(无需关闭)”

如果还有其他节点需要与驱动节点一起启动(例如 ros2_control 节点),可以使用 OnProcessExit 事件处理器:

def get_ros2_control_spawners(*args):
# 在此处声明所有必须在仿真重置时重启的节点
ros_control_node = Node(
package='controller_manager',
executable='spawner',
arguments=['diffdrive_controller']
)
return [
ros_control_node
]
def generate_launch_description():
robot_driver = WebotsController(
robot_name='my_robot',
parameters=[
{'robot_description': robot_description_path}
],
# 每次重置仿真时,控制器会自动重新启动
respawn=True
)
# 启动 Webots
webots = WebotsLauncher(world=PathJoinSubstitution([package_dir, 'worlds', world]))
# 声明重置处理器,在 robot_driver 退出时重启节点
reset_handler = launch.actions.RegisterEventHandler(
event_handler=launch.event_handlers.OnProcessExit(
target_action=robot_driver,
on_exit=get_ros2_control_spawners,
)
)
return LaunchDescription([
webots,
robot_driver,
reset_handler
] + get_ros2_control_spawners())

不能在 ros2_control 节点上使用 respawn 属性,因为 spawner 在启动期间就会退出,而不是在仿真重置时退出。

正确的做法是将节点列表放在一个函数中(例如 get_ros2_control_spawners)。

执行 launch 文件时,该列表中的节点会与其他节点一起启动。

通过 reset_handler,这个函数还被注册为在 robot_driver 节点退出时要执行的操作,而该退出的时刻正对应于在 Webots 界面中重置仿真。

robot_driver 节点仍需将 respawn 属性设置为 True,以便它与 ros2_control 节点一起重启。

使用当前的 ROS 2 launch API,对于需要在重启前关闭节点的 launch 文件(例如包含 Nav2 或 RViz 的场景),无法自动完成重置。

原因是 ROS 2 目前不支持从 launch 文件中关闭特定节点。

有一种变通方案,但需要在按下重置按钮后手动重启节点。

Webots 需要在单独的 launch 文件中启动,不包含其他节点。

def generate_launch_description():
# 启动 Webots
webots = WebotsLauncher(world=PathJoinSubstitution([package_dir, 'worlds', world]))
return LaunchDescription([
webots
])

第二个 launch 文件需要从另一个进程启动。

它包含所有其他节点,包括机器人控制器/插件、Navigation2 节点、RViz、状态发布器等。

def generate_launch_description():
robot_driver = WebotsController(
robot_name='my_robot',
parameters=[
{'robot_description': robot_description_path}
]
)
ros_control_node = Node(
package='controller_manager',
executable='spawner',
arguments=['diffdrive_controller']
)
nav2_node = IncludeLaunchDescription(
PythonLaunchDescriptionSource(os.path.join(
get_package_share_directory('nav2_bringup'), 'launch', 'bringup_launch.py')),
launch_arguments=[
('map', nav2_map),
('params_file', nav2_params),
],
)
rviz = Node(
package='rviz2',
executable='rviz2',
output='screen'
)
# 声明处理器,在 robot_driver 退出时关闭所有节点
shutdown_handler = launch.actions.RegisterEventHandler(
event_handler=launch.event_handlers.OnProcessExit(
target_action=robot_driver,
on_exit=[launch.actions.EmitEvent(event=launch.events.Shutdown())],
)
)
return LaunchDescription([
robot_driver,
ros_control_node,
nav2_node,
rviz,
shutdown_handler
])

第二个 launch 文件中包含一个处理器,在驱动节点退出时(即仿真重置时)触发关闭事件。

按下重置按钮后,需要从命令行手动重启第二个 launch 文件。

在本教程中,你学习了如何在使用 Webots 的机器人仿真中实现重置处理器。

它允许你在 Webots 中按下重置按钮时重启特定节点或执行额外操作。

你根据仿真的复杂程度和节点需求,探索了不同的实现方法。