小機(jī)器人跳舞原理:從PWM到動(dòng)作表全解析)
很多人第一次看到桌面上那只叫“月薪喵”的小機(jī)器人在節(jié)拍里點(diǎn)頭、擺手、轉(zhuǎn)圈時(shí)第一反應(yīng)都是它是不是接了什么高級(jí)算法甚至是不是用了什么 AI 識(shí)別真把外殼拆開(kāi)答案其實(shí)樸素得多——一塊單片機(jī)幾個(gè)舵機(jī)再加上一片驅(qū)動(dòng)板和電池。所謂“跳舞”在代碼層面并沒(méi)有多神秘它就是一套按照固定時(shí)間軸切換關(guān)節(jié)角度的輸出序列。這篇文章不打算替哪款具體產(chǎn)品做參數(shù)介紹因?yàn)槲夷軌蚝藢?shí)的公開(kāi)資料非常有限。我更想順著“到底怎么在單片機(jī)上跳起來(lái)舞”這個(gè)問(wèn)題把一條完整的技術(shù)鏈路拆開(kāi)講清楚。這條路適用于所有用單片機(jī)做小機(jī)器人、電子寵物、桌面擺件的項(xiàng)目不管主控是 51、STM32 還是其它型號(hào)原理幾乎一致。先給一個(gè)主判斷月薪喵能跳舞核心不是“智能”而是“時(shí)序控制”。它看起來(lái)像是自己跟著音樂(lè)在發(fā)揮本質(zhì)上是一個(gè)按節(jié)拍反復(fù)執(zhí)行的有限狀態(tài)機(jī)。真正難的不是讓某個(gè)電機(jī)轉(zhuǎn)起來(lái)而是讓多個(gè)關(guān)節(jié)在同一個(gè)時(shí)間軸上穩(wěn)定協(xié)作不抖動(dòng)、不亂拍、不互相打斷。1. 先把“跳舞”拆成四層不然你根本不知道問(wèn)題出在哪很多新手拿到一個(gè)單片機(jī)小機(jī)器人項(xiàng)目第一件事就是打開(kāi)代碼找“跳舞函數(shù)”。找了一圈發(fā)現(xiàn)根本沒(méi)有這個(gè)函數(shù)于是懵了。其實(shí)“跳舞”不是一個(gè)函數(shù)而是四層結(jié)構(gòu)疊加之后的結(jié)果。1.1 第一層機(jī)械結(jié)構(gòu)決定了動(dòng)作上限月薪喵這類桌面裝置通常只有五到八個(gè)自由度常見(jiàn)是頭、左右臂、身體和底座。自由度少所以動(dòng)作看起來(lái)卡通、機(jī)械但也正因?yàn)樽杂啥壬俨拍苡脝纹瑱C(jī)穩(wěn)定控制。如果它用的是舵機(jī)那么每個(gè)關(guān)節(jié)能轉(zhuǎn)的角度范圍是硬件決定的多數(shù) 9g 舵機(jī)在 0 到 180 度之間。如果它用的是直流減速電機(jī)那么關(guān)節(jié)只能連續(xù)旋轉(zhuǎn)不能直接指定角度。如果用的是步進(jìn)電機(jī)角度精度更高但控制也更復(fù)雜。一個(gè)很關(guān)鍵的認(rèn)知是動(dòng)作上限不是代碼寫(xiě)出來(lái)的是機(jī)械結(jié)構(gòu)先定死的。你想讓它跳一個(gè)很復(fù)雜的街舞動(dòng)作但結(jié)構(gòu)上轉(zhuǎn)頭只有兩個(gè)自由度手臂只能抬到 180 度那代碼寫(xiě)再好也跳不出來(lái)。所以第一步永遠(yuǎn)是看結(jié)構(gòu)而不是看程序。1.2 第二層驅(qū)動(dòng)電路決定它能不能“真的動(dòng)”單片機(jī)的 IO 口不是一個(gè)功率輸出口。它主要目的是輸出邏輯電平不是給電機(jī)供電。一個(gè)普通 51 單片機(jī)的 IO 口輸出高電平時(shí)能提供的電流非常有限直接接電機(jī)或者大功率舵機(jī)結(jié)果通常是三種單片機(jī)復(fù)位、IO 口燒壞、或者電機(jī)根本不轉(zhuǎn)。所以中間必須加驅(qū)動(dòng)電路。常見(jiàn)驅(qū)動(dòng)方案有三種用三極管搭一個(gè)開(kāi)關(guān)電路適合控制單個(gè)直流電機(jī)通斷。用 MOS 管搭驅(qū)動(dòng)電路適合控制稍大電流的電機(jī)。用專用電機(jī)驅(qū)動(dòng)芯片或舵機(jī)驅(qū)動(dòng)板適合多關(guān)節(jié)、需要正反轉(zhuǎn)和 PWM 調(diào)速的場(chǎng)景。很多人在這里會(huì)忽略一個(gè)細(xì)節(jié)電機(jī)斷電瞬間會(huì)產(chǎn)生反向電動(dòng)勢(shì)如果不加續(xù)流二極管高壓尖峰可能會(huì)倒灌進(jìn) IO 口輕則導(dǎo)致單片機(jī)復(fù)位重則燒掉引腳。這也是“為什么單片機(jī)推挽輸出還會(huì)燒”的常見(jiàn)原因之一。1.3 第三層控制信號(hào)是 PWM而不是簡(jiǎn)單的高電平和低電平電機(jī)要調(diào)速舵機(jī)要定角度靠的不是“給一次高電平”就完事。舵機(jī)的控制信號(hào)通常是周期約 20ms 的脈沖高電平時(shí)間在 0.5ms 到 2.5ms 之間具體映射到不同角度。以常見(jiàn)的 180 度舵機(jī)為例1ms 左右對(duì)應(yīng) 0 度1.5ms 對(duì)應(yīng) 90 度2ms 左右對(duì)應(yīng) 180 度。不同品牌和型號(hào)會(huì)有差異所以一定要以實(shí)際測(cè)試為準(zhǔn)。直流電機(jī)的速度控制則靠 PWM 的占空比。占空比越高平均電壓越高轉(zhuǎn)速越快。把“跳舞”拆到這一層你會(huì)發(fā)現(xiàn)MCU 要做的事情是不斷產(chǎn)生穩(wěn)定的 PWM 信號(hào)并且在正確的時(shí)間點(diǎn)改變每一路 PWM 的占空比。整個(gè)任務(wù)的本質(zhì)是在“周期性地輸出不同的占空比”。1.4 第四層主控程序決定動(dòng)作切換的時(shí)機(jī)前幾層正常了程序才有意義。主控程序要解決的問(wèn)題有三個(gè)什么時(shí)刻該切換動(dòng)作。切換之后每一路 PWM 的目標(biāo)值是多少。如果出現(xiàn)按鍵、藍(lán)牙指令、斷電保護(hù)怎么處理。這一層不是孤立的。你寫(xiě)再好的代碼如果機(jī)械結(jié)構(gòu)卡死、電源電流不夠、PWM 輸出引腳不對(duì)最終的“舞蹈”照樣是失敗品。可以把這四層理解成一條流水線機(jī)械結(jié)構(gòu)是舞臺(tái)驅(qū)動(dòng)電路是舞臺(tái)機(jī)械PWM 信號(hào)是操作指令主控程序是導(dǎo)演。導(dǎo)演再厲害舞臺(tái)設(shè)備壞了也白搭。層次作用常見(jiàn)問(wèn)題機(jī)械結(jié)構(gòu)決定動(dòng)作范圍和形態(tài)自由度不足、關(guān)節(jié)卡死、重心不穩(wěn)驅(qū)動(dòng)電路提供電機(jī)/舵機(jī)需要的電流供電不足、燒 IO、續(xù)流保護(hù)缺失PWM 信號(hào)控制速度與角度頻率不對(duì)、占空比映射錯(cuò)、信號(hào)抖動(dòng)主控程序決定動(dòng)作切換時(shí)機(jī)時(shí)序錯(cuò)亂、阻塞延時(shí)、任務(wù)互相搶占2. 為什么只是給高電平它跳不起來(lái)很多初學(xué)者會(huì)陷入一個(gè)直覺(jué)誤區(qū)既然單片機(jī)引腳可以輸出高電平那我讓引腳輸出高電平電機(jī)不就應(yīng)該動(dòng)了嗎聽(tīng)起來(lái)沒(méi)問(wèn)題但實(shí)際動(dòng)手會(huì)發(fā)現(xiàn)要么電機(jī)紋絲不動(dòng)要么單片機(jī)直接復(fù)位。2.1 IO 口不是電源接口單片機(jī)的 IO 口設(shè)計(jì)目標(biāo)是傳遞邏輯狀態(tài)不是給負(fù)載供電。它內(nèi)部有輸出電阻驅(qū)動(dòng)能力有限。比如標(biāo)準(zhǔn) 51 的準(zhǔn)雙向 IO輸出高電平時(shí)拉電流能力很弱輸出低電平時(shí)灌電流能力相對(duì)強(qiáng)一些。即使換了推挽輸出的 STC 或 STM32也只是提升了驅(qū)動(dòng)能力依然不能直接驅(qū)動(dòng)大電流電機(jī)。正確的做法是讓 IO 口去控制驅(qū)動(dòng)芯片的使能端或輸入端由驅(qū)動(dòng)芯片去給電機(jī)供電。這樣 IO 口只走邏輯信號(hào)電流壓力全部由驅(qū)動(dòng)部分承擔(dān)。2.2 舵機(jī)不是“正轉(zhuǎn)反轉(zhuǎn)”就行如果你用的是舵機(jī)它根本不接受簡(jiǎn)單的高電平和低電平。舵機(jī)內(nèi)部有一個(gè)小電機(jī)和電位器反饋它靠持續(xù)刷新 PWM 脈沖來(lái)維持目標(biāo)角度。關(guān)鍵點(diǎn)在于“持續(xù)刷新”。如果你只輸出一次 1.5ms 的脈沖就去做別的事情舵機(jī)可能剛開(kāi)始轉(zhuǎn)動(dòng)就停住了因?yàn)樗诘却乱粋€(gè)脈沖來(lái)確定當(dāng)前角度。如果刷新頻率不穩(wěn)定舵機(jī)就會(huì)抖動(dòng)、發(fā)燙、甚至發(fā)出“滋滋”的叫聲。有些新手用delay_ms()去循環(huán)翻轉(zhuǎn)引腳模擬 PWM這在只有一件任務(wù)時(shí)勉強(qiáng)能用。一旦你要同時(shí)處理按鍵掃描、OLED 顯示、藍(lán)牙接收主循環(huán)稍微卡一下PWM 波形就被切斷了整個(gè)動(dòng)作馬上亂掉。2.3 就算電機(jī)動(dòng)了動(dòng)作之間也沒(méi)有“節(jié)拍”退一步說(shuō)假設(shè)你用 IO 口成功讓電機(jī)轉(zhuǎn)起來(lái)了那也只能解決“某個(gè)關(guān)節(jié)會(huì)動(dòng)”解決不了“一整支舞怎么跳”。舞蹈的本質(zhì)是狀態(tài)隨時(shí)間變化。同一時(shí)刻頭在某個(gè)角度左臂在另一個(gè)角度右臂又在第三個(gè)角度下一個(gè)時(shí)刻這些值都要發(fā)生變化。如果你通過(guò)主循環(huán)順序執(zhí)行一個(gè)個(gè)延時(shí)動(dòng)作比如“轉(zhuǎn)頭-延時(shí)100ms-抬手-延時(shí)100ms”一旦某個(gè)關(guān)節(jié)執(zhí)行時(shí)間偏長(zhǎng)后面的動(dòng)作全部跟著偏整支舞就會(huì)越來(lái)越亂。這也是為什么所有稍微成熟一點(diǎn)的小機(jī)器人項(xiàng)目都不會(huì)在主循環(huán)里靠延時(shí)節(jié)拍來(lái)控制動(dòng)作。它們會(huì)把時(shí)間基準(zhǔn)單獨(dú)拿出來(lái)用定時(shí)器作為“節(jié)拍器”用中斷來(lái)觸發(fā)動(dòng)作幀切換。3. 跳舞的本質(zhì)按節(jié)拍切換狀態(tài)而不是讓電機(jī)隨便轉(zhuǎn)理解了這一層你才算真正入坑。3.1 先把一支舞變成一幀一幀的動(dòng)作表單片機(jī)不會(huì)“即興發(fā)揮”。你要做的是先把一整支舞蹈拆成一幀一幀的動(dòng)作像定格動(dòng)畫(huà)一樣把每一幀每個(gè)關(guān)節(jié)的目標(biāo)角度記錄下來(lái)。最直觀的數(shù)據(jù)結(jié)構(gòu)是一個(gè)結(jié)構(gòu)體數(shù)組。每個(gè)結(jié)構(gòu)體代表一幀里面放所有關(guān)節(jié)的目標(biāo)角度。typedef struct { unsigned char head; // 頭部角度 unsigned char left_arm; // 左臂角度 unsigned char right_arm; // 右臂角度 unsigned char body; // 身體角度 } dance_frame_t; dance_frame_t dance_table[] { {90, 45, 135, 90}, // 第 0 幀準(zhǔn)備 {90, 60, 120, 85}, // 第 1 幀右手抬 {100, 80, 100, 80}, // 第 2 幀身體右傾 // 繼續(xù)補(bǔ)充更多幀…… };用數(shù)組而不是一堆散落的賦值語(yǔ)句好處是一目了然。你可以像看樂(lè)譜一樣看動(dòng)作表也可以很方便地增加新動(dòng)作、調(diào)整某一幀的數(shù)值。3.2 定時(shí)器負(fù)責(zé)節(jié)奏中斷負(fù)責(zé)準(zhǔn)點(diǎn)有了動(dòng)作表下一步就是按時(shí)間節(jié)拍切換幀號(hào)。這里最忌諱的做法是在主循環(huán)里判斷“時(shí)間到了沒(méi)”。因?yàn)橹餮h(huán)里可能有其它任務(wù)判斷時(shí)機(jī)不穩(wěn)定。更推薦的做法是用一個(gè)硬件定時(shí)器產(chǎn)生固定間隔的中斷。每次進(jìn)入中斷就把當(dāng)前幀號(hào)加一然后把動(dòng)作表里的值寫(xiě)到 PWM 輸出寄存器。// 示意定時(shí)器中斷中切換舞蹈幀 void Timer0_ISR(void) interrupt 1 { // 重新裝載定時(shí)初值 TH0 0xXX; TL0 0xXX; current_frame; if (current_frame frame_count) { current_frame 0; // 循環(huán)播放 } set_servo_angles(dance_table[current_frame]); }這里的核心思路是中斷負(fù)責(zé)“準(zhǔn)點(diǎn)更新”主循環(huán)負(fù)責(zé)其它不緊急的事情。中斷里盡量只做輕量操作不要寫(xiě)延時(shí)、不要打印串口、不要做大量浮點(diǎn)運(yùn)算。因?yàn)橹袛喾?wù)函數(shù)執(zhí)行時(shí)間太長(zhǎng)會(huì)直接影響下一個(gè)節(jié)拍的準(zhǔn)點(diǎn)率。3.3 多任務(wù)不是玄學(xué)前后臺(tái)結(jié)構(gòu)就夠了有人會(huì)問(wèn)如果我要同時(shí)處理藍(lán)牙指令、LED 閃爍、OLED 顯示和舞步更新難道要用實(shí)時(shí)操作系統(tǒng)嗎不一定。對(duì)于月薪喵這種級(jí)別的項(xiàng)目一個(gè)簡(jiǎn)單的“前后臺(tái)系統(tǒng)”就夠了。前臺(tái)定時(shí)器中斷每 10ms 或者 20ms 觸發(fā)一次負(fù)責(zé)刷新舞步幀、掃描按鍵、刷新 LED。后臺(tái)主循環(huán)負(fù)責(zé)處理藍(lán)牙解析、OLED 顯示、串口日志。如果任務(wù)再多一些可以在中斷里置一個(gè)“時(shí)間片標(biāo)志”主循環(huán)按優(yōu)先級(jí)處理。比如每 1ms 一個(gè) tick累計(jì)到 10ms 處理按鍵累計(jì)到 20ms 處理舞步累計(jì)到 100ms 處理顯示。這就是最樸素的時(shí)間片輪詢。這里想點(diǎn)破一個(gè)誤區(qū)單片機(jī)“同時(shí)運(yùn)行多個(gè)任務(wù)”并不是真的并行而是把 CPU 時(shí)間切得很碎讓每個(gè)任務(wù)都能在合適的時(shí)機(jī)獲得執(zhí)行機(jī)會(huì)。理解了這一點(diǎn)你就知道為什么不能在主循環(huán)里寫(xiě)死循環(huán)延時(shí)了——它會(huì)把整條時(shí)間線全部堵住。4. 從“會(huì)動(dòng)”到“跳完整支舞”一條可以復(fù)制的落地路徑我自己調(diào)試這類項(xiàng)目的習(xí)慣是嚴(yán)格分階段走絕不跳步。很多人一上來(lái)就想把整支舞寫(xiě)完結(jié)果動(dòng)作全亂最后連問(wèn)題出在哪一幀都找不到。4.1 第一步單關(guān)節(jié)驗(yàn)證先把一個(gè)舵機(jī)接到開(kāi)發(fā)板上單獨(dú)驗(yàn)證它能不能從 0 度轉(zhuǎn)到 180 度再反向轉(zhuǎn)回來(lái)。這時(shí)候你會(huì)實(shí)際測(cè)出這個(gè)舵機(jī)的 PWM 占空比范圍跟網(wǎng)上說(shuō)的 1ms 到 2ms 不一定完全一樣。這一步必須做因?yàn)椴煌鏅C(jī)、不同供電電壓最終的角度映射會(huì)有些偏移。省掉這一步后面動(dòng)作表里寫(xiě)的 90 度可能根本不是正的 90 度。4.2 第二步單步動(dòng)作測(cè)試把動(dòng)作表做好之后不要急著連續(xù)播放。先做一個(gè)“按鍵模式”按一下按鍵前進(jìn)一幀再按一下再前進(jìn)一幀。這個(gè)模式非常有用。你可以逐幀檢查每個(gè)關(guān)節(jié)的動(dòng)作是否符合預(yù)期也能及時(shí)發(fā)現(xiàn)問(wèn)題比如某兩個(gè)關(guān)節(jié)在某一幀發(fā)生了機(jī)械干涉或者某個(gè)角度已經(jīng)超出舵機(jī)最大范圍。避坑提醒如果你在這一步發(fā)現(xiàn)舵機(jī)發(fā)出“咔咔”的聲音說(shuō)明目標(biāo)角度可能已經(jīng)超出了機(jī)械極限或者電源電流不夠。這時(shí)候繼續(xù)寫(xiě)連續(xù)舞蹈大概率會(huì)燒舵機(jī)或復(fù)位單片機(jī)。4.3 第三步連續(xù)播放并調(diào)節(jié)拍單幀確認(rèn)無(wú)誤后再改成定時(shí)器自動(dòng)切換幀并且先把節(jié)拍放慢到實(shí)際速度的一半。觀察動(dòng)作是否連貫有沒(méi)有突然的跳動(dòng)。如果動(dòng)作從第 0 幀直接跳到第 2 幀變化幅度很大看起來(lái)會(huì)很突兀。一個(gè)簡(jiǎn)單的解決思路是在兩幀之間做插值讓角度平滑過(guò)渡。比如當(dāng)前頭角度是 90 度目標(biāo)角度是 120 度中間可以分成 5 步每次增加 6 度。這樣雖然增加了計(jì)算量但觀感會(huì)好很多。4.4 第四步加交互和顯示基礎(chǔ)舞蹈跑通之后再考慮給它加上按鍵切歌、藍(lán)牙串口遙控、OLED 或 LCD1602 顯示當(dāng)前曲目和節(jié)拍數(shù)。這些功能不是必需的但它們能提升整個(gè)項(xiàng)目的完整度也能幫你練習(xí)串口、I2C、SPI 這些常用外設(shè)。要注意的是交互功能不要占用主循環(huán)太久。比如 OLED 刷新一屏可能需要幾十毫秒如果頻繁刷新就會(huì)影響后臺(tái)邏輯的實(shí)時(shí)性。通常做法是降低刷新頻率比如每 200ms 刷一次。5. 如果動(dòng)不了、抖動(dòng)、燒 IO按這個(gè)順序排查做這類項(xiàng)目不怕遇到問(wèn)題怕的是亂猜。我建議遇到問(wèn)題都按一套固定鏈路來(lái)排查先看現(xiàn)象再看輸入再看環(huán)境再看參數(shù)最后看工具邊界。5.1 先看電源和接線再看程序很多“動(dòng)不了”的問(wèn)題最后都出在電源或接線上而不是代碼。優(yōu)先檢查這幾項(xiàng)供電電壓是否在舵機(jī)或電機(jī)允許范圍內(nèi)。供電電流是否足夠。多個(gè)舵機(jī)同時(shí)動(dòng)作時(shí)瞬間電流會(huì)很大。單片機(jī)、舵機(jī)驅(qū)動(dòng)板、傳感器是否已經(jīng)共地。信號(hào)線是否接對(duì)引腳驅(qū)動(dòng)芯片的使能腳有沒(méi)有拉高。如果電源電壓正常但電流不足典型現(xiàn)象是空載測(cè)試能動(dòng)接上所有關(guān)節(jié)之后就開(kāi)始復(fù)位、抖動(dòng)、甚至無(wú)法啟動(dòng)。解決方向是換更大電流的電源或者給舵機(jī)單獨(dú)供電避免和單片機(jī)共用一路電源。5.2 用邏輯分析儀或示波器看 PWM如果程序邏輯看起來(lái)沒(méi)問(wèn)題但舵機(jī)還是顫抖優(yōu)先懷疑 PWM 信號(hào)本身。用邏輯分析儀或者示波器直接看舵機(jī)信號(hào)引腳上的波形。確認(rèn)以下幾點(diǎn)周期是否穩(wěn)定是否一直維持在 20ms 左右。高電平時(shí)間是否在舵機(jī)要求的范圍內(nèi)。在切換動(dòng)作幀時(shí)波形有沒(méi)有突然缺失或跳變。如果你沒(méi)有示波器也可以用帶引腳的調(diào)試方式。在 Keil 的調(diào)試界面里可以查看引腳電平變化也可以用串口把當(dāng)前幀號(hào)和目標(biāo)角度打出來(lái)對(duì)照動(dòng)作表檢查程序走到哪一幀了。注意如果波形明顯不穩(wěn)定先不要調(diào)角度先查你的 PWM 是用什么方式生成的。用延時(shí)函數(shù)在中斷里翻轉(zhuǎn)引腳和用硬件 PWM 模塊生成穩(wěn)定性完全不一樣。5.3 軟件仿真和硬件實(shí)測(cè)要分開(kāi)看如果你在 Proteus 里仿真遇到“仿真不運(yùn)行”“變量顯示空白”“外設(shè)地址無(wú)效”這些問(wèn)題先別懷疑硬件代碼。軟件仿真器和真實(shí)芯片之間存在差異是很正常的。比如某些 STC 單片機(jī)在仿真器里無(wú)法正常仿真外部 RAM或者 Keil 的 System View 完全空白多半是芯片型號(hào)選擇、調(diào)試接口配置或內(nèi)存模型設(shè)置有問(wèn)題而不是你的電路邏輯錯(cuò)了。仿真通過(guò)只能說(shuō)明程序邏輯基本通順。仿真跑得好不等于硬件上一定正常。因?yàn)榉抡嫫鞑粫?huì)模擬真實(shí)的供電電流、電磁干擾、接觸不良和電機(jī)反向電動(dòng)勢(shì)。反之仿真跑不通也不一定代表硬件方案不行可能是仿真器的支持范圍有限。5.4 燒 IO 和發(fā)熱的常見(jiàn)原因如果單片機(jī)引腳發(fā)熱、發(fā)燙、甚至燒毀不用懷疑一定是有電流超過(guò)了引腳的承受能力。常見(jiàn)原因有三個(gè)直接用 IO 口驅(qū)動(dòng)電機(jī)或舵機(jī)信號(hào)線電流過(guò)大。電機(jī)堵轉(zhuǎn)電流飆升但沒(méi)有做堵轉(zhuǎn)保護(hù)。電機(jī)或舵機(jī)斷電瞬間的反向電動(dòng)勢(shì)灌入 IO 口沒(méi)有加續(xù)流二極管。對(duì)于長(zhǎng)期使用的項(xiàng)目建議在電機(jī)兩端并聯(lián)續(xù)流二極管在電源輸入端加一個(gè)大容量濾波電容并且在 IO 口和驅(qū)動(dòng)電路之間用限流電阻或者驅(qū)動(dòng)芯片隔離。不要指望單片機(jī)引腳能扛住所有意外電流。6. 從桌面玩具到工程項(xiàng)目差的不只是代碼如果把目標(biāo)定義為“讓月薪喵跳一支舞”那么上面這些內(nèi)容已經(jīng)夠了。但如果想把它做成一個(gè)可以長(zhǎng)期放在桌上運(yùn)行、偶爾還能給別人展示的項(xiàng)目還差三塊拼圖。6.1 單次跑通和長(zhǎng)期運(yùn)行之間差了穩(wěn)定性第一次跑通是一件很興奮的事但第二次、第三次、第十次還能穩(wěn)定跑才是真正的問(wèn)題。需要補(bǔ)的東西包括電源余量。不要把電源電壓用滿長(zhǎng)期工作建議留出 20% 以上的余量??撮T狗或異?;謴?fù)。如果程序跑飛或者舵機(jī)卡住導(dǎo)致長(zhǎng)時(shí)間超時(shí)系統(tǒng)能自動(dòng)復(fù)位或進(jìn)入安全狀態(tài)。日志和狀態(tài)指示。用串口輸出關(guān)鍵狀態(tài)或者用一個(gè) LED 顯示當(dāng)前模式這樣出問(wèn)題時(shí)你能快速知道它卡在哪一步。6.2 把“手動(dòng)調(diào)動(dòng)作”沉淀成“可維護(hù)的數(shù)據(jù)”剛開(kāi)始動(dòng)作表可能只有十幾幀直接寫(xiě)在主程序里沒(méi)問(wèn)題。但動(dòng)作越加越多就會(huì)變得難以維護(hù)。更好的做法是把動(dòng)作表單獨(dú)放到一個(gè)頭文件或者配置表里與主邏輯分離。以后你只需要修改動(dòng)作數(shù)據(jù)不用改動(dòng)任何控制邏輯。更進(jìn)一步可以做一個(gè)簡(jiǎn)單上位機(jī)用可視化界面拖拽生成動(dòng)作序列再導(dǎo)出成 C 語(yǔ)言數(shù)組。這種流程一旦建立動(dòng)作編排效率會(huì)大幅提升而且不容易出錯(cuò)。6.3 回到最初的問(wèn)題它到底怎么跳舞的月薪喵跳起舞來(lái)說(shuō)到底是四個(gè)環(huán)節(jié)在同步工作機(jī)械結(jié)構(gòu)限制了它“能做什么動(dòng)作”驅(qū)動(dòng)電路給了它“做動(dòng)作的力氣”P(pán)WM 信號(hào)決定了“動(dòng)作做到什么程度”主控程序決定了“什么時(shí)候做下一個(gè)動(dòng)作”??雌饋?lái)像一個(gè)活物其實(shí)是一位非常嚴(yán)格的節(jié)拍器在一遍又一遍地執(zhí)行預(yù)先錄好的動(dòng)作表。真正值得學(xué)習(xí)的是你怎么讓多個(gè)任務(wù)共享同一條時(shí)間軸不讓阻塞互相影響。回到實(shí)操上我的建議永遠(yuǎn)是先做最小可行測(cè)試一個(gè)關(guān)節(jié)一個(gè)關(guān)節(jié)地驗(yàn)證再拼成完整動(dòng)作。這個(gè)過(guò)程看起來(lái)慢卻最穩(wěn)。把這一步走扎實(shí)了往后做任何單片機(jī)小機(jī)器人都不會(huì)慌。