
做PMSM驱动开发的人几乎都经历过这种诡异时刻仿真模型里把电流环带宽拉满也没问题一到真机上电电流要么低频振荡要么高速跑起来之后噪音和发热都压不住。我早期遇到这种情况时怀疑过采样、怀疑过PI参数、甚至怀疑过IGBT驱动最后把问题定位到电机本身的电感参数上。这篇文章不绕弯子目标只有一个用六脉冲电压注入法把PMSM的d轴电感和q轴电感辨识出来。全文按“原理推导→实验准备→C代码→实测排查”的顺序展开适合正在做FOC自整定或离线参数自学习的工程师参考。电机参数辨识是一个系列工程上一篇处理的是绕组电阻这一类静态参数而电感辨识的难度明显上了一个台阶。原因不复杂电阻只要通入直流电就能测电感却和转子位置、电流大小、磁路饱和状态全都有关。六脉冲电压注入法是工程上性价比很高的一种离线辨识方案不需要额外硬件不依赖电机厂商手册用现有驱动板就能跑。下面我会把公式推到能落地的程度再给出一套可以直接抄进工程里的C代码最后结合实测数据说明误差出在哪里。1. 电感不准是怎么一步步“坑”掉整个FOC系统的1.1 电流环PI增益直接由电感决定FOC电流环的PI参数在工程上最常用的整定方法是根据电机电气时间常数来的。电流环的开环传递函数里被控对象基本可以写成G(s) 1 / (Ls R)其中L是定子电感R是定子电阻。你看这个形式就明白了电流环PI的零点要放在Kp/Ki L/R附近才能获得比较好的零极点对消效果。如果电感辨识偏差很大PI零点就放错了位置理论上计算出来的带宽和相位裕度全都不成立。实测中最典型的症状是电流环在低速大电流工况下发出刺耳的啸叫或者给定一个阶跃电流后实际电流要过冲好几次才稳定。这种问题很难靠增大阻尼或者调低带宽彻底解决因为根源是模型参数错了。尤其在高速弱磁区电感对电压前馈的影响会成倍放大电感误差直接导致前馈电压算错电流环只能靠积分项一点点往回拉动态响应自然拉胯。1.2 电感误差还会污染观测器和磁链辨识如果只是电流环影响电感误差还不算致命真正头疼的是它会传递到无感FOC的观测器环节。滑模观测器、龙伯格观测器、模型参考自适应系统全都依赖电机电压方程而方程里电感项的位置非常靠前。电感偏差30%的情况下观测器估算的反电动势相位就会偏移最终表现为高速时转子位置估算滞后电机带载能力下降甚至出现失步。更隐蔽的是很多参数辨识流程里把电感参数当作后续磁链辨识的输入。磁链计算公式λpm ≈ (uq - Rs·iq - Lq·diq/dt) / ω中Lq一旦偏大计算出来的磁链就偏小反过来偏小磁链就偏大。磁链再继续喂给前馈补偿形成一个闭环误差链。我见过一个项目厂商给的名义Lq是0.35mH实际辨识结果是0.22mH差了37%结果磁链辨识值连带错了接近15%。所以做参数辨识时顺序和精度都要较真。1.3 为什么不用厂商手册或公式估算有人会问电机出厂铭牌和手册里不都标了电感吗问题在于手册给出的电感通常是在特定测试条件比如额定电流、特定频率、25℃下测得的而实际电机在不同电流下电感可以变化20%-40%。尤其内置式PMSM的磁路饱和效应非常明显d轴电感和q轴电感随电流变化很大。至于纯公式估算电感比如用绕组匝数和磁路尺寸去算只适合电机设计阶段驱动工程师拿不到那么细的磁路参数算出来的误差也根本没法用于高性能控制。所以离线自学习就成了最靠谱的方案。电机上电后在静止状态下完成电阻、电感、磁链的辨识把结果写进Flash每次开机自动执行或者产线下线执行一次。六脉冲电压注入法就是在这种情况下发挥作用的。2. 六脉冲电压注入法的原理推导一处一处把公式掰开2.1 静止坐标系的电感矩阵与转子角的关系先建立坐标关系。设转子d轴与A相绕组轴线的夹角为θr静止αβ坐标系下的磁链方程可以写成如下形式ψα (Ls ΔL·cos2θr)·iα ΔL·sin2θr·iβ λpm·cosθr ψβ ΔL·sin2θr·iα (Ls - ΔL·cos2θr)·iβ λpm·sinθr其中两个关键参数定义为Ls (Lq Ld) / 2 ΔL (Lq - Ld) / 2Ls是平均电感ΔL是凸极率的一半也就是q轴和d轴电感差的一半。对于表贴式PMSMΔL接近0对于内置式PMSMΔL大于0且通常比较明显。这个矩阵里的cos2θr和sin2θr项就是凸极效应在静止坐标系里的体现也是后面辨识凸极方向的理论基础。2.2 电压-电流响应的近似与适用条件当电机转子静止时永磁磁链不切割绕组不会产生旋转反电动势。忽略定子电阻压降后静止坐标系的电压方程近似为电感矩阵乘以电流导数uα (Ls ΔL·cos2θr)·diα/dt ΔL·sin2θr·diβ/dt uβ ΔL·sin2θr·diα/dt (Ls - ΔL·cos2θr)·diβ/dt这里有两个近似条件需要说明。第一电阻压降被忽略了这在脉冲很短、电流不大的情况下误差可控但如果电流已经上升到额定电流量级电阻压降就不能忽略我会在后面的代码里给出补偿办法。第二假设电机在辨识过程中完全不转动这要求转子被锁定或者至少转速趋近于零。如果转子发生明显旋转方程里会多出运动反电动势项整个辨识结果就会失真。现在向静止坐标系中施加一个幅值为V_mag、方向角为θv的电压矢量uα V_mag·cosθv uβ V_mag·sinθv这个电压会在这个方向上产生一个电流变化率。经过矩阵求逆和一些化简可以得出沿着电压矢量方向的“等效瞬态电感”它有一个非常简洁的形式V_mag / |di/dt| ≈ Ls - ΔL·cos(2·(θv - θr))这个式子是全篇的核心。它告诉我们两件事第一等效电感随注入电压方向呈二次余弦变化偏置项就是平均电感Ls第二振荡幅度就是凸极率ΔL而振荡的相位直接反映转子d轴位置θr。所以只要在多个电压方向下测量等效电感就能把Ls、ΔL、θr全部解出来。2.3 六个方向的数据如何解出三个参数理论上三个方向的数据就能解出Ls、ΔL和θr这三个未知数但工程上几乎不会这么干。因为单个测量本身有噪声三个点解非线性方程对噪声极度敏感稍微偏一点儿结果就完全不对。六脉冲法选择在360°范围内均匀注入六个方向的电压矢量正好对应逆变器的六个基本电压矢量方向0°、60°、120°、180°、240°、300°。这样选择的好处非常明显。六个方向均匀分布时二次余弦在一个整周期内被均匀采样计算平均值和二次分量时不存在谐波泄漏而且计算极其简单Ls (r0 r1 r2 r3 r4 r5) / 6 ΔL -(2/6) · Σ(ri · cos(2·(θvi - θr)))其中ri V_mag / |di/dt|是第i个方向测得的等效电感。算出Ls和ΔL后d/q轴电感自然就出来了Ld Ls - ΔL Lq Ls ΔL整个算法不需要做矩阵求逆不需要迭代拟合六个数据点三次乘法和一次平均就结束了。这种计算量对任何单片机来说都毫无压力真正的工程量反而在数据采集的可靠性和噪声抑制上。3. 跑程序之前请先把这些硬件和测量条件确认好3.1 实验平台和采样链路的基本要求六脉冲电压注入法虽然原理不难但对实验平台有个最低要求电流采样必须在时间上对齐到电压矢量的作用窗口内并且要有足够的采样率。以10kHz开关频率为例一个PWM周期是100μs如果脉冲持续2ms那么每个方向上有20个PWM周期可以用来采样。建议在PWM中心对齐处触发ADC因为中心对齐时逆变器上下桥臂的开关动作已经完成采样点避开了开关噪声最剧烈的时刻。需要确认母线电压采样的准确性。因为整个辨识过程是用指令电压去做运算的如果实际母线电压是400V而采样反馈回来说是380V相当于所有电压数据都偏了5%最终电感辨识结果也会系统性偏大或偏小。我见过不少案例是母线电压分压电阻精度不够导致辨识出来的电机参数跟厂商数据差一大截。上电后先用万用表实测母线电压和程序里的采样值对一遍这个习惯值得养成。3.2 转子锁定与电角度对齐不做这一步全是白算六脉冲辨识最容易被低估的就是转子锁定环节。前面推导公式时假设转子完全静止在已知位置如果这个假设不成立后面所有数据都不可信。转子锁定的方式有几种。最可靠的方式是机械抱闸伺服电机自带的刹车或者自制工装都可以。没有抱闸的话可以采用d轴电流预定位法先给d轴施加一个持续的大电流建议额定电流的50%以上利用电磁转矩把转子强行拉到d轴对齐的位置。这个方法的局限性在于如果负载比较大或者电机本身有齿槽转矩预定位电流太小会拉不到位电流太大又会让电机发热。还有一个工程细节很容易踩坑六个脉冲中的每个脉冲都会产生转矩尤其对凸极率较大的内置式电机即使脉冲只有几毫秒也会让转子产生微小转动。如果六次注入之间的间隔太长转子可能已经蠕动偏移了好几度。我的做法是六次注入做完后主动检查一下θr的辨识结果如果和预设的电角度差超过一定范围要么重新对齐再跑一轮要么对θr做修正。更稳妥的做法是在每次脉冲之间重新执行一个短暂的低压d轴预定位。3.3 电压幅度、脉冲宽度的选取和过流保护设置这两个参数直接决定辨识质量也是最需要根据具体电机去调的。电压幅值太小电流上升慢采样噪声占比大等效电感计算出来乱跳电压幅值太大电流峰值可能过流或者铁芯进入深度饱和电感测出来会偏小。工程上一个实用的设计准则是让脉冲结束时的电流增幅控制在额定电流的0.5到1倍之间。最好像下面这样先估算一遍ΔI ≈ V_mag · T_pulse / Ls比如电机额定电流10A估算Ls为0.2mH开关周期100μs如果选V_mag20V、T_pulse0.5ms那么ΔI≈50A这显然太大了。这时可以把电压降到4V、脉宽保持0.5msΔI≈10A就相对合适。注意实际还要叠加上电机原有的预定位电流不能只看增量。脉冲宽度本身也有讲究。太短了电流采样点太少斜率拟合不稳定太长了电流持续增大后电阻压降占比升高转子也更容易蠕动。一般来说0.5ms到3ms是一个比较合理的区间具体要结合电感大小来权衡。逆变器本身的非线性也必须正视。死区时间、IGBT导通压降、母线电压利用率这些因素在小电压注入时会造成比较大的占空比偏差。解决办法有两个方向一是尽量用中等偏上的电压幅值让非线性误差占比小一些二是在程序里做死区补偿。如果不做任何补偿建议把注入电压提高到母线电压的20%以上实测效果会好不少。4. C语言实现六脉冲状态机、电流斜率提取与最小二乘求解4.1 整体流程设计整个辨识流程可以设计成一个顶层函数在电机上电且完成转子预定位之后调用。大体状态流程是先关断输出让电流回零然后按顺序注入六个电压矢量每个矢量下测量电流变化率计算出等效电感最后统一求解。两次注入之间插入一个零矢量的恢复时间让电流回到零避免下一次注入的初始电流不为零。代码里我会用两个辅助函数来分别处理“单方向数据采集”和“最终参数求解”。单方向数据采集是整个辨识精度的基础如果这里有噪声没有滤掉后面计算结果再怎么精确也没用。求解部分我会用刚才推导的傅里叶系数法代码只需要短短十几行。4.2 单矢量脉冲注入与电流采样代码先定义电压矢量方向表。工程上电机三相绕组的轴线分别是0°、120°、240°逆变器六个基本电压矢量正好是相邻间隔60°。#define IDENT_VECTOR_NUM 6 #define IDENT_VOLTAGE_V 4.0f /* 注入电压幅值单位V */ #define IDENT_PULSE_SEC 0.0005f /* 单个矢量注入时间单位s */ #define IDENT_RECOVER_MS 5 /* 两次注入间的恢复时间 */ const float ident_theta_table[IDENT_VECTOR_NUM] { 0.0f, 1.0472f, 2.0944f, 3.1416f, 4.1888f, 5.2360f };下面这个函数负责在静止坐标系下施加指定方向和幅值的电压。实际FOC里SVPWM模块通常接收的是Uα和Uβ所以这里直接算出后交给SVPWM即可。static void ident_inject_voltage_vector(float theta_v, float V_mag) { float Ualpha V_mag * cosf(theta_v); float Ubeta V_mag * sinf(theta_v); svpwm_set_target_voltage(Ualpha, Ubeta); }然后是电流采样与斜率提取。我这里展示的是两点法代码简洁但抗噪性一般实际工程我建议在每个脉冲窗口内连续采样多个点然后把数据丢进线性回归里求斜率。下面先把基本逻辑写清楚后面实测部分再展开说明怎么加强滤波。typedef struct { float k_alpha; /* alpha轴电流变化率 A/s */ float k_beta; /* beta轴电流变化率 A/s */ float k_norm; /* 电流变化率幅值 A/s */ float i_mid; /* 采样窗口中间电流幅值 A用于电阻压降补偿 */ } IdentSlopeResult; static void read_current_alpha_beta(float *ialpha, float *ibeta) { float ia adc_get_phase_a_current(); float ib adc_get_phase_b_current(); float ic -ia - ib; /* 三相对称负载C相电流由基尔霍夫定律得到 */ /* Clark变换 */ *ialpha ia; *ibeta (ia 2.0f * ib) / 1.7320508f; } IdentSlopeResult ident_measure_slope(float theta_v, float V_mag) { IdentSlopeResult res; float ia0, ib0, ia1, ib1; float i_mid_alpha, i_mid_beta; uint32_t sample_cnt 0; float sum_alpha 0.0f, sum_beta 0.0f; ident_inject_voltage_vector(theta_v, V_mag); delay_us(150); /* 避开开通瞬间的电压尖峰 */ read_current_alpha_beta(ia0, ib0); /* 在脉冲窗口内多次采样做简单的累积平均同时估计中间电流 */ while (sample_cnt 20) { float ia, ib; read_current_alpha_beta(ia, ib); sum_alpha ia; sum_beta ib; sample_cnt; delay_us(15); } read_current_alpha_beta(ia1, ib1); /* 计算斜率 */ res.k_alpha (ia1 - ia0) / IDENT_PULSE_SEC; res.k_beta (ib1 - ib0) / IDENT_PULSE_SEC; res.k_norm sqrtf(res.k_alpha * res.k_alpha res.k_beta * res.k_beta); /* 窗口中间电流用于电阻压降补偿 */ i_mid_alpha sum_alpha / (float)sample_cnt; i_mid_beta sum_beta / (float)sample_cnt; res.i_mid sqrtf(i_mid_alpha * i_mid_alpha i_mid_beta * i_mid_beta); /* 关断输出 */ svpwm_set_target_voltage(0.0f, 0.0f); return res; }注意上面的窗口时间是人为定的实际应该用采样点真实时间差来计算斜率。如果在中断里做可以使用硬件定时器记录两个采样瞬间的时间戳这样更准确不会因为delay函数的抖动导致斜率计算偏差。4.3 六个数据合成求解 Ld/Lq/Ls 的代码六个方向都测完后进入参数合成阶段。这里我提供两个函数一个用来计算每个方向对应的等效电感另一个用傅里叶系数法求解最终参数。float ident_solve_average_and_delta( const float *theta_v, const float *r, float theta_r, float *Ls_out, float *DeltaL_out) { float sum_r 0.0f; float sum_cos 0.0f; int i; for (i 0; i IDENT_VECTOR_NUM; i) { float cos2 cosf(2.0f * (theta_v[i] - theta_r)); sum_r r[i]; sum_cos r[i] * cos2; } *Ls_out sum_r / (float)IDENT_VECTOR_NUM; *DeltaL_out -2.0f * sum_cos / (float)IDENT_VECTOR_NUM; return 0; }顶层调用函数里需要把测量得到的斜率数据换算成等效电感。这里要注意之前提到的电阻压降补偿。如果不补偿直接用指令电压V_mag除以k_norm得到的电感会偏大。补偿思路是把电机在该方向上的平均电阻压降从指令电压里减掉剩下的电压增量才是真正作用在电感上的部分。typedef struct { float Ls; float Ld; float Lq; float DeltaL; } IdentInductanceResult; int ident_run_inductance_identification( float theta_r, /* 转子d轴电角度由预定位得到 */ float R_s, /* 已经辨识出的定子电阻 */ IdentInductanceResult *out) { float r[IDENT_VECTOR_NUM]; IdentSlopeResult slope; int i; for (i 0; i IDENT_VECTOR_NUM; i) { slope ident_measure_slope(ident_theta_table[i], IDENT_VOLTAGE_V); /* 防呆如果电流变化率太小说明电压没有真正施加到绕组上 */ if (slope.k_norm 50.0f) { return -1; } /* 等效瞬态电感 r (V_mag - R_s * I_mid) / k_norm */ r[i] (IDENT_VOLTAGE_V - R_s * slope.i_mid) / slope.k_norm; /* 等电流回零防止下一次测量初始电流不为零 */ svpwm_set_target_voltage(0.0f, 0.0f); delay_ms(IDENT_RECOVER_MS); } ident_solve_average_and_delta( ident_theta_table, r, theta_r, out-Ls, out-DeltaL); out-Ld out-Ls - out-DeltaL; out-Lq out-Ls out-DeltaL; return 0; }这个顶层函数执行完就把辨识结果填到了结构体里。theta_r来自预定位阶段记录的电角度。如果预先做过编码器零位标定这里传的应该是实际的转子d轴绝对电角度。使用六脉冲法时有一点需要特别注意由于余弦函数的周期是180°所以二次谐波项不能区分0°和180°。如果只靠这个算法去定转子方向会有“南北极不分”的问题。所以θr必须由外部方式给定不能指望六脉冲数据本身把转子磁极方向也定出来。这也解释了为什么六脉冲法更适合做电感大小辨识而不是替换磁极初始位置检测。4.4 代码中的保护逻辑与数据合理性判定不管辨识代码写得多完善上电自学习场景下最容易遇到的问题就是过流。所以代码里必须加入过流保护不只是靠驱动板硬件保护软件层也要有退避机制。一个实用的做法是在每次注入电压之前先把电流采样一下如果在恢复期之后电流仍然没有回到零就中止辨识流程。另一个做法是在注入脉冲时限制电流的估算增幅比如根据V_mag * T_pulse / Ls_estimate估算最大电流增量如果这个增量超过额定电流的1.5倍就直接降低电压幅值或者缩短脉宽。这个估算值在辨识前可能不准但用来做防呆已经足够了。数据合理性判定也很重要。识别结果出来后先看几个基本指标检查项合理范围异常时的可能原因Ls与电机铭牌量级一致母线电压采样不准或电阻补偿错误DeltaL表贴式接近0内置式大于0转子没有锁定或θr给错Ld小于等于Ls数据噪声大六个方向测量不一致Lq大于等于Ls电流饱和或逆变器非线性严重我见过有人在代码里不加任何合理性判据辨识出Ld比Lq还大直接写进参数表结果系统一上高速就叫。至少在代码里加一个if (out-DeltaL 0.0f)的报警。虽然内置式PMSM也有少数设计是q轴电感小于d轴但绝大多数情况下DeltaL应该大于零出现反号大概率是过程出错了。5. 实测波形解读与常见误差排查别被漂亮数据骗了5.1 六组数据“标准长相”长什么样假设转子d轴已经被预定位到0°θr0°六个方向的注入角就是0°到300°。理想情况下六个等效电感数据应该沿下面的规律分布在0°和180°方向测到的等效电感最小对应Ld在60°、120°、240°、300°方向测到的等效电感偏大既不等于Ld也不等于Lq位于中间值。这是因为等效电感公式里二次余弦在0°和180°取1在60°和240°取-0.5120°和300°也是-0.5。如果把六组数据画到雷达图上应该是一个沿0°-180°方向凹陷的“花生形”。看到这个形状基本上可以确认数据是可信的。如果画出来是一个旋转了几十度的花生说明转子的实际位置和你预设的θr对不上需要回过去查预定位过程。如果画出来是乱七八糟的星形说明某个脉冲下转子转动了、或者电流采样噪声太大。5.2 最常见的误差来源及修正顺序我基于实测经验给一个排查优先级按这个顺序查通常能快速定位问题。第一优先检查母线电压反馈和指令电压方向。我遇到过好几次“辨识结果整体偏大30%”的案例查到最后是母线电压采样滤波电容太大导致采样值滞后于实际值。这个属于系统误差六组数据形状会正常但数值整体偏移。用万用表实测母线电压和程序采样值做比对是最快的验证方式。第二优先检查死区补偿和逆变器非线性。如果数据形状正常但六组数据大小不对称比如0°和180°两个本来应该相同的结果差了10%以上多半是死区补偿的方向不对。死区补偿的符号和电流极性有关电流过零附近补错结果就会出现“方向性误差”。第三优先检查转子是否真的没动。如果六组数据完全不成规律或者第二次重复测量结果和第一次明显不同大概率是转子在脉冲过程中蠕动了。这时要去检查机械抱闸是否可靠或者预定位电流是否足够。还有一个优先级较低但容易忽略的误差源采样延时和斜率计算的窗口时间不准。两点法的抗噪能力有限如果采样延时不稳定斜率计算就会带进额外误差。更稳妥的做法是在一个脉冲窗口内用固定PWM周期触发采样采完N个点后做一次线性回归把斜率拟合出来。线性回归不光能给出斜率还能通过残差判断这批数据有没有明显异常点。5.3 与高频注入法交叉验证怎么判断该信谁离线辨识做完之后我强烈建议用另一种方法做交叉验证尤其是对要上车量产的项目。无感FOC里常用高频旋转电压注入或者高频脉振注入来在线辨识凸极位置这些方法对ΔL非常敏感正好可以用来验证六脉冲法的结果。交叉验证的思路是这样高频注入法能锁定磁极位置但它直接辨识的是凸极方向对ΔL的具体数值不那么敏感。如果六脉冲法算出来的ΔL方向正好落在高频注入法锁定的方向上两个结果就能互相印证。如果方向不一致误差大概率出在六脉冲法的转子锁定或者电角度对齐环节。这个验证不需要很精确能对上±10°以内就说明六脉冲离线辨识的结果是可信的。还有一个经验对比如果电机在低速带上负载运行平稳、噪音小说明离线辨识的电感配合电流环PI是合适的如果低速运行时有明显的“咔咔”声或电流波形包络呈现低频振荡回头去把电感参数往真实的偏大/偏小方向修正试试往往能比继续死磕PI参数更快解决问题。我自己现在做离线参数自学习已经养成了一个习惯辨识结果不会直接写进正式参数区而是先打一个备份到串口日志手动看一眼Ls、Ld、Lq和DeltaL四个值的方向和量级再决定要不要采纳。这个习惯帮我挡住了至少三次因为编码器零位漂移导致的整套参数错乱。六脉冲电压注入法代码实现简单、原理清晰是PMSM离线参数辨识里性价比很高的一个模块值得在你自己的自学习流程里安排上。