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