在本项目的第二部分中,我们将继续进行项目中相对容易的内容—编程。对六足机器人进行编程通常有两种不同的方法。第一种是仅仅弄清楚机器人向前行走的一系列伺服运转。显然,这是一个艰难却鲜见成效的工作—您所设计的代码无法直接应用于另一台六足机器人上。所以第二种被称为逆运动学的方法应运而生。但是首先,我们需要改进板载微控制器。
硬件
- • Arduino Mega
- • Adafruit 16通道PWM扩展板(或模块;但是,此处强烈建议使用该扩展板,因为它的原型面积很小)
- • 12个带有金属齿轮的微型伺服(MG90S或其他同等规格产品)
- • 4.8V或6V电池(镍氢、锂离子等)
- • 60个M3螺栓+120个螺母和垫圈(仅用于身体,对于其他部件的安装您需要再另外添加)
- • 6个相同的圆珠笔弹簧
- • HC-SR04超声波测距模块(可选)
软件
- • Arduino IDE
- • Github – 您在这里可以找到所有用于打印的 Arduino源代码和3D模型
UNO -> Mega
在上一篇文章中,我们建议在本项目中使用Arduino UNO。但是,在使用UNO时我遇到了一个问题:它没有足够的SRAM内存来进行逆运动学模型正常运行时所需的所有计算。这些计算大多数是用浮点数来完成的。每个数字在使用时将占用4个字节的内存,是整型的两倍。虽然看起来不多,但是UNO只有2kB的RAM,其中一些还会被全局变量占用。如果我们为所有全局变量和其他局部变量保留0.5kB,那么将剩下1.5kB的可用内存,这仅能供384个浮点数占用。384可能看起来挺多,但是对于1K模型所产生的数据量是不够的(请阅读下面的“算法”部分找到相关原因)。所以我们必须想办法获取更大的内存。
实现该目的最简单的办法是将UNO更改为MEGA。MEGA和UNO是兼容的,所以对于原理图不用进行修改。另外,使用MEGA不仅可以为计算部分获取四倍多的RAM,还意味着将有八倍以上的闪存可用于我们的程序存储。我们很可能不会所有都用到,但是有更大的预留空间总是好的。以下是改进后的Fritzing原理图,如果您使用的是Arduino MEGA最新版本 (Rev 3),更换的过程很简单,跟断开UNO之后连接MEGA的过程一样。下面示意图以供参考。
现在,我们来探究一些物理原理、所用到的大量数学知识以及少量代码。
逆运动学简介
可能有些人还记得,在高中的时候,物理课中有一部分内容叫做“运动学”。简单地说,这是力学领域中对一个目标对象(或一个点)运动的描述。这意味着在运动学中,您将使用数学公式和模型来对单个点的已知运动进行分析。顾名思义,逆运动学(IK)恰恰相反:通过一系列数学公式来反推并创建运动。
在机器人领域,通常使用的算法只能根据相应的端点运动来计算所有关节的运动。现在,您可以清楚地看到逆运动学在伺服运动编程部分的难题上所具有的优势——它是可以通用的。从理论上来说,仅一个算法就可以处理机器人所执行的任何运动。从使用者的角度来说,它非常易于使用—您只需要告诉机器人向左转90°,然后直行1米就可以了,而不必考虑每个伺服的位置。
模型
在以上段落中,一个词不断出现:一个(数学)模型。虽然听起来很难,但是对于六足机器人这个项目来说,模型非常简单:机器人能够机械性的所达到的任何位置都可以由一组七个点来定义。一个用于身体,另外六个用于腿部。如果您查看了AP_Utils库(可在 GitHub 上获取),特别是里面的AP_Utils.h,就会看到关于这些点的定义(包含在其他内容中):
struct body {
float x;
float y;
float z;
float facing;
};
struct leg {
uint8_t number;
bool move;
float phi;
float z;
};
您可以看到在AP_Utils类中它们被声明为私有结构。
body origin;
leg legs[6];
将这些结构私有化有以下两个原因:
- 1. 用户不应具有随意修改这些值的权限。这些结构的存在是为了追踪机器人的当前位置,因此,只有当机器人真正产生运动的时候这些结构才会发生变化。假如用户想要更改当前的原点的 z 坐标,会导致IK模型发生不可预测的变化—这显然是不可取的。
- 2. 通常,将共有函数和一个类中的变量数量控制到最低,是一种良好的编程习惯(尤其在C++中)。因此这样做可以提高安全性,并利于API的轻松实现。
如果需要,我们可以将这些点可视化。现在,我们的整个机器人由七个点来表示(图3)。
这些结构用于追踪所有腿部的位置以及机器人本身的位置。您可能注意到了,腿部的位置仅由两个坐标来定义:phi 和 z。这是因为每条腿只有两个自由度,因此只能沿着两个轴进行移动。现在可以通过身体的x、y 和 z 坐标来对所有位置进行定义。每条腿的 phi 和 z 坐标的范围是-1到1,并且仅确定了腿相对于身体的位置。尽管现在来看这种复杂性似乎是不必要的,但是实际上这比每次运动后计算每条腿的 x、y 和 z 坐标容易得多。phi 坐标表示水平运动,z表示垂直运动。
算法
现在,我们对于机器人有了足够简单的数学表达,但是还没有用它来做任何事。下一步就是研究如何通过仅仅修改该模型来实现伺服的运转。我们需要完成的程序是将输入作为一组点坐标,并将其转换为伺服的运行。
这时候另一个问题就出现了,而这次,仅仅替换成另一个Arduino无法解决。当启动伺服时,我们可能需要使大部分伺服同时运转。但是,Arduino(以及所有与此相关的AVRs)一次只能执行一项任务。这意味着如果我们如果想平稳地运转伺服,就需要一个一个进行启动。如果仅仅将伺服从一端直接运转到另一端,整个过程将非常不稳定。
解决该问题的一种方法是事先计算好所有伺服的位置,然后迭代这些数据,并对所有伺服进行设置。因为Arduino MEGA的时钟频率是16MHz,所以所有伺服虽然在实际过程中以很小的增量进行离散化运转,但是整体表现出来的运行过程是连续流畅的。尽管它们只是静态图像的集合,但是这和视频中所产生的连续运动的效果一致。人脑无法对视觉信息进行快速处理。如果我们在每一次位置变换之后添加一个50毫秒的延迟,则很明显,伺服运转实际上是由小的增量所组成的。
这也是我们必须更换Arduino的原因。如果我们想要运行每一个伺服,那么我们需要大量的内存来存储刚刚所计算出的坐标。如果我们运转所有的伺服,我们将需要600个浮点数来存储运动坐标,因为每个伺服都至少需要50个位置才能产生平滑连续的运转效果。600个浮点数大约是 2.3 kB的RAM—这已经超过了UNO的容量。
在AP_Utils库中,将位置转换为伺服运转状况的是traceLeg() 和 setLegs() 函数。traceLeg() 函数仅进行计算:当提供了目标末端的 phi 和 z 坐标时,它将以坐标数组的形式创建一个路径。路径可以具有多种形状,当前支持的是线形(从一个点到另一个点的简单直线)、圆弧和椭圆弧。后两种路径可以使步行变得更加容易。第二种函数 setLegs() 将根据 traceLeg的结果来移动所有指定的支腿,但是,所有这些对常规用户是隐藏的。整个方案的重点是尽可能地方便用户使用。最终用户将无需直接设置 setLegs() ,而仅需调用与行走直接相关的函数即可。
现在我们开始进入IK编程的最后一步,即真正的行走。我们已经完成了所有基础工作:我们创建了一个模型来对所有事物进行追踪,我们可以运行多个伺服,甚至可以使伺服的运转变得相对平滑连续。在以下内容中,支腿的编号与下图中的编号相对应:
该编号系统同时与您在库中找到的代码一致。
我们先从简单的操作开始:在场地上转向。通常,在为支腿设定新的位置时,您必须保证其中三条腿在地面上。这背后的原因很明显—除非您可以非常快地移动支腿,不然机器人将失去稳定性并跌倒。我们可以分几步使机器人转向:
1. 将支腿0、2和4移动到phi 坐标最大值处(最大水平角)。
2. 对支腿1、3和5进行相同操作。
3. 身体转向。这一步是通过沿与步骤1-3中相反的方向运行所有水平方向的伺服来完成的。因为所有的支腿都在地面上且不能移动,所以可以移动的只有身体。
对这些步骤进行重复执行后,机器人可以转弯80°。当然,只要不是在过程中一直保持 phi 轴最大坐标位置,可以实现小于该角度的转向。得益于 traceLeg() 函数背后的巧妙算法,我们不必计算支腿的任何 z 坐标值—这些计算会自动进行,并形成圆或椭圆的形状。您可以在以下视频中观察到这一过程。
最后一步是行走。具体来说,我们希望它至少能向前行走。六足机器人的行走算法有很多种,但大多数算法都基于有三个自由度的支腿。我们的支腿只有两个自由度,因此我们必须自己进行一些设计。我所提出的方法虽然没有达到预期的速度,但是这种方法是最易于编程的,并且易于观察过程中的现象。
1. 首先,支腿0和3向前移动
2. 然后,支腿2和5向相同方向移动
3. 对支腿1和4进行相同操作
4. 现在,身体移动向前,开始重复执行整个过程。
您可以在以下视频中观看整个运动:
基本避障功能
您可能已经注意到了,库中有一部分专门用于SR04超声波测距仪。这是为了获取有关机器人所处环境的一些信息。当然,一个固定不动的传感器是不够的,因此在上一篇文章中我们在一个额外的伺服上也安装了一个传感器。
我相信大多数尝试制造六足机器人的人对超声波测距仪的工作原理是有一定程度了解的。对于与该传感器的接口我建议您使用 AP_Utils::sr04_median 函数。它可以提供库中所有SR04函数最准确的结果。您甚至可以输出数据的单位,目前可以支持毫米、厘米和米!
重要提示: 请注意,您需要Adafruit PWM驱动程序库来使AP_Utils运行,您可以点击此处 下载。下载完成后,和其他Arduino库一样对其进行安装。
以下是一个非常简单的“自主”模式的示例,使用了到目前为止我们所讨论的所有内容:行走、转向和从SR04中读取距离。如果您认真阅读了这篇文章,那么应该能完全理解以下代码中最重要部分的内容。有关所有函数的更多细节,请参考库文件夹中或 GitHub 页面上的 README.md文件。
Arduino 代码
#include <AP_Utils.h>
//define the pins that the SR04 is connected to
#define TRIG 3
#define ECHO 2
//create an instance of AP_Utils class
AP_Utils ardupod;
//you will have to supply your own offsets here
//see examples/calibration.ino for details
int offsets[16] = {5, 0, 0, -7, 10, -3, 6, -4, 3, -5, 10, -3, 0, 0, 0, 0};
void setup() {
//reset the robot
ardupod.begin(offsets);
}
void loop() {
//take one step directly forward
ardupod.walk(0, 1);
//if an obstacle is closer than 20 cm, we have to turn
if(sr04_median(TRIG, ECHO, CM, 100, 500) < 20.0) {
//turn 90 degrees to the right
ardupod.turn(90);
}
}
结论
恭喜您完成了这个最具挑战性的项目之一!做得很好!在尝试使您自己的Ardupod行走之前,请确认同时运行示例文件夹中的 calibration.ino 和 servo_test.ino。这对于正确设置所有伺服至关重要,这样伺服才不会被损坏!在下一篇文章中,我们可能对此项目进行最后一次探索,以对一些机械性能较差的点进行改善,以及,更重要的是,增加一些改进的功能,例如远程控制。