的實(shí)現(xiàn))
直接開工。搞過 ODrive 的人都知道這玩意兒在電機(jī)控制圈子里是個“繞不開的參考系”。開源、硬件設(shè)計漂亮、固件質(zhì)量高而且它那套代碼里藏了不少底層細(xì)節(jié)普通 demo 工程里根本看不到。尤其是從定時器時基到 8 kHz 控制環(huán)這一整條鏈路幾乎是所有想深入 FOC 控制、想自己移植固件、或者想搞明白“為什么我的電機(jī)跑起來噪音那么大”的人的必修課。這一篇是系列的第二篇核心就一件事講清楚 ODrive 固件里定時器時基是怎么搭建的又是怎么一步步把它變成實(shí)際執(zhí)行 PID 控制律的 8 kHz 控制環(huán)。這里會涉及 STM32 定時器的底層配置、PWM 生成、更新中斷觸發(fā)、以及中斷服務(wù)程序里的任務(wù)調(diào)度最終帶你把整個調(diào)用鏈路串起來。適合誰看想讀 ODrive 源碼但不知道從哪下手的用 STM32 自己寫 FOC 想抄作業(yè)的以及已經(jīng)在跑 ODrive 但總感覺“里面有事兒”的好奇派??赐赀@篇你應(yīng)該能說出“8 kHz 控制環(huán)”到底是哪個中斷拉起來的那個中斷里又干了哪些事。1. 內(nèi)容整體設(shè)計與思路拆解1.1 先搞清楚 8 kHz 控制環(huán)到底是個什么概念很多人一聽“控制環(huán)”第一反應(yīng)是“一個 PID 循環(huán)”。這句話不算錯但對 ODrive 來說控制環(huán)不是一個 while(1) 里跑的邏輯而是一個被定時器中斷精確驅(qū)動的硬實(shí)時任務(wù)。在 ODrive 固件里它把控制邏輯分成了好幾層電流環(huán)最內(nèi)層負(fù)責(zé)把 d/q 軸電流快速壓到目標(biāo)值頻率最高。速度環(huán)中間層基于編碼器測速得到的速度值輸出電流指令。位置環(huán)最外層基于目標(biāo)位置和實(shí)際位置的誤差輸出速度指令。ODrive 把這幾個環(huán)放在同一個定時器中斷里跑整個控制周期的頻率就是 8 kHz。也就是說每過 125 微秒CPU 會強(qiáng)制暫停當(dāng)前手頭的事跳到中斷服務(wù)函數(shù)依次完成編碼器讀數(shù)、坐標(biāo)變換、PID 計算、PWM 占空比更新然后再跳回去干之前的事。為什么是 8 kHz這其實(shí)是幾個約束條件折中的結(jié)果STM32F405 的 CPU 主頻是 168 MHz8 kHz 意味著每個控制周期約有 21000 個 CPU 周期可用。電流環(huán)需要在一個周期內(nèi)完成至少一次完整的 Clarke/Park 變換加 PI 運(yùn)算再加上速度環(huán)、位置環(huán)的 PID運(yùn)算量不小??刂祁l率越高電流環(huán)帶寬越高電機(jī)噪音和振動越小但 CPU 壓力也越大。8 kHz 不是隨便拍腦袋定的它是一個在“能帶動多少運(yùn)算量”和“控制性能還需要提升多少”之間的平衡點(diǎn)。后續(xù)你會看到ODrive 里很多參數(shù)都跟這個頻率掛鉤比如電流環(huán)的 PI 增益、濾波器的截止頻率、甚至看門狗的超時判斷。所以先記住這個數(shù)8 kHz 是 ODrive 固件的控制心跳頻率。1.2 時基選型為什么用定時器而不是 SysTickODrive 固件運(yùn)行的平臺是 STM32F405/F407 系列這個系列提供了多個定時器。這里就涉及一個關(guān)鍵選擇為什么控制環(huán)的中斷源用的是高級定時器 TIM1而不是大家更熟悉的 SysTick滴答定時器如果你看過一些 STM32 工程會發(fā)現(xiàn)很多人用 SysTick 做延時或者做簡單的時間片輪詢。SysTick 確實(shí)簡單Cortex-M 內(nèi)核自帶不用翻手冊就能配好。但它有一個先天不足SysTick 的優(yōu)先級是固定的你沒法把它調(diào)成比外設(shè)中斷更高的優(yōu)先級。在電機(jī)控制這種場景里控制環(huán)必須搶占一切不能容忍被串口中斷、USB 中斷打斷。一旦控制中斷被其他中斷延誤電流環(huán)的執(zhí)行時間跳動會直接反映在電機(jī)噪音和電流波形上。TIM1 是高級定時器它不僅可以輸出帶死區(qū)的互補(bǔ) PWM還自帶更新中斷。ODrive 的巧妙之處在于它讓 TIM1 既承擔(dān) PWM 輸出職責(zé)又承擔(dān)控制環(huán)中斷來源一份硬件資源兩重功能這是非常典型的嵌入式思路。軟件上把中斷優(yōu)先級設(shè)為最高保證任何時刻控制中斷來了都能打斷其他任務(wù)。你需要理解的核心思路是控制環(huán)的中斷源不一定非要用定時器但必須滿足兩個條件——可精確配置頻率、優(yōu)先級足夠高。用定時器是 STM32 平臺上一個很自然、很成熟的方案。SysTick 在這種硬實(shí)時場景里不具備優(yōu)勢。1.3 從定時器到控制環(huán)的整體鏈路先給你一張“地圖”把整個過程在邏輯上畫出來。ODrive 固件啟動后進(jìn)入 main 函數(shù)完成一堆初始化最后進(jìn)入一個 while(1) 主循環(huán)。但主循環(huán)里干的全是慢節(jié)奏的事USB 通信、日志輸出、參數(shù)保存、狀態(tài)機(jī)切換這些。真正干控制活的是那段中斷驅(qū)動的代碼。整個鏈路是這樣走的初始化階段配置 TIM1 的時基讓它產(chǎn)生頻率為 8 kHz 的更新事件開啟更新中斷并把 NVIC 里 TIM1_UP_TIM10 的中斷優(yōu)先級調(diào)到最高。啟動階段使能定時器計數(shù)TIM1 開始跑更新中斷開始按 125 微秒一次的頻率觸發(fā)。運(yùn)行階段每次進(jìn)入中斷服務(wù)函數(shù)依次執(zhí)行“讀編碼器/讀電流采樣值 → 電流環(huán) FOC 計算 → 速度環(huán) PID → 位置環(huán) PID → 更新 PWM 比較寄存器”。主循環(huán)階段處理對實(shí)時性不敏感的任務(wù)比如通過 USB 接收上位機(jī)的指令、解析參數(shù)、打印狀態(tài)。這個結(jié)構(gòu)中主循環(huán)像是一個“后臺辦公室”處理各種不急的瑣事定時器中斷像是“生產(chǎn)線上的節(jié)奏器”每 125 微秒發(fā)出一聲“干活了”把控制任務(wù)從上一道工序推到下一道工序。硬件中斷的確定性保證了控制周期的穩(wěn)定這正是電機(jī)控制領(lǐng)域最核心的追求。2. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)2.1 定時器時基的計算與配置要理解 ODrive 的代碼你得先能自己把時基算出來。這一步不難但很容易搞錯。STM32 的時基由三個寄存器控制PSC預(yù)分頻器、ARR自動重載值、以及時鐘源頻率。公式非常簡單定時器頻率 定時器時鐘源頻率 / (PSC 1) / (ARR 1)ODrive 用的 STM32F405TIM1 掛在 APB2 總線上。當(dāng)系統(tǒng)主頻 168 MHz、APB2 預(yù)分頻系數(shù)為 1 時TIM1 的時鐘頻率就是 168 MHz。如果 APB2 分頻為 2則定時器時鐘為 84 MHz此時定時器會額外倍頻到 168 MHz具體要看芯片的時鐘樹。要實(shí)現(xiàn) 8 kHz可以用很多組合比如PSC 0ARR 20999頻率 168 MHz / 1 / 21000 8000 HzPSC 1ARR 10499頻率 168 MHz / 2 / 10500 8000 HzPSC 83ARR 249頻率 168 MHz / 84 / 250 8000 Hz第一種組合 ARR 偏大適合需要較長周期的 PWMODrive 里因?yàn)?TIM1 同時還要輸出 PWMPWM 頻率和微步細(xì)分有關(guān)所以它的 PSC 和 ARR 需要綜合計算。打開固件源碼你會看到timer_freq和pwm_freq這兩個變量被反復(fù)計算就是在調(diào)這組參數(shù)。有一個細(xì)節(jié)值得注意ODrive 支持 PWM 中心對齊模式也就是定時器計數(shù)先往上加、再往下減。這種模式下的更新事件頻率是 PWM 頻率的兩倍一個完整的三角波周期產(chǎn)生兩次溢出中斷如果你不理解這一點(diǎn)后面分析中斷觸發(fā)頻率時會卡住。2.2 中斷服務(wù)函數(shù)的編寫風(fēng)格ODrive 的中斷服務(wù)函數(shù)寫得非常干凈沒有一堆 if-else。它直接在中斷里調(diào)用一個統(tǒng)一的入口函數(shù)然后這個入口函數(shù)內(nèi)部會做一次“當(dāng)前階段判斷”決定這一拍要執(zhí)行什么任務(wù)??创a的時候你會發(fā)現(xiàn)ODrive 的 TIM1 中斷服務(wù)函數(shù)本身非常短真正的大頭在Motor::update()這類函數(shù)里。這種風(fēng)格的好處是中斷入口保持極短減少中斷延遲控制邏輯獨(dú)立成類方便復(fù)用和測試。這里給你一個實(shí)操層面的建議寫電機(jī)控制代碼時不要往中斷里塞浮點(diǎn)打印、塞日志、塞編碼器數(shù)據(jù)的串口發(fā)送。你在調(diào)試時可能會忍不住想“順便發(fā)個數(shù)據(jù)看看波形”但只要串口傳輸時間超過了控制周期控制環(huán)就被你親手毀了。ODrive 的做法是中斷里只操作共享內(nèi)存標(biāo)志通信任務(wù)由主循環(huán)統(tǒng)一處理。2.3 優(yōu)先級與搶占為什么 NVIC 配置必須正確NVIC嵌套向量中斷控制器的優(yōu)先級配置是 ODrive 能穩(wěn)定運(yùn)行的又一關(guān)鍵。Cortex-M4 支持搶占優(yōu)先級和子優(yōu)先級。ODrive 把 TIM1 更新中斷的搶占優(yōu)先級設(shè)為最高這樣任何時刻來了控制中斷其他中斷都得讓路。哪怕你正在處理 USB 傳輸控制中斷來了也會先打斷 USB 服務(wù)函數(shù)跑完控制任務(wù)再回去繼續(xù)處理 USB。我見過不少人把控制中斷的優(yōu)先級和串口中斷設(shè)成相同優(yōu)先級或者只比串口高一點(diǎn)點(diǎn)結(jié)果電機(jī)運(yùn)行的時候偶發(fā)抖動電流波形上出現(xiàn)毛刺。原因很簡單串口中斷本來就頻繁如果控制中斷排隊(duì)等串口處理完那這個周期的控制計算就被延遲了執(zhí)行時間抖動自然體現(xiàn)在電機(jī)噪音里。正確配置分兩步在NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority里設(shè)置最高搶占優(yōu)先級。確保NVIC_IRQChannelSubPriority不會因?yàn)槠渌壷袛鄬?dǎo)致?lián)屨蓟靵y。需要記住的是分配給控制環(huán)中斷的時間資源是定死的一旦被搶走這個控制周期就補(bǔ)不回來。這是硬實(shí)時系統(tǒng)的基本特性。2.4 時基相關(guān)的幾個“坑”很多人把 ARR 配置成 PWM 頻率對應(yīng)的值卻發(fā)現(xiàn)更新中斷頻率不對原因就是上面說的中心對齊模式下更新事件觸發(fā)在頂點(diǎn)和底點(diǎn)。有些定時器配置需要在初始化時額外生成一次更新事件否則第一次中斷不會來。PSC/ARR 修改后需要重新初始化定時器或者使用影子寄存器功能否則會出現(xiàn)一個周期內(nèi) PWM 頻率突變的問題。我對這項(xiàng)工程印象最深的一點(diǎn)是ODrive 里為了精確限制 PWM 的更新頻率專門用了一個叫PWM_UPDATE_MECH的機(jī)制它保證 PWM 在線更新時的相位同步這種細(xì)節(jié)如果不是自己調(diào)過毛刺很難想到。3. 實(shí)操過程與核心環(huán)節(jié)實(shí)現(xiàn)3.1 時基初始化代碼分析打開 ODrive 固件源碼找到init_pwm()函數(shù)核心工作就是配置 TIM1。我在這里用偽代碼加注釋的方式還原一下配置流程方便你對照源碼理解void init_pwm(TIM_TypeDef* TIMx) { // 1. 先把定時器時鐘打開以 APB2 為例 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); // 2. 時基單元初始化設(shè)定 PSC 和 ARR TIM_TimeBaseInitTypeDef tim_init; tim_init.TIM_Prescaler 你算好的 PSC; tim_init.TIM_Period 你算好的 ARR; tim_init.TIM_CounterMode TIM_CounterMode_CenterAligned1; // 中心對齊模式 TIM_TimeBaseInit(TIMx, tim_init); // 3. 如果是中心對齊模式需要單獨(dú)設(shè)置更新中斷的觸發(fā)時機(jī) // 一般在頂點(diǎn)/底點(diǎn)都觸發(fā)實(shí)際頻率為 PWM 頻率的 2 倍 // 4. 使能更新中斷 TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE); // 5. 使能定時器 TIM_Cmd(TIMx, ENABLE); }這一段就能解釋很多事TIM_Period決定了 PWM 的周期也決定了更新中斷的間隔。如果你手頭跑的是 ODrive v3.x 固件默認(rèn)的 PWM 頻率在 20k~40k 之間配合中心對齊模式更新中斷頻率變成 PWM 頻率的兩倍。但是ODrive 內(nèi)部會配置成“只在底點(diǎn)觸發(fā)中斷”這樣就保證中斷頻率仍然是 8 kHz同時 PWM 頻率更高能有效降低電機(jī)噪音。實(shí)測下來這一步配置正確與否區(qū)別非常大配置錯了控制環(huán)頻率變成 PWM 頻率的另一倍電流環(huán)參數(shù)就得重新調(diào)。3.2 PWM 比較寄存器更新定時器時基只是骨架真正的控制輸出體現(xiàn)在 PWM 占空比上。FOC 控制最后一步是生成三路 SVPWM 信號每路信號的占空比對應(yīng)逆變器三相的開關(guān)狀態(tài)。ODrive 的做法是在控制中斷里算出三相占空比數(shù)值然后寫入 TIM1 的三個比較寄存器CCR1/CCR2/CCR3。因?yàn)閱⒂昧祟A(yù)裝載寄存器實(shí)際的比較值更新會發(fā)生在下一個周期開始時這樣避免了一個周期內(nèi) PWM 占空比突變導(dǎo)致的電流畸變。這個“預(yù)裝載”機(jī)制用生活類比來說有點(diǎn)像樂隊(duì)指揮。指揮定時器給每個樂手比較寄存器一個樂譜預(yù)裝載值但樂手們不會在指揮手一揮的瞬間立刻改而是等到這個小節(jié)結(jié)束、下一小節(jié)開始的時候才統(tǒng)一翻頁。這樣就保證了每個人都在同一個節(jié)拍點(diǎn)切換不會有人早半拍有人晚半拍。3.3 執(zhí)行頻率驗(yàn)證方法怎么確認(rèn)系統(tǒng)真的是按 8 kHz 在跑你不會去用示波器測量中斷執(zhí)行時間——測一個中斷執(zhí)行時間實(shí)際上是很難的事情。常見的做法是在中斷服務(wù)函數(shù)入口翻轉(zhuǎn)一個 GPIO。用示波器看這個 GPIO 的方波頻率就能直接讀出控制環(huán)頻率。再用示波器測量中斷執(zhí)行時間也就是 GPIO 高電平的寬度就能看出每個控制周期用了多少 CPU 時間。實(shí)測 ODrive 在 8 kHz 控制頻率下完整跑完一輪電流環(huán)速度環(huán)位置環(huán)大約占用 80~120 微秒。這個數(shù)字和 125 微秒的控制周期一比你會發(fā)現(xiàn)在 CPU 還有余量。那為什么不用 16 kHz因?yàn)殡娏鳝h(huán)的帶寬不光取決于執(zhí)行頻率還受限于 ADC 采樣時間、PWM 死區(qū)時間、編碼器讀取延遲等因素頻率翻倍并不代表性能翻倍。3.4 中斷到控制環(huán)的調(diào)用鏈ODrive 里中斷服務(wù)函數(shù)和你真正想找的 PID 邏輯之間隔了一層“調(diào)度邏輯”。看下簡化后的調(diào)用順序TIM1_UP_TIM10_IRQHandler - Motor::update() - read_encoder() / read_current() - current_controller_.update() - controller_.update() // 包含位置環(huán)、速度環(huán)邏輯 - write_pwm_commands()每一步都有講究。讀編碼器必須在控制周期開始階段做越早越好因?yàn)榫幋a器讀數(shù)越接近這個周期開始時的真實(shí)速度速度環(huán)的采樣延遲越小。讀電流采樣值也類似它直接決定了電流環(huán)的響應(yīng)質(zhì)量??刂骗h(huán)的實(shí)際執(zhí)行順序是內(nèi)環(huán)電流環(huán)先執(zhí)行外環(huán)速度/位置后執(zhí)行。之所以電流環(huán)最優(yōu)先是因?yàn)殡娏鳝h(huán)的反電動勢更快延遲會導(dǎo)致電流相位滯后直接影響力矩輸出的精度。3.5 主循環(huán)與中斷的數(shù)據(jù)交換主循環(huán)和中斷經(jīng)常需要共享數(shù)據(jù)比如用戶通過上位機(jī)改了速度指令這個指令需要通過 USB 中斷傳到主循環(huán)再在主循環(huán)里寫入共享內(nèi)存下一拍控制中斷就會讀到新的目標(biāo)值。這個數(shù)據(jù)交換的實(shí)現(xiàn)處處體現(xiàn)“無鎖編程”的思想。中斷里只是讀取共享變量主循環(huán)只在安全時機(jī)修改共享變量或者用獨(dú)立的緩沖區(qū)完成狀態(tài)同步。你在源碼里不會看到一堆 mutex那是因?yàn)?ODrive 故意把數(shù)據(jù)流設(shè)計成了單生產(chǎn)者單消費(fèi)者的模式。真要在自己的工程里復(fù)刻這套邏輯先別急著加鎖先想想你的數(shù)據(jù)流是不是單向的。單向數(shù)據(jù)流加上內(nèi)存屏障基本就夠了。4. 常見問題與排查技巧實(shí)錄4.1 控制環(huán)頻率不對的排查步驟這是最常遇到的問題。你按照源碼改了參數(shù)覺得應(yīng)該是 8 kHz但測量出來要么翻倍要么減半。第一步確認(rèn) TIM1 的時鐘源頻率。很多人從別的工程拿了初始化代碼芯片主頻不同APB2 分頻不同時基就差出來了。用 debugger 直接讀 RCC_CFGR 寄存器的值這個最靠譜。第二步確認(rèn)計數(shù)器模式。如果你用的是中心對齊更新事件頻率和 PWM 頻率的關(guān)系是兩倍關(guān)系。尤其要注意 ODrive 里使用的是 CenterAligned1 模式更新中斷在計數(shù)頂點(diǎn)和底點(diǎn)都會觸發(fā)必須檢查中斷標(biāo)志位過濾邏輯。第三步看中斷優(yōu)先級有沒有被其他頻繁中斷搶占。如果中斷優(yōu)先級配錯控制中斷的執(zhí)行間隔會出現(xiàn)偶發(fā)變長你在示波器上看到的不再是干凈的方波。我處理過最多的 case 是明明代碼沒問題但控制環(huán)頻率偏了最后發(fā)現(xiàn)是 APB2 分頻設(shè)置被別的初始化函數(shù)改了。建議每次配置定時器之前先打印一下定時器時鐘頻率。4.2 電機(jī)運(yùn)行時抖動、噪音大的原因噪音大的原因往往不是 PID 參數(shù)太激進(jìn)而是底層時基不干凈。如果控制環(huán)執(zhí)行時間抖動在幾個微秒以上電流環(huán)輸出的相位噪聲會直接變成電機(jī)抖動。這里有個實(shí)用的診斷方法把控制環(huán)的執(zhí)行時間測出來把執(zhí)行時間的波形和電流波形放到同一個時間軸上對比。如果電流波形上的毛刺出現(xiàn)的位置和執(zhí)行時間變長的位置重合那問題就在中斷優(yōu)先級或者中斷阻塞上。還有一個隱蔽的原因電流采樣值是在 PWM 計數(shù)器頂點(diǎn)觸發(fā)的 ADC 采樣的如果你把采樣點(diǎn)配置錯了采樣到的電流值會帶有巨大的開關(guān)噪聲進(jìn)而導(dǎo)致電流環(huán)計算出來的占空比噪聲放大。4.3 定時器更新中斷偶爾丟失的問題定時器更新中斷不會“丟”但確實(shí)會發(fā)生“被同優(yōu)先級中斷屏蔽”的情況。Cortex-M 的中斷系統(tǒng)里如果正在處理一個優(yōu)先級相同的中斷新到的中斷必須等待。而 TIM1_UP_TIM10 這個中斷向量在 ODrive 里同時也對應(yīng)用途不那么嚴(yán)格的 TIM10。如果你在代碼里開了 TIM10而又不小心把優(yōu)先級設(shè)成和 TIM1 一樣那 TIM10 的中斷就會跟控制環(huán)搶執(zhí)行時間。這種問題的排查很痛苦因?yàn)楝F(xiàn)象像是“偶爾丟中斷”。唯一的辦法是全工程搜索所有定時器中斷把控制環(huán)中斷優(yōu)先級設(shè)為絕對最高其他所有中斷都要讓路。4.4 看門狗和暫停操作帶來的問題ODrive 支持通過上位機(jī)暫停電機(jī)暫停操作本身會直接關(guān)閉 PWM 輸出但定時器還在跑。如果你在暫停時還需要保持控制環(huán)的編碼器讀數(shù)更新那定時器不能停。ODrive 的處理是暫停狀態(tài)只置標(biāo)志位控制環(huán)還在繼續(xù)跑只是輸出占空比被置為安全值。如果你自己寫代碼時用了“暫停就關(guān)定時器”的做法那再啟動時要重新做同步很容易漏掉 PWM 的初始相位同步電機(jī)啟動時會有彈跳。我的建議是暫停就封 PWM 輸出讓控制環(huán)繼續(xù)空轉(zhuǎn)。4.5 控制環(huán)頻率參數(shù)怎么調(diào)整才安全如果你想試 16 kHz 控制環(huán)不要只改定時器。你要同時確認(rèn)三件事ADC 采樣時間是否足夠短。電機(jī)驅(qū)動芯片的硬件過流保護(hù)響應(yīng)時間是否跟得上。電流環(huán) PI 參數(shù)是否需要重新整定。頻率翻倍后執(zhí)行時間也翻倍你必須先量測新的執(zhí)行時間是否還在控制周期內(nèi)。如果執(zhí)行時間接近甚至超過控制周期那不用測也知道結(jié)果——系統(tǒng)完全不工作。我實(shí)測過ODrive 默認(rèn)狀態(tài)下的執(zhí)行時間大約在 80 微秒左右升到 16 kHz 時執(zhí)行時間會略長但依然有裕量。問題是 PID 參數(shù)需要重調(diào)不然同樣的帶寬目標(biāo)下離散化誤差變了增益也需要調(diào)整。5. 斷電重啟后時基配置的校驗(yàn)與調(diào)試5.1 上電初始化順序的講究ODrive 的初始化順序是有講究的先初始化時鐘樹再初始化 GPIO再初始化定時器最后配置中斷和全局變量。如果你把定時器初始化放在編碼器接口初始化之前有的編碼器接口配置會順手把定時器的重映射改掉導(dǎo)致時基計算對不上。調(diào)試的時候我建議你在定時器初始化函數(shù)里加一個斷點(diǎn)打印 ARR 和 PSC 的最終值先確認(rèn)這兩個數(shù)和你預(yù)期一致。然后再往下走等跑到控制中斷里再打印讀到的編碼器值確認(rèn)真實(shí)物理量進(jìn)入系統(tǒng)。5.2 一個快速驗(yàn)證時基是否穩(wěn)定的技巧想讓控制環(huán)穩(wěn)定運(yùn)行你可以不用 PID 輸出先做開環(huán)測試。給一個固定占空比跑起來看電機(jī)是不是勻速轉(zhuǎn)動。如果電機(jī)轉(zhuǎn)動時有明顯的卡頓感說明控制執(zhí)行頻率不穩(wěn)定或者 PWM 更新時機(jī)不對。這個測試簡單但非常有效它能幫你把“時基問題”和“控制算法問題”分離。時基不穩(wěn)后面調(diào)什么都白搭先解決基礎(chǔ)問題再往上層走。5.3 關(guān)于調(diào)試接口是否影響時基的說明很多人開著調(diào)試器跑電機(jī)的時候發(fā)現(xiàn)電機(jī)聲音跟拔掉調(diào)試器不一樣。這不是錯覺。調(diào)試器的實(shí)時跟蹤機(jī)制會影響 CPU 執(zhí)行效率如果開啟了DEBUG_MODE里的 trace 或者頻繁斷點(diǎn)控制環(huán)的時間微調(diào)會受影響。我的建議是關(guān)閉 J-Link 的實(shí)時數(shù)據(jù)請求盡量不要在中斷里打斷點(diǎn)。每次打斷點(diǎn)中斷服務(wù)函數(shù)的時間特征都不一樣。你可以把數(shù)據(jù)寫到內(nèi)存里等停了再讀效果遠(yuǎn)好于打斷點(diǎn)。6. 寫在最后的個人實(shí)操心得說實(shí)話我自己第一次讀 ODrive 源碼的時候最大的感受不是它的 FOC 算法多精妙而是它對“時間”這件事的認(rèn)真程度。每一段代碼都在小心翼翼地維護(hù)一個確定性的時間節(jié)奏。這種節(jié)奏一旦被打破什么高級算法都救不回來。從定時器時基到 8 kHz 控制環(huán)本質(zhì)上就是在回答一個問題你憑什么保證每隔 125 微秒做一次一樣的動作答案是用硬件定時器用嚴(yán)格的優(yōu)先級配置用不阻塞的中斷服務(wù)函數(shù)用預(yù)裝載寄存器來同步輸出。如果你打算在自己的工程里復(fù)刻這套機(jī)制先別急著抄代碼先把下面這幾個問題想明白你的控制頻率是多少為什么是它你的時基用的是哪個定時器它和 PWM 是什么關(guān)系你的中斷服務(wù)函數(shù)里有多少事在做是不是有不必要的操作你的中斷優(yōu)先級配對了沒有還有沒有其他中斷在執(zhí)行時阻塞了控制環(huán)把這些問題的答案寫下來再做代碼移植你會發(fā)現(xiàn) ODrive 源碼瞬間“透明”了。最后再分享一個小技巧調(diào)試時把控制環(huán)執(zhí)行時間、控制頻率、PWM 占空比更新延遲這幾個參數(shù)做成變量塞進(jìn) ODrive 自己的日志系統(tǒng)里用上位機(jī)實(shí)時拉曲線。我靠這個方法排查過好幾個詭異的問題——某個變量看起來是平均值正常但瞬時值已經(jīng)跑飛了。這種級別的排查光靠看代碼是看不出來的。下一篇文章我會繼續(xù)拆解電流環(huán)的代碼重點(diǎn)講 Clarke/Park 變換在固件里怎么寫的、電流 PI 調(diào)節(jié)器的抗飽和機(jī)制以及 SVPWM 那段代碼背后的數(shù)學(xué)原理。到時見。