嗨伙计们,还记得上次提到的三种webots机器人建模方式吗?我们先来简单回顾一下:
① 使用webots模型树建模,同时这也是我们环境建模的主要方式;
② 使用三维建模软件导出VRML,通常我们导入模型的零部件,然后在webots内进行装配;
③ 利用已有的urdf/xacro转换,使用urdf2webots功能包可直接获得所需的模型;
今天跟大家分享一下在webots中是如何完成建模的,主要包括环境建模和已有节点的添加,最后呢我们将通过一个简单的控制器让它动起来!
1. 环境建模
除了机器人建模,当然还要包括环境建模。不管是哪一款机器人仿真软件,它们的使用套路都是差不多的。通常环境建模主要包括背景设置、光线设置以及环境物体的建模,接下来我们来看看在webots中该怎样完成环境建模!
首先我们来新建一个新世界(Ctrl + Shift + N),可以在场景树看到两个节点:WorldInfo和Viewpoint,右击你会发现这两个节点是无法删除的,也就是说这是一个世界的基本节点。
其中WorldInfo用来描述一些通用信息,比如重力加速度gravity、基本仿真时间步长basicTimeStep等等,在对仿真过程要求不高的前提下使用默认参数即可;Viewpoint用来描述我们在3D场景显示区中的视角,通常情况我们使用鼠标来完成调整视角的操作:
鼠标左键 鼠标右键 鼠标滚轮 |
旋转视图 平移视图 放大/缩小视图 |
1.1 地面建模
接下来我们添加地面节点, Ctrl + Shift + A弹出节点添加窗口,选择PROTO nodes(Webots Projects) → objects → floors,可以看到有4种地面可供我们选择,如果仿真崎岖的野外环境可以选择UnevenTerrain节点,在这儿我们选择带有边界的方形地面RectangleArena。
Webots R2020a版本中默认没有背景且无灯光,因此添加完地面如果不选中地面节点,我们将看不到任何东西。
小提示:
① 地面节点是必须要添加的,如果没有添加地面,仿真开始后受重力影响模型会一直往下掉!
② webots中世界坐标系的垂直方向为y(绿色轴),红色轴为x,蓝色轴为z。
1.2 灯光建模
为了能够看清环境,下一步我们来添加一组灯光,webots中有3种光源类型:点光源、平行光源和锥形光源。Ctrl + Shift + A弹出节点添加窗口添加一个点光源,选择Base nodes → PointLight,强度设置为3,光源设置为[0,2,0]。
光源类型 |
节点名称 |
常用参数 |
点光源 |
PointLight |
光源位置location、光强intensity |
平行光源 |
DirectionalLight |
方向direction、光强intensity |
锥形光源 |
SpotLight |
方向direction、光源位置location、光强intensity、半径radius |
1.3 背景建模
对于背景的添加,自定义背景我们可以使用Base nodes → Background节点,即可通过指定颜色(skyColor)来设置背景,又可通过链接6个背景图片来实现背景的全景设置,为了突显点源灯光效果,本文使用指定颜色(RGB=[0,0,0])设置背景,这与未添加背景的情况是一样的。此外webots还集成了一些现有的背景,在PROTO nodes(Webots Projects) → objects → TextureBackground,与之对应的还有TextureBackgroundLight节点,用于设置背景光线。
小提示:基本节点Base nodes中的Background与PROTO nodes提供的Background是互斥的,也就是说二者只能添加一个。
1.4 环境物体建模
完成以上步骤,我们开始进行环境物体的建模。先来通过3个球体来区分一下没有物理属性(ball_1)、有物理属性无边界(ball_2)和有物理属性有边界(ball_3)的区别:
(1)首先完成ball_1的建模。Ctrl + Shift + A添加一个Solid节点,选中children来添加形状节点,它包含appearance和geometry两项,appearance用来描述实体表面纹理,geometry用来具体指定实体形状。选中geometry以同样的方式添加Sphere节点,调整球的半径radius = 0.12m,细分度设置为2(数字越大球越圆,范围1-5);选中appearance为球添加一个纹理图片,具体操作如下:appearance → texture → Base nodes → ImageTexture → url → 选择纹理图片路径,我们选择一个water纹理,至此完成第一个球体的建模。
(2)接下来对ball_2完成建模,因为ball_2仅仅在ball_1基础上增加了物理属性,我们可以直接选中ball_1的Solid节点使用Ctrl + C、Ctrl + V完成复制操作。粘贴完的实体与原实体完全相同,因此我们需要修改其名称为ball_2,为方便区分,将纹理信息修改为grass。然后选择Solid下的physics选项,添加Physics节点。通过该节点可设置实体的密度(density)、重量(mass)、质心位置(centerOfMass)、惯性矩阵(inertiaMatrix)以及阻尼参数(damping),值得注意的是density和mass只能设置一个,另外一个必须指定参数为-1使其失效。
(3)继续以ball_2为本体复制产生ball_3,为其添加碰撞边界。选中boundingObject,采用(1)中添加形状的方式可实现边界的设置。更简便的方法是,我们直接可通过选中children下的Shape节点并复制,然后选中boundingObject直接粘贴实现。
运行仿真,我们可以发现ball_1无任何反应,ball_2由于没有设置边界导致穿过地板无限自由落体,而ball_3则正常的与地面发生碰撞。因此boundingObject的设置显得至关重要。对于ball_1由于没有添加物理属性因此不受重力影响,所以我们可以利用这一特性达到某些我们想要的目的,比如把机械臂底座的物理属性去掉可以防止机械臂运动过程中由于惯性力作用发生的倾倒现象。
接下来添加一些桌子等物品,实现环境的完整建模。
现在我们来看下目前的世界文件代码:
#VRML_SIM R2020a utf8
WorldInfo {
info [
"time:2020-04-09"
"author:罗伯特祥"
]
}
Viewpoint {
orientation -0.10078377713631789 0.9507223609768183 0.293205768368907 2.5098883980917908
position 2.735411223357777 2.9491281803766305 -2.808776032303232
follow "e-puck"
}
RectangleArena {
floorSize 2 2
}
PointLight {
attenuation 0 0 1
intensity 3
location 0 2 0
}
Background {
}
DEF ball_1 Solid {
translation -0.5 0.5 -0.5
children [
DEF ball_1 Shape {
appearance Appearance {
texture ImageTexture {
url [
"textures/water.jpg"
]
}
}
geometry Sphere {
radius 0.12
subdivision 2
}
}
]
}
DEF ball_2 Solid {
translation 0 0.5 -0.5
rotation 1 0 0 0
children [
DEF ball_1 Shape {
appearance Appearance {
texture ImageTexture {
url [
"textures/grass.jpg"
]
}
}
geometry Sphere {
radius 0.12
subdivision 2
}
}
]
name "solid(1)"
physics Physics {
}
}
DEF ball_3 Solid {
translation 0.5 0.5 -0.5
rotation 1 0 0 0
children [
DEF ball_1 Shape {
appearance Appearance {
texture ImageTexture {
url [
"textures/steel_floor.jpg"
]
}
}
geometry Sphere {
radius 0.12
subdivision 2
}
}
]
name "solid(2)"
boundingObject DEF ball_1 Shape {
geometry Sphere {
radius 0.12
subdivision 2
}
}
physics Physics {
}
}
Desk {
translation 0 0 0.58
}
Laptop {
translation 0 0.709 0.51
controller ""
}
BiscuitBox {
translation 0.43 0.7 0.61
rotation 0 1 0 -2.094395307179586
}
WoodenChair {
translation -0.15752 0 0.234796
rotation 0 1 0 0.523599
}
BunchOfSunFlowers {
translation -0.41 0.7 0.59
}
E-puck {
translation -0.38826 -2.66454e-15 -0.331799
controller "my_first_controller"
}
从以上代码我们可得知,在世界文件(*.wbt)的描述代码中各个Solid节点是并列存在的,而在ball_3 Solid中,它的某些特征是呈嵌套形式出现的,由此我们可以大胆猜测一个机器人实体必然也是通过多个节点嵌套组成的,而机器人实体与环境中的其他物体在世界文件中平行存在。实际上webots的世界文件(*.wbt)与gazebo的世界文件(*.world)结构是类似的,区别只在于前者使用VRML语言描述,而后者使用XML语言描述。
2. 让机器人动起来!
机器人的建模方式与环境建模方法完全相同,只不过涉及到添加传感器等操作。篇幅有限,我们先以e-puck为例,使用python作为控制器的编程语言(个人强烈推荐,毕竟人生苦短),让机器人动起来!
首先添加机器人,Ctrl + Shift + A,PROTO nodes(Webots Projects) → robots → gctronic → e-puck → E-puck,选中添加的机器人,然后按住Shift键使用鼠标左键将机器人平移到某一合适位置。另外几个操作物体移动/旋转的快捷键:
Shift + 鼠标左键 |
平移选中的模型 |
Shift + 鼠标右键 |
旋转模型 |
Shift + 鼠标滚轮 |
上下平移模型 |
在工具栏中选择向导→新机器人控制器→Python创建一个python的控制器文件,命名为my_first_controller.py,系统会自动生成一个编程模板,我们在此基础上,根据E-puck的距离传感器布局尝试写一个让E-puck避障的程序。
控制器代码如下:
"""my_first_controller controller."""
# -*- coding: UTF-8 -*-
# author:罗伯特祥
# Created on:2020-04-09
from controller import Robot
# 参数定义
sensor_num = 8
speed_max = 6.28
range_ = 1024 / 2
ps_sensor_name = ['ps0','ps1','ps2','ps3','ps4','ps5','ps6','ps7']
matrix = []
matrix.append([11, 12, 8, -2, -3, -5, -7, -9])
matrix.append([-9, -8, -5, -1, -2, 6, 12, 11])
def main():
# 实例化机器人类
robot = Robot()
# 获取当前世界的基本时间步长
timestep = int(robot.getBasicTimeStep())
# 关联电机设备
left_motor = robot.getMotor('left wheel motor')
right_motor = robot.getMotor('right wheel motor')
# 关联并使能传感器设备
ps_sensor = []
for i_ in range(sensor_num):
ps_sensor.append(robot.getDistanceSensor(ps_sensor_name[i_]))
ps_sensor[i_].enable(timestep)
# 设置电机运行模式
left_motor.setPosition(float('inf'))
right_motor.setPosition(float('inf'))
# 设置初始速度
left_motor.setVelocity(3)
right_motor.setVelocity(3)
speed = [0,0]
# Main loop:
while robot.step(timestep) != -1:
# 获取传感器的值
ps_sensor_value = []
for i_ in range(sensor_num):
ps_sensor_value.append(ps_sensor[i_].getValue())
print(ps_sensor_name[i_] + " = " + str(ps_sensor_value[i_]))
for i_ in range(2):
speed[i_] = 0;
for j_ in range(sensor_num):
speed[i_] += matrix[i_][j_] * (1 - (ps_sensor_value[j_] / range_))
left_motor.setVelocity(0.5 * speed[0])
right_motor.setVelocity(0.5 * speed[1])print("my first controller of the e-puck robot started...")
main()
小提示:如果想用PyCharm来写控制器程序,可以试着问问度娘《webots控制器在PyCharm中的配置》。
相信到这儿你已经轻松掌握了webots的建模方法和编程思路,剩下的就靠查手册(https://www.cyberbotics.com/doc/reference/index)了,下次我们将来讨论一下具体的机器人建模。
3. 总结:
最后我们来总结一下,仿真环境建模的主要步骤和控制器程序的主体控制框架。
(1)仿真环境的搭建:
① 添加地面
② 添加灯光或背景
③ 添加其他环境物体及机器人模型
(2)控制器编程步骤:
① 实例化机器人
② 将变量与模型中的设备关联起来
③ 使能传感器
④ 设置电机运行模式
⑤ 运动循环
本文使用的webots版本为webots R2020a revision 1 官方发行版,官网的下载速度实在让人抓狂,有需要的小伙伴网盘取吧,包含Linux和Windows两个系统版本。如果有什么问题,欢迎评论区留言!
① 安装包链接: https://pan.baidu.com/s/1PzfDFJJEl6mkOTH0r7V0DQ 提取码: jnme
② 本文涉及的项目文件链接:https://share.weiyun.com/51ziSiy
读万卷书,也要行万里路!我是罗伯特祥,下次见~