器人雙腦架構(gòu):Linux+MCU安全設(shè)計(jì)實(shí)戰(zhàn))
1. 掃地機(jī)器人雙腦架構(gòu)到底在解決什么問題掃地機(jī)器人這個(gè)品類從最早的隨機(jī)碰撞式走到今天的激光導(dǎo)航、視覺SLAM硬件方案已經(jīng)迭代了十幾輪。但如果你拆過幾臺主流機(jī)型會(huì)發(fā)現(xiàn)一個(gè)很有意思的現(xiàn)象幾乎所有的中高端掃地機(jī)內(nèi)部都不是一顆芯片在干活而是至少兩顆——一顆跑Linux的應(yīng)用處理器一顆跑RTOS的MCU。這個(gè)結(jié)構(gòu)在圈內(nèi)被叫做“雙腦架構(gòu)”也有人叫“主控協(xié)處理”或者“APMCU”方案。為什么非要搞兩顆芯片一顆性能強(qiáng)一點(diǎn)的SoC全包了不行嗎這個(gè)問題我在早期做原型機(jī)的時(shí)候也想過當(dāng)時(shí)覺得多加一顆MCU純粹是增加BOM成本和PCB面積。但真正把機(jī)器跑起來、跑夠幾百小時(shí)之后我才理解這個(gè)設(shè)計(jì)的必要性——它本質(zhì)上不是性能問題而是安全問題。具體來說掃地機(jī)器人是一個(gè)“移動(dòng)的、帶旋轉(zhuǎn)部件的、有大電流驅(qū)動(dòng)的、可能撞到人或?qū)櫸锏牡孛嬖O(shè)備”。它同時(shí)具備幾個(gè)危險(xiǎn)屬性第一它有輪子會(huì)自己跑第二它有滾刷和邊刷會(huì)卷東西第三它有風(fēng)機(jī)和電池涉及大功率第四它工作在有人活動(dòng)的家庭環(huán)境里可能碰到小孩、寵物、電線、液體。這些屬性疊加起來意味著它的任何一次控制失效都可能造成物理傷害或財(cái)產(chǎn)損失。而Linux恰恰是一個(gè)在實(shí)時(shí)性和確定性上“不太靠譜”的系統(tǒng)。這不是說Linux不好而是它的設(shè)計(jì)目標(biāo)從來就不是硬實(shí)時(shí)。Linux要處理內(nèi)存管理、進(jìn)程調(diào)度、文件系統(tǒng)、網(wǎng)絡(luò)協(xié)議棧、各種驅(qū)動(dòng)任何一個(gè)環(huán)節(jié)出現(xiàn)延遲抖動(dòng)對于“必須在10毫秒內(nèi)切斷電機(jī)”這種需求來說都是災(zāi)難。你可能會(huì)說Linux有RT補(bǔ)丁啊PREEMPT_RT不是能把延遲壓到幾十微秒嗎理論上可以但實(shí)際產(chǎn)品里你還要跑SLAM、跑路徑規(guī)劃、跑WiFi通信、跑語音識別這些任務(wù)會(huì)互相搶占最壞情況下的延遲是不可預(yù)測的。所以雙腦架構(gòu)的核心邏輯就一句話讓Linux負(fù)責(zé)“聰明”的部分讓MCU負(fù)責(zé)“安全”的部分兩者之間用明確的邊界隔開。Linux可以死機(jī)、可以重啟、可以卡頓但MCU必須始終在跑始終在監(jiān)控始終能在異常發(fā)生時(shí)把機(jī)器停下來。這就是標(biāo)題里說的“安全永遠(yuǎn)不能交給Linux”的真正含義——不是Linux不能用而是安全相關(guān)的最后一道防線必須由一顆獨(dú)立的、確定性的MCU來守。這套架構(gòu)適合誰來參考如果你是做機(jī)器人、智能家居、工業(yè)控制、汽車電子這類涉及“移動(dòng)執(zhí)行器人身安全”的產(chǎn)品這套思路可以直接借鑒。如果你只是做純信息處理類的設(shè)備那單顆SoC就夠了不需要雙腦。下面我會(huì)把這套架構(gòu)從設(shè)計(jì)思路、硬件選型、通信協(xié)議、安全機(jī)制到實(shí)際調(diào)試踩坑完整拆一遍。2. 雙腦架構(gòu)的整體設(shè)計(jì)與職責(zé)劃分2.1 為什么是“LinuxMCU”而不是“LinuxLinux”或者“MCUMCU”先說說為什么不是兩顆Linux。有人會(huì)想既然一顆Linux不夠安全那我搞兩顆Linux互相監(jiān)控行不行答案是不行或者說性價(jià)比極低。兩顆Linux意味著兩套完整的內(nèi)存、存儲、電源、時(shí)鐘系統(tǒng)成本直接翻倍而且兩顆Linux之間的通信延遲和不確定性依然存在。更關(guān)鍵的是Linux的失效模式往往是“整機(jī)卡死”或“內(nèi)核panic”這時(shí)候另一顆Linux未必能及時(shí)感知并接管。你等于用兩個(gè)不確定的系統(tǒng)去互相保證確定性邏輯上就不成立。那為什么不是兩顆MCU因?yàn)閽叩貦C(jī)需要跑SLAM、需要處理激光雷達(dá)點(diǎn)云、需要跑WiFi協(xié)議棧、需要做語音交互這些任務(wù)對算力和內(nèi)存的需求遠(yuǎn)超MCU的能力范圍。MCU通常只有幾百KB的RAM和幾十到幾百M(fèi)Hz的主頻跑個(gè)簡單的控制邏輯沒問題但跑視覺算法和網(wǎng)絡(luò)通信就力不從心了。所以“LinuxMCU”的組合是算力與確定性的最優(yōu)分工Linux這邊有GB級的內(nèi)存、GHz級的主頻、完整的網(wǎng)絡(luò)和文件系統(tǒng)適合做“重計(jì)算、可容忍延遲”的任務(wù)MCU這邊有納秒級的中斷響應(yīng)、硬件級的定時(shí)器、看門狗適合做“輕計(jì)算、必須準(zhǔn)時(shí)”的任務(wù)。兩者通過一條明確的通信通道連接各司其職。2.2 職責(zé)邊界怎么劃哪些歸Linux哪些歸MCU這個(gè)邊界劃分是整個(gè)架構(gòu)設(shè)計(jì)的核心劃錯(cuò)了后面全是坑。我自己的經(jīng)驗(yàn)是遵循一個(gè)原則凡是“失效后可能導(dǎo)致物理傷害或不可逆損失”的功能全部歸MCU凡是“失效后只是體驗(yàn)變差或功能暫時(shí)不可用”的功能歸Linux。具體到掃地機(jī)器人我一般這樣分功能模塊歸屬理由電機(jī)驅(qū)動(dòng)與調(diào)速M(fèi)CU電機(jī)失控會(huì)撞人、卷線、燒毀碰撞檢測與急停MCU必須毫秒級響應(yīng)不能等Linux調(diào)度懸崖檢測MCU掉下樓梯是重大事故電池過充過放保護(hù)MCU涉及起火風(fēng)險(xiǎn)看門狗與復(fù)位管理MCU系統(tǒng)最后一道防線傳感器原始數(shù)據(jù)采集MCU需要精確時(shí)序如超聲波、紅外SLAM與路徑規(guī)劃Linux計(jì)算密集延遲容忍度高WiFi/藍(lán)牙通信Linux協(xié)議棧復(fù)雜MCU跑不動(dòng)語音識別與交互Linux需要算力和大內(nèi)存OTA升級管理Linux需要文件系統(tǒng)和網(wǎng)絡(luò)地圖存儲與用戶界面Linux需要存儲和顯示這張表不是絕對的不同產(chǎn)品會(huì)有調(diào)整。比如有些低端機(jī)型把WiFi放在MCU上跑用ESP32之類的方案但那是成本妥協(xié)不是最優(yōu)設(shè)計(jì)。中高端機(jī)型基本都遵循“安全歸MCU、智能歸Linux”的原則。2.3 通信通道的選擇UART、SPI還是CAN兩顆芯片之間怎么通信這個(gè)選擇直接影響系統(tǒng)的可靠性和實(shí)時(shí)性。常見的選項(xiàng)有UART、SPI、I2C、CAN我逐個(gè)說一下實(shí)際使用感受。UART是最常用的成本低、引腳少、協(xié)議簡單幾乎每顆MCU都有。缺點(diǎn)是速率有限通常115200到921600bps而且沒有硬件仲裁和錯(cuò)誤重傳機(jī)制需要自己在協(xié)議層做校驗(yàn)和重傳。對于掃地機(jī)這種數(shù)據(jù)量不大的場景UART其實(shí)夠用——Linux發(fā)給MCU的主要是速度指令、模式切換、查詢狀態(tài)MCU發(fā)給Linux的主要是傳感器數(shù)據(jù)、異常事件、心跳包。我實(shí)測下來115200bps跑一個(gè)50Hz的控制循環(huán)加事件上報(bào)完全沒問題。SPI速率高可以到幾MHz甚至幾十MHz適合大數(shù)據(jù)量傳輸比如MCU采集的原始點(diǎn)云數(shù)據(jù)傳給Linux。但SPI是主從結(jié)構(gòu)通常Linux做主機(jī)MCU做從機(jī)這意味著MCU不能主動(dòng)發(fā)起通信只能等Linux來讀。對于“MCU檢測到碰撞要立刻通知Linux”這種場景SPI就不太合適除非額外加一根中斷線。I2C速率更低而且總線仲裁機(jī)制復(fù)雜不適合做主要通信通道一般只用來掛低速傳感器。CAN總線在汽車和工業(yè)上很成熟有硬件仲裁、錯(cuò)誤檢測、自動(dòng)重傳可靠性最高。但CAN需要額外的收發(fā)器芯片成本略高而且Linux這邊需要CAN控制器或USB-CAN轉(zhuǎn)換器。如果產(chǎn)品定位高端、對可靠性要求極高CAN是值得的。我做過一個(gè)工業(yè)AGV項(xiàng)目用的就是CAN確實(shí)穩(wěn)但掃地機(jī)這種消費(fèi)級產(chǎn)品UART加協(xié)議層保護(hù)已經(jīng)足夠。我最終的選擇通常是主通信走UART關(guān)鍵事件用獨(dú)立GPIO中斷線。這樣既控制了成本又保證了緊急事件的響應(yīng)速度。比如MCU檢測到碰撞除了通過UART發(fā)消息還會(huì)拉低一根“緊急”GPIOLinux那邊用中斷處理響應(yīng)時(shí)間可以壓到微秒級。3. MCU側(cè)的核心安全機(jī)制與實(shí)操要點(diǎn)3.1 看門狗怎么喂才安全獨(dú)立看門狗與窗口看門狗看門狗是MCU安全機(jī)制的基石但很多人用錯(cuò)了。最常見的錯(cuò)誤是“在定時(shí)器中斷里喂狗”這樣即使主循環(huán)卡死中斷還在跑狗照樣被喂系統(tǒng)實(shí)際上已經(jīng)失控了但看門狗沒起作用。正確的做法是在主循環(huán)的最后喂狗并且喂狗條件要包含“關(guān)鍵任務(wù)都已完成”的判斷。STM32通常有獨(dú)立看門狗IWDG和窗口看門狗WWDG。IWDG用獨(dú)立的低速時(shí)鐘即使主時(shí)鐘掛了它還在跑適合做最后防線。WWDG有“窗口”概念喂狗太早或太晚都會(huì)復(fù)位適合檢測任務(wù)執(zhí)行時(shí)間是否異常。我的做法是兩級都用IWDG設(shè)一個(gè)較長的超時(shí)比如500msWWDG設(shè)一個(gè)較短的窗口比如10ms到20ms之間必須喂這樣既能檢測任務(wù)超時(shí)又能防止主循環(huán)完全卡死。具體配置IWDG的時(shí)候預(yù)分頻和重裝載值的計(jì)算要注意。假設(shè)內(nèi)部低速時(shí)鐘是32kHz預(yù)分頻設(shè)為64那么計(jì)數(shù)頻率是500Hz周期2ms。如果重裝載值設(shè)為250超時(shí)就是500ms。這個(gè)500ms要大于你最慢任務(wù)的執(zhí)行時(shí)間但又要小于“人反應(yīng)過來去拔電源”的時(shí)間。我一般取200到500ms之間。注意調(diào)試的時(shí)候一定要先關(guān)掉看門狗否則單步調(diào)試時(shí)狗會(huì)一直復(fù)位根本沒法調(diào)??梢栽诔跏蓟a里加一個(gè)“調(diào)試模式”判斷檢測到調(diào)試器連接就禁用看門狗。3.2 急?;芈酚布袛啾溶浖袛喔煽寇浖蓖5牧鞒淌菣z測到異常 - MCU執(zhí)行中斷 - 關(guān)閉PWM - 電機(jī)停轉(zhuǎn)。這個(gè)流程即使再快也有幾個(gè)毫秒的延遲而且如果MCU本身跑飛了軟件急停就失效了。所以真正可靠的設(shè)計(jì)是硬件急停回路用一顆獨(dú)立的模擬開關(guān)或繼電器直接切斷電機(jī)驅(qū)動(dòng)器的使能引腳這個(gè)切斷信號由“碰撞傳感器MCU的GPIO”共同控制甚至可以是純硬件的——碰撞開關(guān)直接串在使能回路里。我做過一個(gè)方案碰撞傳感器是一個(gè)常閉開關(guān)串在電機(jī)驅(qū)動(dòng)器的EN引腳上。正常時(shí)開關(guān)閉合EN拉高電機(jī)可以轉(zhuǎn)碰撞時(shí)開關(guān)斷開EN被下拉電阻拉低電機(jī)立刻斷電。這個(gè)響應(yīng)時(shí)間是微秒級的完全不依賴MCU。MCU這邊只是“知道”發(fā)生了碰撞然后去處理后續(xù)邏輯比如后退、轉(zhuǎn)向。這樣即使MCU死機(jī)碰撞時(shí)電機(jī)也會(huì)停。當(dāng)然純硬件急停會(huì)增加線束復(fù)雜度而且碰撞開關(guān)的可靠性本身也要考慮。所以我的折中方案是硬件急停做第一道防線MCU軟件急停做第二道防線Linux的監(jiān)控做第三道防線。三道防線層層遞進(jìn)任何一道生效都能避免事故。3.3 電池管理過充過放保護(hù)的獨(dú)立監(jiān)控掃地機(jī)的電池通常是鋰電池組過充會(huì)起火過放會(huì)損壞電池。很多方案是用專門的充電管理IC但I(xiàn)C也可能失效所以MCU要獨(dú)立監(jiān)控電池電壓和溫度。我的做法是MCU用ADC持續(xù)采樣電池電壓用NTC采樣電池溫度一旦電壓超過4.25V每節(jié)或溫度超過60度立刻切斷充電MOS并通過UART通知Linux上報(bào)異常。這里有個(gè)細(xì)節(jié)ADC采樣要加RC濾波否則電機(jī)啟動(dòng)時(shí)的電壓波動(dòng)會(huì)誤觸發(fā)保護(hù)。我一般用10k電阻加100nF電容截止頻率約160Hz能濾掉大部分開關(guān)噪聲。另外保護(hù)閾值要加遲滯比如過壓保護(hù)在4.25V觸發(fā)但要等到電壓降到4.15V以下才恢復(fù)避免在閾值附近反復(fù)跳變。3.4 傳感器數(shù)據(jù)采集的時(shí)序保證MCU采集傳感器數(shù)據(jù)最怕的是“時(shí)序抖動(dòng)”。比如超聲波測距需要先發(fā)一個(gè)10微秒的脈沖然后計(jì)時(shí)等待回波。如果這個(gè)10微秒的脈沖因?yàn)橹袛嘌舆t變成了15微秒測距結(jié)果就會(huì)偏差。所以MCU這邊要用硬件定時(shí)器來產(chǎn)生脈沖和捕獲回波而不是用軟件延時(shí)。STM32的定時(shí)器輸入捕獲功能很適合做這個(gè)。配置一個(gè)定時(shí)器通道輸出PWM固定10微秒脈沖另一個(gè)通道做輸入捕獲記錄回波上升沿和下降沿的時(shí)間戳差值就是回波時(shí)間。整個(gè)過程硬件完成CPU只需要讀寄存器。這樣即使有中斷打斷測量精度也不受影響。紅外懸崖傳感器也是類似需要精確的調(diào)制頻率通常38kHz用硬件PWM產(chǎn)生軟件只負(fù)責(zé)讀接收頭的輸出。如果軟件模擬38kHzCPU占用率高不說頻率還不準(zhǔn)。4. Linux側(cè)的任務(wù)調(diào)度與異常處理4.1 Linux側(cè)的進(jìn)程優(yōu)先級與CPU親和性設(shè)置Linux這邊雖然不負(fù)責(zé)安全但它的任務(wù)調(diào)度會(huì)間接影響MCU的通信。比如SLAM進(jìn)程如果占滿CPUUART通信線程可能得不到調(diào)度導(dǎo)致MCU發(fā)來的異常事件延遲處理。所以Linux側(cè)也要做優(yōu)先級管理。我的做法是把與MCU通信的線程設(shè)為實(shí)時(shí)優(yōu)先級SCHED_FIFO優(yōu)先級設(shè)成80左右比普通進(jìn)程高但比內(nèi)核關(guān)鍵線程低。然后用taskset把這個(gè)線程綁定到一個(gè)獨(dú)立的CPU核心上如果SoC是多核的避免被其他計(jì)算任務(wù)搶占。SLAM和路徑規(guī)劃這些計(jì)算任務(wù)設(shè)為普通優(yōu)先級SCHED_OTHER讓它們用剩下的核心。具體命令示例# 把通信線程綁定到CPU核心2并設(shè)為實(shí)時(shí)優(yōu)先級 taskset -cp 2 pid chrt -f -p 80 pid在代碼里可以用pthread_setschedparam和pthread_setaffinity_np來實(shí)現(xiàn)。注意實(shí)時(shí)優(yōu)先級線程里不能有阻塞操作否則會(huì)卡住整個(gè)核心。UART讀寫要用非阻塞模式或者帶超時(shí)的poll。4.2 通信協(xié)議設(shè)計(jì)幀結(jié)構(gòu)、校驗(yàn)與重傳Linux和MCU之間的UART通信不能裸發(fā)數(shù)據(jù)必須有幀結(jié)構(gòu)。我一般用這樣的格式幀頭(2字節(jié)) | 長度(1字節(jié)) | 命令(1字節(jié)) | 數(shù)據(jù)(N字節(jié)) | CRC16(2字節(jié)) | 幀尾(1字節(jié))幀頭用0xAA 0x55這種不容易出現(xiàn)在數(shù)據(jù)里的組合。長度字段表示數(shù)據(jù)段的長度。CRC16用CCITT多項(xiàng)式能檢測大部分傳輸錯(cuò)誤。幀尾用0x0D或0x0A。接收方要做狀態(tài)機(jī)解析先找?guī)^再讀長度再收數(shù)據(jù)最后校驗(yàn)CRC。如果CRC錯(cuò)誤丟棄并請求重傳。重傳機(jī)制用序列號加ACK發(fā)送方發(fā)一幀后等待ACK超時(shí)沒收到就重發(fā)重發(fā)3次還失敗就上報(bào)通信故障。這里有個(gè)坑如果MCU在發(fā)數(shù)據(jù)時(shí)被高優(yōu)先級中斷打斷可能導(dǎo)致幀內(nèi)字節(jié)間隔過大Linux側(cè)的串口驅(qū)動(dòng)可能認(rèn)為幀結(jié)束了。解決辦法是MCU發(fā)送時(shí)關(guān)中斷或者用DMA發(fā)送。STM32的UART DMA發(fā)送可以保證字節(jié)連續(xù)不被打斷。4.3 Linux死機(jī)時(shí)MCU如何接管這是雙腦架構(gòu)最關(guān)鍵的安全場景Linux因?yàn)閮?nèi)存溢出、驅(qū)動(dòng)bug、看門狗超時(shí)等原因死機(jī)了MCU必須能檢測到并讓機(jī)器安全停下。檢測機(jī)制是心跳包Linux每隔100ms通過UART發(fā)一個(gè)心跳幀給MCUMCU收到后重置一個(gè)心跳計(jì)數(shù)器。如果MCU連續(xù)500ms沒收到心跳就判定Linux失聯(lián)執(zhí)行安全動(dòng)作停止電機(jī)、關(guān)閉風(fēng)機(jī)、點(diǎn)亮故障燈、蜂鳴器報(bào)警。然后MCU可以嘗試復(fù)位Linux通過控制Linux的復(fù)位引腳如果復(fù)位后還是沒心跳就保持停機(jī)狀態(tài)。這里要注意心跳包不能只是“收到了就行”還要檢查內(nèi)容。比如心跳包里可以帶Linux的當(dāng)前狀態(tài)正常、低電量、故障MCU根據(jù)狀態(tài)決定是否繼續(xù)允許運(yùn)動(dòng)。如果Linux報(bào)告“傳感器故障”MCU應(yīng)該限制速度或停止。另外MCU復(fù)位Linux的引腳要設(shè)計(jì)成“開漏”或“可控”避免MCU自己復(fù)位時(shí)誤觸發(fā)Linux復(fù)位。我一般用一個(gè)MOS管做電平隔離MCU的GPIO控制MOS管柵極MOS管漏極接Linux的復(fù)位引腳。4.4 OTA升級時(shí)的雙腦協(xié)同OTA升級是另一個(gè)容易出問題的場景。Linux負(fù)責(zé)下載固件包但MCU的固件也要升級。流程通常是Linux下載包含MCU固件的包 - Linux通過UART把MCU固件傳給MCU - MCU寫入自己的Flash - MCU重啟生效。這里的安全點(diǎn)是MCU固件升級過程中機(jī)器必須處于安全狀態(tài)停在充電座上電機(jī)斷電。而且MCU要有“回滾”機(jī)制如果新固件啟動(dòng)失敗比如看門狗超時(shí)要能回退到舊固件。STM32可以用雙Bank Flash或者外部EEPROM存儲舊固件。我一般用雙Bank方案升級時(shí)寫入備用Bank啟動(dòng)時(shí)如果備用Bank校驗(yàn)失敗就切回主Bank。Linux這邊升級失敗相對好處理因?yàn)長inux有文件系統(tǒng)可以保留舊版本。但要注意Linux升級時(shí)MCU不能斷電否則MCU可能處于未知狀態(tài)。所以升級流程要設(shè)計(jì)成“MCU先進(jìn)入安全模式Linux再升級升級完成后MCU退出安全模式”。5. 常見問題與排查技巧實(shí)錄5.1 通信丟包與誤碼的排查思路UART通信出問題最常見的原因是波特率不匹配、地線沒接好、或者干擾。我遇到過一次MCU和Linux的波特率都設(shè)的115200但實(shí)際通信誤碼率很高。用示波器看波形發(fā)現(xiàn)MCU的TX信號上升沿很緩原來是上拉電阻太大10k加上線纜電容導(dǎo)致邊沿變圓。換成4.7k上拉后就好了。另一個(gè)常見問題是地環(huán)路。如果MCU和Linux的電源地不是同一點(diǎn)兩地之間有壓差UART信號就會(huì)偏移。解決辦法是用單點(diǎn)接地或者加隔離芯片。我一般會(huì)在UART線上串22歐姆電阻并在接收端加對地電容100pF能濾掉高頻干擾。排查步驟我總結(jié)成一張表現(xiàn)象可能原因排查方法完全無數(shù)據(jù)接線錯(cuò)誤、波特率不對示波器看TX是否有波形偶發(fā)誤碼干擾、地線問題檢查地線、加濾波電容幀頭對但CRC錯(cuò)字節(jié)丟失、時(shí)鐘偏差降低波特率測試通信一段時(shí)間后死掉緩沖區(qū)溢出、中斷未清檢查驅(qū)動(dòng)緩沖區(qū)大小MCU收不到但Linux能收MCU RX配置錯(cuò)誤檢查MCU串口初始化代碼5.2 MCU跑飛后的恢復(fù)策略MCU跑飛通常表現(xiàn)為看門狗復(fù)位、HardFault、或者程序卡在某個(gè)循環(huán)里。STM32的HardFault可以通過讀取SCB-CFSR寄存器定位原因比如是總線錯(cuò)誤、內(nèi)存訪問錯(cuò)誤還是未定義指令。我一般會(huì)在HardFault_Handler里把關(guān)鍵寄存器存到備份RAM然后復(fù)位復(fù)位后讀取備份RAM分析原因。預(yù)防跑飛的措施第一所有指針使用前判空第二數(shù)組訪問加邊界檢查第三中斷服務(wù)函數(shù)盡量短復(fù)雜邏輯放到主循環(huán)第四棧大小要留夠我一般給每個(gè)任務(wù)至少512字節(jié)??臻g主棧1KB以上。如果MCU頻繁復(fù)位先看是不是電源問題。電機(jī)啟動(dòng)時(shí)電流突變可能導(dǎo)致MCU供電跌落觸發(fā)BOR復(fù)位。解決辦法是在MCU電源引腳加100uF電解電容加100nF陶瓷電容并且電機(jī)電源和MCU電源用磁珠隔離。5.3 Linux側(cè)串口被占用或阻塞的處理Linux的串口設(shè)備/dev/ttyS0或/dev/ttyAMA0如果被其他進(jìn)程打開通信線程會(huì)打開失敗。我遇到過調(diào)試時(shí)用minicom占了串口導(dǎo)致主程序打不開。解決辦法是在程序啟動(dòng)時(shí)檢查串口是否可用如果被占用就報(bào)錯(cuò)退出而不是靜默失敗。另一個(gè)問題是串口讀阻塞。如果用阻塞模式讀沒有數(shù)據(jù)時(shí)線程會(huì)掛起心跳包就發(fā)不出去。所以要用非阻塞模式O_NONBLOCK或者用select/poll加超時(shí)。我一般用poll超時(shí)設(shè)10ms這樣既能及時(shí)讀數(shù)據(jù)又不會(huì)阻塞太久。還有Linux的串口默認(rèn)可能有流控RTS/CTS如果MCU沒接流控線要關(guān)掉流控否則可能發(fā)不出數(shù)據(jù)。用stty命令可以配置stty -F /dev/ttyS0 115200 cs8 -cstopb -parenb -crtscts5.4 電機(jī)干擾導(dǎo)致MCU復(fù)位的解決這是掃地機(jī)最經(jīng)典的坑電機(jī)一啟動(dòng)MCU就復(fù)位。原因是電機(jī)換向時(shí)產(chǎn)生高頻噪聲通過電源線或空間輻射耦合到MCU。解決辦法分幾層第一電機(jī)兩端加續(xù)流二極管和RC吸收電路第二電機(jī)電源線用雙絞線并加磁環(huán)第三MCU電源加LC濾波第四PCB布局時(shí)電機(jī)驅(qū)動(dòng)部分和MCU部分分區(qū)地平面分割單點(diǎn)連接。我實(shí)測下來最有效的是在電機(jī)端子處加一個(gè)100nF加100歐姆的RC吸收再加一個(gè)TVS管鉗位。這樣能把換向尖峰壓到安全范圍。另外MCU的復(fù)位引腳要加100nF電容到地防止干擾誤觸發(fā)復(fù)位。5.5 常見問題速查表問題現(xiàn)象解決MCU頻繁復(fù)位電機(jī)啟動(dòng)時(shí)復(fù)位加電源濾波、RC吸收通信誤碼數(shù)據(jù)偶爾錯(cuò)誤檢查地線、加串阻和電容看門狗誤復(fù)位正常運(yùn)行時(shí)復(fù)位檢查喂狗位置和超時(shí)時(shí)間Linux收不到心跳MCU判定失聯(lián)檢查串口配置和線程優(yōu)先級急停不生效碰撞后電機(jī)不停檢查硬件急?;芈泛虴N引腳OTA后MCU不啟動(dòng)升級后死機(jī)檢查雙Bank切換和校驗(yàn)電池保護(hù)誤觸發(fā)電量正常但停機(jī)調(diào)整閾值和遲滯超聲波測距偏差數(shù)據(jù)跳動(dòng)大用硬件定時(shí)器捕獲6. 雙腦架構(gòu)的擴(kuò)展與個(gè)人經(jīng)驗(yàn)這套雙腦架構(gòu)不只適用于掃地機(jī)器人。我后來做割草機(jī)器人、AGV、甚至智能門鎖都用了類似的思路一顆Linux做智能交互和計(jì)算一顆MCU做安全控制和實(shí)時(shí)響應(yīng)。區(qū)別只是通信協(xié)議和安全等級的要求不同。比如割草機(jī)器人對刀片電機(jī)的安全要求更高M(jìn)CU這邊要加雙重冗余的急?;芈稟GV對通信可靠性要求高就換成CAN總線。我個(gè)人在實(shí)際操作中的體會(huì)是雙腦架構(gòu)的難點(diǎn)不在硬件而在“邊界定義”和“異常處理”。硬件選型、通信協(xié)議這些都有成熟方案但“什么情況下MCU該接管”“接管后做什么”“怎么恢復(fù)到正常狀態(tài)”這些邏輯需要反復(fù)推敲和測試。我一般會(huì)做故障注入測試故意讓Linux死機(jī)、故意拔掉傳感器、故意讓電機(jī)堵轉(zhuǎn)看MCU的反應(yīng)是否符合預(yù)期。這個(gè)過程能發(fā)現(xiàn)很多設(shè)計(jì)時(shí)想不到的邊界情況。最后分享一個(gè)小技巧在MCU的Flash里留一塊“黑匣子”區(qū)域記錄最近幾次異常事件的時(shí)間戳和狀態(tài)碼。機(jī)器出問題時(shí)讀這塊區(qū)域就能快速定位原因比看日志高效得多。這個(gè)習(xí)慣幫我省了很多調(diào)試時(shí)間。