欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

机器人学编程简介~2019~

程序员文章站 2022-06-08 13:02:40
...

参考链接:


先来看一组问题:

  • 什么是机器人?

机器人是一种机器,其传感器和机械组件连接到电子板或CPU并由其控制。它们处理信息并将改变(控制)应用于物理世界。机器人大多是自主的,可以替代或帮助人类完成从日常工作到非常危险的任务。

  • 机器人的应用场合?

机器人在工厂和农场中用于执行繁重或重复的任务。它们被用来探索行星和海洋、清洁房屋,并帮助老年人。研究人员和工程师也在尝试在灾难情况,医学分析和手术中​​使用机器人。自动驾驶汽车也是机器人!

  • 如何构建机器人?

机器人的创建需要多个步骤:零件的机械布局、传感器和驱动器的设计以及机器人软件的开发。通常,在工厂中完成原型构建,软件在第一批工作原型上开发和测试。

  • 如何编程机器人?

涉及三个步骤。首先,可以使用现成的驱动程序运行电机和传感器。然后开发基本构建块,以便移动机器人并读取其传感器。最后,使用它来开发智能,复杂的软件例程,以创建您想要的行为。

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

在机器人技术中使用时,两种主要的编程语言是最好的:C++Python,它们通常一起使用,因为每种语言都有利有弊。C++用于控制循环,图像处理和低级硬件接口。Python用于处理高级行为并快速开发测试或概念证明。

  • 如何使用JAVA编程机器人?

假设您能够在机器人上运行Java虚拟机,则可以使用套接字或RPC将Java代码与电机和传感器驱动程序连接。直接用Java编写设备驱动程序可能比其他语言(如C++)更难,因此最好专注于开发高级行为!

  • 什么是机器人工程?

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

  • 机器人过程自动化(RPA)和机器人编程有什么区别?

这两个领域都开发软件以帮助或替换人类,但RPA的目标通常是由人在电脑前完成,例如发送电子邮件,提交收据或浏览网站。机器人技术改为在现实世界中执行任务,例如清洁、驾驶、建造或制造。

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

第一个移动机器人是由Charles Rosen和Nils Nilsson领导的团队于1966年在斯坦福研究所创建的。仅使用24位CPU和196 KB RAM,它可以在避开障碍物的同时自动在办公室中移动。由于它在移动时震动,他们的创造者称它为Shakey。


常用编程语言:PythonC++Matlab

机器人学编程简介~2019~

这是一个很多机器人学生/专家/工程师在职业生涯初期中至少会问过一次的问题。很遗憾,这也是一个没有简单答案的问题。在这篇文章中,将介绍机器人技术中使用的5大最流行的编程语言,并将讨论它们的优点和缺点,以及使用它们的原因与不适用场合。

这实际上是一个非常合理的问题。毕竟,如果事实证明你永远不会使用它,那么投入大量时间和精力来学习新的编程语言有什么意义呢?如果您是刚开始从事机器人方向的学生/专家/工程师,那么希望学习实际上对职业有用的编程语言。

为什么“它取决于”是一个无用的答案?
不幸的是,如果你问“什么是最好的机器人编程语言?”,你永远不会得到一个简单的答案。整个房间的机器人专业人士(或Stack Overflow,Quora,Trossen,Reddit或Research Gate等论坛)。

电子工程师将给出工业机器人技术人员的不同答案。计算机视觉程序员将提供与认知机器人专家不同的答案。每个人都不同意什么是“最好的编程语言”。最后,大多数人都会同意的答案是“它取决于。”对于试图决定哪个的新机器人来说,这是一个相当无用的答案。首先要学习的语言。即使这是最现实的答案,因为它取决于你想要开发的应用程序类型以及你正在使用的系统。

我应该先学习哪种编程语言?
最好问一下,您应该首先学习哪种编程语言?你仍然会得到不同的意见,但很多机器人专家可以就主要语言达成一致。

正如我们在帖子中讨论的关于必要的机器人技能一样,对于机器人专家来说,最重要的是开发“编程思维模式”而不是精通某种特定语言。在许多方面,您首先学习哪种编程语言并不重要。您学习的每种语言都可以提高您对编程思维的熟练程度,并且可以在需要时更轻松地学习任何新语言。

1. C / C ++
最后,我们达到机器人技术的第一编程语言!很多人都认为C和C ++是新机器人专家的一个很好的起点。为什么?因为很多硬件库都使用这些语言。它们允许与低级硬件交互,允许实时性能并且是非常成熟的编程语言。现在,您可能会使用C ++而不是C,因为该语言具有更多功能。C ++基本上是C的扩展。首先要学习至少一点C是很有用的,这样你就可以在找到用C编写的硬件库时识别它.C / C ++不是那么简单易用,比方说,Python或MATLAB。使用C实现相同的功能可能需要更长的时间,并且需要更多的代码行。但是,由于机器人技术非常依赖于实时性能,

2. Python
近年来,Python已经出现了巨大的复苏,特别是在机器人领域。其中一个原因可能是Python(和C ++)是ROS中的两种主要编程语言。像Java一样,它是一种解释性语言。与Java不同,该语言的主要关注点是易用性。许多人都认为它很好地实现了这一点。Python省去了许多在编程中花费时间的常见事情,例如定义和转换变量类型。此外,还有大量的免费库,这意味着当您需要实现一些基本功能时,您不必“重新发明*”。由于它允许使用C / C ++代码进行简单绑定,这意味着可以使用这些语言实现代码中性能较高的部分,以避免性能损失。Raspberry Pi),我们可能会在机器人技术中看到更多的Python。

3. Java
作为一名电子工程师,我总是惊讶于一些计算机科学学位将Java作为他们的第一门编程语言教给学生。Java“隐藏”程序员的底层内存功能,这使得编程比C语言更容易,但这也意味着您对代码的实际操作了解较少。如果你从计算机科学背景(很多人,特别是在研究中)来到机器人技术,你可能已经学习过Java。与C#和MATLAB一样,Java是一种解释性语言,这意味着它不会编译成机器代码。相反,Java虚拟机在运行时解释指令。使用Java的理论是,由于Java虚拟机,您可以在许多不同的机器上使用相同的代码。在实践中,这并不总是有效,有时会导致代码运行缓慢。但是,Java在机器人技术的某些部分非常流行,因此您可能需要它。

4. C# / .NET
C#是Microsoft提供的专有编程语言。我在这里包含C#/ .NET主要是​​因为Microsoft Robotics Developer Studio使用它作为主要语言。如果您打算使用这个系统,您可能不得不使用C#。但是,首先学习C / C ++可能是长期开发编码技能的好选择。

5. MATLAB
MATLAB及其开源亲属,如Octave,在一些机器人工程师中非常受欢迎,用于分析数据和开发控制系统。还有一个非常受欢迎的MATLAB 机器人工具箱。我认识那些仅使用MATLAB开发整个机器人系统的人。如果您想分析数据,生成高级图形或实现控制系统,您可能需要学习MATLAB。


机器人编程入门教程(翻译)

让我们面对现实,机器人很酷。它们有一天会出现在世界各地,希望到时它们会对其可怜的柔软肉质创造者(也就是机器人开发者)表示同情,并帮助我们建立一个充满了充足空间的乌托邦。我当然是开玩笑,但只是开玩笑。

我希望能够对这个问题产生一些小影响,去年我参加了一个自动机器人控制理论课程,最后我建立了一个基于Python的机器人模拟器,这使我能够在一个简单的移动可编程机器人上练习控制理论。 。

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

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

  • 数学 - 我们将使用一些三角函数和向量
  • Python-Python是最流行的基本机器人编程语言之一 - 我们将使用基本的Python库和函数

这里显示的代码片段只是整个模拟器的一部分,它依赖于类和接口,因此为了直接读取代码,您可能需要一些Python和面向对象编程的经验。

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

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

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

机器人学编程简介~2019~

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

因此,控制设计的第一步是提出现实世界的抽象,称为模型,用它来解释我们的传感器读数并做出决策。只要现实世界按照模型的假设行事,我们就可以做出好的猜测并施加控制。然而,一旦现实世界偏离这些假设,我们将无法做出正确的猜测,并且将失去控制权。通常,一旦控制失去,就永远无法恢复。(除非一些仁慈的外力恢复它。)

这是机器人编程如此困难的关键原因之一。我们经常在实验室里看到最新研究机器人的视频,表现出灵巧,导航或团队合作的奇妙成就,我们很想问:“为什么这不适用于现实世界?”好吧,下次你看到这样的视频,看看实验室环境的高度控制。在大多数情况下,只要环境条件保持在其内部模型的狭窄范围内,这些机器人只能执行这些令人印象深刻的任务。因此,机器人技术发展的一个关键是开发更复杂,更灵活,更健壮的模型 - 并且所述进步受到可用计算资源的限制。

推动机器人技术的一个关键是开发更复杂,更灵活,更健壮的模型。

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

可编程机器人仿真模拟器

我构建的模拟器是用Python编写的,非常巧妙地称为Sobot Rimulator你可以在GitHub上找到v1.0.0。它没有很多花里胡哨的东西,但是它可以很好地完成一件事:提供一个精确的移动机器人模拟,并为一个有抱负的机器人专家提供一个简单的机器人软件编程框架。虽然拥有一个真正的机器人总是更好,但一个好的Python机器人模拟器更容易获得,是一个很好的起点。

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

我编写的软件模拟了一个名为Khepera的真实研究机器人,但它可以适应各种尺寸和传感器的移动机器人。由于我尝试将模拟器编程为尽可能与真实机器人的功能相似,因此控制逻辑可以加载到真正的Khepera机器人中,只需最少的重构,并且它将执行与模拟机器人相同的操作。实施的具体功能参考Khepera III,但它们可以很容易地适应新的Khepera IV。

换句话说,编程模拟机器人类似于编程真实机器人。如果模拟器可以用于开发和评估不同的控制软件方法,那么这一点至关重要。

在本教程中,我将描述Sobot Rimulator v1.0.0附带的机器人控制软件体系结构,并提供Python源代码的片段(为清晰起见略作修改)。但是,我鼓励你潜入源头并乱七八糟。模拟器已经分叉并用于控制不同的移动机器人,包括来自iRobot的Roomba2 。同样,请随意分叉项目并进行改进。

机器人的控制逻辑受限于这些Python类/文件:

  • models/supervisor.py - 这个类负责机器人周围的模拟世界和机器人本身之间的相互作用。它演变了我们的机器人状态机并触发控制器以计算所需的行为。
  • models/supervisor_state_machine.py- 该类表示机器人可以处于的不同状态,具体取决于其对传感器的解释。
  • 目录中的models/controllers文件 - 这些类在给定已知环境状态的情况下实现机器人的不同行为。特别地,根据状态机选择特定控制器。

目标

与人一样,机器人需要生活中的目标。我们的软件控制这个机器人的目标将非常简单:它将尝试进入预定的目标点。这通常是任何移动机器人应具备的基本功能,从自动驾驶汽车到机器人吸尘器。在**机器人之前,目标的坐标被编程到控制软件中,但是可以从监视机器人运动的附加Python应用程序生成。例如,想想它通过多个航路点开车。

然而,使问题复杂化的是,机器人的环境可能充满障碍物。机器人在飞往目标的途中不得与障碍物发生碰撞。因此,如果机器人遇到障碍物,它将不得不找到它的方式,以便它可以继续前往目标。

可编程机器人

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

首先要注意的是,在本指南中,我们的机器人将成为一个自动移动机器人。这意味着它将在空间中*移动,并且它将在其自己的控制下这样做。这与例如遥控机器人(不是自主的)或工厂机器人臂(不是移动的)形成对比。我们的机器人必须自己弄清楚如何实现其目标并在其环境中生存。对于新手机器人程序员来说,这被证明是一项令人惊讶的难度挑战。

控制输入​​:传感器

机器人可以通过许多不同的方式来监控其环境。这些可以包括接近传感器,光传感器,保险杠,相机等等。此外,机器人可以与外部传感器通信,这些传感器为他们提供他们自己无法直接观察的信息。

我们的参考机器人配备了9个红外传感器 - 较新的型号有8 个红外传感器和5个超声波接近传感器 - 在每个方向的“裙边”排列。有更多的传感器面向机器人的前部而不是后部,因为机器人通常更重要的是知道它前面的是什么。

除了接近传感器,机器人还有一跟踪车轮运动的车轮定位器。这些允许您跟踪每个车轮产生的转数,一个车轮的一个完整的向前转弯是2,765个刻度。向后反转向后计数,减少滴答计数而不是增加它。您不必担心本教程中的特定数字,因为我们将编写的软件使用以米为单位表示的行进距离。稍后我将向您展示如何使用简单的Python函数从ticks计算它。

控制输出:移动性

一些机器人在腿上移动。有些像滚球一样滚动。有些人甚至像蛇一样滑行。

我们的机器人是差动驱动机器人,这意味着它在两个*上滚动。当两个车轮以相同的速度转弯时,机器人以直线移动。当车轮以不同的速度移动时,机器人转动。因此,控制该机器人的运动归结为适当地控制这两个车轮中的每一个转动的速率。

API

在Sobot Rimulator中,机器人“计算机”和(模拟)物理世界之间的分离由文件体现,该文件robot_supervisor_interface.py定义了与“真实机器人”传感器和电机交互的整个API:

  • read_proximity_sensors() 以传感器的本机格式返回九个值的数组
  • read_wheel_encoders() 返回一个包含两个值的数组,表示自开始以来的总计滴答数
  • set_wheel_drive_rates( v_l, v_r ) 取两个值(以弧度/秒为单位)并将*的左右速度设置为这两个值

该接口内部使用机器人对象,该对象提供来自传感器的数据以及移动电机或车轮的可能性。如果你想创建一个不同的机器人,你只需要提供一个可以被同一个界面使用的不同的Python机器人类,其余的代码(控制器,管理程序和模拟器)将开箱即用!

仿真模拟器

正如您在现实世界中使用真实机器人而不过多关注所涉及的物理定律那样,您可以忽略机器人的模拟方式,直接跳到控制器软件的编程方式,因为它几乎是相同的在现实世界和模拟之间。但如果你很好奇,我会在这里简要介绍一下。

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

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

最后,它调用负责执行机器人脑软件的机器人监督员。

阶梯函数在循环中执行,以便robot.step_motion()使用在先前模拟步骤中由监督者计算的轮速来移动机器人。

# step the simulation through one time interval
def step( self ):
dt = self.dt
# step all the robots
for robot in self.robots:
# step robot motion
robot.step_motion( dt )

# apply physics interactions
self.physics.apply_physics()

# NOTE: The supervisors must run last to ensure they are observing the "current" world
# step all of the supervisors
for supervisor in self.supervisors:
supervisor.step( dt )

# increment world time
self.world_time += dt

apply_physics()功能在内部更新机器人接近传感器的值,以便管理员能够在当前模拟步骤中估计环境。相同的概念适用于编码器。

一个简单的模型

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

  • 地形总是平坦而均匀
  • 障碍永远不会是圆的
  • 车轮永不滑动
  • 什么都不会推动机器人
  • 传感器永远不会失败或给出错误的读数
  • *在被告知时总是转动

虽然大多数这些假设在类似房屋的环境中是合理的,但是可能存在圆形障碍。我们的避障软件有一个简单的实现,并遵循障碍的边界,以绕过它们。我们将提醒读者如何改进我们机器人的控制框架,并进行额外检查以避免出现圆形障碍物。

控制回路

我们现在将进入控制软件的核心并解释我们想要在机器人内部编程的行为。可以在此框架中添加其他行为,您应该在阅读完毕后尝试自己的想法!基于行为的机器人软件是在20多年前提出的,它仍然是移动机器人的强大工具。例如,在2007 年,DARPA城市挑战中使用了一系列行为 - 第一个自动驾驶汽车竞赛!

机器人是动态系统。机器人的状态,传感器的读数以及控制信号的影响是不断变化的。控制事件发生的方式涉及以下三个步骤:

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

这些步骤一遍又一遍地重复,直到我们实现了目标。我们每秒执行此操作的次数越多,我们对系统的控制就越精细。Sobot Rimulator机器人每秒重复这些步骤20次(20 Hz),但许多机器人必须每秒执行数千或数百万次才能获得足够的控制。请记住我们之前介绍的针对不同机器人系统和速度要求的不同机器人编程语言。

通常,每次我们的机器人使用其传感器进行测量时,它使用这些测量来更新其对世界状态的内部估计 - 例如,距其目标的距离。它将该状态与它想要状态的参考值(对于距离,它希望它为零)进行比较,并计算所需状态和实际状态之间的误差。一旦知道该信息,就可以将生成新的控制信号减少到最小化误差的问题,该误差最终将机器人移向目标。

一个漂亮的技巧:简化模型

为了控制我们想要编程的机器人,我们必须向左轮发送一个信号,告诉它转弯的速度,并向右轮发出单独的信号,告诉转弯的速度。让我们把这些信号vLv。然而,在vLvR方面不断思考是非常麻烦的。而不是问:“我们希望左轮转动的速度有多快,我们想要右转轮的速度有多快?”更自然地问:“我们希望机器人向前移动的速度有多快,以及如何我们想要它转向或改变其航向吗?“让我们称这些参数为速度v和角度(旋转)速度ω(读“欧米茄”)。事实证明我们可以将整个模型基于vω而不是vLvR,并且只有在我们确定了我们希望编程机器人如何移动时,才能将这两个值数学转换为我们需要的vLvR实际控制机器人车轮。这被称为独轮车控制模型

机器人学编程简介~2019~

 

这是实现最终转换的Python代码supervisor.py。请注意,如果ω为0,则两个车轮将以相同的速度转弯:

# generate and send the correct commands to the robot
def _send_robot_commands( self ):
  # ...
  v_l, v_r = self._uni_to_diff( v, omega )
  self.robot.set_wheel_drive_rates( v_l, v_r )

def _uni_to_diff( self, v, omega ):
  # v = translational velocity (m/s)
  # omega = angular velocity (rad/s)

  R = self.robot_wheel_radius
  L = self.robot_wheel_base_length

  v_l = ( (2.0 * v) - (omega*L) ) / (2.0 * R)
  v_r = ( (2.0 * v) + (omega*L) ) / (2.0 * R)

  return v_l, v_r

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

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

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

前两个属性由接近传感器读数确定,并且非常简单。API函数read_proximity_sensors()返回一个包含九个值的数组,每个传感器一个。我们提前知道,例如,第七次读数对应于指向机器人右侧75度的传感器。

因此,如果该值显示对应于0.1米距离的读数,我们知道在距离左侧75度处有0.1米的障碍物。如果没有障碍物,传感器将返回其最大范围为0.2米的读数。因此,如果我们在传感器7上读取0.2米,我们将假设在该方向上实际上没有障碍物。

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

# update the distances indicated by the proximity sensors
def _update_proximity_sensor_distances( self ):
    self.proximity_sensor_distances = [ 0.02-( log(readval/3960.0) )/30.0 for
        readval in self.robot.read_proximity_sensors() ]

同样,我们在这个Python机器人框架中有一个特定的传感器模型,而在现实世界中,传感器附带的软件应该提供从非线性值到米的类似转换函数。

确定机器人的位置和方向(一起称为机器人编程中的姿势)更具挑战性。我们的机器人使用里程计来估计它的姿势。这是轮式报警器的用武之地。通过测量自控制回路的最后一次迭代以来每个车轮转动了多少,可以很好地估计机器人的姿势如何变化 - 但只有在变化很小的情况下

这是在现实世界的机器人中非常频繁地迭代控制回路很重要的一个原因,其中移动*的马达可能不是完美的。如果我们等待太长时间来测量车轮报警器,那么两个车轮都可以完成很多工作,并且无法估计我们最终的位置。

鉴于我们目前的软件模拟器,我们可以负担20 Hz的测距计算 - 与控制器相同的频率。但是,让一个单独的Python线程运行得更快以捕获更小的代码移动可能是一个好主意。

以下是supervisor.py更新机器人姿势估计的完整里程计功能。请注意,机器人的姿势由坐标xy标题组成,标题theta是从正X轴以弧度为单位测量的。积极x向东,积极y向北。因此,标题0表示机器人正对着东方。机器人总是假设它的初始姿势是(0, 0), 0

# update the estimated position of the robot using it's wheel encoder readings
def _update_odometry( self ):
  R = self.robot_wheel_radius
  N = float( self.wheel_encoder_ticks_per_revolution )
  
  # read the wheel encoder values
  ticks_left, ticks_right = self.robot.read_wheel_encoders()
  
  # get the difference in ticks since the last iteration
  d_ticks_left = ticks_left - self.prev_ticks_left
  d_ticks_right = ticks_right - self.prev_ticks_right
  
  # estimate the wheel movements
  d_left_wheel = 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 )
  
  # calculate the new pose
  prev_x, prev_y, prev_theta = self.estimated_pose.scalar_unpack()
  new_x = prev_x + ( d_center * cos( prev_theta ) )
  new_y = prev_y + ( d_center * sin( prev_theta ) )
  new_theta = prev_theta + ( ( d_right_wheel - d_left_wheel ) / self.robot_wheel_base_length )
  
  # update the pose estimate with the new values
  self.estimated_pose.scalar_update( new_x, new_y, new_theta )
  
  # save the current tick count for the next iteration
  self.prev_ticks_left = ticks_left
  self.prev_ticks_right = ticks_right

现在我们的机器人能够生成对现实世界的良好估计,让我们使用这些信息来实现我们的目标。

相关: 视频游戏物理教程 - 固体物体的碰撞检测

Python机器人编程方法:前进目标行为

我们的小机器人在这个编程教程中存在的最高目的是达到目标点。那么我们如何让车轮转向那里呢?让我们从简化我们的世界观开始,并假设在路上没有障碍。

这将成为一项简单的任务,可以在Python中轻松编程。如果我们面向目标前进,我们将到达那里。由于我们的测距法,我们知道我们当前的坐标和标题是什么。我们也知道目标的坐标是什么,因为它们是预先编程的。因此,使用一点线性代数,我们可以确定从我们的位置到目标的向量,如go_to_goal_controller.py

# return a go-to-goal heading vector in the robot's reference frame
def calculate_gtg_heading_vector( self ):
  # get the inverse of the robot's pose
  robot_inv_pos, robot_inv_theta = self.supervisor.estimated_pose().inverse().vector_unpack()
  
  # calculate the goal vector in the robot's reference frame
  goal = self.supervisor.goal()
  goal = linalg.rotate_and_translate_vector( goal, robot_inv_theta, robot_inv_pos )
  
  return goal

请注意,我们在向机器人的参考系中获取目标,而不是在世界坐标中。如果目标位于机器人参考系中的X轴上,则表示它直接位于机器人前方。因此,该向量与X轴的角度是我们的航向与我们想要的航向之间的差异。换句话说,它是我们当前状态与我们想要的当前状态之间的误差。因此,我们想要调整我们的转弯率ω,以便我们的航向和目标之间的角度将朝0变化。我们希望最小化误差:

# calculate the error terms
theta_d = atan2( self.gtg_heading_vector[1], self.gtg_heading_vector[0] )

# calculate angular velocity
omega = self.kP * theta_d

self.kP在上面的控制器片段中,Python实现是一个控制增益。这是决定了我们如何快速转了系数比例如何远离我们所面对的目标。如果我们的标题中的错误是0,那么转弯率也是0。在文件中的真实Python函数中go_to_goal_controller.py,您将看到更多类似的增益,因为我们使用PID控制器而不是简单的比例系数。

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

# calculate translational velocity
# velocity is v_max when omega is 0,
# drops rapidly to zero as |omega| rises
v = self.supervisor.v_max() / ( abs( omega ) + 1 )**0.5

详细说明这个公式的建议是考虑我们通常在接近目标时减速以便以零速度到达目标。这个公式将如何变化?它必须以某种方式包括v_max()与距离成比例的东西的替代。好的,我们几乎完成了一个控制循环。剩下要做的唯一事情就是将这两个独轮车模型参数转换为差速轮速,并将信号发送到车轮。这是一个机器人在进入目标控制器下的轨迹的例子,没有障碍物:

机器人学编程简介~2019~

 

正如我们所看到的,目标向量是我们基于控制计算的有效参考。它是“我们想要去的地方”的内部表示。正如我们将要看到的,目标与其他行为之间唯一的主要区别在于,有时朝着目标前进是一个坏主意,所以我们必须计算一个不同的参考矢量。

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

当那个方向存在障碍时朝着目标前进是一个很好的例子。让我们尝试编写一个让机器人避开它们的控制律,而不是按照我们的方式运行。

为了简化场景,现在让我们完全忘记目标点,并将目标作为我们的目标: 当我们面前没有障碍时,向前迈进。遇到障碍物时,请远离它,直到它不再在我们面前。

因此,当我们面前没有障碍时,我们希望我们的参考向量简单地指向前方。那么ω将为零,v将是最大速度。然而,一旦我们用接近传感器检测到障碍物,我们希望参考矢量指向远离障碍物的任何方向。这将导致ω射击以使我们远离障碍物,并导致v下降以确保我们不会在此过程中意外碰到障碍物。

生成我们想要的参考向量的一种简洁方法是将我们的九个邻近读数转换为向量,并采用加权和。当没有检测到障碍物时,矢量将对称地求和,从而产生直接指向所需的参考矢量。但是,如果右侧的传感器拾取障碍物,它将向总和贡献较小的矢量,结果将是向左移动的参考矢量。

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

 

机器人学编程简介~2019~

以下是执行此操作的代码avoid_obstacles_controller.py

# sensor gains (weights)
self.sensor_gains = [ 1.0+( (0.4*abs(p.theta)) / pi )
                      for p in supervisor.proximity_sensor_placements() ]

# ...

# return an obstacle avoidance vector in the robot's reference frame
# also returns vectors to detected obstacles in the robot's reference frame
def calculate_ao_heading_vector( self ):
  # initialize vector
  obstacle_vectors = [ [ 0.0, 0.0 ] ] * len( self.proximity_sensor_placements )
  ao_heading_vector = [ 0.0, 0.0 ]             
  
  # get the distances indicated by the robot's sensor readings
  sensor_distances = self.supervisor.proximity_sensor_distances()
  
  # calculate the position of detected obstacles and find an avoidance vector
  robot_pos, robot_theta = self.supervisor.estimated_pose().vector_unpack()
  
  for i in range( len( sensor_distances ) ):
    # calculate the position of the obstacle
    sensor_pos, sensor_theta = self.proximity_sensor_placements[i].vector_unpack()
    vector = [ sensor_distances[i], 0.0 ]
    vector = linalg.rotate_and_translate_vector( vector, sensor_theta, sensor_pos )
    obstacle_vectors[i] = vector   # store the obstacle vectors in the robot's reference frame
    
    # accumulate the heading vector within the robot's reference frame
    ao_heading_vector = linalg.add( ao_heading_vector,
                                 linalg.scale( vector, self.sensor_gains[i] ) )
                                 
  return ao_heading_vector, obstacle_vectors

使用结果ao_heading_vector作为我们的机器人试图匹配的参考,这里是仅使用避障障碍控制器在模拟中运行机器人软件的结果,完全忽略了目标点。机器人漫无目的地反弹,但它从不与障碍物碰撞,甚至设法在一些非常狭小的空间中航行:

机器人学编程简介~2019~

 

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

到目前为止,我们已经分别描述了两种行为 - 进入目标和避免 - 障碍。两者都表现出令人钦佩的功能,但为了在充满障碍的环境中成功达到目标,我们需要将它们结合起来。

我们将开发的解决方案在于一类具有极其酷炫的混合自动机标识的机器。混合自动机被编程为具有若干不同的行为或模式,以及监督状态机。监督状态机在离散时间(当达到目标或环境突然改变太多时)从一种模式切换到另一种模式,而每种行为使用传感器和*对环境变化作出持续反应。该解决方案被称为混合解决方案,因为它以离散和连续的方式发展。

我们的Python机器人框架在文件中实现状态机supervisor_state_machine.py

配备我们的两个方便行为,一个简单的逻辑表明自己: 当没有检测到障碍物时,使用去目标行为。当检测到障碍物时,切换到避障障碍行为,直到不再检测到障碍物。

然而,事实证明,这种逻辑会产生很多问题。当这个系统遇到障碍物时,它往往会做什么,就是转离它,然后一旦它离开它,再向右转,再次碰到它。结果是无限循环的快速切换使机器人无用。在最坏的情况下,机器人可以在控制循环的每次迭代之间切换行为- 称为芝诺条件的状态

这个问题有多种解决方案,寻找更深层知识的读者应该检查一下DAMN软件架构

我们对简单模拟机器人的需求是一个更简单的解决方案:还有一种行为专门用于绕过障碍物并到达另一侧。

Python机器人编程方法:跟随墙行为

这是一个想法:当我们遇到障碍物时,取两个最接近障碍物的传感器读数并用它们来估计障碍物的表面。然后,只需将我们的参考矢量设置为与此曲面平行即可。继续沿着这堵墙走,直到A)障碍不再在我们和目标之间,并且B)我们比我们开始时更接近目标。然后我们可以肯定我们已经正确地驾驭了障碍物。

由于我们的信息有限,我们无法肯定地说,围绕左侧或右侧的障碍物是否会更快。为了决定我们的想法,我们选择的方向将使我们立即更接近目标。为了确定哪种方式,我们需要知道去目标行为和避免障碍行为的参考向量,以及两个可能的跟随参考向量。以下是最终决策的说明(在这种情况下,机器人将选择向左):

机器人学编程简介~2019~

 

确定跟随参考向量比避免障碍或去目标参考向量更复杂。看看Python代码,follow_wall_controller.py看看它是如何完成的。

最终控制设计

最终的控制设计使用了几乎所有遇到障碍物的跟随行为。然而,如果机器人发现自己处于一个紧密的位置,危险地靠近碰撞,它将切换到纯粹的避障障碍模式,直到距离更安全,然后返回到跟随墙。一旦障碍物成功协商,机器人就会切换到目标。这是最终的状态图,它在以下内部编程supervisor_state_machine.py

机器人学编程简介~2019~

 

以下是使用此控制方案成功导航拥挤环境的机器人:

 

机器人学编程简介~2019~

您可以尝试实施的状态机的另一个功能是通过尽快切换到目标而不是跟随障碍物边界直到结束(圆形物体不存在!)来避免圆形障碍物的方法! )

调整,调整,调整:试用和错误

Sobot Rimulator附带的控制方案经过精心调校。这里需要花费很多时间来调整一个小变量,并在那里调整另一个等式,以使其以我满意的方式工作。机器人编程通常涉及大量简单的反复试验。机器人非常复杂,并且很少有捷径可以让它们在机器人模拟器环境中表现得最佳......至少,没有太多的直接机器学习,但这是另外一种蠕虫。

机器人技术通常涉及大量普通的反复试验。

我鼓励您在Sobot Rimulator中使用控制变量并观察并尝试解释结果。对以下内容的更改都会对模拟机器人的行为产生深远影响:

  • kP每个控制器的错误增益
  • 避免障碍物控制器使用的传感器增益
  • 在每个控制器中计算v作为ω的函数
  • 跟随控制器使用的障碍物间隔距离
  • 使用的切换条件 supervisor_state_machine.py
  • 几乎任何其他东西

可编程机器人失败时

我们已经做了很多工作来达到这一点,这个机器人看起来非常聪明。然而,如果您通过几个随机地图运行Sobot Rimulator,不久之后您就会发现这个机器人无法处理的地图。有时它会将自己直接驱动到狭窄的角落并发生碰撞。有时它只是在障碍物的错误一侧无休止地来回摆动。偶尔它会被合法地监禁,没有可能的目标路径。在我们进行所有测试和调整之后,有时我们必须得出结论,我们正在使用的模型不能胜任工作,我们必须改变设计或添加功能。

在移动机器人世界中,我们的小机器人的“大脑”处于更简单的一端。它遇到的许多故障情况可以通过添加一些更高级的软件来克服。更高级的机器人利用映射等技术来记住它的位置并避免一遍又一遍地尝试相同的事情; 启发式,当没有完美的决定被发现时产生可接受的决定; 和机器学习,以更完美地调整控制机器人行为的各种控制参数。

什么来的样本

机器人已经为我们做了很多事情,他们将来只会做更多的事情。虽然基本的机器人编程是一个艰难的学习领域,需要极大的耐心,但它也是一个令人着迷且非常有益的一个。

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


致谢:我要感谢佐治亚理工学院的Magnus Egerstedt博士Jean-Pierre de la Croix教授我所有这些东西,感谢他们对我在Sobot Rimulator工作的热情。