资讯动态

28335 DSP电机FOC控制:从电流采样到SVPWM的完整实现与调试

发布时间:2026/10/16 11:22:37 来源:尧图企业网站定制
简介基于TI C2000系列28335 DSP的电机FOC与SVPWM控制完整工程包面向电机控制研发人员、嵌入式工程师及自动化专业学生帮助理解磁场定向控制从三相电流采样、坐标变换、电流环PI调节到SVPWM电压输出的全流程实现与工程落地。包体共135个文件包体大小1.25MB以C语言源文件、头文件为主配合库文件、汇编启动中断代码、链接命令文件以及GEL初始化文件可直接导入CCS工程环境中完成编译、烧录与调试。资源内容覆盖三相电流采样处理、克拉克与帕克变换、双电流环PI控制、SVPWM波形生成等核心环节同时附带DSP2833x底层驱动、CMD内存分配和完整工程配置便于对照源码逐模块学习也可基于现有框架进行二次开发。整套资料打包为RAR压缩格式已有2211人学习下载适合需要快速掌握28335平台FOC实现细节或搭建电机控制原型的开发者。1. 28335 跑电机 FOC 控制这套组合为什么还是工程主流用 28335 做电机 FOC 控制是很多做伺服和无刷驱动的人绕不开的组合。28335 这颗 150MHz 带浮点单元的 DSP片上集成了 12 位 ADC、三对互补 PWM 和正交编码接口刚好把电流环、速度环、位置环需要的采样、坐标变换、调制输出全包了。如果你手头有一块基于 28335 的电机控制板准备跑永磁同步电机的 FOC却不知道代码从哪下手、参数怎么定、坑在哪这份资源能帮你把整套链路走通从三相电流采样标定到 Clarke/Park 变换、PI 调节器、七段式 SVPWM再到开环对齐和闭环调试顺序。适合刚接手电机项目的工程师和研究生也适合熟手对照着查参数边界。2. FOC 控制链拆解从电流采样到 SVPWM 的六步闭环2.1 FOC 到底在算哪六个量FOC 的核心思想是把三相交流电流变换到与转子同步旋转的 dq 坐标系里让原本正弦变化的量变成直流分量这样用两个 PI 就能做到无静差控制。完整链路按顺序是三相电流采样 → Clarke 变换abc 到 αβ→ Park 变换αβ 到 dq→ 电流 PIid/iq 各自闭环→ 逆 Park 变换dq 到 αβ→ SVPWM 输出占空比再加上编码器角度作为 Park 变换的实时输入这就构成了电流环。dq 轴的两个电流物理含义要分清id 是励磁分量对表贴式永磁同步电机默认控在 0iq 是转矩分量速度环的输出就是给它的指令。所以整套系统的层级是速度环在最外层输出 iq_ref电流环在最内层跑 10~20kHz 的中断位置环通常在速度环外层用的周期更慢。2.2 28335 的外设分工ADC、ePWM、QEP 各管一段28335 做 FOC 不需要外挂太多东西三组外设就能把整个闭环撑起来。下表是我在板子上分配任务的参考外设在 FOC 里的角色关键配置点12 位 ADC16 通道双采样保持三相电流、母线电压采样用 PWM 下溢沿触发 SOC采样窗口避开开关噪声ePWM6 路输出可配成 3 对互补SVPWM 波形生成周期即载波周期互补模式加死区CMPA 用影子装载QEP 正交编码接口转子位置与速度反馈4 倍频解码index 信号用于零位对齐PWM 频率我一般取 10~20kHz对应电流环中断周期 50~100us。28335 有 FPU跑一套电流环加 SVPWM 的计算量在 150MHz 主频下只占很小比例剩余时间可以留给通信和状态机。ADC 的触发时机是这里最容易做错的地方必须在 PWM 计数到 0下溢时触发确保采到的是逆变桥中间电平阶段的电流而不是开关边沿附近的噪声。2.3 电流采样与标幺化把 ADC 原始码变成 dq 轴的安培值电流采样回路一般是采样电阻加运放把双极性电流信号抬升到 0~3V 的 ADC 输入范围静态工作点大概在 1.5V 附近。运放输出必然带一个偏置电压这个偏置不扣掉dq 轴反馈里就会混入直流分量电机还没转就在发热。所以上电第一件事是零漂标定。// 上电零漂标定必须在逆变桥无 PWM 输出、电机静止时调用 void CurrentOffsetCalibrate(uint16_t *adcBuf, float *offset, uint16_t count) { uint32_t sum[3] {0, 0, 0}; for (uint16_t i 0; i count; i) { sum[0] adcBuf[0]; sum[1] adcBuf[1]; sum[2] adcBuf[2]; } offset[0] (float)sum[0] / count; offset[1] (float)sum[1] / count; offset[2] (float)sum[2] / count; }这段代码的逻辑很简单无 PWM 输出时三相电流都是零这时候采集 N 次取平均得到的值就是运放偏置加 ADC 本身的零漂。count 取 32 或 64 次就够取多了意义不大。标定完成后实际电流的工程值是(ADC_raw - offset) * (3.0f / 4096.0f) / (R_shunt * Gain)R_shunt 是采样电阻阻值Gain 是运放放大倍数。为了进 PI 之前量纲统一再除以电机额定电流峰值做标幺化这样 Kp/Ki 的数值不受电机规格影响太大换电机时初值更好猜。提示三相电流之和恒为零可以只采两相第三相用 ic -(ia ib) 算出来省一路 ADC。但代价是死区时段这个关系会有偏差动态性能略差要求高的场合还是三路都采。零漂标定做完把三个 offset 存进 Flash 参数区。每次上电先加载等母线电容充满、温度稳定后再决定要不要重标定。运放偏置随温度漂移是真实存在的我在实际项目里见过同一个板子冷机和热机零漂相差 20 多个 LSB换算成电流就是几十毫安。3. 能落地的 FOC 代码骨架Clarke、Park 与 PI 的 C 语言实现3.1 数据结构与参数初始化FOC 代码最好一开始就把数据结构拆清楚后面调试才不糊。我的习惯是把 PI 调节器单独定义成一个结构体电流环再套一层这样 id 和 iq 两个轴的参数可以独立初始化、独立限幅。typedef struct { float Kp; float Ki; float integral; // 积分累加值 float out_max; // 输出上限 float out_min; // 输出下限 } PI_TypeDef; typedef struct { float id_ref; float iq_ref; float id_fbk; float iq_fbk; PI_TypeDef pi_id; PI_TypeDef pi_iq; float vd; float vq; } CurrentLoop_TypeDef;初始化时把 integral 清零把输出限幅设成 SVPWM 线性区对应的最大值。对电流环来说vq 的输出限幅直接决定电压利用率一般先设成 0.9标幺左右避免进过调制区。3.2 Clarke、Park 与逆 Park三个变换函数坐标变换是 FOC 的数学骨架。28335 有硬件 FPUsinf/cosf 的浮点计算开销在这个主频下完全可以接受代码先求可读性不用过早优化成查表。// Clarke三相电流到静止两相坐标ic 由 iaibic0 省略 void Clarke_Transform(float ia, float ib, float *ialpha, float *ibeta) { *ialpha ia; *ibeta (ia 2.0f * ib) * 0.577350269f; // 1/sqrt(3) } // Park静止两相到旋转 dq 坐标theta 是电角度 void Park_Transform(float ialpha, float ibeta, float theta, float *id, float *iq) { float sint sinf(theta); float cost cosf(theta); *id ialpha * cost ibeta * sint; *iq -ialpha * sint ibeta * cost; } // 逆 Parkdq 电压回到静止两相供 SVPWM 使用 void InvPark_Transform(float vd, float vq, float theta, float *valpha, float *vbeta) { float sint sinf(theta); float cost cosf(theta); *valpha vd * cost - vq * sint; *vbeta vd * sint vq * cost; }三个函数放一起说Clarke 的 0.577 就是 1/√3由等幅值变换的系数推出来的Park 和逆 Park 的符号约定在不同工程里有差异有的把 q 轴定义成超前于 d 轴有的定义成滞后。我踩过的坑是代码按某一本书抄通电后发现 iq 反馈极性反了力矩方向不对。解决方法是先不纠结符号以「空载时给定正的 iq_ref电机输出正向力矩」为准反了就把 Park 里 iq 那行的符号调过来。3.3 PI 调节器与抗积分饱和电流环 PI 是 FOC 的心脏。工程上和教科书上的 PI 有个区别必须做抗积分饱和否则限幅时积分还在累加退出饱和要等很久表现为电流阶跃响应的「尾巴」特别长。// PI 更新err 为给定减反馈dt 为中断周期 float PI_Update(PI_TypeDef *pi, float err, float dt) { pi-integral pi-Ki * err * dt; // 积分限幅先夹住积分量避免 windup if (pi-integral pi-out_max) pi-integral pi-out_max; if (pi-integral pi-out_min) pi-integral pi-out_min; float out pi-Kp * err pi-integral; // 输出限幅 if (out pi-out_max) out pi-out_max; if (out pi-out_min) out pi-out_min; return out; }代码里积分限幅和输出限幅都做了。区别在于只做输出限幅不做积分限幅积分项会一直涨到很高等误差反转时要好几个周期才能消化两个都做退出饱和就快。Ki 的量纲是「每秒的积分增量」所以乘了 dt。dt 就是 PWM 周期10kHz 时是 0.0001s。有个偷懒的写法是把 Ki 预先乘好 dt中断里少一次乘法但换来的是换 PWM 频率时参数含义不清我不推荐。3.4 七段式 SVPWM扇区判断与比较值映射SVPWM 的输出是把 vd/vq 变回三相占空比。七段式是工程上最常见的实现一个 PWM 周期内开关序列对称分布谐波特性好。// 七段式 SVPWM输入 αβ 轴电压标幺值线性区最大 0.866输出三路 CMPA 值 void SVPWM_Calc(float valpha, float vbeta, uint16_t period, uint16_t *cmpA, uint16_t *cmpB, uint16_t *cmpC) { float u1 vbeta; float u2 0.8660254f * valpha - 0.5f * vbeta; float u3 -0.8660254f * valpha - 0.5f * vbeta; // 扇区判断u1/u2/u3 的正负组合对应六个扇区 uint16_t sector 0; if (u1 0.0f) sector | 1; if (u2 0.0f) sector | 2; if (u3 0.0f) sector | 4; float t1, t2; switch (sector) { case 1: t1 u3; t2 u2; break; case 2: t1 u2; t2 -u1; break; case 3: t1 -u3; t2 u1; break; case 4: t1 -u1; t2 u3; break; case 5: t1 u1; t2 -u2; break; case 6: t1 -u2; t2 -u3; break; default: t1 0.0f; t2 0.0f; break; } // 过调制保护线性区 T1T2 最大 0.866超过就等比压缩 float t_active t1 t2; if (t_active 0.8660254f) { float k 0.8660254f / t_active; t1 * k; t2 * k; } // 归一化到 PWM 周期T0 为零矢量时间按七段式对称分配 float t0 1.0f - (t1 t2); float ta t0 * 0.25f; float tb ta t1 * 0.5f; float tc tb t2 * 0.5f; // 按扇区把三个时间点映到三相比较值映射表与 PWM 极性强相关 switch (sector) { case 1: *cmpA (uint16_t)(tb * period); *cmpB (uint16_t)(ta * period); *cmpC (uint16_t)(tc * period); break; case 2: *cmpA (uint16_t)(tc * period); *cmpB (uint16_t)(ta * period); *cmpC (uint16_t)(tb * period); break; case 3: *cmpA (uint16_t)(tc * period); *cmpB (uint16_t)(tb * period); *cmpC (uint16_t)(ta * period); break; case 4: *cmpA (uint16_t)(ta * period); *cmpB (uint16_t)(tb * period); *cmpC (uint16_t)(tc * period); break; case 5: *cmpA (uint16_t)(ta * period); *cmpB (uint16_t)(tc * period); *cmpC (uint16_t)(tb * period); break; case 6: *cmpA (uint16_t)(tb * period); *cmpB (uint16_t)(tc * period); *cmpC (uint16_t)(ta * period); break; default: *cmpA 0; *cmpB 0; *cmpC 0; break; } }这段代码分四步先算三个线电压组合量 u1/u2/u3它们的正负组合直接给出扇区号然后查表得到该扇区相邻两个基本矢量的作用时间 t1/t2接着做过调制压缩保证在线性区以内最后把时间点映射到三相比较值。ta/tb/tc 对应的是七段式开关序列里三个通道各自的动作时刻映射表决定了哪一相先开通。这张表在网上有不同版本有的极性定义反了整张表要平移所以换板子时必须用示波器验证相序别直接抄。注意SVPWM 输出到 ePWM 的 CMPA 后还要确认互补通道的死区极性。死区极性反了会出现上下管直通的隐患这是上电即炸管的典型原因之一。4. 把电机转起来从开环对角度到电流环闭环的调试顺序4.1 开环强制换相先验证功率级与编码器方向第一次上电别直接闭环先把功率级和反馈通路验证清楚。做法是开环电角度 theta 按固定步长递增vq 给一个固定的较小值vd 给 0电机理论上会以一个粗略的速度转起来。// 开环换相中断里 theta 每次累加固定步长vq 恒定 __interrupt void OpenLoopISR(void) { static float theta 0.0f; theta speed_step; if (theta 2.0f * PI) theta - 2.0f * PI; float valpha, vbeta; InvPark_Transform(0.0f, open_loop_vq, theta, valpha, vbeta); SVPWM_Calc(valpha, vbeta, period, cmpA, cmpB, cmpC); EPwm1Regs.CMPA cmpA; EPwm2Regs.CMPA cmpB; EPwm3Regs.CMPA cmpC; }speed_step 就是每中断周期电角度的增量10kHz 中断下想转 10 转/秒对 4 对极电机来说电频率 40Hzspeed_step 2π×40/10000 ≈ 0.025。vq 先给额定电压的 5%电机能转就行别给大。这一步验证三件事功率级开关动作是否正常、三相相序是否接对、编码器计数方向是否和电角度方向一致。如果电机反转优先检查编码器方向配置而不是改相序。提示开环阶段电流没有闭环保护vq 给大了母线电流会失控串口或面板上要留一个急停或者直接在代码里限幅。4.2 电流闭环PI 参数初值与带宽预估电流环 PI 参数在很多人手里成了玄学其实初值可以算。工程上的近似公式是 Kp ≈ Ls × 2π×带宽Ki ≈ Rs / Ls × Kp其中 Ls 是相电感Rs 是相电阻。电流环带宽取 PWM 频率的 1/10 到 1/2010kHz 载波时取 500Hz~1kHz。电机参数数值目标带宽 600Hz 时初值相电阻 Rs1.1ΩKp ≈ 3.2mH × 2π×600 ≈ 12相电感 Ls3.2mHKi 先取 0静态误差出现再加额定电流峰值8.5A输出限幅 0.9标幺调试顺序我习惯这样走先只上 PKp 从计算值的一半开始加看阶跃响应有没有振荡稳定后再加 Ki从小往大调观察静态误差和超调。别一上来 P 和 I 同时调出问题都不知道怪谁。电流环的阶跃测试不要用手拧电机轴直接改 iq_ref用 DA 输出看 iq_fbk 的跟随情况。4.3 速度环级联把电流环当作内环来用速度环的参数设计建立在电流环已经稳定的前提下。速度环带宽取电流环的 1/5 到 1/10也就是 60~120Hz 左右速度环周期我一般用 1ms比电流环慢一个数量级。速度环的输出是 iq_ref必须限幅否则力矩指令不受控。速度反馈用 QEP 测速M 法在高速时精度高低速时脉冲太少误差大T 法在低速时好但高速时更新慢。工程上常用 M/T 法或者低速切 T 法。速度环的 Kp 初值没有电流环那么好算我一般从 0.01 开始往上试看到速度阶跃有回摆就降下来。速度环积分限幅直接对应力矩限幅这个值就是 iq_ref 的上限别设成和输出限幅一样大否则过流保护形同虚设。5. FOC 调试翻车盘点零漂、死区、编码器对齐与中断抖动5.1 电流反馈带直流偏置ADC 零漂没标定现象电机不转时 DA 输出看 id_fbk、iq_fbk 不是零而是几十毫安的直流偏置电机转起来后偏置又随温度缓慢漂移电机空载就发热。原因运放偏置电压没扣除或者标定时机不对。零漂标定必须在无 PWM、电机静止时做如果在运行状态下标定采到的平均值里混着真实电流偏置反而被污染。解决上电后等母线电容充满确认 PWM 输出被禁止再执行 2.3 节的标定函数把三个 offset 存参数区。换板子、换运放、换温度环境都要重标。如果硬件上没预留这种标定条件软件里可以加一个低通滤波估算静态偏置但这是补救方案不如把采样电路做好。5.2 相电流过零附近削顶死区效应现象低速轻载时相电流波形在过零点附近出现台阶甚至削顶电机声音发闷电流环在这段区域抖动iq_fbk 出现周期性毛刺。原因互补 PWM 的死区时间内续流路径由电流方向决定小电流时死区时间占比高逆变器输出电压误差大。这是 FOC 的低速老大难和算法本身无关。解决先把死区时间从 2us 降到 1us前提是功率管的开关参数允许观察波形有没有改善。再用电流方向补偿根据三相电流符号在 SVPWM 占空比上做 ±Δ 修正Δ 一般是死区时间对应的占空比变化量。死区补偿代码不复杂但电流过零点的方向判断要带回滞不然会在零点附近来回抖。5.3 带载力矩波动加啸叫编码器零位没对齐现象轻载正常负载一加上去力矩脉动明显伴随周期性噪音iq_fbk 里能看到和极对数相关的纹波。原因电角度零位和编码器机械零位没对齐偏差超过十几度dq 轴解耦就失效了id/iq 互相串扰力矩自然波动。这是 FOC 里最典型的「差一点差很多」的问题。解决用标准对齐法重做给 vd 一个固定电压让电机转子吸到 d 轴方向这个位置就是电角度零点然后把此刻的编码器计数值记为 zero_offset。之后实际电角度 编码器读数 - zero_offset。这个值要存 Flash掉电不丢。对齐时电流要限住给 30% 额定电流以内的 vd吸合过程大概一两秒。5.4 电流环带宽提不上去ADC 触发相位与中断抖动现象Kp 加到某值就啸叫振荡但阶跃响应还是很慢怎么调都感觉「肉」。用示波器看 ISR 的 GPIO 翻转信号周期忽长忽短。原因ADC 采样没绑到 PWM 下溢沿采到了开关动作边沿附近的噪声或者电流环中断里塞了通信、状态机等耗时操作中断响应时间本身在抖。电流环对这种抖动非常敏感一抖就是相位裕度下降。解决把 ADC 排序器的 SOC 触发源设成 PWM 下溢事件保证每次都在 PWM 周期同一位置采样电流环 ISR 里只放变换和 PI把所有日志、通信、显示代码挪到主循环或低速定时中断。用 GPIO 翻转加示波器量一下 ISR 周期抖动抖动在 1us 以内才算合格否则先解决中断问题再调参数。6. 用 DA 输出扒开电流环扫频注入与相移判断电流环调完不能只看「转得顺不顺」要验证闭环带宽和相位裕度不然高速场合随时可能啸叫。28335 没有片上 DAC我一般用 SPI 外挂一片 12 位 DAC把 iq_fbk 实时送出去没有外挂的话用一路 PWM 加 RC 滤波也可以截止频率取 10kHz 以上别把有用信号滤没了。验证方法是扫频注入在 iq_ref 指令上叠加一个已知频率的正弦扰动幅值取额定电流的 5% 左右频率按对数步进从 100Hz 扫到 2kHz。示波器 CH1 接指令源CH2 接 DA 输出的 iq_fbk直接读幅值比和相位差。// 扫频注入iq_ref 基值 正弦扰动f_sweep 主循环按对数步进更新 float wobble sweep_amp * sinf(2.0f * PI * f_sweep * runtime); iq_ref_set iq_base wobble;判定标准幅值衰减到 -3dB 的频率点相位滞后大约 45°两者基本重合说明电流环带宽和设计值一致工程上就算合格。如果扫到某个频点幅值不衰减反而放大超过 1dB说明 PI 相位裕度不足这时候降 Kp 或者适当加阻尼比继续加 Ki 有用得多。这里有一个我至今记得的血泪教训某块板子扫频时 800Hz 处电流反馈比指令还大折腾了两天才查到是 ADC 排序器配置错位采到的通道根本不是电流通道而是一路悬空的引脚。从那以后我每次换板子都强制走一遍固定流程开环换相验证功率级和编码器方向、零漂标定、电流阶跃看响应、扫频验证带宽四步走完才敢往上叠速度环和位置环。这套流程帮我挡掉了不少莫名其妙的问题希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑