☰
有感FOC三环控制实战:电流环、速度环与位置环调参
2026/11/5 4:14:33 网站建设 项目流程

简介:面向电机控制与嵌入式开发学习者的有感FOC三环控制实现工程,聚焦在STM32F4平台上完成坐标变换、PID调节、SVPWM输出、编码器位置反馈与电流采样等关键环节,适合具备一定C语言和单片机基础、希望打通有感FOC从算法到落地流程的读者参考。压缩包共194个文件,约5.05MB,以57个.h头文件和46个.c源文件为主体,另有.d、.o、.crf、.axf、.hex、.map等编译中间文件与可执行产物,以及Keil工程文件、启动文件、库文件和少量说明文本,能够较完整地保留工程目录与构建痕迹,便于对照源码理解模块划分和外设驱动组织。目前已有979人学习下载。资源价值在于提供一套亲手编写的三环控制代码,覆盖从电流环、速度环到位置环的调节链路,读者可据此梳理FOC算法结构、核对STM32F4外设配置与工程编译设置,并借助源码中的坐标变换、SVPWM和采样实现加深对电机控制细节的理解。

1. 有感 FOC 算法实现三环:从“电机能转”到“电机动准”

手上那台带增量式编码器的无刷电机第一次上电就啸叫,八成不是电机坏了,是三环还没调通。有感 FOC 算法实现三环,做的事情就是拿编码器做转子位置反馈,把电流环、速度环、位置环由内到外嵌套起来,让一台永磁同步电机从“能转”,做到“能稳住转速”,再做到“能死死停在一个位置上”。整套东西用 C 写在裸机中断里,PWM 与 ADC 联动,讲究的是采样时序、定点数缩放和参数整定,跟跑模型那套不是一回事。

它适合两类人:手上已经有一块带高级定时器、ADC 和编码器接口的 MCU 板子,想把电机真正转起来的新手;以及电机已经能转,但速度环增益一加就抖、位置环一动就超调,想回头把结构和参数理干净的熟手。下面按“架构 → 电流环 → 速度环与位置环 → 踩坑 → 二次开发”的顺序拆。

2. 三环的嵌套结构:16kHz 电流环怎么带 500Hz 位置环

三环最容易翻车的地方不在算法本身,在频率关系。电流环挂在 PWM 中断里跑,一般是 8k 到 20k;速度环降一个数量级,1k 左右;位置环再降,500Hz 到 1kHz 都有人用。带宽也要跟着降:电流环做 1 到 1.5kHz,速度环 100 到 150Hz,位置环 20 到 50Hz。经验规则是外环带宽不超过内环的五分之一到十分之一,这不是教科书上的礼节,是调参时唯一能救你的约束——外环带宽一旦压过内环,内环的相位滞后就会把外环变成一个正反馈回路,表现就是“增益一加就啸叫”。

2.1 三个环的频率、带宽与输出对象

把每个环的输入输出理清楚,代码就不会写乱。电流环的参考是 iq_ref 和 id_ref(表贴式电机 id 固定给 0),反馈是采样得到的三相电流变换结果,输出是 ud、uq,最终变成三路占空比;速度环的参考是转速,反馈是编码器差分算出来的 rpm,输出直接就是 iq_ref;位置环的参考是目标机械角,反馈是编码器累计位置,输出是速度环的给定。三层之间只通过这一个标量传递,别在环之间塞共享全局变量,不然调参时你永远不知道是哪一层在捣鬼。

环执行位置频率目标带宽输入输出
电流环PWM 中断16kHz1~1.5kHzia/ib/ic、电角度ud、uq → 占空比
速度环1ms 定时器1kHz100~150Hz编码器差分iq_ref
位置环1ms 定时器分频500Hz20~50Hz编码器累计位置速度给定

提示:三环共用同一个 tick 是常见的省事写法,但外环一旦算得慢(比如位置环里塞了浮点开方),抖动会顺着参考值传到电流环,波形上看起来像电流环自己不稳,实际是被外层带崩的。

2.2 目录结构与模块职责

工程按“应用层 / 算法层 / 驱动层 / 配置层”四层切,算法层不碰任何寄存器,驱动层不出现任何控制逻辑。这么切的好处是换 MCU 时只动 drv 目录,换电机时只动 cfg 目录。

foc_three_loop/ ├── app/ │ ├── main.c 上电流程、状态机、标定入口 │ ├── loop_scheduler.c 三环调度与中断服务 │ └── trace.c 串口/DA 波形输出(非阻塞环形缓冲) ├── foc/ │ ├── foc_core.c foc_ctx_t 上下文与主流程 │ ├── clarke_park.c 正反变换、sin/cos 查表 │ ├── svpwm.c 占空比计算与钳位 │ ├── pi_ctrl.c PI/PID 通用结构 + 抗饱和 │ ├── ramp.c 梯形轨迹规划 │ └── filter.c 一阶低通、滑动平均 ├── drv/ │ ├── drv_pwm.c 中心对齐 PWM 与 ADC 触发 │ ├── drv_adc.c 注入通道采样与偏置标定 │ ├── drv_encoder.c 定时器编码器模式、原始计数读取 │ └── drv_gate.c 使能、故障引脚、过流保护 ├── cfg/ │ └── motor_param.h 电阻、电感、极对数、磁链、限幅 └── math/ └── trig_table.c 512 点正弦表,Q15 存储

2.3 一个 PWM 周期里发生的事

顺序很关键:ADC 必须在三相下桥臂都导通的窗口里触发,通常挂在中心对齐 PWM 的计数器零点或峰值;转换完成中断进来读电流、减偏置、做 Clarke 与 Park;注意 Park 用的角度不能是本周期采样的角度,而要补偿掉“采样到占空比真正生效”之间的延迟,一般补 1.5 个 PWM 周期。

/* loop_scheduler.c * 电流环在 PWM 中断里跑满 16kHz; * 速度环与位置环挂在 1ms 的定时器里,靠分频得到 500Hz。 */ void PWM_IRQHandler(void) { drv_adc_start_injected(); /* 与中心对齐 PWM 同步触发,此时下桥臂导通 */ foc_current_loop(&g_foc); /* 采样 -> Clarke -> Park -> PI -> 前馈 -> 反 Park -> SVPWM */ drv_pwm_set_duty(&g_foc.duty); /* 三路比较值一次写入影子寄存器 */ drv_enc_latch(); /* 锁存编码器计数,测速用,避免读到半更新的值 */ } void TIM_1ms_IRQHandler(void) { g_tick++; if ((g_tick & 0x01) == 0) { /* 500Hz 位置环 */ pos_loop(&g_pos, g_pos.ref, enc_get_position()); } spd_loop(&g_spd, g_spd.ref, enc_get_speed_rpm()); /* 1kHz 速度环 */ /* 速度环输出即 iq 参考,id 在表贴式电机上固定为 0 */ g_foc.iq_ref = g_spd.out; }

g_foc.iq_ref是速度环唯一向外层暴露的接口,限幅在速度环出口就做掉,别等电流环再做一次——两层都限幅会让积分器不知道该信谁。drv_enc_latch那一句看着多余,实际上编码器计数器是硬件自增的,16kHz 中断和 1kHz 中断同时读同一个寄存器可能读到跨零点的半截值,锁存一份最省心。位置环做 2 分频不只是为了省 CPU,主要是把位置环带宽压下去,给速度环留出相位裕度。

3. 电流环落地:Clarke/Park 变换与 SVPWM 的 C 实现

电流环是整个系统里唯一跑在中断里的硬实时部分,写法上要抠两件事:中断里绝不跑三角函数,绝不动态分配。角度相关的 sin/cos 全部走查表加线性插值,表长 512 点就够用,误差在千分之一量级,对电流环的带宽没什么影响。

3.1 采样窗口与 ADC 偏置标定

偏置标定必须在上电、桥臂还没使能的时候做。有些板子为了省事在运行中做偏置跟踪,低速小电流时看着没问题,一旦大电流跑起来,跟踪算法会把真实电流当成偏置吃掉,输出力矩凭空少一截。

/* drv_adc.c * 上电标定:桥臂全关,读 256 次取平均作为零点偏置。 * 偏置不准的直接后果是 iq 里带一个直流分量,表现为低速时电机轻微发热。 */ void adc_offset_calib(void) { int32_t sum_a = 0, sum_b = 0; drv_pwm_disable(); /* 必须先关桥,否则测到的是母线上的电流 */ delay_ms(10); /* 等运放输出稳定 */ for (int i = 0; i < 256; i++) { sum_a += drv_adc_raw(ADC_CH_IA); sum_b += drv_adc_raw(ADC_CH_IB); delay_us(50); } g_foc.ia_offset = (float)sum_a * (1.0f / 256.0f); g_foc.ib_offset = (float)sum_b * (1.0f / 256.0f); g_foc.ic_offset = -(g_foc.ia_offset + g_foc.ib_offset); /* 三相和为零,第三相直接推 */ }

采样时刻选择上,三电阻方案在计数器零点(下桥臂全开)触发最稳;单电阻方案要复杂得多,需要在不同扇区选择不同的采样窗口,还得处理窗口太窄时的相移补偿,一般工程上会直接把最小占空比钳到 3% 以上,用这个代价换采样可靠性。

3.2 坐标变换与 PI 抗饱和

等幅值 Clarke 变换的系数是 2/3,这一点和等功率变换的 sqrt(2/3) 不一样,混用会让 iq 的实际值和标定量差 1.22 倍,调电流环时以为是增益大了,其实是变换系数错了。

/* clarke_park.c:一次算完正变换,避免在中断里反复展开 */ static inline void clarke_park(foc_t *f) { /* 等幅值 Clarke:系数 2/3 */ f->ialpha = (2.0f / 3.0f) * (f->ia - 0.5f * f->ib - 0.5f * f->ic); f->ibeta = (2.0f / 3.0f) * (0.8660254f * (f->ib - f->ic)); /* sqrt(3)/2 */ /* Park:sin_t/cos_t 由角度查表得到,不在中断里调 sinf() */ f->id = f->ialpha * f->cos_t + f->ibeta * f->sin_t; f->iq = -f->ialpha * f->sin_t + f->ibeta * f->cos_t; }

PI 的抗饱和我一般用回算(back-calculation),比单纯钳位积分项更平滑。钳位式在退饱和瞬间会有一段“积分器已经满了但输出还没跟上”的死区,回算把超出的部分直接反写回积分器,恢复快得多。

/* pi_ctrl.c:带回算抗饱和的 PI,三个环共用同一份实现 */ float pi_calc(pi_t *p, float ref, float fdb) { float err = ref - fdb; p->integral += p->ki * err; p->out = p->kp * err + p->integral; if (p->out > p->out_max) { p->out = p->out_max; p->integral = p->out_max - p->kp * err; /* 回算:把超出的部分从积分里扣掉 */ } else if (p->out < p->out_min) { p->out = p->out_min; p->integral = p->out_min - p->kp * err; } return p->out; }

kp、ki的单位要统一:如果 err 是安培、out 是伏特,那 kp 的单位是欧姆,量纲上正好对应电机的相电阻量级,整定时可以从 R 的 0.3 到 1 倍开始试,比盲猜快得多。out_max、out_min要按母线电压除以 sqrt(3) 来设,设成 udc 的一半也能跑,但电压利用率会掉一截。

3.3 SVPWM:扇区法还是中点注入

扇区法写起来长,要判六个扇区、算 T1/T2/T0、再分配七段式的比较值,好处是直观、占空比上限精确。中点注入法代码短得多,本质是给三相电压注入一个共模量,等效于七段式 SVPWM,母线利用率一样是 1.15 倍。

/* svpwm.c:三相电压 -> 占空比,共模注入等效 SVPWM */ void svpwm_duty(float ua, float ub, float uc, float udc, duty_t *d) { float umax = fmaxf(ua, fmaxf(ub, uc)); float umin = fminf(ua, fminf(ub, uc)); float ucom = 0.5f * (umax + umin); /* 共模 = 三相极值的中点 */ float inv = 1.0f / udc; d->a = 0.5f + (ua - ucom) * inv; d->b = 0.5f + (ub - ucom) * inv; d->c = 0.5f + (uc - ucom) * inv; /* 最小脉宽钳位:给自举电容充电,也给下桥臂采样留窗口 */ const float dmin = 0.03f, dmax = 0.97f; d->a = clampf(d->a, dmin, dmax); d->b = clampf(d->b, dmin, dmax); d->c = clampf(d->c, dmin, dmax); }

ucom这个共模量在三相星形负载里不产生任何电流,所以可以随便注入,这是中点注入法能等价于 SVPWM 的根本原因。dmin/dmax的钳位看着粗暴,但少了它,轻载时占空比会算出接近 0 或接近 1 的值,自举电容充不上电,上桥臂驱动会掉,现象是“轻载时电机偶尔抽一下”。

3.4 交叉耦合前馈与死区补偿

转速上到几千 rpm 之后,光靠 PI 压不住 d 轴和 q 轴之间的耦合项,id 会被转速拖出零点,白白发热。加前馈就两行:

/* foc_core.c:电流环单步,PI + 交叉耦合前馈 */ void foc_current_loop(foc_t *f) { float ud_pi = pi_calc(&f->pi_d, f->id_ref, f->id); float uq_pi = pi_calc(&f->pi_q, f->iq_ref, f->iq); /* 反电动势与交叉耦合前馈,低速时可以整段跳过 */ float ud_ff = -f->omega_e * MOTOR_LQ * f->iq; float uq_ff = f->omega_e * (MOTOR_LD * f->id + MOTOR_FLUX); f->ud = ud_pi + ud_ff; f->uq = uq_pi + uq_ff; inv_park(f); /* ud/uq -> ualpha/ubeta */ svpwm_duty(f->ualpha, f->ubeta, f->udc, &f->duty); }

omega_e是电角速度,单位 rad/s,别拿机械角速度直接用,差一个极对数。MOTOR_FLUX是永磁体磁链,单位 Wb,这个参数如果不准,前馈反而会引入偏差,测不准就先把前馈关掉,靠 PI 硬扛,扛不住再回来查参数。死区补偿同理,补偿量正比于死区时间乘母线电压,符号跟着相电流方向走,方向判错的补偿还不如不补,波形上会看到电流过零点附近出现台阶。

4. 速度环与位置环:编码器反馈、测速与轨迹规划

外环的难点全在反馈质量上。电流环的反馈是硬件给的,采样准不准基本看布板;速度环和位置环的反馈是自己算出来的,算得糙,参数怎么调都是白搭。

4.1 电角度零偏标定

有感方案的第一个必答题是“编码器读数怎么变成电角度”。增量式编码器只有相对计数,上电不知道转子在哪,只能靠强制对齐:给 d 轴通一个小电流,把转子拉到电角度零点,读此时编码器的原始计数,这个数就是机械零偏。

/* foc_core.c:d 轴对齐标定电角度零偏 */ int32_t enc_zero_calib(void) { int32_t raw_sum = 0; foc_force_ud(0.2f * MOTOR_U_RATED, 0.0f); /* 只给 d 轴电压,iq 保持 0 */ delay_ms(1000); /* 等转子稳定,别急着读 */ for (int i = 0; i < 32; i++) { raw_sum += drv_enc_read_raw(); delay_us(200); } drv_pwm_disable(); return raw_sum / 32; /* 32 次平均,滤掉细分抖动 */ }

对齐电流太小带不动负载,太大会让电机发烫,取额定电流的 20% 到 30% 一般能兼顾。标定完还要验方向:手动正转电机,电角度应该单调递增,如果递减,说明编码器 A/B 相接反或者相序定义反了,把ENCODER_DIR改成 -1 就行,别去改接线。

4.2 测速:M 法、T 法与滑动滤波

高速用 M 法(固定时间数脉冲),低速用 T 法(固定脉冲数时间),交叉点大概在额定转速的十分之一。这套东西写起来不复杂,坑在切换的那一点上:两种方法算出来的值天生对不齐,切换瞬间速度反馈会跳一下,速度环立刻还你一个电流尖峰。

/* drv_encoder.c:1ms 定周期 M 法测速 */ float enc_speed_rpm(int32_t delta_cnt, float dt_s) { const float cpr = ENC_CPR; /* 四倍频后每转计数,2500 线 -> 10000 */ float rev = (float)delta_cnt / cpr; return rev / dt_s * 60.0f; /* r/s -> rpm */ } /* 一阶低通跟着用,alpha 越小越平滑、相位滞后越大 */ static inline float lpf1(float y, float x, float alpha) { return y + alpha * (x - y); }

alpha取 0.1 到 0.3 之间,超过 0.5 基本等于没滤。滤波带来的相位滞后会直接吃掉速度环的相位裕度,所以这不是“越平滑越好”的参数,是“刚好够用”的参数。低速段我一般直接强制走 T 法,宁可速度反馈有点噪声,也不要 M 法在低计数下产生的量化跳变。

4.3 速度环 PI 与位置环 P 的分工

位置环我喜欢用纯 P 加前馈,积分放在速度环。理由很实际:位置环带宽低,积分时间常数大,一旦积分饱和,退出来要好几百毫秒,表现出来就是“到位后先冲过去,再慢慢爬回来”。速度环带积分已经能吃掉稳态误差,位置环再加 I 属于重复劳动。另外,位置环用 P、速度环用 PI,这套结构在负载惯量变化时更稳。

/* foc_core.c:位置环 P + 速度前馈 */ void pos_loop(pos_t *p, float ref, float fdb) { float err = ref - fdb; float out = p->kp * err + p->ff_gain * p->vel_ref; /* 前馈项消除跟随误差 */ p->out = clampf(out, -p->vel_max, p->vel_max); g_spd.ref = p->out; /* 输出即速度给定 */ }

ff_gain理想值就是 1,实际取 0.8 到 1.0,因为前馈给多了会超前、给少了会滞后。vel_max是位置环输出的速度上限,这个限幅同时保护了速度环和电流环,一层限幅顶三层用。

4.4 梯形规划与加速度前馈

直接给位置环一个阶跃参考,不管参数怎么调都会超调,因为速度给定是瞬间跳变的,电机物理上跟不上。所以位置参考要先过一层轨迹规划,把阶跃变成梯形速度曲线。

/* ramp.c:梯形规划,用 sqrt(2*a*s) 自然形成减速段 */ void traj_step(traj_t *t, float dt) { float dist = t->target - t->pos; float v_allow = sqrtf(2.0f * t->acc * fabsf(dist)); /* 当前距离下允许的最大速度 */ float v_des = fminf(t->v_max, v_allow); if (v_des > t->vel) { t->vel = fminf(t->vel + t->acc * dt, v_des); /* 加速段 */ } else { t->vel = fmaxf(t->vel - t->acc * dt, -v_des); /* 减速段 */ } t->pos += t->vel * dt; }

acc的取值决定定位时间,也决定电流峰值:加速度大,需要的转矩大,电流环就容易顶到限幅。把这个加速度再乘上转动惯量和转矩常数,就是可以直接前馈到 iq 上的量,加了这个前馈,位置环的 kp 还能再往上提一档。

5. 避坑排查:三环调试里最容易翻车的五处

这一节写的都是我在台架上真实撞过的墙,现象、原因、解决按顺序对号入座,能省掉大半天。

5.1 上电就过流保护,电流波形像被刀切

现象:一使能就触发过流,示波器上看相电流突然冲到限幅值然后被硬件保护切断,复位后重复。原因多半是电流采样零偏没标定,或者标定时桥臂没关,读到的“偏置”其实是运放的输出饱和值,导致算出来的电流凭空大了一截;也有可能是 PWM 与 ADC 的触发时刻没对齐,采到了开关瞬间的振铃。解决:先确认drv_pwm_disable()真的关了桥,把三相占空比全写成 50% 再测偏置;然后用示波器双通道同时看 PWM 的计数零点标志和 ADC 的转换完成信号,两者必须对齐在同一个窗口内。

5.2 电机能转,但低速一顿一顿,偶尔还反转一下

现象:开环强拖正常,切到闭环低速就抖,手按住负载更明显。原因通常是电角度零偏不准,或者编码器方向与相序相反,电角度每转一圈错一次,错的那一下转矩反向。解决:重新做 d 轴对齐标定,标定时把 32 次平均改成 128 次并延长时间;然后手动转电机,把电角度通过串口打出来看是不是单调递增,不是就把编码器方向取反。顺便检查极对数,7 对极写成 8 对极,现象跟这个一模一样。

5.3 速度环 Kp 一过某个值就啸叫

现象:Kp 从小往大调,调过某一点之后电机开始发出尖锐啸叫,电流波形出现固定频率的振荡。原因不是速度环本身,而是速度反馈的滤波太弱或太强——太弱噪声进环,太强相位滞后,两种都会在带宽接近内环五分之一时引发振荡;也可能是测速周期和速度环周期没对齐。解决:先把电流环单独闭环验证,给 iq 阶跃看响应,确认电流环本身不振荡;然后把速度反馈滤波的 alpha 固定在 0.2,从头再扫一遍 Kp。血泪经验是每次只改一个参数,改完做一次同样的阶跃,不然你永远不知道是哪个参数起的作用。

5.4 位置到位后来回弹两下才停住

现象:定位动作结束时超调一点,然后反向修回来,再超一点,两三个来回才稳定。原因基本是位置环里带了积分,或者位置环的 kp 相对速度环带宽给大了。解决:把位置环退成纯 P,积分清零,看看还弹不弹;如果还弹,把ff_gain从 1.0 降到 0.8,同时降低轨迹规划的acc。另外检查位置反馈是不是有死区,编码器分辨率低的时候,一个计数内的来回会导致输出在两点之间反复切换,加个最小误差阈值就能压住。

5.5 一打开串口打印就炸机

现象:调试时加了printf输出电流值,结果一开打印就过流或者丢步。原因是printf在中断里阻塞,或者重定向的串口发送是查询方式,16kHz 的中断被拖成几百 Hz,PWM 占空比更新不及时。解决:所有调试输出改成环形缓冲 + 主循环发送,中断里只写 buffer 指针;或者干脆用 DA 输出配合示波器看波形,别在实时中断里碰任何阻塞式外设。这个坑没有后悔药,只能靠规矩——中断里只做确定性的事。

6. 二次开发:先改哪几个文件,改完怎么验

拿到一份能跑的三环代码,多数人的第一反应是改参数,结果改完发现不是快了就是抖了。我的建议是先做单位统一,再动参数。

6.1 单位制与参数集中管理

把所有电机参数收进一个头文件,单位写清楚,浮点还是定点写清楚。单位混用是三环调试里最隐蔽的错:电感用 mH 传进去,前馈项直接大一千倍;转速用 rad/s 传进按 rpm 算的 PI,增益怎么调都不对。

/* cfg/motor_param.h:所有参数集中在此,改电机只改这个文件 */ #define MOTOR_RS 0.42f /* 相电阻,欧姆,万用表实测相间除以 2 */ #define MOTOR_LD 0.00012f /* d 轴电感,H */ #define MOTOR_LQ 0.00015f /* q 轴电感,H,表贴式可与 LD 相同 */ #define MOTOR_FLUX 0.0085f /* 永磁体磁链,Wb */ #define MOTOR_POLE_PAIRS 7 /* 极对数,不是极数 */ #define MOTOR_U_RATED 24.0f /* 额定电压,V */ #define MOTOR_I_RATED 3.0f /* 额定电流,A */ #define PWM_FREQ_HZ 16000 #define CUR_LOOP_FREQ_HZ 16000 #define SPD_LOOP_FREQ_HZ 1000 #define POS_LOOP_FREQ_HZ 500

极对数是最容易被写成极数的一项,7 对极写成 14,电角度直接快一倍,电机能转但一转就失步,查起来非常费时间。磁链如果测不了,可以先用反电动势常数反推:磁链等于线反电动势峰值除以电角速度。

6.2 上电自检状态机

二次开发时最值得先加的是一套状态机,把“标定 → 校验 → 使能 → 运行 → 故障”这几步显式写出来,别用一个if (start)硬扛。

/* app/main.c:上电状态机,任何一步失败都能定位到具体环节 */ typedef enum { ST_INIT = 0, ST_ADC_CALIB, /* ADC 偏置标定 */ ST_ENC_CALIB, /* 电角度零偏标定 */ ST_VERIFY, /* 小电流开环验证转动方向 */ ST_READY, ST_RUN, ST_FAULT } sys_state_t; void app_step(void) { switch (g_sys.state) { case ST_ADC_CALIB: adc_offset_calib(); g_sys.state = ST_ENC_CALIB; break; case ST_ENC_CALIB: g_sys.enc_zero = enc_zero_calib(); g_sys.state = ST_VERIFY; break; case ST_VERIFY: /* 给 id 一个小电流,读电角度增量是否与预期方向一致 */ if (verify_direction() == 0) { g_sys.state = ST_READY; } else { g_sys.state = ST_FAULT; g_sys.err_code = ERR_DIR; } break; case ST_RUN: g_pos.ref = g_sys.target_pos; traj_step(&g_traj, DT_POS); break; default: break; } }

每个状态只做一件事,失败就落到ST_FAULT并写错误码,比到处打日志干净得多。ST_VERIFY这一步别省,它会替你把编码器方向和相序问题在上电阶段就拦下来。

6.3 改完怎么验证

改任何一处,都按同一套动作验一遍,别只看着“转起来了”就算完。

验证项方法通过标准
电流环iq 给 10% 额定阶跃,示波器看相电流上升时间小于 1ms,无持续振荡
速度反馈手动匀速转电机,读 rpm与实际转速偏差小于 3%
速度环给 500rpm 阶跃超调小于 10%,稳定时间小于 200ms
位置环给 30 度阶跃无反向回弹,稳态误差在一个编码器计数内
堵转手动按住输出轴电流限幅生效,不触发过流保护

这套动作我从最早调第一台电机开始就没变过。后来养成一个习惯:不管改的是 PI 参数、限幅还是采样时刻,只要动了foc/或cfg/目录下的任何一行,都强制走一遍上面这张表,把电流波形、速度阶跃、位置阶跃三条曲线各抓一次存档。有过一次偷懒跳过了电流环那一步,结果位置环调了两天都不对,回头一看是电流环的限幅被顺手改小了。希望这些能帮你在自己的台架上少走两圈弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询