作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
尼克·麦克雷的头像

尼克·麦克雷博士

尼古拉斯是一名专业的软件工程师,对高质量的工艺充满热情. 他喜欢架构和编写一流的代码.

以前在

deepblocks
分享
编者注:10月16日, 2018, 这篇文章经过修改,以适应最新的技术.

让我们面对现实吧,机器人很酷. 总有一天他们也会统治世界, 希望, 到那时,它们会怜悯它们可怜的、柔软的、肉质的创造者.k.a. 机器人技术开发人员),并帮助我们建立一个充满物质的太空乌托邦. 我当然是在开玩笑,但是 只是有点.

出于我想对这件事施加一点影响的野心,我采取了 自主机器人控制理论课程 去年, 最终,我建立了一个基于python的机器人模拟器,使我能够在一个简单的机器人上实践控制理论, 移动, 可编程的机器人.

在本文中, 我将展示如何使用Python机器人框架来开发控制软件, 描述我为模拟机器人开发的控制方案, 说明它如何与环境相互作用并实现其目标, 并讨论我在机器人编程过程中遇到的一些基本挑战.

为了遵循这个教程机器人编程的初学者, 你应该对两件事有基本的了解:

  • 数学我们会用到一些三角函数和向量
  • Python——因为Python是比较流行的基本机器人编程语言之一——我们将使用基本的Python库和函数

这里显示的代码片段只是整个模拟器的一部分, 哪个依赖于类和接口, 为了直接读取代码, 你可能需要一些Python和 面向对象程序设计.

最后, 可以帮助您更好地理解本教程的可选主题是了解什么是状态机以及距离传感器和编码器如何工作.

可编程机器人的挑战:感知vs. 现实与控制的脆弱性

所有机器人技术的根本挑战是:永远不可能知道环境的真实状态. 机器人控制软件只能根据传感器返回的测量值来猜测真实世界的状态. 它只能试图通过产生控制信号来改变现实世界的状态.

这张图演示了在练习Python机器人编程时,物理机器人和计算机控件之间的交互.

机器人控制软件只能根据传感器返回的测量值来猜测真实世界的状态.

因此, 控制设计的第一步是对现实世界进行抽象, 被称为 模型,用它来解释我们的传感器读数并做出决定. 只要真实世界的行为符合模型的假设, 我们可以做出正确的猜测并加以控制. 一旦现实世界偏离了这些假设, 然而, 我们将无法再做出正确的猜测, 控制就会失去. 通常情况下,一旦失去控制,就再也无法恢复. (除非有仁慈的外部力量恢复它.)

这是机器人编程如此困难的关键原因之一. 我们经常在实验室里看到最新的研究机器人的视频, 表演惊人的灵巧技艺, 导航, 或团队合作, 我们忍不住要问, “为什么这在现实世界中没有使用??“好吧,下次你看到这样的视频时,看看实验室环境是如何高度控制的. 在大多数情况下, 只有当环境条件保持在其内部模型的狭窄范围内时,这些机器人才能执行这些令人印象深刻的任务. 因此, 机器人技术进步的一个关键是向更复杂的方向发展, 灵活的, 而强健的模型和上述的进步受制于可用计算资源的限制.

机器人技术进步的一个关键是向更复杂的方向发展, 灵活的, 强健的模型.

[旁注:哲学家和心理学家都会注意到,生物也会对自己的感官告诉他们的内在感知产生依赖. 机器人技术的许多进步来自于对生物的观察,以及观察它们对意外刺激的反应. 想想看. 你对世界的内在模式是什么? 它既不同于蚂蚁,也不同于鱼? (希望.然而,就像蚂蚁和鱼一样,它可能过于简化了世界上的一些现实. 当你对世界的假设不正确时, 它会让你有失去控制的风险. 有时我们称之为“危险”.“就像我们的小机器人在未知的宇宙中挣扎求生一样,我们也都是如此. 这对机器人专家来说是一个强有力的见解.]

可编程机器人模拟器

我构建的模拟器是写在里面的 Python 非常巧妙的命名 Sobot Rimulator. 你可以找到v1.0.GitHub上的0. 它没有很多花哨的东西,但它的目的是做一件事很好:提供一个移动机器人的精确模拟,并给一个有抱负的机器人专家一个简单的框架来练习机器人软件编程. 虽然有一个真正的机器人玩总是更好的, 一个好的Python机器人模拟器更容易访问,是一个很好的起点.

在现实世界的机器人中, 产生控制信号的软件(“控制器”)需要以非常高的速度运行并进行复杂的计算. 这影响了机器人编程语言的最佳选择:通常, c++用于这类场景, 但在更简单的机器人应用中, Python是执行速度和易于开发和测试之间的一个很好的折衷.

我写的软件模拟了一个真实的研究机器人叫做 Khepera 但它可以适应一系列具有不同尺寸和传感器的移动机器人. 因为我试图将模拟器编程得尽可能接近真实机器人的能力, 控制逻辑可以通过最小的重构加载到真正的Khepera机器人中, 它的功能和模拟机器人一样. 具体实现的功能参考Khepera III, 但它们可以很容易地适应新的Khepera IV.

换句话说,为模拟机器人编程与为真实机器人编程是相似的. 如果模拟器要用于开发和评估不同的控制软件方法,这是至关重要的.

在本教程中,我将描述v1附带的机器人控制软件体系结构.0.0 of Sobot Rimulator,并提供来自Python源代码的代码片段(为了清晰起见稍作修改)。. 但是,我鼓励您深入研究源代码并进行研究. 该模拟器已被分叉并用于控制不同的移动机器人,包括来自 iRobot公司. 同样,请随意分叉项目并对其进行改进.

机器人的控制逻辑被限制在这些Python类/文件中:

  • 模型/主管.py这个类负责机器人周围的模拟世界和机器人本身之间的交互. 它进化了我们的机器人状态机,并触发控制器来计算期望的行为.
  • 模型/ 主管_state_machine.py-这个类代表了不同 机器人可以在其中,取决于它对传感器的解释.
  • 文件中的文件 模型/控制器 目录——这些类在给定已知环境状态的情况下实现机器人的不同行为. 特别是,根据状态机选择特定的控制器.

我们的目标

机器人和人一样,需要生活的目标. 我们的软件控制这个机器人的目标非常简单:它会试图到达一个预定的目标点. 这通常是任何移动机器人都应该具备的基本特征, 从自动驾驶汽车到机器人真空吸尘器. 在机器人被激活之前,目标的坐标被编程到控制软件中,但可以从一个监督机器人运动的额外Python应用程序生成. 例如,想象它通过多个路径点.

然而,更复杂的是,机器人的环境可能布满了障碍. 机器人在到达目标的途中不得与障碍物发生碰撞. 因此, 如果机器人遇到障碍物, 它必须找到自己的路,这样它才能继续朝着目标前进.

可编程机器人

每个机器人都有不同的功能和控制问题. 让我们熟悉一下我们的模拟可编程机器人.

首先要注意的是,在本指南中,我们的机器人将是一个 自主移动机器人. 这意味着它将在空间中自由移动,并且在自己的控制下移动. 这与, 说, 遥控机器人(非自主的)或工厂机械臂(不可移动的). 我们的机器人必须自己弄清楚如何实现目标并在环境中生存. 这对于新手机器人程序员来说是一个非常困难的挑战.

控制输入:传感器

机器人可以用许多不同的方式来监控环境. 这些可以包括接近传感器、光传感器、保险杠、摄像头等等. 除了, 机器人可以与外部传感器通信,这些传感器提供给它们自己无法直接观察到的信息.

我们的参考机器人配备了 9个红外传感器-新型号有8个红外线和5个超声波接近传感器,排列在每个方向的“裙子”上. 机器人前面的传感器比后面的多,因为对机器人来说,知道前面的东西比知道后面的东西更重要.

除了接近传感器,机器人还有一个 一对车轮轮胎 履带轮运动. 这些可以让你跟踪每个轮子的旋转次数, 车轮向前完全转动一次的速度为2,765个时钟节拍. 相反方向的转弯计数向后,减少滴答计数而不是增加它. 在本教程中,你不必担心具体的数字,因为我们将编写的软件使用以米表示的行进距离. 稍后,我将向您展示如何使用一个简单的Python函数从tick中计算它.

控制输出:移动性

有些机器人靠腿移动. 有些像球一样滚动. 有些甚至像蛇一样滑行.

我们的机器人是一个 差动驱动 机器人,意思是它在两个轮子上滚动. 当两个轮子以相同的速度转动时,机器人沿直线移动. 当轮子以不同的速度移动时,机器人就会转弯. 因此, 控制机器人的运动归结为适当地控制这两个轮子转动的速度.

API

在Sobot simulator中, 机器人“计算机”与(模拟的)物理世界的分离体现在文件上 机器人_主管_interface.py,它定义了与“真正的机器人”传感器和电机交互的整个API:

  • read_proximity_sensors () 以传感器的原生格式返回一个包含9个值的数组
  • read_wheel_encoders () 返回一个包含两个值的数组,这些值表示自开始以来的总刻度
  • Set_wheel_drive_rates (v_l, v_r) 获取两个值(以弧度每秒为单位),并将车轮的左右速度设置为这两个值

该接口内部使用一个机器人对象,该对象提供来自传感器的数据,并可能移动电机或车轮. 如果你想创造一个不同的机器人, 您只需要提供一个可以由相同接口使用的不同的Python机器人类, 其余的代码(控制器), 主管, 和模拟器)将开箱即用!

模拟器

就像你在现实世界中使用真正的机器人一样,不用太注意其中的物理定律, 你可以忽略机器人是如何模拟的,直接跳到控制器软件是如何编程的, 因为现实世界和模拟世界几乎是一样的. 但如果你好奇,我在这里简单介绍一下.

该文件 世界.py 是一个代表模拟世界的Python类,里面有机器人和障碍物. 这个类中的step函数负责进化我们的简单世界:

  • 将物理规则应用到机器人的运动中
  • 考虑与障碍物的碰撞
  • 为机器人传感器提供了新的价值

最后,它会呼叫负责执行机器人大脑软件的机器人主管.

阶跃函数在循环中执行,因此 机器人.step_motion () 使用上一步仿真中主管计算的轮速移动机器人.

#通过一个时间间隔步进模拟
Def step(自我):
Dt = 自我.dt
# step所有的机器人
对于机器人本身.机器人:
步进机器人运动
机器人.Step_motion (dt)

#应用物理相互作用
自我.物理.apply_物理 ()

注意:主管必须跑在最后,以确保他们观察到“当前”的世界
#步骤所有的主管
自我监督.主管:
主管.步骤(dt)

#增量世界时间
自我.World_time += dt

apply_物理 () 函数内部更新机器人接近传感器的值,以便主管能够在当前仿真步骤中估计环境. 同样的概念也适用于编码器.

简单模型

首先,我们的机器人将有一个非常简单的模型. 它会对世界做出很多假设. 其中一些重要的包括:

  • 地形总是平坦的
  • 障碍永远是圆的
  • 车轮从不打滑
  • 没有什么能推动机器人
  • 传感器从不故障或给出错误读数
  • 轮子总是按命令转动的

尽管这些假设在类似房屋的环境中是合理的, 圆形障碍物可能存在. 我们的避障软件有一个简单的实现,并遵循障碍的边界,以绕过他们. 我们将提示读者如何改进我们的机器人的控制框架与一个额外的检查,以避免圆形障碍物.

控制回路

现在我们将进入控制软件的核心,并解释我们想要在机器人内部编程的行为. 可以向该框架添加其他行为, 你应该在读完后尝试自己的想法! 基于行为的机器人 软件是在20多年前提出的,它仍然是移动机器人的强大工具. 举个例子,2007年 一组行为 在美国国防部高级研究计划局的城市挑战赛中使用,这是第一次自动驾驶汽车的比赛!

机器人是一个动态系统. 机器人的状态, 传感器的读数, 其控制信号的影响是恒定的. 控制事件发展的方式包括以下三个步骤:

  1. 应用控制信号.
  2. 衡量结果.
  3. 产生新的控制信号,使我们更接近目标.

这些步骤一遍又一遍地重复,直到我们达到目标. 每秒这样做的次数越多,我们对系统的控制就越好. Sobot simulator机器人每秒重复这些步骤20次(20赫兹)。, 但许多机器人必须每秒这样做数千次或数百万次,才能获得足够的控制. 还记得我们之前介绍的针对不同机器人系统和速度要求的不同机器人编程语言吗.

在一般情况下, 每次我们的机器人用它的传感器进行测量, 例如,它使用这些测量来更新其对世界状态的内部估计, 距离目标的距离. 它将这个状态与a进行比较 参考 它的价值 希望 (对于距离的)状态, 它希望它为0), 并计算期望状态与实际状态之间的误差. 一旦知道了这些信息,产生新的控制信号就可以减少到一个问题 最小化误差 哪一个最终会把机器人推向目标.

一个巧妙的技巧:简化模型

来控制我们想要编程的机器人, 我们得给左轮发个信号告诉它要转多快, 和一个单独的信号告诉右轮 it 转得有多快. 我们称这些为信号 vLvR. 然而,不断思考的方面 vLvR 非常麻烦. 而不是问, “我们想让左轮转多快, 我们想让正确的轮子转多快?这样问更自然, “我们希望机器人前进的速度有多快, 我们想让它转多快, 或者改变标题?“我们称这些参数为速度 v 角速度(旋转速度) ω (读“ω”). 我们可以把整个模型建立在 vω 而不是 vLvR, 只有当我们确定了我们想要我们的程序机器人如何移动, 用数学方法把这两个值转换成 vLvR 我们需要控制机器人的轮子. 这被称为a 单轮车模型 的控制.

在机器人编程中, 了解独轮车和差动驱动车型之间的区别很重要.

下面是实现最终转换的Python代码 主管.py. 注意,如果 ω 为0时,两个轮子以相同的速度转动:

生成并发送正确的命令给机器人
Def _send_机器人_comm和s(自我):
  # ...
  V_l, v_r = 自我._uni_to_diff(v, omega)
  自我.机器人.Set_wheel_drive_rates (v_l, v_r)

Def _uni_to_diff(自我, v, omega):
  平移速度(m/s)
  角速度(rad/s)

  R =自我.机器人_wheel_radius
  L =自我.机器人_wheel_base_length

  V_l = (2).0 * v) - (*L)) / (2.0 * R)
  V_r = (2).0 * v + (*L)) / (2.0 * R)

  返回v_l, v_r

估计状态:机器人,了解你自己

利用它的传感器, 机器人必须尝试估计环境的状态以及它自己的状态. 这些估计永远不会是完美的, 但它们必须相当好,因为机器人将根据这些估计做出所有决定. 仅使用其接近传感器和车轮提示器,它必须尝试猜测以下内容:

  • 障碍的方向
  • 距离障碍物的距离
  • 机器人的位置
  • 机器人的方向

前两个属性由接近传感器读数决定,相当直接. API函数 read_proximity_sensors () 返回一个包含9个值的数组,每个值代表一个传感器. 我们提前知道第七次阅读, 例如, 对应于指向机器人右侧75度的传感器.

因此,如果该值显示的读数对应于0.1米的距离,我们知道有一个障碍物.1米远,左边75度角. 如果没有障碍物,传感器将返回其最大范围0的读数.2米. 因此,如果读取0.在传感器7上2米,我们假设在那个方向上没有障碍物.

由于红外传感器的工作方式(测量红外反射), 它们返回的数字是检测到的实际距离的非线性变换. 因此, 用于确定所指示距离的Python函数必须将这些读数转换为米. 这是在 主管.py 如下:

#更新接近传感器显示的距离
Def _update_proximity_sensor_distances(自我):
    自我.Proximity_sensor_distances = [0 ..02 -(日志(readval / 3960.0) )/30.为0
        自我评价.机器人.read_proximity_sensors ())

再一次。, 我们在这个Python机器人框架中有一个特定的传感器模型, 而在现实世界中, 传感器附带软件,应该提供类似的从非线性值到仪表的转换功能.

确定机器人的位置和方向(统称为 构成 在机器人编程中更具有挑战性. 我们的机器人使用 测程法 来估计它的姿态. 这就是轮票器的作用. 通过测量自控制回路最后一次迭代以来每个轮子转动了多少, 我们可以很好地估计机器人的姿势是如何变化的,但是 只有当变化很小的时候.

这就是为什么在现实世界的机器人中频繁迭代控制回路很重要的原因之一, 驱动轮子的马达可能不完美. 如果我们等太久才去测量车轮上的轮胎, 两个轮子都可以做很多事, 而且我们不可能估计我们最终会走到哪里.

考虑到我们目前的软件模拟器, 我们可以负担得起在20赫兹运行里程计计算-与控制器相同的频率. 但是,让一个单独的Python线程运行得更快,以捕捉报时器的较小移动,这可能是一个好主意.

的完整里程计函数 主管.py 这就更新了机器人的姿态估计. 注意,机器人的姿态是由坐标组成的 xy,以及标题 θ即从x轴到正轴的弧度. 积极的 x 是向东和正的吗 y 在北边. 因此标题为 0 表明机器人正面向东方. 机器人总是假设它的初始姿态为 (0, 0), 0.

使用机器人的车轮编码器读数更新机器人的估计位置
Def _update_测程法(自我):
  R =自我.机器人_wheel_radius
  N = float(自我.wheel_encoder_ticks_per_revolution)
  
  #读取车轮编码器值
  Ticks_left, ticks_right = 自我.机器人.read_wheel_encoders ()
  
  #获取自上次迭代以来滴答数的差异
  D_ticks_left = ticks_left - 自我.prev_ticks_left
  D_ticks_right = ticks_right - 自我.prev_ticks_right
  
  #估计车轮运动
  d_left = 2*pi*R*(d_ticks_left / N)
  d_right_wheel = 2*pi*R*(d_ticks_right / N)
  D_center = 0.5 * (d_left_wheel + d_right_wheel)
  
  #计算新姿势
  Prev_x, prev_y, prev_θ = 自我.estimated_构成.scalar_unpack ()
  New_x = prev_x + (d_center * cos(prev_θ))
  New_y = prev_y + (d_center * sin(prev_θ))
  New_θ = prev_θ + ((d_right_wheel - d_left_wheel) / 自我.机器人_wheel_base_length)
  
  #用新值更新姿态估计
  自我.estimated_构成.Scalar_update (new_x, new_y, new_θ)
  
  #保存当前的滴答计数为下一次迭代
  自我.Prev_ticks_left = ticks_left
  自我.Prev_ticks_right = ticks_right

现在我们的机器人能够很好地估计现实世界, 让我们利用这些信息来实现我们的目标.

Python机器人编程方法:Go-to-Goal行为

在这个编程教程中,我们的小机器人存在的最高目的是到达目标点. 那么我们如何让轮子转动来实现它呢? 让我们先简化一下我们的世界观,假设道路上没有障碍.

这就变成了一个简单的任务,可以很容易地用Python编程. 如果我们朝着目标前进,我们就会到达那里. 感谢我们的里程计,我们知道我们当前的坐标和航向. 我们还知道目标的坐标,因为它们是预先设定好的. 因此, 用一点线性代数, 我们可以确定从我们的位置到目标的矢量, 就像在 go_to_goal_controller.py:

在机器人的参考系中返回一个到达目标的航向向量
Def calculate_gtg_heading_vector(自我):
  #得到机器人姿势的逆
  Robot_inv_pos, 机器人_inv_θ = 自我.主管.estimated_构成 ().逆().vector_unpack ()
  
  计算机器人参照系中的目标向量
  目标=自我.主管.目标()
  目标=线性.Rotate_和_translate_vector(目标,机器人_inv_θ, 机器人_inv_pos)
  
  返回的目标

注意,我们得到了指向目标的向量 在机器人的参考系中,而非世界坐标. 如果目标在机器人参照系的x轴上, 这意味着它就在机器人的正前方. 因此, 这个向量与x轴的夹角就是我们的方向和我们想要的方向之差. 换句话说,它是 错误 在我们现在的状态和我们想要的状态之间. 因此,我们想要 调整转弯速度 ω 这样头球和球门之间的夹角就会变成0. 我们想要最小化误差:

#计算误差项
的ta_d = atan2(自我.gtg_heading_vector[1],自我.gtg_heading_vector [0])

#计算角速度
=自我.kP * d

自我.kP 在上面的控制器代码片段中,Python实现是一个控制增益. 它是一个系数,决定了我们的速度 比例 我们离目标有多远. 如果我们的航向错误是 0,则旋转速率为 0. 在真正的Python函数文件中 go_to_goal_controller.py,您将看到更多类似的增益,因为我们使用了 PID控制器 而不是简单的比例系数.

现在我们有了角速度 ω,我们如何确定前进的速度 v? 一个很好的一般经验法则是你可能本能地知道的:如果我们没有转弯, 我们可以全速前进, 然后我们转得越快, 我们越应该放慢脚步. 这通常有助于我们保持系统稳定,并在模型的范围内运行. 因此, v 是的函数 ω. In go_to_goal_controller.py 方程是:

#计算平移速度
当为0时,速度为v_max,
随着|omega|上升,#迅速降至零
V =自我.主管.V_max () / (abs(omega) + 1)**0.5

关于这个公式的一个建议是,考虑到我们通常在接近目标时减速,以便以零速度到达目标. 这个公式会如何变化? 它必须包含某种形式的替换 v_max () 和距离成正比. 好了,我们几乎完成了一个控制回路. 剩下唯一要做的就是把这两个独轮车模型参数转换成差速轮速, 然后把信号发送给轮子. 以下是机器人在无障碍物的情况下,在目标控制器下的轨迹示例:

这是一个程序机器人轨迹的例子.

我们可以看到, 目标向量是我们控制计算的有效参考. 它是“我们想去的地方”的一种内在表现.“我们会看到的, 实现目标和其他行为之间唯一的主要区别是,有时候朝着目标前进是个坏主意, 所以我们必须计算一个不同的参考向量.

Python机器人编程方法:避障行为

当方向上有障碍时朝着目标前进就是一个很好的例子. 而不是一头扎进阻碍我们的事物中, 让我们试着编一个控制法则,让机器人避开它们.

为了简化场景, 现在让我们完全忘记目标点,只制定以下目标: 当我们面前没有障碍时,就勇往直前. 当遇到障碍时,转过身去,直到它不再在我们面前.

相应的, 当我们面前没有障碍的时候, 我们想让参考向量简单地指向前方. 然后 ω 将为零 v 将是最高速度. 然而, 只要我们用接近传感器探测到障碍物, 我们希望参考向量指向远离障碍物的任何方向. 这会导致 ω 把我们从障碍中引开,并造成 v 为了确保我们不会在这个过程中不小心碰到障碍.

生成所需参考向量的一种简便方法是将9个接近读数转换为向量, 然后取加权和. 当没有检测到障碍物时, 这些向量会对称地和, 产生一个参考向量,按预期指向前方. 但如果传感器打开, 说, 右边捡起一个障碍物, 它将为和贡献一个较小的向量, 结果将是一个向左移动的参考向量.

对于一般的机器人来说,传感器的位置是不同的, 同样的想法也可以应用,但当传感器在机器人的前后对称时,可能需要改变重量和/或额外的注意, 因为加权和可以变为零.

当编程正确时,机器人可以避开这些复杂的障碍物.

下面是完成这个任务的代码 avoid_obstacles_controller.py:

#传感器增益(权重)
自我.sensor_gain = [1.0+( (0.4 * abs (p.(的ta / PI)
                      对于p in 主管.proximity_sensor_placements ())

# ...

返回机器人参照系中的避障向量
#还返回机器人参照系中检测到的障碍物的向量
Def calculate_ao_heading_vector(自我):
  #初始化向量
  Obstacle_vectors = [0.0, 0.[0] * len(自我.proximity_sensor_placements)
  Ao_heading_vector = [0.0, 0.0 ]             
  
  #获取机器人传感器读数显示的距离
  sensor_distance = 自我.主管.proximity_sensor_distances ()
  
  #计算检测到的障碍物的位置,并找到一个回避向量
  Robot_pos, 机器人_θ = 自我.主管.estimated_构成 ().vector_unpack ()
  
  对于I在range(len(sensor_distances)):
    #计算障碍物的位置
    Sensor_pos, sensor_θ = 自我.proximity_sensor_placements[我].vector_unpack ()
    向量= [sensor_距离[i], 0.0 ]
    向量=线性.Rotate_和_translate_vector (vector, sensor_θ, sensor_pos)
    将障碍物向量存储在机器人的参照系中
    
    #在机器人的参考系中累积方向矢量
    Ao_heading_vector =线性.添加(ao_heading_vector
                                 linalg.Scale(向量,自我。.sensor_gain [i])
                                 
  返回ao_heading_vector, obstle_vectors

使用结果 ao_heading_vector 作为我们对机器人的参考来尝试匹配, 以下是仅使用避障控制器运行机器人软件的仿真结果, 完全忽略了目标点. 机器人漫无目的地跳来跳去, 但它从不与障碍物相撞, 甚至还能在一些非常狭小的空间里航行:

这个机器人成功地避开了Python机器人模拟器中的障碍物.

Python机器人编程方法:混合自动机(行为状态机)

到目前为止,我们已经单独描述了两种行为——实现目标和避免障碍. 两者都发挥了令人钦佩的作用, 但要想在充满障碍的环境中成功达到目标, 我们需要把它们结合起来.

我们将开发的解决方案在于一类机器,它的名称听起来非常酷 混合自动机. 混合自动机是用几种不同的行为来编程的, 或模式, 以及一个监督状态机. 监督状态机在离散时间内从一种模式切换到另一种模式(当目标实现或环境突然改变太多时), 而每一种行为都使用传感器和轮子对环境变化做出持续的反应. 这个解决方案叫做 混合动力 因为它以离散和连续的方式发展.

我们的Python机器人框架在文件中实现了状态机 主管_state_machine.py.

有了这两个方便的行为,一个简单的逻辑就出现了: 当没有检测到障碍时,使用go-to-goal行为. 当检测到障碍物时, 切换到避障行为,直到不再检测到障碍物.

然而,事实证明,这种逻辑会产生很多问题. 当这个系统遇到障碍时,它会倾向于避开它, 然后当它离开它的时候, 向右转回来,又碰到它了. 结果是一个无休止的快速切换循环,使机器人毫无用处. 在最坏的情况下,机器人可能会在行为之间切换 每次迭代过程中 控制回路的一种状态,称为 芝诺 条件.

这个问题有多种解决方案, 想了解更深层次知识的读者应该去看看, 例如, DAMN软件架构.

对于这个简单的模拟机器人,我们需要的是一个更简单的解决方案:一个专门用于获取任务的行为 周围 一个障碍,到达另一边.

Python机器人编程方法:Follow-Wall行为

思路是这样的:当我们遇到障碍时, 取离障碍物最近的两个传感器读数,并用它们来估计障碍物的表面. 然后,简单地设置我们的参考向量平行于这个表面. 一直沿着这堵墙走,直到我们和目标之间不再有障碍, 第二,我们比开始时更接近目标. 然后我们就可以确定我们已经正确地越过了障碍.

在我们有限的信息下, 我们不能肯定地说,绕着障碍物向左走还是向右走哪个更快. 为了下定决心,我们选择了能让我们立即接近目标的方向. 来弄清楚这是哪条路, 我们需要知道趋近目标行为和避障行为的参考向量, 以及两个可能的随壁参考向量. 下面是一个如何做出最终决定的例子(在这种情况下), 机器人会选择向左走):

利用几种类型的行为,编程机器人避开障碍物并继续前进.

确定跟墙参考向量比确定避障参考向量或到达目标参考向量要复杂一些. 中的Python代码 follow_wall_controller.py 看看是怎么做的.

最终控制设计

最后的控制设计在几乎所有遇到障碍物的情况下都使用了随墙行为. 然而, 如果机器人发现自己陷入困境, 危险地接近碰撞, 它将切换到纯粹的避障模式,直到它是一个安全的距离, 然后回到follow-wall. 一旦成功越过障碍,机器人就会切换到直奔目标模式. 这是最终状态图,它被编程在 主管_state_machine.py:

这张图说明了机器人编程行为之间的切换,以实现目标和避免障碍.

这是机器人使用这种控制方案成功地在拥挤的环境中导航:

机器人模拟器成功地让机器人软件避开障碍物,实现了其原有的目的.

您可以尝试实现的状态机的另一个特性是一种避免圆形障碍物的方法,方法是尽快切换到go-to- target,而不是一直沿着障碍物边界走到最后(对于圆形对象来说,这是不存在的)!)

调整,调整,再调整:尝试和错误

Sobot模拟器自带的控制方案是非常精细的. 我花了好几个小时才调整了一个小变量, 这是另一个方程, 让它以我满意的方式工作. 机器人编程通常涉及大量简单的试错过程. 机器人非常复杂,至少在机器人模拟器环境中,没有什么捷径能让它们表现出最佳状态, 完全是机器学习, 但那完全是另一回事了.

机器人技术通常涉及大量简单而老套的试错过程.

我鼓励您使用Sobot simulator中的控制变量,观察并尝试解释结果. 以下的变化都会对模拟机器人的行为产生深远的影响:

  • 误差增益 kP 在每个控制器中
  • 避障控制器使用的传感器增益
  • 的计算 v 作为的函数 ω 在每个控制器中
  • 跟随墙控制器使用的障碍物距离
  • 所使用的开关条件 主管_state_machine.py
  • 几乎所有的东西

可编程机器人出现故障

我们为此做了很多工作,这个机器人看起来很聪明. 然而,, 如果你在几个随机地图上运行Sobot simulator, 用不了多久你就会发现这个机器人无法处理的问题. 有时它会直接驶入狭窄的角落并发生碰撞. 有时它只是在障碍物的错误一侧不停地来回摆动. 有时,它会被合理地禁锢起来,没有通往目标的可能路径. 在我们所有的测试和调整之后, 有时我们必须得出这样的结论:我们正在使用的模型无法胜任这项工作, 我们必须改变设计或增加功能.

在移动机器人的世界里,我们的小机器人的“大脑”是最简单的. 它遇到的许多失败案例可以通过添加一些更高级的软件来克服. 更先进的机器人使用诸如 映射记住它在哪里,避免一遍又一遍地尝试同样的事情; 启发式, to generate acceptable decisions when there is no perfect decision to be found; 和 机器学习,以更完美地调整控制机器人行为的各种控制参数.

即将发生的事情的一个样本

机器人已经为我们做了很多事情,它们在未来只会做得更多. 即使是基本的机器人编程也是一个艰难的研究领域,需要极大的耐心, 这也是一个令人着迷和非常有益的过程.

在本教程中, 我们学习了如何使用高级编程语言Python为机器人开发响应式控制软件. 但是,还有许多更高级的概念可以通过Python机器人框架快速学习和测试,类似于我们在这里创建的原型. 我希望你能考虑一下 参与 在塑造未来的事物!


确认: 我要感谢 Dr. 马格努斯Egerstedt让-皮埃尔·德拉克罗伊 感谢乔治亚理工学院教授我这些知识, 感谢他们对我在Sobot simulator上的工作的热情.

了解基本知识

  • 什么是机器人?

    机器人是一种带有传感器和机械部件的机器,这些传感器和机械部件由电路板或cpu连接并控制. 它们处理信息并将变化应用于物理世界. 机器人大多是自主的,在从日常生活到非常危险的任务中取代或帮助人类.

  • 机器人是用来做什么的?

    机器人在工厂和农场被用来做繁重或重复的工作. 它们被用来探索行星和海洋,打扫房屋,帮助老年人. 研究人员和工程师也在尝试在灾难情况下使用机器人, 医学分析, 和手术. 自动驾驶汽车也是机器人!

  • 如何制造机器人?

    机器人的创建需要多个步骤:零件的机械布局, 传感器和驱动器的设计, 以及机器人软件的开发. 通常, 原始的车身是在工厂制造的,软件是在第一批工作原型上开发和测试的.

  • 如何给机器人编程?

    这涉及到三个步骤. 首先,你用现成的驱动器来运行马达和传感器. 然后你开发基本的构建模块,这样你就可以移动机器人并读取它的传感器. 最后,用它来开发智能的、复杂的软件程序来创建你想要的行为.

  • 机器人最好的编程语言是什么?

    在机器人中使用两种主要的编程语言是最好的:c++和Python, 通常一起使用,因为每个都有优点和缺点. c++用于控制回路、图像处理和底层硬件接口. Python用于处理高级行为和快速开发测试或概念证明.

  • 如何使用Java为机器人编程?

    假设您能够在您的机器人上运行Java虚拟机, 您可以使用套接字或RPC将Java代码与电机和传感器驱动程序连接起来. 直接在Java中编写设备驱动程序可能比在其他语言(如c++)中更难, 所以最好把重点放在培养高级行为上!

  • 什么是机器人工程?

    机器人工程是一个广泛的工程领域,专注于整个机器人系统的设计和集成. 因此它需要机械知识, 电子, 软件。, 控制系统, 与各个领域的专业工程师进行互动,以实现给定机器人的要求和目标.

  • 机器人过程自动化(RPA)和机器人编程之间的区别是什么?

    这两个领域开发的软件都是为了帮助或取代人类, 但RPA针对的是通常由人在电脑前完成的任务, 比如发送电子邮件, 提交收据, 或者浏览一个网站. 机器人在现实世界中执行任务,比如清洁, 开车, 建筑, 或制造.

  • 谁发明了世界上第一个机器人?

    第一个移动机器人于1966年在斯坦福研究所由查尔斯·罗森和尼尔斯·尼尔森领导的团队创造出来. 仅使用24位CPU和196 KB RAM, 它能够在避开障碍物的情况下在办公室里自主移动. 因为它在移动的时候会摇晃,所以他们的创造者称它为Shakey.

聘请Toptal这方面的专家.
现在雇佣
尼克·麦克雷的头像
尼克·麦克雷博士

位于 丹佛,科罗拉多州,美国

成员自 2014年7月8日

作者简介

尼古拉斯是一名专业的软件工程师,对高质量的工艺充满热情. 他喜欢架构和编写一流的代码.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

以前在

deepblocks

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.