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 RAM,14KB完全可以接受。如果用int16存储,只要7.2KB。这个约束也不紧。
约束四:标定时间。标定时电机要慢慢转一圈,每个采样点停留一下采集数据。如果每个点采集10ms,3600个点就是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 × H
H是齿槽转矩需要补偿的最高次谐波次数。工程上H取10到20,所以K取20到40。取K=32或K=42都是常见选择。
对于12槽14极电机,Ncog=84,取K=42,Ns=84×42=3528,约等于3600。对于其他极槽配合,Ncog不同,但odrive固件里通常把采样点数固定在一个值附近,然后通过插值来适配不同电机。这就是3600的来历。
如果你自己写抗齿槽算法,可以按这个公式算:
import math def calc_sample_points(slots, poles, harmonics=20): # 齿槽周期数 = 槽数与极数的最小公倍数 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个点标定要十几分钟,用户不会接受。第二,内存占用增长到144KB,STM32F405的RAM会被吃掉一大半,留给其他功能的空间就不够了。第三,也是最关键的,编码器分辨率限制了有效精度。如果编码器只有14位,一圈16384个计数,你采36000个点,平均每个编码器计数对应2.2个采样点,这些采样点之间的位置差异编码器根本分辨不出来,测出来的补偿值会有大量重复和噪声,反而降低补偿质量。
所以采样点数的上限由编码器分辨率决定,下限由谐波次数决定,3600正好落在中间。
4. odrive固件里的抗齿槽实现细节
4.1 标定流程:从手动转动到自动采集
odrive的抗齿槽标定流程大致是这样的:
- 电机进入闭环电流模式,q轴电流给定为0,d轴电流给定一个小值用于维持磁场定向。
- 上位机发送标定命令,驱动器控制电机以极低速度匀速转动一圈。
- 转动过程中,每个采样位置停下来,等待电流和速度稳定,记录当前编码器角度和q轴电流值。
- 由于q轴电流在速度环作用下会自动产生抵消齿槽转矩的力矩,所以记录下来的q轴电流就是该位置的齿槽补偿量。
- 一圈转完后,把3600个点的数据存入非易失存储。
这里有个细节:标定时速度要足够慢,慢到速度环的响应不会引入额外相位滞后。实测下来,标定转速在0.1到0.5转/秒比较合适。太快了速度环跟不上,测出来的补偿量会偏小;太慢了标定时间太长。
4.2 查表与插值:运行时怎么用这张表
运行时,驱动器每个电流环周期(通常8kHz到20kHz)读一次编码器角度,然后:
- 把机械角度对齿槽周期取模,得到当前在齿槽周期内的相位。
- 根据相位在3600点的表里找到对应的索引。
- 由于编码器角度是连续的,索引通常落在两个采样点之间,需要做线性插值。
- 插值得到的补偿量叠加到q轴电流给定上。
线性插值这一步很关键。如果不插值,直接用最近邻采样点,补偿量会呈阶梯状变化,在低速时会产生新的抖动。插值后补偿量连续变化,效果平滑很多。
在STM32F405上,这个查表和插值的计算量很小,每个电流环周期多花几微秒,对20kHz的电流环没有压力。这也是为什么odrive选STM32F405而不是更低端的F103——F405的168MHz主频和硬件浮点单元让这类实时计算很轻松。
4.3 数据存储格式:float还是int16
odrive固件里,抗齿槽表通常用float存储,因为STM32F405有FPU,float运算和int16运算速度差不多,但float省去了定标转换的麻烦。3600个float占14.4KB,对于192KB RAM来说可以接受。
如果你用STM32F103C8T6这种没有FPU的芯片做类似功能,建议用int16存储,把补偿量按一个比例因子定标。比如补偿量范围是±2A,定标因子取1000,那么int16范围±32767对应±32.767A,精度足够。这样3600个点只占7.2KB,F103的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极的无刷电机上做过完整测试,编码器是AS5047P,14位分辨率。标定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更稳妥。
还有一次遇到补偿后抖动变大的情况,排查了半天发现是编码器方向设反了,补偿量符号错了,相当于把齿槽转矩放大了一倍。这个坑很隐蔽,因为标定数据本身看起来是正常的,只是方向反了。后来我在标定流程里加了一步自动检测:标定完成后让电机低速转一圈,看位置误差是变大还是变小,如果变大就自动翻转补偿表。
最后分享一个小技巧:如果你的电机齿槽转矩特别大,标定时速度环可能会因为补偿量太大而振荡。这时候可以分两步标定:第一步用较小的速度环增益粗标一次,得到一个粗略的补偿表;第二步用这个表做前馈,把速度环增益调回正常值,再精标一次。这样标定出来的表更准,电机也不会在标定过程中抖得厉害。