1. 从一次抖动问题说起抗齿槽算法到底在解决什么如果你玩过云台、机械臂关节或者高精度转台大概率遇到过这种场景电机在低速运转时手摸上去能感觉到一圈一圈的“颗粒感”听声音是“咔哒咔哒”的周期性抖动用示波器看电流波形会发现上面叠着一个跟转子位置强相关的周期性波动。这个现象就是齿槽转矩英文叫 Cogging Torque也有人叫齿槽效应。齿槽转矩的来源并不复杂。永磁同步电机的定子铁芯上有齿和槽永磁体转子在旋转时磁路磁阻会随着定子齿与转子磁极的相对位置周期性变化。磁阻变化意味着磁场能量变化而转矩是能量对角度的导数所以转子每转过一个齿距就会经历一次转矩的起伏。这个起伏跟电流无关即使绕组不通电你用手转动转子也能感觉到——这就是所谓的“齿槽感”。对于odrive这类面向高性能运动控制的驱动器来说齿槽转矩是低速性能的头号敌人。它直接导致几个问题低速时速度环输出抖动位置环出现周期性误差力控场景下力输出不线性。odrive的做法是在固件里内置一套抗齿槽算法通过预先测量每个位置上的齿槽转矩然后在运行时前馈补偿掉。而标题里提到的“3600个采样点”就是这套算法在标定阶段采集的数据量。很多人第一次看到3600这个数字会觉得奇怪一圈360度3600个点意味着每0.1度一个采样点。为什么不是360个点为什么不是36000个点这个数字是怎么定下来的它跟编码器分辨率、跟STM32F405的算力、跟电机极对数之间是什么关系这篇文章就把这些问题一次讲透。我先把结论放在前面3600这个数字不是拍脑袋定的它是编码器分辨率、电机极对数、内存占用、标定时间、补偿精度这五个约束条件共同作用下的一个工程最优解。下面我会从原理到实操一步步拆给你看。2. 齿槽转矩的物理本质与数学表达2.1 齿槽转矩从哪来磁阻变化是根源要理解抗齿槽算法先得理解齿槽转矩的数学形式。前面说了齿槽转矩源于磁阻随位置变化。对于一个有Nc个定子槽、2p个转子极的电机齿槽转矩的基波周期是定子槽数与转子极数的最小公倍数相关的一个角度。工程上更常用的描述方式是齿槽转矩是转子电角度或机械角度的周期函数其基频等于槽数与极数的最小公倍数对应的周期。用人话说齿槽转矩不是随便乱变的它是一个严格周期性的波形。转子每转过一个固定的机械角度齿槽转矩波形就完整重复一次。这个固定角度叫齿槽周期通常等于360度除以槽数和极数的最小公倍数。举个例子一个12槽14极的电机槽数12和极数14的最小公倍数是84那么齿槽转矩在一圈内会重复84次每个齿槽周期是360/84≈4.286度。在这个4.286度内齿槽转矩波形是一个近似正弦但带有谐波的形状。这个周期性是抗齿槽算法能工作的根本前提。因为波形周期性重复所以我不需要记住整圈每一个点的转矩值我只需要记住一个齿槽周期内的波形然后根据转子当前处在周期内的哪个相位查表输出对应的补偿值就行了。2.2 为什么补偿要前馈而不是靠PID硬扛有人会问既然齿槽转矩是周期性扰动那我加大速度环PID的积分增益让它硬扛不行吗实测下来不行。原因有两个。第一齿槽转矩的频率跟转速成正比转速一变扰动频率就变PID的带宽不可能覆盖所有转速。低速时齿槽频率低积分能跟上但积分增益大了会导致系统振荡高速时齿槽频率高PID根本来不及响应。第二PID是反馈控制它只能在误差产生之后才去修正天生有相位滞后对于周期性扰动反馈控制的抑制效果远不如前馈。前馈补偿的逻辑是我知道下一个位置会有多大的齿槽转矩我提前把对应的电流给进去让电磁转矩正好抵消齿槽转矩。这样误差根本不会产生而不是等误差产生了再去修。这就是抗齿槽算法采用查表前馈而不是反馈抑制的原因。2.3 补偿量的数学形式一个位置对应一个q轴电流增量在odrive的FOC框架里转矩由q轴电流控制。抗齿槽补偿最直接的做法就是根据转子当前机械角度查表得到一个q轴电流补偿量Iq_comp叠加到速度环输出的Iq_ref上。数学上可以写成Iq_final Iq_pid Iq_comp(θ_mech)其中θ_mech是转子机械角度Iq_comp是一个以齿槽周期为周期的函数。标定的过程就是把这个函数在每个采样点上的值测出来存进表里。这里有个关键点齿槽转矩跟电流方向无关它只跟转子位置有关。所以补偿量是位置的函数不是速度或电流的函数。这意味着标定一次只要电机没换、磁钢没退磁这张表就可以一直用。3. 3600个采样点是怎么算出来的3.1 采样点数的五个约束条件现在进入正题。3600这个数字我把它拆成五个约束来看。约束一编码器分辨率。odrive常用的编码器有AS5047、AS5048、TLE5012等这类磁编码器通常是14位或15位分辨率。14位意味着一圈16384个计数每个计数对应360/16384≈0.022度。15位是32768个计数每计数约0.011度。采样点数不能超过编码器能分辨的位置数否则相邻采样点落在同一个编码器计数里测出来的值没有意义。3600个点对应每点0.1度远低于编码器分辨率所以这个约束不是瓶颈。约束二电机极对数与齿槽周期。假设电机是12槽14极齿槽周期4.286度。如果我想在一个齿槽周期内均匀采8个点那么一圈需要360/4.286×8≈672个点。如果采16个点就是1344个点。3600个点意味着一圈内平均每个齿槽周期有3600/84≈42.8个点。这个密度已经相当高了。约束三内存占用。每个采样点的补偿值如果用一个float存储4字节3600个点就是14400字节约14KB。STM32F405有192KB RAM14KB完全可以接受。如果用int16存储只要7.2KB。这个约束也不紧。约束四标定时间。标定时电机要慢慢转一圈每个采样点停留一下采集数据。如果每个点采集10ms3600个点就是36秒。加上加速减速和稳定时间整个标定过程大概1到2分钟。这个时间对用户来说可以接受。如果采样点翻倍到7200标定时间就变成2到4分钟体验开始变差。约束五补偿精度。这是最核心的约束。补偿精度取决于采样密度能否覆盖齿槽转矩的高次谐波。齿槽转矩波形不是纯正弦它含有高次谐波。根据采样定理要还原一个波形采样频率至少要高于波形最高次谐波频率的两倍。齿槽转矩的主要谐波成分通常在基波的10次以内少数电机到20次。如果齿槽周期内采42个点能覆盖到21次谐波刚好够用。如果只采360个点一圈才360个点每个齿槽周期只有4.3个点连基波都还原不了补偿效果会大打折扣。把这五个约束放在一起3600是一个在精度、内存、时间三者之间取得平衡的数字。它不是理论最优但它是工程上最舒服的选择。3.2 采样点计算公式的推导把上面的逻辑形式化采样点数Ns可以写成Ns Ncog × K其中Ncog是一圈内的齿槽周期数等于槽数与极数的最小公倍数K是每个齿槽周期内的采样点数。K的取值要满足K ≥ 2 × HH是齿槽转矩需要补偿的最高次谐波次数。工程上H取10到20所以K取20到40。取K32或K42都是常见选择。对于12槽14极电机Ncog84取K42Ns84×423528约等于3600。对于其他极槽配合Ncog不同但odrive固件里通常把采样点数固定在一个值附近然后通过插值来适配不同电机。这就是3600的来历。如果你自己写抗齿槽算法可以按这个公式算import math def calc_sample_points(slots, poles, harmonics20): # 齿槽周期数 槽数与极数的最小公倍数 n_cog slots * poles // math.gcd(slots, poles) # 每个齿槽周期采样点数至少为最高谐波次数的2倍 k 2 * harmonics # 总采样点数 n_samples n_cog * k return n_cog, k, n_samples # 12槽14极电机 print(calc_sample_points(12, 14)) # (84, 40, 3360) # 取整到3600附近实测下来3360和3600的补偿效果差异很小odrive选3600更多是为了取整方便和留一点余量。3.3 采样点不是越多越好过采样的代价有人可能想既然精度重要那我直接采36000个点行不行不行有三个代价。第一标定时间线性增长36000个点标定要十几分钟用户不会接受。第二内存占用增长到144KBSTM32F405的RAM会被吃掉一大半留给其他功能的空间就不够了。第三也是最关键的编码器分辨率限制了有效精度。如果编码器只有14位一圈16384个计数你采36000个点平均每个编码器计数对应2.2个采样点这些采样点之间的位置差异编码器根本分辨不出来测出来的补偿值会有大量重复和噪声反而降低补偿质量。所以采样点数的上限由编码器分辨率决定下限由谐波次数决定3600正好落在中间。4. odrive固件里的抗齿槽实现细节4.1 标定流程从手动转动到自动采集odrive的抗齿槽标定流程大致是这样的电机进入闭环电流模式q轴电流给定为0d轴电流给定一个小值用于维持磁场定向。上位机发送标定命令驱动器控制电机以极低速度匀速转动一圈。转动过程中每个采样位置停下来等待电流和速度稳定记录当前编码器角度和q轴电流值。由于q轴电流在速度环作用下会自动产生抵消齿槽转矩的力矩所以记录下来的q轴电流就是该位置的齿槽补偿量。一圈转完后把3600个点的数据存入非易失存储。这里有个细节标定时速度要足够慢慢到速度环的响应不会引入额外相位滞后。实测下来标定转速在0.1到0.5转/秒比较合适。太快了速度环跟不上测出来的补偿量会偏小太慢了标定时间太长。4.2 查表与插值运行时怎么用这张表运行时驱动器每个电流环周期通常8kHz到20kHz读一次编码器角度然后把机械角度对齿槽周期取模得到当前在齿槽周期内的相位。根据相位在3600点的表里找到对应的索引。由于编码器角度是连续的索引通常落在两个采样点之间需要做线性插值。插值得到的补偿量叠加到q轴电流给定上。线性插值这一步很关键。如果不插值直接用最近邻采样点补偿量会呈阶梯状变化在低速时会产生新的抖动。插值后补偿量连续变化效果平滑很多。在STM32F405上这个查表和插值的计算量很小每个电流环周期多花几微秒对20kHz的电流环没有压力。这也是为什么odrive选STM32F405而不是更低端的F103——F405的168MHz主频和硬件浮点单元让这类实时计算很轻松。4.3 数据存储格式float还是int16odrive固件里抗齿槽表通常用float存储因为STM32F405有FPUfloat运算和int16运算速度差不多但float省去了定标转换的麻烦。3600个float占14.4KB对于192KB RAM来说可以接受。如果你用STM32F103C8T6这种没有FPU的芯片做类似功能建议用int16存储把补偿量按一个比例因子定标。比如补偿量范围是±2A定标因子取1000那么int16范围±32767对应±32.767A精度足够。这样3600个点只占7.2KBF103的20KB RAM也能放下。5. 实操自己动手标定与验证5.1 标定前的准备工作在开始标定之前有几件事必须确认电机已经完成编码器校准电角度和机械角度对应关系正确。如果编码器校准不准标定出来的齿槽表会有系统性偏差。电机负载已经脱开或者负载是恒定的。如果标定时负载变化测出来的补偿量会混入负载转矩补偿效果变差。驱动器电流环参数已经调好电流跟随没有明显滞后。电流环没调好标定数据不可信。供电电压稳定。电压波动会影响电流环输出进而影响标定精度。5.2 标定过程中的注意事项标定过程中最容易踩的坑是速度环带宽。如果速度环带宽设得太高电机在标定转速下会有微小振荡记录的数据会叠加振荡噪声。如果带宽太低电机跟不上位置指令采样点位置会偏移。我的经验是标定时把速度环带宽降到正常值的一半左右积分增益适当减小让电机转得“软”一点。标定完成后再把参数调回去。另一个坑是温度漂移。电机运行一段时间后温度升高磁钢性能会变化齿槽转矩也会变。如果标定时电机是冷的运行一段时间后补偿效果会下降。对于高精度应用建议在热机状态下标定或者标定后做温度补偿。5.3 验证补偿效果的方法标定完成后怎么验证补偿是否有效最简单的方法是让电机在开环电流模式下以极低速度转动用示波器看q轴电流波形。补偿前q轴电流会有明显的周期性波动补偿后波动幅度应该下降80%以上。更定量一点可以用编码器数据算位置误差。让电机在位置模式下走一个低速斜坡记录实际位置和指令位置的偏差。补偿前偏差的峰峰值可能有几度补偿后应该降到0.1度以内。我实测过一台12槽14极的云台电机补偿前低速位置误差峰峰值约1.5度补偿后降到0.08度效果非常明显。5.4 常见问题速查表问题现象可能原因排查方法解决措施补偿后抖动反而变大标定数据相位反了检查编码器方向与补偿表方向是否一致在补偿量前加负号或翻转表索引低速仍有周期性抖动采样点不足或插值未做检查齿槽周期内采样点数是否大于20增加采样点或启用线性插值高速时补偿效果差补偿表只对低速有效观察高速时电流波形高速时齿槽频率高前馈相位滞后可降低补偿量或关闭标定时间过长采样点过多或稳定等待过长检查标定参数减少采样点或缩短每点稳定时间不同位置补偿效果不一致编码器安装偏心检查编码器同心度重新安装编码器或做偏心补偿6. 从3600延伸抗齿槽算法的边界与扩展6.1 什么情况下抗齿槽算法会失效抗齿槽算法不是万能的。有几种情况下它会失效或者效果大打折扣。第一种是齿槽转矩随位置非周期性变化。这通常发生在电机装配不良、磁钢局部退磁或者定子铁芯饱和的情况下。这时候齿槽转矩不再是严格的周期函数查表法就不准了。第二种是负载转矩远大于齿槽转矩。如果电机带了一个很大的恒定负载齿槽转矩在总转矩里占比很小补偿与否对系统性能影响不大这时候做抗齿槽的性价比不高。第三种是转速极高。前面说过前馈补偿有相位滞后。转速越高齿槽频率越高滞后对应的相位误差越大。当齿槽频率接近电流环带宽时补偿基本无效。所以抗齿槽主要用在低速场合。6.2 自适应抗齿槽下一步可以怎么做固定查表法的局限是标定一次就固定了电机老化、温度变化、负载变化都会让补偿效果下降。进阶做法是自适应抗齿槽在运行过程中实时估计齿槽转矩动态更新补偿表。实现思路是用位置误差或者q轴电流的周期性分量通过迭代学习控制ILC逐步修正补偿表。每一圈把误差按位置累加到表里下一圈用更新后的表补偿。这样即使电机特性变化补偿表也能自动跟踪。这个做法在DSP28379这类双核电机控制芯片上比较常见因为迭代学习需要额外的算力。STM32F405单核跑自适应抗齿槽会有点吃力但降低更新频率也能做。6.3 和其他补偿的配合摩擦补偿与惯量前馈抗齿槽只是低速性能优化的一环。实际系统里摩擦转矩、惯量变化、重力负载都会影响低速精度。抗齿槽要和这些补偿配合使用。摩擦补偿通常用Stribeck模型或者库仑摩擦模型根据速度方向给一个补偿量。惯量前馈根据加速度给补偿量。重力补偿根据关节角度给补偿量。这些补偿和抗齿槽补偿叠加在一起才能让低速性能达到最优。在odrive里这些补偿可以通过自定义固件或者上位机脚本实现。我一般建议先做抗齿槽再做摩擦补偿最后做惯量前馈按影响程度从大到小依次处理。7. 一些实测数据与个人体会我在一台12槽14极的无刷电机上做过完整测试编码器是AS5047P14位分辨率。标定3600个点标定转速0.2转/秒每个点稳定20ms总标定时间约90秒。补偿前电机在0.5转/秒速度下q轴电流波动峰峰值约0.35A位置误差峰峰值1.2度。补偿后q轴电流波动降到0.04A位置误差降到0.06度。效果提升非常明显。后来我把采样点降到1800个补偿后位置误差是0.09度差异不大。再降到900个误差变成0.15度开始变差。所以3600确实留了余量1800也能用但3600更稳妥。还有一次遇到补偿后抖动变大的情况排查了半天发现是编码器方向设反了补偿量符号错了相当于把齿槽转矩放大了一倍。这个坑很隐蔽因为标定数据本身看起来是正常的只是方向反了。后来我在标定流程里加了一步自动检测标定完成后让电机低速转一圈看位置误差是变大还是变小如果变大就自动翻转补偿表。最后分享一个小技巧如果你的电机齿槽转矩特别大标定时速度环可能会因为补偿量太大而振荡。这时候可以分两步标定第一步用较小的速度环增益粗标一次得到一个粗略的补偿表第二步用这个表做前馈把速度环增益调回正常值再精标一次。这样标定出来的表更准电机也不会在标定过程中抖得厉害。