戰(zhàn):基于RISC-V與CMOS攝像頭的嵌入式系統(tǒng)開發(fā)全解析)
1. 從賽道到代碼一場智能車競賽的完整復(fù)盤剛結(jié)束的第十六屆全國大學(xué)生智能汽車競賽像一場持續(xù)了數(shù)月的技術(shù)馬拉松終于沖過了終點(diǎn)線。從最初拿到賽題規(guī)則時(shí)的茫然到中期調(diào)車調(diào)參的焦頭爛額再到最后賽場上的驚心動(dòng)魄整個(gè)過程濃縮了嵌入式開發(fā)、自動(dòng)控制、機(jī)器視覺和團(tuán)隊(duì)協(xié)作的幾乎所有核心挑戰(zhàn)。我所在的隊(duì)伍選擇了基礎(chǔ)四輪組主控芯片沒有隨大流用STM32而是嘗試了基于RISC-V架構(gòu)的CH32V103R8T6這讓我們?cè)谟布偷讓域?qū)動(dòng)上多花了不少功夫但也收獲了更深入的理解。這篇總結(jié)我想拋開那些官方的技術(shù)報(bào)告模板從一個(gè)一線隊(duì)員的視角聊聊我們是怎么把一堆芯片、傳感器和代碼變成一輛能在賽道上自主狂奔的智能車的。無論你是準(zhǔn)備參賽的新手還是對(duì)嵌入式AI小車感興趣的愛好者希望這些踩過的坑和總結(jié)的經(jīng)驗(yàn)?zāi)芙o你一條更清晰的路徑。2. 整體方案設(shè)計(jì)與核心思路拆解2.1 賽題理解與組別選擇背后的邏輯第十六屆的規(guī)則在繼承經(jīng)典元素的同時(shí)也引入了一些新變化。比如基礎(chǔ)四輪組賽道元素更加復(fù)雜增加了環(huán)島、十字路口、坡道等組合對(duì)路徑識(shí)別的魯棒性和控制算法的適應(yīng)性提出了更高要求。我們選擇基礎(chǔ)四輪組主要是基于團(tuán)隊(duì)技術(shù)棧的考量攝像頭圖像處理是我們的強(qiáng)項(xiàng)而平衡節(jié)能、雙車接力等組別對(duì)機(jī)械、控制的要求維度不同。選擇往往比努力更重要在備賽初期花時(shí)間吃透規(guī)則、評(píng)估自身團(tuán)隊(duì)在視覺、控制、機(jī)械、電路等方面的能力長短板是決定后續(xù)所有工作方向的基礎(chǔ)。注意不要盲目追求“熱門”或“高端”組別。比如攝像頭三輪組對(duì)圖像處理和控制耦合要求極高節(jié)能組對(duì)硬件能效和軟件優(yōu)化是極致考驗(yàn)。選擇與團(tuán)隊(duì)核心能力最匹配的組別才能最大化備賽效率。2.2 “RISC-VCMOS攝像頭”核心架構(gòu)選型我們的硬件核心是CH32V103R8T6 MCU和一款全局快門的MT9V034 數(shù)字?jǐn)z像頭。選型理由如下MCU為什么是CH32V103R8T6性價(jià)比與可控性在芯片普遍緊缺和價(jià)格上漲的背景下這款基于RISC-V內(nèi)核的MCU提供了不錯(cuò)的性能108MHz主頻和豐富的外設(shè)ADC、定時(shí)器、DMA等且價(jià)格相對(duì)穩(wěn)定。更重要的是其開發(fā)環(huán)境MounRiver Studio和底層庫相對(duì)簡潔迫使我們?nèi)ジ钊氲乩斫饧拇嫫髋渲煤蜁r(shí)序而不是過度依賴黑盒化的HAL庫。這對(duì)于學(xué)習(xí)嵌入式本質(zhì)是有益的。挑戰(zhàn)與機(jī)遇挑戰(zhàn)在于社區(qū)資源遠(yuǎn)不如STM32豐富很多驅(qū)動(dòng)需要自己移植或從頭編寫。機(jī)遇也在于此沒有現(xiàn)成的“保姆式”代碼逼著我們搞清楚了從時(shí)鐘樹配置、GPIO初始化到中斷優(yōu)先級(jí)管理的每一個(gè)細(xì)節(jié)這種能力在賽后看來是無價(jià)的。傳感器攝像頭 vs. 電磁我們堅(jiān)定選擇了攝像頭方案。雖然電磁方案受光線影響小穩(wěn)定性高但攝像頭方案的信息密度是碾壓級(jí)的。它能識(shí)別賽道邊界、中心線、元素環(huán)島、十字等為控制算法提供前瞻性和預(yù)判能力。MT9V034全局快門可以有效避免果凍效應(yīng)在車體高速晃動(dòng)時(shí)依然能獲取清晰的圖像這是成功的關(guān)鍵。核心思路將復(fù)雜的賽道環(huán)境通過攝像頭轉(zhuǎn)化為二值化的圖像矩陣再從中提取出用于決策的“特征”——通常是左右賽道邊界線。一切控制都基于這個(gè)抽象的“賽道模型”。2.3 系統(tǒng)框架從像素到電機(jī)PWM的信號(hào)流我們的軟件系統(tǒng)是一個(gè)典型的“感知-決策-控制”閉環(huán)但具體實(shí)現(xiàn)上分層清晰[圖像采集 (DMA)] - [圖像處理 (二值化尋線)] - [路徑計(jì)算 (中線曲率)] - [控制決策 (PID 狀態(tài)機(jī))] - [執(zhí)行輸出 (電機(jī)PWM 舵機(jī)PWM)]每一層都通過清晰的接口變量、結(jié)構(gòu)體耦合方便單獨(dú)調(diào)試和優(yōu)化。例如圖像處理模塊只負(fù)責(zé)輸出一個(gè)包含左右邊線位置的數(shù)組控制模塊不關(guān)心這個(gè)數(shù)組是如何得來的。這種模塊化設(shè)計(jì)在后期調(diào)參時(shí)節(jié)省了大量時(shí)間。3. 核心模塊實(shí)現(xiàn)與關(guān)鍵技術(shù)細(xì)節(jié)3.1 CH32V103R8T6的底層驅(qū)動(dòng)構(gòu)建這是所有工作的地基。由于缺乏成熟的生態(tài)我們相當(dāng)于自己搭建了一個(gè)微型“操作系統(tǒng)”。時(shí)鐘系統(tǒng)配置CH32V103最高可運(yùn)行在108MHz。我們選擇使用外部8MHz晶振通過PLL倍頻到96MHz作為系統(tǒng)主頻。這一步需要在啟動(dòng)文件startup_ch32v10x.S和系統(tǒng)初始化函數(shù)中正確配置相關(guān)的時(shí)鐘控制寄存器。關(guān)鍵點(diǎn)必須確保為外設(shè)如定時(shí)器、ADC、攝像頭接口分配的時(shí)鐘源和分頻系數(shù)正確否則后續(xù)所有時(shí)序都會(huì)錯(cuò)亂。我們?cè)驗(yàn)锳DC時(shí)鐘配置錯(cuò)誤導(dǎo)致采樣值完全不可信排查了大半天。GPIO與中斷配置攝像頭的數(shù)據(jù)引腳D0-D7、行場同步信號(hào)VSYNC, HREF需要配置為浮空輸入模式并開啟上升沿/下降沿中斷。DMA應(yīng)用這是提升性能的關(guān)鍵。我們配置了DMA通道將攝像頭數(shù)據(jù)端口GPIO組直接搬運(yùn)到內(nèi)存中的圖像緩沖區(qū)。在VSYNC幀同步中斷中啟動(dòng)DMA在HREF行同步中斷中配合DMA完成一行數(shù)據(jù)的接收。這樣CPU幾乎不參與數(shù)據(jù)搬運(yùn)解放出來進(jìn)行圖像處理。定時(shí)器與PWM生成使用高級(jí)定時(shí)器TIM1產(chǎn)生四路PWM分別控制兩個(gè)電機(jī)的正反轉(zhuǎn)和速度實(shí)際上用了兩路PWM加兩個(gè)GPIO方向控制實(shí)現(xiàn)雙極性驅(qū)動(dòng)。使用通用定時(shí)器TIM2配置為編碼器模式直接讀取電機(jī)編碼器的脈沖數(shù)用于計(jì)算實(shí)際速度構(gòu)成速度閉環(huán)。PID計(jì)算中斷另一個(gè)通用定時(shí)器TIM3被配置為固定頻率如1kHz的中斷在這個(gè)中斷服務(wù)函數(shù)中進(jìn)行速度PID和方向PID的計(jì)算并更新PWM占空比。中斷優(yōu)先級(jí)要合理設(shè)置確??刂浦芷诜€(wěn)定。3.2 圖像采集與二值化處理優(yōu)化圖像處理的速度和穩(wěn)定性直接決定了車的上限。穩(wěn)定采集除了使用DMA還要注意信號(hào)消抖。攝像頭VSYNC和HREF信號(hào)在硬件連接較長時(shí)可能有毛刺。我們?cè)谥袛喾?wù)函數(shù)入口添加了簡單的延時(shí)判斷連續(xù)采樣幾次引腳狀態(tài)確認(rèn)是穩(wěn)定電平變化后才執(zhí)行后續(xù)操作避免了誤觸發(fā)。動(dòng)態(tài)閾值二值化固定閾值在光線變化時(shí)效果很差。我們采用了大津法OTSU或自適應(yīng)局部閾值。大津法在每幀圖像開始處理前對(duì)整幀或感興趣區(qū)域ROI的灰度直方圖進(jìn)行計(jì)算自動(dòng)得出一個(gè)最佳分割閾值。計(jì)算量稍大但全局效果穩(wěn)定。自適應(yīng)閾值將圖像分成若干小網(wǎng)格對(duì)每個(gè)網(wǎng)格單獨(dú)計(jì)算閾值如取均值或中值。這種方法對(duì)光照不均的賽道適應(yīng)性更強(qiáng)。我們最終采用了網(wǎng)格自適應(yīng)因?yàn)橘悎鲰敼馔鶗?huì)造成賽道中間亮、兩邊暗。優(yōu)化技巧不必對(duì)整幅圖像例如188*120進(jìn)行閾值計(jì)算。可以只對(duì)圖像下方幾行車近處的賽道和上方幾行遠(yuǎn)處的前瞻分別計(jì)算閾值既能適應(yīng)光照變化又減少了計(jì)算量。3.3 賽道元素識(shí)別與狀態(tài)機(jī)設(shè)計(jì)識(shí)別出直道、彎道、十字、環(huán)島等元素是進(jìn)行智能決策的前提。邊界搜索與中線提取采用經(jīng)典的“爬邊線”算法。從圖像底部中心開始向左向右搜索黑白跳變點(diǎn)作為初始邊線。然后逐行向上以上一行的邊線位置為起點(diǎn)進(jìn)行小范圍搜索得到當(dāng)前行的邊線。將所有邊線點(diǎn)擬合成兩條曲線其中間線即為引導(dǎo)線。丟線處理當(dāng)某一行搜索不到邊線時(shí)不能簡單停止。我們采用“記憶外推”策略用之前幾行邊線的斜率或曲率預(yù)測當(dāng)前行的可能位置擴(kuò)大搜索窗口。如果連續(xù)多行丟線則觸發(fā)“丟線狀態(tài)”控制策略切換到保守模式如減速、按上一有效曲率轉(zhuǎn)向。元素識(shí)別邏輯十字路口當(dāng)左右邊線同時(shí)大幅向外發(fā)散且中間區(qū)域在一定行數(shù)內(nèi)持續(xù)為白色賽道色時(shí)判定為十字。我們的策略是進(jìn)入十字后保持進(jìn)入前的角度和速度直行通過同時(shí)抑制邊線搜索防止誤搜到十字的橫向邊線。環(huán)島這是難點(diǎn)。我們通過識(shí)別邊線的“突變”來判斷環(huán)島入口。當(dāng)單邊邊線例如右邊線突然向內(nèi)凹陷形成弧口而另一邊線相對(duì)正常時(shí)初步判定為環(huán)島入口。隨后進(jìn)入一個(gè)專門的“環(huán)島狀態(tài)機(jī)”。環(huán)島狀態(tài)機(jī)設(shè)計(jì)狀態(tài)1進(jìn)入識(shí)別到入口特征開始記錄內(nèi)側(cè)邊線軌跡。狀態(tài)2循跡不再跟蹤外側(cè)邊線而是以識(shí)別到的內(nèi)側(cè)弧線作為單邊引導(dǎo)線同時(shí)結(jié)合陀螺儀MPU6050的Z軸角速度積分判斷車身是否已繞行約270度。狀態(tài)3出島當(dāng)角度積分達(dá)到預(yù)設(shè)值且圖像重新出現(xiàn)正常的雙邊線時(shí)切換回普通循跡模式。關(guān)鍵出島判斷必須圖像和陀螺儀數(shù)據(jù)融合單靠任何一個(gè)都容易出錯(cuò)提前出島或卡在島內(nèi)。3.4 控制算法PID與更高級(jí)的策略雙閉環(huán)PID控制速度環(huán)內(nèi)環(huán)輸入是目標(biāo)速度由路徑曲率映射得到彎道慢直道快和編碼器反饋的實(shí)際速度輸出是電機(jī)PWM。使用PI控制器即可積分項(xiàng)能消除靜差但要注意積分飽和需要做抗飽和處理。方向環(huán)外環(huán)輸入是期望路徑的橫向偏差中線與圖像中心的偏差和偏差變化率輸出是舵機(jī)目標(biāo)角度或直接是舵機(jī)PWM。使用PD控制器。微分項(xiàng)能預(yù)測偏差趨勢讓轉(zhuǎn)向更平滑抑制過沖。參數(shù)整定心得先調(diào)方向環(huán)再調(diào)速度環(huán)。在車靜止時(shí)用手推動(dòng)小車產(chǎn)生橫向偏差觀察舵機(jī)能否快速、無超調(diào)地將車輪回正。然后低速在直道上跑微調(diào)參數(shù)使車能沿直線行駛。最后上彎道重點(diǎn)調(diào)整微分系數(shù)抑制振蕩。前瞻與曲率預(yù)瞄簡單的偏差控制是“滯后”的因?yàn)樗谲嚠?dāng)前位置的誤差。我們引入了“前瞻點(diǎn)”概念。從圖像中線上選取一個(gè)距離車頭一定距離的點(diǎn)例如圖像頂部往下20行計(jì)算該點(diǎn)與圖像中心的橫向偏差。用這個(gè)前瞻偏差來控制舵機(jī)相當(dāng)于讓車提前轉(zhuǎn)向過彎更加流暢自然。曲率計(jì)算通過擬合出的中線計(jì)算其曲率。曲率可以直接映射為目標(biāo)速度大曲率-低速也可以作為前饋量加入到方向環(huán)控制中實(shí)現(xiàn)“彎道提前打舵”。4. 系統(tǒng)調(diào)試與性能優(yōu)化全記錄4.1 開發(fā)調(diào)試環(huán)境搭建硬件調(diào)試器我們使用WCH-Link通過SWD接口對(duì)CH32V103進(jìn)行程序下載和在線調(diào)試。MounRiver Studio內(nèi)置的調(diào)試功能基本夠用可以查看變量、設(shè)置斷點(diǎn)。軟件調(diào)試“后門”無線串口在車上加裝一個(gè)藍(lán)牙串口模塊如HC-05將關(guān)鍵數(shù)據(jù)如圖像行數(shù)據(jù)、邊線位置、PID輸出、狀態(tài)機(jī)狀態(tài)實(shí)時(shí)發(fā)送到電腦上位機(jī)。上位機(jī)軟件我們基于Python的PyQt5和OpenCV自己編寫了一個(gè)簡單的上位機(jī)。它能繪制出攝像頭看到的二值化圖像、提取的邊線、計(jì)算出的中線并以波形圖形式顯示速度、偏差等數(shù)據(jù)。這是調(diào)試效率提升十倍的關(guān)鍵。你可以直觀地看到車“眼中”的世界以及控制算法是如何理解的。參數(shù)在線調(diào)參在上位機(jī)中制作滑動(dòng)條通過無線串口動(dòng)態(tài)修改車上的PID參數(shù)、閾值等并立即觀察效果避免了反復(fù)修改代碼、編譯、下載的繁瑣過程。4.2 機(jī)械結(jié)構(gòu)與參數(shù)調(diào)?!败浖Q定上限機(jī)械決定下限。”再好的算法如果車裝得歪歪扭扭也跑不好。重心與陀螺儀安裝電池、主板等重物盡量放低、居中。MPU6050陀螺儀模塊必須用海綿膠牢牢固定在車體中心并與車身軸線平行避免振動(dòng)干擾。前輪前束與主銷內(nèi)傾對(duì)于舵機(jī)轉(zhuǎn)向的車前輪通常設(shè)置為微小的“內(nèi)八字”前束這有助于提高直行穩(wěn)定性。這些微調(diào)需要耐心每次只動(dòng)一點(diǎn)然后上路測試直行是否跑偏。輪胎處理新輪胎表面光滑抓地力不足。我們用細(xì)砂紙輕輕打磨輪胎表面增加摩擦力。胎壓也要一致不能一軟一硬。4.3 代碼級(jí)優(yōu)化技巧當(dāng)算法邏輯正確后優(yōu)化就是為了跑得更快。減少計(jì)算量縮小ROI車在高速運(yùn)行時(shí)遠(yuǎn)處圖像細(xì)節(jié)來不及處理。我們動(dòng)態(tài)調(diào)整圖像處理的行數(shù)車速越快處理的圖像行數(shù)越少只關(guān)注近處賽道保證控制周期穩(wěn)定在5ms以內(nèi)。查表法將一些頻繁計(jì)算的結(jié)果預(yù)先算好存入數(shù)組。例如atan2、sqrt函數(shù)非常耗時(shí)我們可以根據(jù)偏差和偏差變化率預(yù)先計(jì)算一個(gè)PD控制量的二維查找表。整數(shù)運(yùn)算在MCU上浮點(diǎn)運(yùn)算速度遠(yuǎn)慢于整數(shù)運(yùn)算。我們將所有PID參數(shù)、誤差等變量都乘以一個(gè)放大系數(shù)如1024用int32_t類型進(jìn)行整數(shù)運(yùn)算最后輸出時(shí)再縮小。精度完全足夠。提高系統(tǒng)穩(wěn)定性看門狗一定要開啟獨(dú)立看門狗IWDG。在main函數(shù)循環(huán)和關(guān)鍵任務(wù)中定期“喂狗”。一旦程序跑飛或陷入死循環(huán)看門狗能復(fù)位系統(tǒng)至少讓車停下來而不是撞毀。堆棧大小設(shè)置在啟動(dòng)文件中適當(dāng)調(diào)大堆棧Stack_Size。復(fù)雜的函數(shù)調(diào)用和局部變量可能造成棧溢出導(dǎo)致各種難以復(fù)現(xiàn)的詭異錯(cuò)誤。5. 賽場實(shí)戰(zhàn)問題排查與應(yīng)急方案無論實(shí)驗(yàn)室跑得多好賽場永遠(yuǎn)是另一回事。以下是我們遇到和觀察到的高頻問題問題現(xiàn)象可能原因排查步驟與解決方案發(fā)車后原地不動(dòng)或抽搐1. 電機(jī)驅(qū)動(dòng)橋未使能或損壞。2. 編碼器接線松動(dòng)速度反饋為0導(dǎo)致PID輸出異常。3. 程序未正常進(jìn)入主循環(huán)。1. 檢查電機(jī)驅(qū)動(dòng)芯片的使能引腳電平測量電機(jī)兩端電壓。2. 晃動(dòng)編碼器接線在調(diào)試器查看編碼器計(jì)數(shù)值是否變化。3. 檢查啟動(dòng)代碼添加LED閃爍指示確認(rèn)程序運(yùn)行。直道左右搖擺振蕩1. 方向環(huán)PID微分系數(shù)D太小或?yàn)樨?fù)。2. 機(jī)械松動(dòng)舵機(jī)連桿有虛位。3. 圖像處理延時(shí)過大控制滯后。1.首要降低P值增加D值。D是抑制振蕩的關(guān)鍵。2. 用手輕輕晃動(dòng)前輪檢查是否有松動(dòng)。3. 通過上位機(jī)查看從圖像采集到舵機(jī)輸出的總延時(shí)優(yōu)化代碼。過彎時(shí)沖出賽道1. 彎道速度過快。2. 前瞻距離設(shè)置太短轉(zhuǎn)向不及時(shí)。3. 圖像在彎道丟線策略未正確處理。1. 建立曲率-速度映射表彎道曲率越大目標(biāo)速度越低。2. 增加前瞻點(diǎn)距離讓車“看得更遠(yuǎn)”。3. 加強(qiáng)彎道處的丟線處理邏輯如使用預(yù)測線。元素十字、環(huán)島誤識(shí)別或漏識(shí)別1. 光線變化導(dǎo)致二值化閾值失效。2. 元素識(shí)別條件閾值設(shè)置不合理。3. 車體經(jīng)過元素時(shí)姿態(tài)不穩(wěn)定圖像抖動(dòng)。1. 必須使用動(dòng)態(tài)閾值。2. 在賽場不同光照下大量測試記錄數(shù)據(jù)調(diào)整識(shí)別條件的閾值如白色連續(xù)行數(shù)、邊線發(fā)散角度。3. 在元素識(shí)別期間適當(dāng)降低速度提高圖像穩(wěn)定性。跑著跑著突然復(fù)位1. 電源不穩(wěn)定電壓跌落觸發(fā)欠壓復(fù)位。2. 程序堆棧溢出。3. 看門狗未及時(shí)喂狗。1. 用示波器監(jiān)測電池電壓大電流負(fù)載時(shí)是否跌落到MCU最低工作電壓以下。加大電容穩(wěn)壓。2. 增加堆棧大小檢查是否有大型局部數(shù)組。3. 檢查看門狗喂狗函數(shù)是否在所有可能的主循環(huán)路徑中都被調(diào)用。無線調(diào)試突然斷開1. 賽場無線環(huán)境復(fù)雜2.4GHz頻段干擾。2. 藍(lán)牙模塊供電不足。1. 準(zhǔn)備備用方案將關(guān)鍵數(shù)據(jù)通過IO口輸出到邏輯分析儀或使用SD卡離線記錄數(shù)據(jù)。2. 為藍(lán)牙模塊單獨(dú)使用LDO供電避免電機(jī)啟動(dòng)時(shí)拉低電壓。賽場最后24小時(shí)檢查清單硬件所有螺絲點(diǎn)膠固定所有線纜用扎帶或熱熔膠固定避免松脫電池電量滿格電極片用酒精擦拭干凈輪胎清潔無灰塵傳感器鏡頭擦拭干凈。軟件準(zhǔn)備多個(gè)版本的固件激進(jìn)速度版、穩(wěn)定完賽版確認(rèn)撥碼開關(guān)或按鍵可以切換版本將最重要的PID參數(shù)、速度映射表放在易修改的位置如通過按鍵加減調(diào)整。心理制定比賽策略。前一兩圈求穩(wěn)確保完賽拿到基礎(chǔ)成績。后面幾圈再嘗試逐步提升速度。永遠(yuǎn)把穩(wěn)定性放在速度前面?;仡櫿麄€(gè)備賽過程最大的收獲不是獎(jiǎng)狀而是這套從問題定義、方案設(shè)計(jì)、模塊實(shí)現(xiàn)、集成調(diào)試到現(xiàn)場排錯(cuò)的完整工程實(shí)踐能力。選擇CH32V103讓我們被迫深入底層雖然過程痛苦但回過頭看那些對(duì)著數(shù)據(jù)手冊(cè)調(diào)寄存器的夜晚才是真正理解嵌入式系統(tǒng)如何工作的時(shí)刻。智能車競賽就像一個(gè)微縮的機(jī)器人項(xiàng)目它教會(huì)你的遠(yuǎn)不止如何讓一輛小車跑起來而是如何讓一個(gè)復(fù)雜的軟硬件系統(tǒng)可靠、智能地工作。如果再來一次我可能還是會(huì)選那個(gè)“麻煩”的RISC-V芯片因?yàn)槟阒滥愠酝傅拿恳稽c(diǎn)都是別人拿不走的資本。最后一個(gè)小建議盡早開始做“系統(tǒng)集成”不要等每個(gè)模塊都完美了再拼起來。車只有跑起來你才知道真正的問題在哪里。