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