本節書摘來華章計算機《仿人機器人原理與實戰》一書中的第1章 ,第1.4節,作者布萊恩·伯傑倫(bryan bergeron) 托馬斯b. 塔爾博特(thomas b. talbot) 王偉 魏洪興 劉斐 譯, 更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。
在仿人機器人的設計中,這種簡單的反射弧模拟裝置不僅功能齊全而且十分有用。同人類的反射一樣,直至反射被觸發,舵機才正常運轉。不過,你可以将程式稍作改進,以便提供更多有實用價值的功能。按照如下順序,我們将這些功能添加到模拟器的硬體和代碼庫中。
第一個改進是以程式設計方式定義反射方向。如果把開關s1安裝在舵機搖臂上,那麼反射可以使按鈕的運動方向朝向或躲避撞擊物體。如果反射方向不能滿足設計要求,那麼你可以移動開關,或者減小位置變量的值至0。以下是需要替換的遞增代碼語句:

以下語句可以實作position變量的遞減:
我們假設把開關s1安裝在雙足仿人機器人的腳趾位置。總的來說,當某物體激活開關s1後,适當的反射行為應是将腳從物體上挪開。但在你覺得大功告成之前,想想你自己碰到腳趾時的正常反應。然而,在特殊的情景中,有一些自然反射可能并不合時宜,是以你需要通過程式控制來改變反射弧方向。讓我們在程式中引入方向變量reflectdirection和子程式makereflex,如清單1-2所示。
清單1-2 控制反射方向的arduino代碼
在主循環中,用子程式makereflex替換了舵機角度遞增代碼。隻要手動或由程式自動改變reflectdirection變量的值,舵機便能相應地轉換反射方向(注意:變量reflectdirection的取值為0或1)。别忘了,你可以輕松自如地從網絡上擷取相關程式代碼。
在這一點上,我們的反射弧模拟器本身就存在一段絕對不應期,即開關s1無響應的時段,這是由于舵機和機械附件的機電設計原理導緻的。因為不存在完全相同的兩個舵機,是以最好定義一段絕對不應期,這樣通過設定絕對不應期能夠從程式上消除不同舵機的響應差異。當涉及多肢體甚至更多舵機的時候,這種協調能力會顯得更加重要。
現在,我們定義一個常數absoluterefractory來表示絕對不應期,以毫秒為機關,在此期間開關s1不能觸發任何反射活動。但是在旋轉電位器旋鈕p1時,舵機可以正常運轉。在反射活動完成之後,我們可以通過調用delay()函數設定一段絕對不應期,但在清單1-3中,我們列出了一種更好的解決途徑。
清單1-3 帶絕對不應期的簡單反射弧的arduino代碼
因為在絕對不應期内我們想禁止反射活動的發生,是以我們需要估算這段時間。對于這一點,我們可以通過調用millis()函數實作,該函數會傳回一個以毫秒為機關的數值,描述從上次通電後arduino的開機時間。變量previousmillis和currentmillis可以用來存儲millis()函數在不同程式段的傳回值,進而實作了時間間隔的測量。
雖然這個方法比調用delay()函數更複雜,但好處是在設定、檢查、複位的時間内并沒有讓處理器休眠。從本質上來看,delay()函數暫停了arduino微控制器的運作,在此期間,機器人如同“盲人”一樣對傳感器沒有響應。
從上一次反射完成後的一段時間起,産生反射的刺激會随時間逐漸減弱,是以,增設相對不應期不僅增加了反射活動的真實感,也是添加新硬體的最好理由。确切地說,我們要用一個模拟傳感器取代開關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,否則需要等待幾毫秒或幾秒,再輕觸力敏電阻即可。如果想驗證這一點,不妨從舵機搖臂上卸下力敏電阻,并将其安裝在你的桌子上。
這個名詞讓我們想到大腦對反射弧的一些負面影響。但是,當在特定環境下反射是一件壞事的時候,大腦當機對仿人機器人可能非常重要。想象一個攻擊型機器人正隐藏在茂密的雨林中,但是一些動物正在啃它的腳踝,此時,他最好站着不動,否則,冒險殺死這些害蟲就可能被敵人發現。現在,我們再次使用開關s1模拟大腦皮層抑制,并且同樣沿用圖1-10中的電路連接配接引腳。
,s1抑制反射在建構絕對和相對抑制工作的時候,抑制反射弧的相關代碼如清單1-5所示。我們要定義一個關于s1激活狀态的新變量,即globalinhibition,并且通過它來增加噪聲水準變量noiselevel的值,隻有當信号電平達到noiselevel的值時才能激發反射弧。
清單1-5 反射弧的反射抑制的arduino代碼
當開關s1按下時,引腳inhibitionpin d7接地,noiselevel的值升到250,進而大幅提高了觸發makereflex()函數所需的壓力傳感器最小值。
我們用電位器代替瞬時接觸開關s1,使得模拟過程變得更加真實。但你很快就會發現:由于相對不應期的交叉作用,我們很難調節系統,并且對于中等量抑制,系統的調節量變小。
對于人類而言,我們時常會抑制某些特定的反射。假設你的仿人機器人正漫步在庭院裡,而“腳趾”傳感器一直被腳底下的長草觸發,那麼在地勢變得平坦之前,相關反射應該被抑制。
在生死存亡的緊要關頭,仿人機器人可能不得不打破正常的操作限制以便立即實施救援。假設你的仿人機器人隻有30秒的時間可用于到達四旋翼救援無人機,并且需要先拔除限位裝置才能沖向它。我們把arduino的數字引腳d6與另外一個開關s2相連接配接,模拟這個情景中腎上腺素的沖動,如圖1-11所示。
考慮到腎上腺素的總體效果與大腦皮層抑制完全相反,我們決定使用類似的代碼結構,降低而不是增加有效的噪聲水準,如清單1-6中所示。
清單1-6 對反射弧增加了腎上腺素沖動的arduino代碼
事實上,正如inhibitionstate和globalinhibition是模拟反射抑制的變量一樣,excitationstate和globalexcitation是模拟腎上腺素的變量。唯一的差别是,globalexcitation會降低noiselevel的值,是以降低門檻值有利于激活反射。雖然你能根據要求無限制地改變噪聲水準的值,但是必須将其限制在一定水準之上。如果把這個值降低到接近0,你的仿人機器人就會特别“緊張”,一個輕微的振動都很有可能激活一系列反射弧,而這樣敏感的性格其實并不适用于仿人機器人。
注意,我們用簡單的代數求和公式将抑制與激勵信号綜合,最終得到觸發電平的值,計算如下:
你也可以把抑制與激勵的值權重實作傳感器融合,進而得到觸發電平的值。通過這個算法,你可以讓抑制信号的權值是激勵信号的三倍或是相反,隻需要增加一個常數乘子。或者你可以跟蹤先前的計算值并将其代入預測算法中。簡而言之,不要簡單地将傳感器值相加,在實驗中采用傳感器讀數融合群組合的方法是為了實作你想要的機器人行為。
如同之前模拟反射抑制的實驗一樣,可以用開關s2代替電位器,這麼做可以感覺到傳感器輸入與激勵和抑制效果互相較量的互動性。在協調多個傳感器和舵機共同工作時,用電位器操控含抑制與激勵的反射弧可以節約很多時間。
例如,仿人機器人的一隻胳膊上也許擁有一個比較遲鈍的舵機,而另一隻胳膊上一起工作的舵機設定的不應期是無效的。我們不用在編輯器上重複微調變量然後編譯,而是直接在運作過程中調整電位器p1的值并觀察效果。一旦最優值确定,便可以簡單地清除相關元件和代碼。
而且,你可以通過編寫程式改變由開關s1、s2觸發的抑制和激勵的相對水準。正如之前所提及的,切換開關比讀取一對電位器值的工作量要小得多。當你了解了各類操作參數的不同之處時,開關就會發揮很大作用。例如,你可能會有兩個電池包,一個是标準型号,另外一個是加長版(很重),此時,開關能很好地修改反射的預置值,以此來補償額外的重量和重心的變化。
現在你已經具備相關基礎知識,我們可以在系統中添加更多執行器(即舵機)。讓我們把第二個舵機添加到系統中,如圖1-12所示,用它來模拟第二塊肌肉或者同一塊肌肉上的第二束肌肉纖維。
第二個舵機的相關代碼如清單1-7所示。在這個例子中,第二舵機是第一個舵機動作的鏡像。此外,我們還需要再增加一個電位器和相關代碼。
清單1-7 支援第二個舵機的arduino程式
關鍵是通過聲明myservo2語句首先建立第二個舵機對象,然後在setup()子函數中将第二個舵機與引腳号關聯起來。在makereflex()子程式中調用write函數可以使兩個舵機同步協調運動。
你可以通過添加附件控制或代碼來指定不應期和抑制期的方式,以及模拟腎上腺素沖動的方式來修改第二個舵機的響應。作為練習,考慮如何抑制一個舵機同時保持另一個舵機激活,即模拟肱二頭肌和肱三頭肌或肱四頭肌和肌腱的反射。這時,你将會遇到一些困難,即arduino上可用于傳感器的模拟端口有限且沒有端口複用等補救措施。
如果arduino uno微控制器負載過重,你可以求助于專用舵機控制器,它能騰出端口并處理傳感器和其他任務。此外,專用舵機控制器允許設定pwm脈沖頻率和脈沖寬度,為每個舵機指定速度、加速度以及啟動位置,且所有指令都是通過标準的串行連接配接。你也可以像反射那樣編排若幹序列,從arduino發出單個串行指令以實作觸發。
pololu生産的maestro舵機控制器擁有豐富的功能集,形式多樣,可用于自由構架的控制程式。parallax生産的螺旋槳式控制器是完全可程式設計的,并且帶有16個舵機的硬體資源和連接配接端口。這些控制器或其他專用舵機控制器的缺點是成本過高—大約和一個arduino微控制器的價格一樣。
parallax也提供了一個介于兩者之間的選擇,它不算是一個控制器,但也可以為arduino騰出運算資源。parallax的servopal子產品是一個廉價的微型元件,插在舵機引腳和電線之間。它隻是簡單地重複從arduino中接收的前一組pwm脈沖,整個過程無需程式設計。
在以上執行個體電路中,任何有數值輸出功能的傳感器通常都可以替代瞬時接觸開關,但往往需要重新編寫少量代碼。例如,水銀傾斜開關、磁簧開關、光敏半導體都可以連接配接到一個數字輸入引腳,而不需要重複程式設計。但其他數字輸出傳感器都需要适度程式設計,例如紅外(ir)和超音波測距儀。
使用數字傳感器最大的優勢是arduino有大量數字引腳,和大多數微控制器一樣。然而,當事情變得複雜,例如有一對以上的舵機和傳感器時,就需要把傳感器、舵機、微控制器的電源和信号線隔離。用不同的獨立電源供應舵機和傳感器,通過一種光學隔離器(例如4n35)把傳感器的信号和微控制器連接配接起來。
與數字傳感器相比,模拟傳感器往往有更大的靈活性。然而,他們通常需要更多的系統開銷,當然也需要占用其中一個本來就很少的模拟量輸入端口。在反射弧背景下,熱傳感器、壓力傳感器、霍爾效應傳感器和聲音傳感器都很值得研究。