教程 1:Webots 中的第一次模拟 (30分钟)


目标与简介

本教程将带你熟悉 Webots 的界面和基本概念。你将创建一个包含地板、墙壁、箱子和 e-puck 机器人的仿真环境,并编写控制器让机器人运动。

Webots e-puck 教程效果图

第一个教程完成后你应该看到的效果

目录

1. 启动 Webots

📝 动手实践 #1: 双击Webots 图标 Webots图标 启动软件(或在终端命令行运行)。首次运行会提示选择主题,可直接关闭导览。建议浏览官方导览,了解Webots 的强大功能。

2. 创建新世界

World(世界文件)是一个包含场景中所有对象位置、外观、交互方式、天空颜色、重力、摩擦、物体质量等信息的文件。它定义了仿真的初始状态。不同的对象称为 Nodes(节点),并以层级结构组织在 Scene Tree(场景树)中,因此一个节点可以包含子节点。世界文件以 .wbt 为扩展名,文件格式基于 VRML97 语言,且为人类可读。所有世界文件必须直接存放在名为 worlds 的目录下。

📝 动手实践 #2: 通过单击 3D 视图上的 暂停按钮 暂停当前仿真(请参阅用户界面说明以找出按钮)。如果主工具栏上的虚拟时间计数器停止,则模拟将暂停。从File / New / New Project Directory... ("文件"/"新建"/"新建项目目录...")菜单项创建一个新项目,然后按照说明进行操作:
  1. 1.项目目录命名为 my_first_simulation,不要用默认的 my_project
  2. 2.世界文件命名为 my_first_simulation.wbt,不要用默认的 empty.wbt
  3. 3.勾选所有选项,包括默认未勾选的 “Add a rectangle arena”

Webots 会显示刚刚创建的目录和文件列表,这就是 Webots 项目的标准文件结构。点击 Finish(Windows, Linux)或 Done(macOS)按钮关闭此窗口。

恭喜!你已经创建了第一个 Webots 世界。3D 视图中会显示一个带有方格地板的正方形场地。你可以用鼠标左键、右键和滚轮在 3D 视图中移动视角(鼠标操作说明)。

Webots 世界文件中的节点以树状结构(scene tree)组织。主窗口有两个子窗口可查看场景树:3D 视图(窗口中央,显示场景树的三维效果)和 场景树视图(左侧,显示场景树的层级结构)。你可以在场景树视图中修改节点和属性。此时应能看到如下节点:

每个节点都有一些可自定义的属性,称为 Fields。我们来修改这些属性以调整矩形场地:

📝 动手实践 #3: 在场景树中双击 RectangleArena 节点,展开并显示其属性。
  1. 1.选择 floorTileSize 字段,将其值设置为 0.25 0.25(原值为 0.5 0.5)。你会在 3D 视图中立即看到效果。
  2. 2.选择 wallHeight 字段,将其值改为 0.05(原值为 0.1)。场地的墙会变矮。

在场景树视图中,如果某个字段的值与默认值不同,会以不同颜色显示(具体颜色取决于主题)。

3. 添加箱子

现在我们来添加一些物体到场景中。我们将添加几个木箱来丰富仿真环境。

📝 动手实践 #4: 双击场景树中的 RectangleArena 节点将其关闭并选中。点击场景树顶部的 Add 按钮 ADD图标 。在弹出的对话框中,选择 PROTO nodes (Webots Projects) / objects / factory / containers / WoodenBox (Solid)。此时场地中央会出现一个大箱子。双击该箱子以展开其属性。
  1. 1.将 size 改为 0.1 0.1 0.1(原值为 0.6 0.6 0.6)。
  2. 2.将 translation 改为 0 0 0.05(原值为 0 0 0.3)。也可以用 3D 视图中的蓝色箭头调整 translation.z
  3. 3.按住 Shift 拖动箱子到场地角落。
  4. 4.选中箱子,按 Ctrl+CCtrl+V(Windows, Linux)或 ⌘ command+C⌘ command+V(macOS)复制粘贴,再拖动新箱子到其他位置。共创建三个箱子。
  5. 5.移动箱子,确保没有箱子在场地中央。可用蓝色旋转箭头或右键拖动旋转箱子,也可在场景树中修改 WoodenBox 节点的 rotation 字段。
  6. 6.满意后点击保存按钮save_button图标保存世界。

如何用平移和旋转手柄移动物体,可参考本节说明

4. 添加 e-puck 机器人

e-puck 是一款小型差速轮机器人,带有 10 个 LED 和多个传感器(包括 8 个 距离传感器 和一个 摄像头)。本教程只用到它的轮子,其他功能将在后续教程中介绍。

现在我们要将 e-puck 机器人添加到世界中。请确保仿真已暂停且虚拟时间为 0(如不是,请点击 重置按钮 重置按钮)。

当你打算保存 Webots 世界时,务必先暂停仿真并重置到初始状态(主工具栏虚拟时间应为 0:00:00:000),否则每次保存时 3D 对象的位置可能会累计误差。因此,修改世界的正确顺序是:暂停、重置、修改并保存

我们无需从零创建 e-puck,只需导入一个 E-puck 节点即可。这个节点其实是一个 PROTO 节点,和前面用到的 RectangleArenaWoodenBox 一样。PROTO 机制允许你创建和复用自定义对象。

📝 动手实践 #5: 在场景树中选中最后一个 WoodenBox 节点。点击场景树顶部的 Add 按钮 ADD图标。在对话框中选择 PROTO nodes (Webots Projects) / robots / gctronic / e-puck / E-puck (Robot)。此时场地中央会出现一台 e-puck 机器人。像移动箱子一样拖动和旋转机器人。保存仿真,并点击 运行按钮 按钮运行仿真。

机器人会自动移动、闪灯并避障,这是因为它有默认的控制器。你可能会注意到 3D 视图左上角弹出一个黑色小窗口,显示 e-puck 机器人摄像头的画面。只有当控制器显式启用摄像头时,这个画面才会显示内容。你可以拖动该窗口移动位置,也可以拖动右下角调整大小,点击右上角"x"关闭。需要时可在 Overlays 菜单的 Camera Devices 子菜单中重新打开。由于本教程暂不需要摄像头画面,可以直接关闭。

现在,仿真运行时我们来体验一下物理效果:

📝 动手实践 #6: 按住 Alt左键拖动(Mac 上用 ⌥ Option,Linux 需 Ctrl+Alt+左键)可对机器人施加力。
默认情况下,WoodenBox 节点没有质量,被视为固定在地面,无法施加力。要让箱子参与物理仿真,请将 WoodenBoxmass 字段设置为非零值(如 0.2kg),这样就可以对箱子施加力了。

仿真可以通过 暂停按钮 暂停、单步按钮 单步、运行按钮 实时运行或 快速运行按钮 快速运行。

现在我们要修改世界,将物理仿真的步长(时间步长)调小,这样可以提升仿真的精度和稳定性(但会降低最大仿真速度)。

📝 动手实践 #7: 暂停仿真并重置。在场景树视图中,展开第一个 WorldInfo 节点,将 basicTimeStep 字段设置为 16,然后保存仿真。

控制器(controller)是定义机器人行为的程序。Webots 控制器可以用多种编程语言编写:C、C++、Java、Python、MATLAB 等。C、C++ 和 Java 控制器需要编译后才能运行,Python 和 MATLAB 控制器为解释型语言,无需编译即可直接运行。本教程以 C 语言为例,所有代码片段也可用于 C++、Java、Python 和 MATLAB。关于不同语言的控制器设置方法,请参考语言章节

controller 字段用于指定 Robot 节点当前关联的控制器。注意:同一个控制器可以被多个机器人共用,但每个机器人同一时刻只能使用一个控制器。每个控制器会作为独立的子进程由 Webots 启动,因此它们拥有独立的地址空间,可能运行在不同的 CPU 核心上。

📝 动手实践 #8: 新建一个 C(或其他语言)控制器,命名为 epuck_go_forward(C++/Java 命名为 EPuckGoForward),通过菜单 File / New / New Robot Controller...。这将在 my_first_simulation/controllers 目录下创建一个新的 epuck_go_forward(或 EPuckGoForward)文件夹。选择“在文本编辑器中打开源文件”。

新建的源文件会在 Webots 的文本编辑器中显示。Python 代码可直接编译,但初始代码没有实际功能。接下来我们将把新建的 epuck_go_forward(或 EPuckGoForward)控制器关联到 E-puck 节点。

📝 动手实践 #9: 在场景树中,选中 E-puck 节点的 controller 字段,在底部属性编辑器中点击 Select...,然后在列表中选择 epuck_go_forward。控制器关联后,保存世界。
修改控制器程序,获取电机设备(如 leftMotor = robot.getDevice('left wheel motor')),并设置电机目标位置(如 leftMotor.setPosition(10.0)):
from controller import Robot, Motor

TIME_STEP = 64

# 创建机器人实例
robot = Robot()

# 获取电机设备
leftMotor = robot.getDevice('left wheel motor')
rightMotor = robot.getDevice('right wheel motor')
# 设置电机目标位置
leftMotor.setPosition(10.0)
rightMotor.setPosition(10.0)

while robot.step(TIME_STEP) != -1:
    pass
保存修改后的源代码(File / Save Text File),重置并运行仿真。

5. 创建与扩展控制器

创建新控制器

机器人的轮子通常用速度(velocity)而不是位置(position)来控制。要让轮子以速度模式运行,需要将目标位置设为无穷大(float('inf')),然后设置期望的速度。

📝 动手实践 #10: 按如下方式修改控制器程序,保存并运行:
from controller import Robot, Motor
            
TIME_STEP = 64
MAX_SPEED = 6.28

# 创建机器人实例
robot = Robot()

# 获取电机句柄,并将目标位置设为无穷大(速度控制)
leftMotor = robot.getDevice('left wheel motor')
rightMotor = robot.getDevice('right wheel motor')
leftMotor.setPosition(float('inf'))
rightMotor.setPosition(float('inf'))

# 设置电机速度为最大速度的10%
leftMotor.setVelocity(0.1 * MAX_SPEED)
rightMotor.setVelocity(0.1 * MAX_SPEED)

while robot.step(TIME_STEP) != -1:
    pass
            
现在机器人会持续前进(轮子以0.2弧度/秒的速度旋转)。如果没有反应,请点击 Build / Build 菜单或工具栏齿轮图标编译代码。编译错误会在控制台以红色显示,修正后重新编译并重载世界。

6. 总结

恭喜你完成了第一个 Webots 仿真项目!你已经学会了:

  • 世界由节点(Node)组成,树状结构组织。
  • 世界保存在 .wbt 文件中,位于 Webots 项目目录下。
  • 项目还包含定义机器人行为的控制器程序。
  • 控制器可用 C 或其他语言编写。
  • C/C++/Java 控制器需编译后才能运行。
  • 控制器通过 controller 字段与 Robot 节点关联。

下一步:教程 2中,你将学习如何修改环境和添加更多对象到仿真世界中。