在本項目的第二部分中,我們將繼續(xù)進行項目中相對容易的內(nèi)容—編程。對六足機器人進行編程通常有兩種不同的方法。第一種是僅僅弄清楚機器人向前行走的一系列伺服運轉(zhuǎn)。顯然,這是一個艱難卻鮮見成效的工作—您所設(shè)計的代碼無法直接應(yīng)用于另一臺六足機器人上。所以第二種被稱為逆運動學的方法應(yīng)運而生。但是首先,我們需要改進板載微控制器。
硬件
- ? Arduino Mega
- ? Adafruit 16通道PWM擴展板(或模塊;但是,此處強烈建議使用該擴展板,因為它的原型面積很小)
- ? 12個帶有金屬齒輪的微型伺服(MG90S或其他同等規(guī)格產(chǎn)品)
- ? 4.8V或6V電池(鎳氫、鋰離子等)
- ? 60個M3螺栓+120個螺母和墊圈(僅用于身體,對于其他部件的安裝您需要再另外添加)
- ? 6個相同的圓珠筆彈簧
- ? HC-SR04超聲波測距模塊(可選)
軟件
- ? Arduino IDE
- ? Github – 您在這里可以找到所有用于打印的 Arduino源代碼和3D模型
UNO -> Mega
在上一篇文章中,我們建議在本項目中使用Arduino UNO。但是,在使用UNO時我遇到了一個問題:它沒有足夠的SRAM內(nèi)存來進行逆運動學模型正常運行時所需的所有計算。這些計算大多數(shù)是用浮點數(shù)來完成的。每個數(shù)字在使用時將占用4個字節(jié)的內(nèi)存,是整型的兩倍。雖然看起來不多,但是UNO只有2kB的RAM,其中一些還會被全局變量占用。如果我們?yōu)樗腥肿兞亢推渌植孔兞勘A?.5kB,那么將剩下1.5kB的可用內(nèi)存,這僅能供384個浮點數(shù)占用。384可能看起來挺多,但是對于1K模型所產(chǎn)生的數(shù)據(jù)量是不夠的(請閱讀下面的“算法”部分找到相關(guān)原因)。所以我們必須想辦法獲取更大的內(nèi)存。
實現(xiàn)該目的最簡單的辦法是將UNO更改為MEGA。MEGA和UNO是兼容的,所以對于原理圖不用進行修改。另外,使用MEGA不僅可以為計算部分獲取四倍多的RAM,還意味著將有八倍以上的閃存可用于我們的程序存儲。我們很可能不會所有都用到,但是有更大的預(yù)留空間總是好的。以下是改進后的Fritzing原理圖,如果您使用的是Arduino MEGA最新版本 (Rev 3),更換的過程很簡單,跟斷開UNO之后連接MEGA的過程一樣。下面示意圖以供參考。
現(xiàn)在,我們來探究一些物理原理、所用到的大量數(shù)學知識以及少量代碼。
逆運動學簡介
可能有些人還記得,在高中的時候,物理課中有一部分內(nèi)容叫做“運動學”。簡單地說,這是力學領(lǐng)域中對一個目標對象(或一個點)運動的描述。這意味著在運動學中,您將使用數(shù)學公式和模型來對單個點的已知運動進行分析。顧名思義,逆運動學(IK)恰恰相反:通過一系列數(shù)學公式來反推并創(chuàng)建運動。
在機器人領(lǐng)域,通常使用的算法只能根據(jù)相應(yīng)的端點運動來計算所有關(guān)節(jié)的運動。現(xiàn)在,您可以清楚地看到逆運動學在伺服運動編程部分的難題上所具有的優(yōu)勢——它是可以通用的。從理論上來說,僅一個算法就可以處理機器人所執(zhí)行的任何運動。從使用者的角度來說,它非常易于使用—您只需要告訴機器人向左轉(zhuǎn)90°,然后直行1米就可以了,而不必考慮每個伺服的位置。
模型
在以上段落中,一個詞不斷出現(xiàn):一個(數(shù)學)模型。雖然聽起來很難,但是對于六足機器人這個項目來說,模型非常簡單:機器人能夠機械性的所達到的任何位置都可以由一組七個點來定義。一個用于身體,另外六個用于腿部。如果您查看了AP_Utils庫(可在 GitHub 上獲取),特別是里面的AP_Utils.h,就會看到關(guān)于這些點的定義(包含在其他內(nèi)容中):
struct body {
float x;
float y;
float z;
float facing;
};
struct leg {
uint8_t number;
bool move;
float phi;
float z;
};
您可以看到在AP_Utils類中它們被聲明為私有結(jié)構(gòu)。
body origin;
leg legs[6];
將這些結(jié)構(gòu)私有化有以下兩個原因:
- 1. 用戶不應(yīng)具有隨意修改這些值的權(quán)限。這些結(jié)構(gòu)的存在是為了追蹤機器人的當前位置,因此,只有當機器人真正產(chǎn)生運動的時候這些結(jié)構(gòu)才會發(fā)生變化。假如用戶想要更改當前的原點的 z 坐標,會導(dǎo)致IK模型發(fā)生不可預(yù)測的變化—這顯然是不可取的。
- 2. 通常,將共有函數(shù)和一個類中的變量數(shù)量控制到最低,是一種良好的編程習慣(尤其在C++中)。因此這樣做可以提高安全性,并利于API的輕松實現(xiàn)。
如果需要,我們可以將這些點可視化。現(xiàn)在,我們的整個機器人由七個點來表示(圖3)。
這些結(jié)構(gòu)用于追蹤所有腿部的位置以及機器人本身的位置。您可能注意到了,腿部的位置僅由兩個坐標來定義:phi 和 z。這是因為每條腿只有兩個自由度,因此只能沿著兩個軸進行移動。現(xiàn)在可以通過身體的x、y 和 z 坐標來對所有位置進行定義。每條腿的 phi 和 z 坐標的范圍是-1到1,并且僅確定了腿相對于身體的位置。盡管現(xiàn)在來看這種復(fù)雜性似乎是不必要的,但是實際上這比每次運動后計算每條腿的 x、y 和 z 坐標容易得多。phi 坐標表示水平運動,z表示垂直運動。
算法
現(xiàn)在,我們對于機器人有了足夠簡單的數(shù)學表達,但是還沒有用它來做任何事。下一步就是研究如何通過僅僅修改該模型來實現(xiàn)伺服的運轉(zhuǎn)。我們需要完成的程序是將輸入作為一組點坐標,并將其轉(zhuǎn)換為伺服的運行。
這時候另一個問題就出現(xiàn)了,而這次,僅僅替換成另一個Arduino無法解決。當啟動伺服時,我們可能需要使大部分伺服同時運轉(zhuǎn)。但是,Arduino(以及所有與此相關(guān)的AVRs)一次只能執(zhí)行一項任務(wù)。這意味著如果我們?nèi)绻肫椒€(wěn)地運轉(zhuǎn)伺服,就需要一個一個進行啟動。如果僅僅將伺服從一端直接運轉(zhuǎn)到另一端,整個過程將非常不穩(wěn)定。
解決該問題的一種方法是事先計算好所有伺服的位置,然后迭代這些數(shù)據(jù),并對所有伺服進行設(shè)置。因為Arduino MEGA的時鐘頻率是16MHz,所以所有伺服雖然在實際過程中以很小的增量進行離散化運轉(zhuǎn),但是整體表現(xiàn)出來的運行過程是連續(xù)流暢的。盡管它們只是靜態(tài)圖像的集合,但是這和視頻中所產(chǎn)生的連續(xù)運動的效果一致。人腦無法對視覺信息進行快速處理。如果我們在每一次位置變換之后添加一個50毫秒的延遲,則很明顯,伺服運轉(zhuǎn)實際上是由小的增量所組成的。
這也是我們必須更換Arduino的原因。如果我們想要運行每一個伺服,那么我們需要大量的內(nèi)存來存儲剛剛所計算出的坐標。如果我們運轉(zhuǎn)所有的伺服,我們將需要600個浮點數(shù)來存儲運動坐標,因為每個伺服都至少需要50個位置才能產(chǎn)生平滑連續(xù)的運轉(zhuǎn)效果。600個浮點數(shù)大約是 2.3 kB的RAM—這已經(jīng)超過了UNO的容量。
在AP_Utils庫中,將位置轉(zhuǎn)換為伺服運轉(zhuǎn)狀況的是traceLeg() 和 setLegs() 函數(shù)。traceLeg() 函數(shù)僅進行計算:當提供了目標末端的 phi 和 z 坐標時,它將以坐標數(shù)組的形式創(chuàng)建一個路徑。路徑可以具有多種形狀,當前支持的是線形(從一個點到另一個點的簡單直線)、圓弧和橢圓弧。后兩種路徑可以使步行變得更加容易。第二種函數(shù) setLegs() 將根據(jù) traceLeg的結(jié)果來移動所有指定的支腿,但是,所有這些對常規(guī)用戶是隱藏的。整個方案的重點是盡可能地方便用戶使用。最終用戶將無需直接設(shè)置 setLegs() ,而僅需調(diào)用與行走直接相關(guān)的函數(shù)即可。
現(xiàn)在我們開始進入IK編程的最后一步,即真正的行走。我們已經(jīng)完成了所有基礎(chǔ)工作:我們創(chuàng)建了一個模型來對所有事物進行追蹤,我們可以運行多個伺服,甚至可以使伺服的運轉(zhuǎn)變得相對平滑連續(xù)。在以下內(nèi)容中,支腿的編號與下圖中的編號相對應(yīng):
該編號系統(tǒng)同時與您在庫中找到的代碼一致。
我們先從簡單的操作開始:在場地上轉(zhuǎn)向。通常,在為支腿設(shè)定新的位置時,您必須保證其中三條腿在地面上。這背后的原因很明顯—除非您可以非常快地移動支腿,不然機器人將失去穩(wěn)定性并跌倒。我們可以分幾步使機器人轉(zhuǎn)向:
1. 將支腿0、2和4移動到phi 坐標最大值處(最大水平角)。
2. 對支腿1、3和5進行相同操作。
3. 身體轉(zhuǎn)向。這一步是通過沿與步驟1-3中相反的方向運行所有水平方向的伺服來完成的。因為所有的支腿都在地面上且不能移動,所以可以移動的只有身體。
對這些步驟進行重復(fù)執(zhí)行后,機器人可以轉(zhuǎn)彎80°。當然,只要不是在過程中一直保持 phi 軸最大坐標位置,可以實現(xiàn)小于該角度的轉(zhuǎn)向。得益于 traceLeg() 函數(shù)背后的巧妙算法,我們不必計算支腿的任何 z 坐標值—這些計算會自動進行,并形成圓或橢圓的形狀。您可以在以下視頻中觀察到這一過程。
最后一步是行走。具體來說,我們希望它至少能向前行走。六足機器人的行走算法有很多種,但大多數(shù)算法都基于有三個自由度的支腿。我們的支腿只有兩個自由度,因此我們必須自己進行一些設(shè)計。我所提出的方法雖然沒有達到預(yù)期的速度,但是這種方法是最易于編程的,并且易于觀察過程中的現(xiàn)象。
1. 首先,支腿0和3向前移動
2. 然后,支腿2和5向相同方向移動
3. 對支腿1和4進行相同操作
4. 現(xiàn)在,身體移動向前,開始重復(fù)執(zhí)行整個過程。
您可以在以下視頻中觀看整個運動:
基本避障功能
您可能已經(jīng)注意到了,庫中有一部分專門用于SR04超聲波測距儀。這是為了獲取有關(guān)機器人所處環(huán)境的一些信息。當然,一個固定不動的傳感器是不夠的,因此在上一篇文章中我們在一個額外的伺服上也安裝了一個傳感器。
我相信大多數(shù)嘗試制造六足機器人的人對超聲波測距儀的工作原理是有一定程度了解的。對于與該傳感器的接口我建議您使用 AP_Utils::sr04_median 函數(shù)。它可以提供庫中所有SR04函數(shù)最準確的結(jié)果。您甚至可以輸出數(shù)據(jù)的單位,目前可以支持毫米、厘米和米!
重要提示: 請注意,您需要Adafruit PWM驅(qū)動程序庫來使AP_Utils運行,您可以點擊此處 下載。下載完成后,和其他Arduino庫一樣對其進行安裝。
以下是一個非常簡單的“自主”模式的示例,使用了到目前為止我們所討論的所有內(nèi)容:行走、轉(zhuǎn)向和從SR04中讀取距離。如果您認真閱讀了這篇文章,那么應(yīng)該能完全理解以下代碼中最重要部分的內(nèi)容。有關(guān)所有函數(shù)的更多細節(jié),請參考庫文件夾中或 GitHub 頁面上的 README.md文件。
Arduino 代碼
#include
//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);
}
}
結(jié)論
恭喜您完成了這個最具挑戰(zhàn)性的項目之一!做得很好!在嘗試使您自己的Ardupod行走之前,請確認同時運行示例文件夾中的 calibration.ino 和 servo_test.ino。這對于正確設(shè)置所有伺服至關(guān)重要,這樣伺服才不會被損壞!在下一篇文章中,我們可能對此項目進行最后一次探索,以對一些機械性能較差的點進行改善,以及,更重要的是,增加一些改進的功能,例如遠程控制。
審核編輯:湯梓紅
評論