《仿人機器人原理與實戰》一1.4 反射弧實驗進階
本節書摘來異步社區《仿人機器人原理與實戰》一書中的第1章 ,第1.4節,作者布萊恩·伯傑倫(Bryan Bergeron) 托馬斯B. 塔爾博特(Thomas B. Talbot) 王偉 魏洪興 劉斐 譯, 更多章節內容可以訪問雲棲社區“異步社區”公眾號查看。
1.4 反射弧實驗進階
在仿人機器人的設計中,這種簡單的反射弧模擬裝置不僅功能齊全而且十分有用。同人類的反射一樣,直至反射被觸發,舵機才正常運轉。不過,你可以將程序稍作改進,以便提供更多有實用價值的功能。按照如下順序,我們將這些功能添加到模擬器的硬件和代碼庫中。
1.4.1 反射方向
第一個改進是以編程方式定義反射方向。如果把開關S1安裝在舵機搖臂上,那麼反射可以使按鈕的運動方向朝向或躲避撞擊物體。如果反射方向不能滿足設計要求,那麼你可以移動開關,或者減小位置變量的值至0。以下是需要替換的遞增代碼語句:
我們假設把開關S1安裝在雙足仿人機器人的腳趾位置。總的來說,當某物體激活開關S1後,適當的反射行為應是將腳從物體上挪開。但在你覺得大功告成之前,想想你自己碰到腳趾時的正常反應。然而,在特殊的情景中,有一些自然反射可能並不合時宜,所以你需要通過程序控製來改變反射弧方向。讓我們在程序中引入方向變量reflectDirection和子程序makeReflex,如清單1-2所示。
清單1-2 控製反射方向的Arduino代碼
在主循環中,用子程序makeReflex替換了舵機角度遞增代碼。隻要手動或由程序自動改變reflectDirection變量的值,舵機便能相應地轉換反射方向(注意:變量reflectDirection的取值為0或1)。別忘了,你可以輕鬆自如地從網絡上獲取相關程序代碼。
1.4.2 絕對不應期
在這一點上,我們的反射弧模擬器本身就存在一段絕對不應期,即開關S1無響應的時段,這是由於舵機和機械附件的機電設計原理導致的。因為不存在完全相同的兩個舵機,所以最好定義一段絕對不應期,這樣通過設置絕對不應期能夠從程序上消除不同舵機的響應差異。當涉及多肢體甚至更多舵機的時候,這種協調能力會顯得更加重要。
現在,我們定義一個常數absoluteRefractory來表示絕對不應期,以毫秒為單位,在此期間開關S1不能觸發任何反射活動。但是在旋轉電位器旋鈕P1時,舵機可以正常運轉。在反射活動完成之後,我們可以通過調用delay()函數設定一段絕對不應期,但在清單1-3中,我們列出了一種更好的解決途徑。
清單1-3 帶絕對不應期的簡單反射弧的Arduino代碼
因為在絕對不應期內我們想禁止反射活動的發生,所以我們需要估算這段時間。對於這一點,我們可以通過調用millis()函數實現,該函數會返回一個以毫秒為單位的數值,描述從上次通電後Arduino的開機時間。變量previousMillis和currentMillis可以用來存儲millis()函數在不同程序段的返回值,從而實現了時間間隔的測量。
雖然這個方法比調用delay()函數更複雜,但好處是在設置、檢查、複位的時間內並沒有讓處理器休眠。從本質上來看,delay()函數暫停了Arduino微控製器的運行,在此期間,機器人如同“盲人”一樣對傳感器沒有響應。
1.4.3 相對不應期
從上一次反射完成後的一段時間起,產生反射的刺激會隨時間逐漸減弱,因此,增設相對不應期不僅增加了反射活動的真實感,也是添加新硬件的最好理由。確切地說,我們要用一個模擬傳感器取代開關S1,對於不同程度的刺激,模擬傳感器都可以做出相應的響應。
為了找到最簡單的解決辦法,我們選用Sparkfun 生產的力敏電阻FSR1。無外力時,FSR1的阻值最大可達1MΩ,若用手指輕觸,阻值最小為1kΩ。力敏電阻比較敏感(100g),所以能準確測定刺激的強度,而且價格適中。用自粘性橡膠帶來固定力敏電阻是小菜一碟的事情。力敏電阻的唯一缺點是引線。不過沒有焊接那麼複雜。如果你手頭有繞線工具或者Grove接線端子,就可以把傳感器和Arduino微控製器連接起來。圖1-7是力敏電阻的特寫。
這種力敏電阻並沒有什麼特別,可以隨便用你手頭的東西替代它,從有複位彈簧的線性電位器到一對嵌入在1英尺導電泡沫管中的裸露銅線都可以。有一點需要預先聲明,傳感器對於刺激應該比較敏感。如果你選擇的傳感器隻有在鐵錘的敲擊下來能觸發,那我們的整個實驗可能都無法正常運作了。
還有一種選擇是用壓電拾音器代替電阻式傳感器。盡管對手指輕觸產生的壓力十分敏感,但壓電拾音器的輸出是非線性的,加大了區別刺激強度的難度。你也可以用應變儀準確測量壓力讀數,但是準確度的提高意味著可能付出相當大的代價。目前,我們仍然選擇用力敏電阻。
按照圖1-8所示連接新添加的零件。首先,我們增設兩個零件—10kΩ的電阻R1和力敏電阻FSR1,構成分壓回路。傳感器一端連接5V直流電源,另一端連接引腳A1和電阻R1。而電阻R1的另一端接地。
如果無外力作用於力敏電阻,理論上10kΩ的電阻R1幾乎沒有分得電壓。現在用你的手指輕觸電阻,力敏電阻阻值一下從1MΩ以上降至約2kΩ,因此電阻R1分得了大部分電壓。回想分壓方程,一對電阻中,其中一個電阻的壓降與其電阻值除以兩個電阻值總和成比例。在這個實驗中,電阻R1的壓降(VR1)等於總電壓(5V)乘以R1的阻值再除以兩個電阻值(R1+FSR1)的總和:
VR1 = 5V × R1/(R1+FSR1)
所以,當FSR1無負荷時,可得
VR1 = 5V × 10 000/(10 000+1 000 000) = 0.05V
當FSR1滿負荷時,可得
VR1 = 5V × 10 000/(10 000+1 000) = 4.55V
如清單1-4所示,這段代碼添加了一段相對不應期。注意,程序中加入了一個新的複位子程序。
清單1-4 帶絕對不應期和相對不應期的簡單反射弧模擬器的Arduino代碼
如下麵這一段代碼所示,如果係統處於絕對不應期,程序就不能接收力傳感器的信號,更不用說計算不應期的時間。
一旦絕對不應期結束,設置相對不應期的代碼會緊跟著運行。在程序列表中,變量sensorRange 定義了力傳感器的範圍,即相對不應期。
如圖1-9所示,相對不應期內觸發電平以指數形式減弱,我們可以用斜直線來逼近該曲線。直線方程是y=mx+b,其中x、y為軸線,m為直線的斜率,而b是常數。這個程序中,我們定義斜率為0.25,常數b的值是1000,這些都由變量sensorRange來表示。
相關代碼包括首次確定最小觸發電平值triggerVar,它是有關時間和係統噪聲級noiseLevel的函數。盡管在通常意義下是沒有噪聲的,但是變量noiseLevel可以很方便地逼近反射在最小觸發條件下的波動。
此外,因為按周期對力敏電阻做采樣,返回字符串的峰值往往位於中間位置,所以我們利用max函數作為峰值檢測算法。一旦確定最小觸發電平的值,我們便可以很輕鬆地把它與力敏電阻返回的信號峰值進行比較,再儲存到變量FSRMaxVal中。
為了觀察處於相對不應期時係統的響應,我們可以一隻手旋轉電位器旋鈕P1,另一隻手壓在力敏電阻上,嚐試觸發反射活動。如果需要在上一個反射完成後的幾毫秒內立刻觸發下一個反射,那麼你必須重壓FSR1,否則需要等待幾毫秒或幾秒,再輕觸力敏電阻即可。如果想驗證這一點,不妨從舵機搖臂上卸下力敏電阻,並將其安裝在你的桌子上。
1.4.4 大腦凍結
這個名詞讓我們想到大腦對反射弧的一些負麵影響。但是,當在特定環境下反射是一件壞事的時候,大腦凍結對仿人機器人可能非常重要。想象一個攻擊型機器人正隱藏在茂密的雨林中,但是一些動物正在啃它的腳踝,此時,他最好站著不動,否則,冒險殺死這些害蟲就可能被敵人發現。現在,我們再次使用開關S1模擬大腦皮層抑製,並且同樣沿用圖1-10中的電路連接引腳。
,S1抑製反射在構建絕對和相對抑製工作的時候,抑製反射弧的相關代碼如清單1-5所示。我們要定義一個關於S1激活狀態的新變量,即globalInhibition,並且通過它來增加噪聲水平變量noiseLevel的值,隻有當信號電平達到noiseLevel的值時才能激發反射弧。
清單1-5 反射弧的反射抑製的Arduino代碼
當開關S1按下時,引腳inhibitionPin D7接地,noiseLevel的值升到250,從而大幅提高了觸發makeReflex()函數所需的壓力傳感器最小值。
我們用電位器代替瞬時接觸開關S1,使得模擬過程變得更加真實。但你很快就會發現:由於相對不應期的交叉作用,我們很難調節係統,並且對於中等量抑製,係統的調節量變小。
對於人類而言,我們時常會抑製某些特定的反射。假設你的仿人機器人正漫步在庭院裏,而“腳趾”傳感器一直被腳底下的長草觸發,那麼在地勢變得平坦之前,相關反射應該被抑製。
1.4.5 腎上腺素衝動
在生死存亡的緊要關頭,仿人機器人可能不得不打破正常的操作限製以便立即實施救援。假設你的仿人機器人隻有30秒的時間可用於到達四旋翼救援無人機,並且需要先拔除限位裝置才能衝向它。我們把Arduino的數字引腳D6與另外一個開關S2相連接,模擬這個情景中腎上腺素的衝動,如圖1-11所示。
考慮到腎上腺素的總體效果與大腦皮層抑製完全相反,我們決定使用類似的代碼結構,降低而不是增加有效的噪聲水平,如清單1-6中所示。
清單1-6 對反射弧增加了腎上腺素衝動的Arduino代碼
事實上,正如inhibitionState和globalInhibition是模擬反射抑製的變量一樣,excitationState和globalExcitation是模擬腎上腺素的變量。唯一的差別是,globalExcitation會降低noiseLevel的值,因此降低閾值有利於激活反射。雖然你能根據要求無限製地改變噪聲水平的值,但是必須將其限製在一定水平之上。如果把這個值降低到接近0,你的仿人機器人就會特別“緊張”,一個輕微的振動都很有可能激活一係列反射弧,而這樣敏感的性格其實並不適用於仿人機器人。
注意,我們用簡單的代數求和公式將抑製與激勵信號綜合,最終得到觸發電平的值,計算如下:
你也可以把抑製與激勵的值加權實現傳感器融合,從而得到觸發電平的值。通過這個算法,你可以讓抑製信號的權值是激勵信號的三倍或是相反,隻需要增加一個常數乘子。或者你可以跟蹤先前的計算值並將其代入預測算法中。簡而言之,不要簡單地將傳感器值相加,在實驗中采用傳感器讀數融合和組合的方法是為了實現你想要的機器人行為。
如同之前模擬反射抑製的實驗一樣,可以用開關S2代替電位器,這麼做可以感覺到傳感器輸入與激勵和抑製效果互相較量的交互性。在協調多個傳感器和舵機共同工作時,用電位器操控含抑製與激勵的反射弧可以節約很多時間。
例如,仿人機器人的一隻胳膊上也許擁有一個比較遲鈍的舵機,而另一隻胳膊上一起工作的舵機設置的不應期是無效的。我們不用在編輯器上重複微調變量然後編譯,而是直接在運行過程中調整電位器P1的值並觀察效果。一旦最優值確定,便可以簡單地清除相關元件和代碼。
而且,你可以通過編寫程序改變由開關S1、S2觸發的抑製和激勵的相對水平。正如之前所提及的,切換開關比讀取一對電位器值的工作量要小得多。當你了解了各類操作參數的不同之處時,開關就會發揮很大作用。例如,你可能會有兩個電池包,一個是標準型號,另外一個是加長版(很重),此時,開關能很好地修改反射的預置值,以此來補償額外的重量和重心的變化。
1.4.6 添加舵機
現在你已經具備相關基礎知識,我們可以在係統中添加更多執行器(即舵機)。讓我們把第二個舵機添加到係統中,如圖1-12所示,用它來模擬第二塊肌肉或者同一塊肌肉上的第二束肌肉纖維。
第二個舵機的相關代碼如清單1-7所示。在這個例子中,第二舵機是第一個舵機動作的鏡像。此外,我們還需要再增加一個電位器和相關代碼。
清單1-7 支持第二個舵機的Arduino程序
關鍵是通過聲明myservo2語句首先創建第二個舵機對象,然後在setup()子函數中將第二個舵機與引腳號關聯起來。在makeReflex()子程序中調用write函數可以使兩個舵機同步協調運動。
你可以通過添加附件控製或代碼來指定不應期和抑製期的方式,以及模擬腎上腺素衝動的方式來修改第二個舵機的響應。作為練習,考慮如何抑製一個舵機同時保持另一個舵機激活,即模擬肱二頭肌和肱三頭肌或肱四頭肌和肌腱的反射。這時,你將會遇到一些困難,即Arduino上可用於傳感器的模擬端口有限且沒有端口複用等補救措施。
1.4.7 其他控製器
如果Arduino Uno微控製器負載過重,你可以求助於專用舵機控製器,它能騰出端口並處理傳感器和其他任務。此外,專用舵機控製器允許設定PWM脈衝頻率和脈衝寬度,為每個舵機指定速度、加速度以及啟動位置,且所有命令都是通過標準的串行連接。你也可以像反射那樣編排若幹序列,從Arduino發出單個串行命令以實現觸發。
Pololu生產的Maestro舵機控製器擁有豐富的功能集,形式多樣,可用於自由構架的控製程序。Parallax生產的螺旋槳式控製器是完全可編程的,並且帶有16個舵機的硬件資源和連接端口。這些控製器或其他專用舵機控製器的缺點是成本過高—大約和一個Arduino微控製器的價格一樣。
Parallax也提供了一個介於兩者之間的選擇,它不算是一個控製器,但也可以為Arduino騰出運算資源。Parallax的ServoPAL模塊是一個廉價的微型組件,插在舵機引腳和電線之間。它隻是簡單地重複從Arduino中接收的前一組PWM脈衝,整個過程無需編程。
1.4.8 其他傳感器
在以上實例電路中,任何有數值輸出功能的傳感器通常都可以替代瞬時接觸開關,但往往需要重新編寫少量代碼。例如,水銀傾斜開關、磁簧開關、光敏晶體管都可以連接到一個數字輸入引腳,而不需要重複編程。但其他數字輸出傳感器都需要適度編程,例如紅外(IR)和超聲波測距儀。
使用數字傳感器最大的優勢是Arduino有大量數字引腳,和大多數微控製器一樣。然而,當事情變得複雜,例如有一對以上的舵機和傳感器時,就需要把傳感器、舵機、微控製器的電源和信號線隔離。用不同的獨立電源供應舵機和傳感器,通過一種光學隔離器(例如4N35)把傳感器的信號和微控製器連接起來。
與數字傳感器相比,模擬傳感器往往有更大的靈活性。然而,他們通常需要更多的係統開銷,當然也需要占用其中一個本來就很少的模擬量輸入端口。在反射弧背景下,熱傳感器、壓力傳感器、霍爾效應傳感器和聲音傳感器都很值得研究。
最後更新:2017-06-21 10:02:15