Webots控制器编程
本文主要内容是如何编写Webots控制器,使用语言为Python。
文章目录
- 1. 新增控制器
- 2. Hello World Example
- 3. 读取传感器
- 4. 使用执行器
- 5. 理解step和robot.step函数
- 6. 同时使用传感器和执行器
- 7. 控制器参数
1. 新增控制器
对机器人Robot新增控制器的方式:
Wizards -> New Robot Controller
默认的Python代码模版是:
# You may need to import some classes of the controller module. Ex:
# from controller import Robot, Motor, DistanceSensor
# 1. 导入相关控制器类
from controller import Robot# create the Robot instance.
# 2. 实例化控制器类得到控制的对象
robot = Robot()# get the time step of the current world.
# 3. 获取环境仿真时间步
timestep = int(robot.getBasicTimeStep())# You should insert a getDevice-like function in order to get the
# instance of a device of the robot. Something like:
# 4. 获取要操控的设备并设置仿真时间步
# motor = robot.getDevice('motorname')
# ds = robot.getDevice('dsname')
# ds.enable(timestep)# Main loop:
# - perform simulation steps until Webots is stopping the controller
# 5. 主循环中按照时间步进行仿真
while robot.step(timestep) != -1:# Read the sensors:# Enter here functions to read sensor data, like:# val = ds.getValue()# Process sensor data here.# Enter here functions to send actuator commands, like:# motor.setPosition(10.0)pass# Enter here exit cleanup code.
可以看到一般控制器程序的代码可以分为五部分:
- 导入相关控制器类
- 实例化控制器类得到控制的对象
- 获取环境仿真时间步
- 获取要操控的设备并设置仿真时间步
- 主循环中按照时间步进行仿真
2. Hello World Example
from controller import Robotrobot = Robot()while robot.step(32) != -1:print("Hello World!")
在主循环中每隔32ms会进行执行一次,执行的内容可能需要1ms也可能需要1min的时间完成这次仿真的推进,具体的时间需要看执行内容的复杂程度。

3. 读取传感器
from controller import Robot, DistanceSensorTIME_STEP = 32robot = Robot()# my_distance_sensor这个需要根据你命名的传感器名称进行定义
sensor = robot.getDevice("my_distance_sensor")
# 传感器需要先启用才能使用,TIME_STEP传感器的两次数据更新的时间间隔
# 一般设置TIME_STEP和仿真时间步相同
sensor.enable(TIME_STEP)while robot.step(TIME_STEP) != -1:value = sensor.getValue()print("Sensor value is: ", value)
需要注意的是有些传感器返回的值不是标量而是向量,如下代码所示:
GPS.getValues()
Accelerometer.getValues()
Gyro.getValues()# return the sensor measurement as an array of 3 floating point numbers: `[x, y, z]`.
values = gps.getValues()# OK, to read the values they should never be explicitly deleted by the controller code
print("MY_ROBOT is at position: %g %g %g" % (values[0], values[1], values[2]))# there is no need to copy these values
4. 使用执行器
下面的示例显示了如何使用 2 Hz 正弦信号使旋转电机振荡。
from controller import Robot, Motor
from math import pi, sinTIME_STEP = 32robot = Robot()
# 获取机器人上名为"my_motor"的电机设备
motor = robot.getDevice("my_motor")# 设置频率F为2 Hz,用于控制电机的周期性运动,使电机每秒进行两个完整周期的运动。
F = 2.0 # frequency 2 Hz
t = 0.0 # elapsed simulation timewhile robot.step(TIME_STEP) != -1:position = sin(t * 2.0 * pi * F)motor.setPosition(position)t += TIME_STEP / 1000.0
与传感器不同,执行器不需要明确启用。为了控制运动,通常有用的是将运动分解为与控制步骤相对应的离散步骤。和以前一样,这里使用无限循环:在每次迭代时,根据正弦方程计算新的目标位置。
需要注意的是,motor.setPosition 函数存储新位置,但它不会立即启动电机。有效驱动调用 robot.step 函数开始。
当 robot.step 函数返回时,电机已移动一定的(线性或旋转)量,该量取决于目标位置、控制步骤的持续时间(使用 robot.step 函数参数指定)、速度、加速度、力等电机“.wbt”描述中指定的参数。例如,如果指定非常小的控制步长或较低的电机速度,则当 robot.step 函数返回时,电机不会移动太多。在这种情况下,旋转电机需要几个控制步骤才能到达目标位置。如果指定更长的持续时间或更高的速度,则当 robot.step 函数返回时,电机可能已完全完成运动。
请注意,motor.setPosition 函数仅指定所需的目标位置。就像真实的机器人一样,旋转电机有可能无法到达该位置(仅在基于物理的模拟中),因为它被障碍物阻挡或因为电机的扭矩(最大力)不足以抵抗重力等。
如果要同时控制多个旋转电机的运动,则需要使用motor.setPosition 函数分别为每个旋转电机指定所需的位置。然后您需要调用一次 robot.step 函数来同时驱动所有旋转电机。
5. 理解step和robot.step函数
Webots 使用两种不同的时间步长:
- The simulation step(在场景树中指定:
WorldInfo.basicTimeStep) - The control step(指定为每个机器人的
robot.step函数的参数)
The simulation step(模拟步长)是 WorldInfo.basicTimeStep 中指定的值(以毫秒为单位)。它表示一步模拟的持续时间,即两次计算每个模拟对象的位置、速度、碰撞等的时间间隔。如果模拟使用physics (vs. kinematics),则模拟步骤还指定需要应用于模拟刚体的力和扭矩的两次计算之间的间隔。
The control step(控制步长)是控制循环迭代的持续时间。它与传递给 robot.step 函数的参数相对应。robot.step 函数将指定持续时间的控制器时间提前。同时,它还会根据控制器时间将传感器和执行器数据与模拟器同步。
每个控制器都需要定期调用 robot.step 函数。如果控制器不调用 robot.step 函数,则传感器和执行器将不会更新,并且模拟器将阻塞(仅在同步模式下)。因为需要定期调用,所以 robot.step 函数调用通常放在控制器的主循环中。
a simulation step的执行是一个原子操作:它不能被中断。因此,传感器测量或电机驱动只能在两个simulation step之间进行。因此,每个 robot.step 函数调用指定的The control step必须是simulation step的倍数。因此,例如,如果simulation step为 16 ms,则传递给 robot.step 函数的控制步参数可以是 16、32、64、128 等。
如果模拟以逐步模式运行,即通过单击Step按钮,则执行具有模拟步骤持续时间的单个步骤。下图详细描述了仿真状态、控制器状态和Step点击之间的同步。

6. 同时使用传感器和执行器
Webots 和每个机器人控制器在不同的进程中执行。例如,如果模拟涉及两个机器人,则总共会有三个进程:一个是 Webots 进程,两个是两个机器人进程。在调用 robot.step 函数时,每个机器人控制器进程都会与 Webots 进程交换传感器和执行器数据。例如,my_leg.setPosition 函数不会立即将数据发送给 Webots。相反,它会在本地存储数据,并在调用 robot.step 函数时有效发送数据。
因此,下面的代码片段是一个糟糕的示例。显然,第一次调用 my_leg.setPosition 函数时指定的值将被第二次调用覆盖:
my_leg.setPosition(0.34) # BAD: ignored
my_leg.setPosition(0.56)
robot.step(40) # BAD: we don't test the return value of this function
同样,这段代码也没有什么意义:
while robot.step(40) != -1:d1 = sensor.getValue()d2 = sensor.getValue()if d2 > d1: # WRONG: d2 will always equal d1 hereavoidCollision()
由于在两次传感器读数之间没有调用 robot.step 函数,因此传感器返回的值不可能在此期间发生变化。一个正确的版本如下:
while robot.step(40) != -1:d1 = sensor.getValue()if robot.step(40) == -1:breakd2 = sensor.getValue()if d2 > d1:avoidCollision()
然而,通常推荐的方法是在主控制循环中调用一个 robot.step 函数,并使用它同时更新所有传感器和执行器,如下所示:
while robot.step(40) != -1:readSensors()actuateMotors()
请注意,在循环开始时调用 robot.step函数非常重要,以确保传感器在进入 readSensors 函数之前已经具有有效值。
这是一起使用传感器和执行器的完整示例。这里使用的机器人使用差速转向。它使用两个距离传感器(DistanceSensor)来检测障碍物。
from controller import Robot, Motor, DistanceSensorTIME_STEP = 32robot = Robot()left_sensor = robot.getDevice("left_sensor")
right_sensor = robot.getDevice("right_sensor")
left_sensor.enable(TIME_STEP)
right_sensor.enable(TIME_STEP)left_motor = robot.getDevice("left_motor")
right_motor = robot.getDevice("right_motor")
left_motor.setPosition(float('inf'))
right_motor.setPosition(float('inf'))
left_motor.setVelocity(0.0)
right_motor.setVelocity(0.0)while robot.step(TIME_STEP) != -1:# read sensorsleft_dist = left_sensor.getValue()right_dist = right_sensor.getValue()# compute behavior (user functions)left = compute_left_speed(left_dist, right_dist)right = compute_right_speed(left_dist, right_dist)# actuate wheel motorsleft_motor.setVelocity(left)right_motor.setVelocity(right)
在Webots仿真环境中,控制电机的方式主要有两种:位置控制和速度控制。而在这段代码中,通过将电机的位置设置为“无限”float('inf'),就可以使电机进入速度控制模式 。
- 位置控制模式:通常情况下,如果给电机设置一个固定的目标位置(例如一个角度或距离),电机会尝试旋转到该位置并停在那里,这就是“位置控制”。
- 速度控制模式:通过将电机位置设为“无限”
float('inf'),Webots将认为电机的目标位置是无限远的,这种情况下,电机会进入“速度控制模式”,即不再关注目标位置,而是只根据设定的速度值进行旋转或移动。
7. 控制器参数
在.wbt 文件中,可以指定控制器启动时传递的参数。这些参数在机器人节点的 controllerArgs 字段中指定,并作为主函数的参数传递。例如,这可用于指定每个机器人控制器的不同参数。请注意,使用 MATLAB 时不支持控制器参数检索。

比如:
Robot {...controllerArgs "one two three"...
}
如果控制器的名称是 “demo”,那么就会出现这段示例控制器代码
from controller import Robot
import sysrobot = Robot()for i in range(0, len(sys.argv)):print("argv[%i]=%s" % (i, sys.argv[i]))
argv[0]=demo
argv[1]=one
argv[2]=two
argv[3]=three
相关文章:
Webots控制器编程
本文主要内容是如何编写Webots控制器,使用语言为Python。 文章目录 1. 新增控制器2. Hello World Example3. 读取传感器4. 使用执行器5. 理解step和robot.step函数6. 同时使用传感器和执行器7. 控制器参数 1. 新增控制器 对机器人Robot新增控制器的方式࿱…...
舷外机,高效动力的选择,可靠性能的保障_鼎跃安全
舷外机是现代船只动力系统中的核心设备,广泛应用于娱乐船、渔船、巡逻船、救援船等多种场景。它不仅提供船只的动力支持,还因其结构简便、操作灵活和维护方便,成为水上作业的重要组成部分。 一、舷外机的功能作用 1. 强劲动力源 舷外机是船…...
计算机新手练级攻略——如何搜索问题
目录 计算机学生新手练级攻略——如何搜索问题1.明确搜索意图2.使用精确关键词3.使用专业引擎搜索4.利用好技术社区1. Stack Overflow2. GitHub3. IEEE Xplore4. DBLP 5.使用代码搜索工具1. GitHub 代码搜索2. Stack Overflow 代码搜索3. Papers with Code4. IEEE Xplore 6.查阅…...
echarts-gl 3D柱状图配置
1. 源码 此demo可以直接在echarts的编辑器中运行 option {title: {text: 产量图,textStyle: {color: rgba(255, 255, 255, 1),fontSize: 17},left: center},tooltip: {},legend: {show: false,orient: vertical,x: left,top: 0,right: 20,textStyle: {fontSize: 12}},visualM…...
设计模式之模版方法模式(Template)
一、模版方法模式介绍 1、模版方法模式定义: 模板方法模式(template method pattern)原始定义是:在操作中定义算法的框架,将一些 步骤推迟到子类中。模板方法让子类在不改变算法结构的情况下重新定义算法的某些步骤。 模板方法中的算法可以理…...
背包九讲——背包问题求具体方案
目录 背包问题求具体方案 1. 01 背包问题 题目:12. 背包问题求具体方案 - AcWing题库 算法思路: 代码实现: 2. 多重背包问题 算法思路: 3. 完全背包问题 算法思路: 代码实现: 背包问题第九讲—…...
Python http打印(http打印body)flask demo(http调试demo、http demo、http printer)
文章目录 代码解释 代码 # flask_http_printer.pyfrom flask import Flask, request, jsonify import jsonapp Flask(__name__)app.route(/printinfo, methods[POST]) def print_info():# 分隔符separator "-" * 60# 获取请求头headers request.headers# 获取 JS…...
JSF HTML标签教程一口气讲完!(下)
JSF OutputScript示例 JSF教程 - JSF OutputScript示例 h:outputScript标记渲染类型为“script"的HTML元素,类型为“text/javascript"。 此标记将外部JavaScript文件添加到JSF页面。 以下JSF标记 <h:outputScript library"js" name"…...
cmake报错The link interface of target “gRPC::grpc“ contains: OpenSSL::SSL 解决
系统环境:麒麟V10 报错描述: The link interface of target "gRPC::grpc" contains: OpenSSL::SSL but the target was not found. Possible reasons include: * There is a typo in the target name. * A find_package call is missing fo…...
C语言PythonBash:空白(空格、水平制表符、换行符)与转义字符
C语言 空白 C语言中的空白(空格、水平制表符、换行符)被用于分隔Token,因此Token间可以有任意多个空白。 // 例1 printf("Hello, World!"); 例1中存在5个Token,分别是: printf("Hello, World! \n&qu…...
【Python】轻松解析JSON与XML:Python标准库的json与xml模块
轻松解析JSON与XML:Python标准库的json与xml模块 在现代数据处理与交换中,JSON(JavaScript Object Notation)和XML(eXtensible Markup Language)是最常用的两种数据格式。它们广泛应用于API数据传输、配置…...
物联网对商业领域的影响
互联网彻底改变了通信方式,并跨越了因地理障碍造成的人与人之间的鸿沟。然而,物联网(IoT)的引入通过使设备能够连接到互联网,改变了设备的功能。想象一下,你的闹钟连接到互联网,并且能够用你的声…...
第16章 SELECT 底层执行原理
一、SELECT查询的完整结构 1.1 方式一(SQL 92语法) SELECT ..., ..., ... FROM ..., ..., ... WHERE 多表的连接条件 AND 不包含组函数的过滤条件 GROUP BY ..., ... HAVING 包含组函数的过滤条件 ORDER BY ... ASC/DESC LIMIT ..., ... 1.2 方式二&a…...
python查询日志,并组装sql,修复缺失的数据
前言 由于mysql链接超时波动,导致数据缺失,需要根据日志填补数据 流程 获取确实数据的订单列表 搜索日志,获取请求日志 根据请求日志拼装sql 打印sql供修复数据 代码 因为我们日志打印的有问题,所以这里用字符串截取获取入…...
RecyclerView进阶知识讲解
在 Android 开发中,RecyclerView 是一种高效的列表和网格布局控件,用于显示大规模数据。尽管基本使用方法简单,但深入理解并掌握其高级进阶用法能大幅提升用户体验和应用性能。下面,我将从布局管理、动画和手势、自定义缓存、优化…...
C语言 函数
时间:2024.11.10-11.11 一、学习内容 1、什么是函数 函数:程序中独立的功能。将反复书写的代码,又不确定什么时候回用到的代码打包起来。 2、函数的基本格式 函数的定义格式(写在main函数外) void 函数名() { 函数…...
windows中docker安装redis和redisinsight记录
创建一个Redis运行容器,命令如下 docker run -it -d --name redis -p 6379:6379 redis --bind 0.0.0.0 --protected-mode no -d 代表Redis容器后台运行 --name redis 给创建好的容器起名叫redis -p 6379:6379 将容器的6379端口映射到宿主机的6379端口,注…...
itextpdf打印A5的问题
使用A5打印的时候,再生成pdf是没有问题的。下面做了一个测试,在打印机中,使用A5的纸张横向放入,因为是家用打印机,A5与A4是同一个口,因此只能这么放。 使用itextpdf生成pdf,在浏览器中预览pdf是…...
qt QUndoView详解
1、概述 QUndoView 是 Qt 框架中用于显示 QUndoStack(撤销堆栈)内容的视图类。它通常与 QUndoStack 一起使用,为用户提供了一个可视化的界面来查看和操作撤销/重做历史。QUndoView 可以显示堆栈中的每个命令,并允许用户通过界面进…...
python+智谱AI-实现钉钉消息自动回复
python智谱AI-实现钉钉消息自动回复 实现了电脑窗口切换,截图识别未读消息,与语言模型交互后,将答案带入到钉钉窗口中。偷个懒,直接贴代码了,后续不断完善注释,如果遇到读不懂的地方,欢迎交流。…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
