2D LUA 机器人模拟器






4.89/5 (25投票s)
一篇关于设计自己的机器人模拟器的文章

引言
在这里,我想介绍一个二维移动机器人模拟器。通过这个模拟器,我们可以使用一套我们设计的规则来设计机器人如何在二维世界中导航。这些规则是通过 Lua 脚本设计的。Lua 本身是一种强大、快速、轻量级、可嵌入的脚本语言。使用 Lua 将为我们设计移动机器人算法提供许多好处。对于世界编辑器,我们使用了 GDI 设备上下文编程。用户可以通过点击拖动的方式来创建用于测试机器人的环境。以下是功能概述:
- 差速驱动机器人
- 多机器人仿真
- 声纳和激光束式距离传感器
- 嵌入式 Lua 脚本用于机器人代码
- 图形化世界编辑器
- 带语法高亮和自动完成的代码编辑器
也需要 Lua 知识。Lua 并不难学。你可以查看 Lua 网站。
机器人理论
我们这里的机器人是轮式机器人。它有两个轮子。它通过差速驱动系统进行导航。差速驱动系统就像一个带轮子的椅子。通过改变轮子的速度可以控制带轮子椅子的转向。如果一个轮子转速更快,带轮子的椅子就会走曲线路径。如果两个轮子速度相同,它会走直线路径。
机器人数学
有关移动机器人理论的详细信息,您可以参考 G.W. Lucas 的教程。为了简单起见,这里是用于模拟差速转向行为的方程:

如果左轮和右轮速度相同,则上述方程无法实现,因为会导致除零错误。使用洛必达法则,可以证明该方程的极限趋近于直线(请再次查看 G.W. Lucas 的教程)。因此,当左轮和右轮速度相同时,使用此方程(请注意,dx/dt
表示当前 x
位置与前一个 x
位置的差值)。

要获取机器人的当前位置,我们只需要将时间、左轮速度和右轮速度输入上述方程。机器人角度也是我们需要计算的。这是计算机器人角度的方程:

最后,这是这些方程的实现代码:
void CRobot::goRobotGo(double *t){
if (canResetTime){
*t = 0;
canResetTime = false;
}
int plusFactor = m_robot.rWheelSpeed + m_robot.lWheelSpeed;
int minusFactor = m_robot.lWheelSpeed - m_robot.rWheelSpeed;
if (m_robot.lWheelSpeed - m_robot.rWheelSpeed != 0.0){
m_robot.theta = m_theta0 + minusFactor * (*t) / m_robot.size ;
m_robot.pos.x = ceil(m_pos0.x + m_robot.size / 2 * plusFactor / minusFactor
* (sin(minusFactor * (*t) / m_robot.size + m_robot.theta0)
- sin(m_robot.theta0)));
m_robot.pos.y = m_pos0.y - m_robot.size / 2 * plusFactor / minusFactor
* (cos(minusFactor * (*t) / m_robot.size + m_robot.theta0)
- cos(m_robot.theta0));
}
else{
m_robot.pos.x = plusFactor / 2 * cos(m_robot.theta) * (*t) + m_pos0.x;
m_robot.pos.y = plusFactor / 2 * sin(m_robot.theta) * (*t) + m_pos0.y;
}
}
所有与机器人相关的部分都放在 CRobot
类中,位于 Robot.h 和 Robot.cpp 文件中。
世界编辑器
世界编辑器只是 GDI 设备上下文编程的一个实现。我们使用简单的图形,如矩形、椭圆和线条来创建房间和障碍物。通过一些数学运算,我们可以使这些图形可选、可移动和可调整大小。这个世界编辑器基于我以前的工作。我知道它非常简单,而且不太好,因为我收到了不少负面反馈。如果有时间,我会改进它。所有与世界编辑器相关的部分都在 CCanvas
类中,位于 Canvas.h 和 Canvas.cpp 文件中。
代码编辑器
对于代码编辑器,我使用了 Scintilla 库。有了 Scintilla 库,我们可以轻松制作一个支持语法高亮显示的编辑器。我从 CodeProject 上的一篇文章中学到了这个库。可以在这里和这里查看。
嵌入式 Lua
Lua 是一种非常好的编程语言。它是一种轻量级、小巧的编程语言,旨在扩展应用程序。在这里,我使用 RhicadS 创建的 Lua C++ 包装器,将几个 C++ 函数嵌入到 Lua 中。
readsensor(integer index)
接受传感器索引;返回活动机器人的测量距离。setspeed(integer left, integer right)
接受活动机器人的左右轮速度;不返回值。getangle()
不接受参数;返回活动机器人当前的角位置(以弧度为单位)。getnumofrobots()
不接受参数;返回现有机器人的数量。getposition()
不接受参数;返回活动机器人的 x 和 y 位置。gettarget(int index)
接受目标索引;返回选定目标的 x 和 y 位置。textline(string msg)
接受要显示的文本;不返回值。setactiverobot(integer index)
激活指定的机器人。stepforward()
模拟向前进行一个时间步。
基本上,这些函数用于操作机器人。Lua 本身有许多内部函数可用于开发算法。您可以查看 Lua 参考手册,了解可用的函数,例如:数学、字符串或文件操作的函数。
如何使用
您可以通过点击 视图 >> 编辑器,或者点击工具栏上的 查看代码编辑器(Ctrl+E)来显示代码编辑器窗口。世界文件保存为 *.wld 扩展名,代码文件保存为 *.lua 扩展名。加载和保存是分开进行的。
让我们来试试。首先,在世界编辑器中绘制一个大椭圆。然后,在第一个椭圆内绘制另一个较小的椭圆(或者您可以加载文件 doubled_wall.wld)。将机器人拖到这两个椭圆形成的通道内。加载代码编辑器并粘贴以下代码。运行模拟。当机器人消失时,点击菜单 机器人 >> 重置位置。
function azolla.main(azolla)
azolla:setspeed(20,20)
while true do
a = azolla:readsensor(1)
b = azolla:readsensor(5)
if (a - b > 2) then
azolla:setspeed(30,20)
end
if (a - b < -2) then
azolla:setspeed(20,30)
end
if( (a - b > -2) and (a - b < 2) ) then
azolla:setspeed(20,20)
end
azolla:stepforward()
end
end
上面的代码是为了让机器人沿着墙壁前进。看看它是如何读取传感器值的。机器人默认有六个传感器。您可以使用菜单 **机器人** >> **设置属性** 来修改传感器的数量。它们都是激光束式距离传感器。我们知道,还有声纳式距离传感器。看图片。传感器编号从机器人头部开始,并按顺时针方向递增。

让我们再试一个。在演示中,我包含了两个文件 trinity.wrl 和 trinity.lua。加载这两个文件。复制并粘贴以下代码:
function azolla.main(azolla)
azolla:textline("START...\n")
while(true) do
for i = 0, azolla:getnumofrobots() - 1 do
azolla:setactiverobot(i)
front = azolla:readsensor(0)
left = azolla:readsensor(5)
right = azolla:readsensor(1)
if (front < 10) then
azolla:setspeed(4,-4)
else
delta = 0.5 * (right - left)
azolla:setspeed(4 + delta,4 - delta)
end
end
azolla:stepforward()
end
end
让我们仔细看看。function azolla.main(azolla)
将始终是第一个被调用的。它是主函数。该函数总是写成这样:function azolla.function_name(azolla)
delta
) 将基于左右传感器值之间的差异进行计算。此控制信号将用于校正两个轮子的速度。
多机器人仿真
自 1.0.2 版本起,Azolla 现在支持多机器人仿真。我们可以添加多个机器人并同时运行它们。要激活某个机器人,必须使用 setactiverobot
。看看下面的代码:
function azolla.main(this)
azolla:setspeed(20,20)
while true do
--ACTIVATE THE ROBOT ONE BY ONE!!!
for i = 0, azolla:getnumofrobot() - 1 do
--This part is for wall following
azolla:setactiverobot(i)
a = azolla:readsensor(1)
b = azolla:readsensor(5)
if (a - b > 2) then
azolla:setspeed(30,20)
end
if (a - b < -2) then
azolla:setspeed(20,30)
end
if( (a - b > -2) and (a - b < 2) ) then
azolla:setspeed(20,20)
end
end
azolla:stepforward()
end
end
上面的代码是为了控制几个机器人,使它们能够向左侧和右侧的墙壁前进。这与第一个示例(doubled_wall.wld)相同。对于沿墙行走的这部分,我们可以使用相同的代码。正如我们所见,在移动机器人之前,我们应该决定要移动哪个机器人。我们可以通过迭代来逐个移动所有机器人。
限制
Azolla 不是实时机器人模拟器。如果我们添加的机器人越来越多,仿真运行速度就会变慢。为了加快仿真速度,我们可以增加仿真的时间步长。
而对于传感器读取,它是基于屏幕像素读取的。在这种情况下,我们必须确保仿真在主窗口区域运行。如果机器人超出了主窗口,传感器算法将读取错误的屏幕像素。此外,如果主窗口上方有另一个窗口,并且传感器能够到达该窗口,传感器算法也将读取错误的屏幕像素。对于下一个版本,我计划实现一种几何方法来进行传感器测量,而不是读取屏幕像素值。
关注点
我真心希望您尝试一下这个模拟软件。自第一个版本发布以来,我做了大量的改进。以前,仿真在多核计算机上无法工作。该 bug 已被修复。过高的 CPU 使用率问题也得到了解决。总的来说,我认为它运行得非常好。希望您喜欢它,并对您有所帮助。您可以阅读“历史”部分,查看改进的详细信息。有关更多信息,请参阅提供的 PDF 文件。
参考文献
未来工作
对于未来的工作,我想让这个模拟软件更加可靠,以便在移动机器人领域用于学习和研究目的。为了实现这一目标,还有很多工作要做。
历史
- 1.0.0 2009 年 2 月
- 初次发布
- 1.0.0 2009 年 3 月
- 文章更新。
- 次要 bug 修复
- 1.0.1 2010 年 2 月
- 文章更新。
- 名称更改为 Azolla
- 仿真数据类型从
double
更改为float
- 修复:错误的运动学方程,导致机器人运动异常
- 修复:多线程 bug,导致 CPU 使用率过高,且仿真在多核 CPU 上无法工作
- 新增:用户可以显示/隐藏机器人轨迹
- 新增:用户可以显示/隐藏世界编辑器中的网格
- 新增:声纳式传感器
- 新增:用户可以修改声纳传感器的最大、最小和锥角(如果锥角设置为零,则传感器将变为激光束式传感器)
- 新增:用户可以在传感器读数上应用高斯噪声
- 新增:用户可以修改仿真时间步长
- 新增:代码编辑器中的自动完成功能
- 新增:代码编辑器中的“查找下一个”功能
- 新增:世界编辑器中的“全选”功能
- 1.0.2 2010 年 2 月
- 文章更新。
- 修复:运动学方程运行速度更快
- 修复:机器人正面看起来更好
- 修复:运行仿真时出现的小故障
- 新增:支持多机器人仿真
- 新增:工具栏上有更多图标
- 1.03 2010 年 3 月
- 修复:显示轨迹时的 bug
- 新增:放大和缩小功能
- 新增:可以为每个机器人分配颜色
- 新增:简单的碰撞检测
- 许多其他 bug 修复和功能增强
- 1.04 2010 年 7 月
- 新增:日志窗口和目标标记
- 新增:几个新命令
- 传感器现在使用几何方法工作,例如:线与线、线与矩形、线与椭圆的交点
- 更好的碰撞检测
- 更具信息量的错误消息
- 许多其他 bug 修复和功能增强