拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

增量式编码器零位校准:FOC电机控制的关键步骤与STM32实现

增量式编码器零位校准:FOC电机控制的关键步骤与STM32实现

做FOC电机控制的朋友,多半都经历过这种场面:程序烧进去,电流环参数已经调得很温和,上电给一个小指令,电机非但不转,反而“嗡”的一声定子发烫,电流表直接往上飙。这时候十有八九不是PI参数的问题,而是增量式编码器的零位没校准。FOC、电机控制、增量式编码器、零位校准、STM32,这几个词放在一起,就是很多刚接触FOC的工程师和新手的第一个拦路虎。

这篇内容我会把增量式编码器零位校准这件事彻底讲明白,从为什么要校准、校准前要做什么准备,到5个关键步骤本身,再到可以直接抄的STM32代码,最后附上我实际调试中踩过的坑和排查方法。适合刚入门FOC、手头有用增量式编码器的电机、或者调电流环一直调不出来的人。只要照着做一遍,基本半小时内就能让电机正常出力。

1. 零位校准到底在解决什么问题

先说清楚一个容易混淆的概念:增量式编码器装在电机轴上以后,反馈给控制器的是“相对位置变化”,它本身并不知道转子当前转到哪个角度。但FOC的电流环在Park变换和反Park变换里,需要的是转子磁极的绝对电角度,这个角度差一点也不行。

1.1 FOC电流环为什么非要电角度不可

FOC之所以叫磁场定向控制,核心就是把定子电流分解成d轴和q轴两个分量,d轴对应励磁电流,q轴对应转矩电流。要做到这一点,先得知道转子磁极到底朝哪个方向,然后才能把三相电流变换到那个坐标系里。

这个变换就是Park变换,公式里的sin、cos都来自电角度。如果电角度错了30度,实际施加的电流方向就会偏30度,q轴分量变小,d轴分量变大,电机出力下降、发热增加。如果角度直接偏了90度,那励磁和转矩完全对调,电机不仅不转,还可能剧烈抖动甚至反转,电流一大就把MOS管或驱动芯片烧了。

打个比方:推秋千要在秋千荡到最高点前发力,如果发力时机完全反了,秋千不但荡不起来,还会被推回原地。FOC里的电角度,就是决定“什么时候发力”的关键信息。

1.2 增量式编码器的“增量”二字是关键

增量式编码器输出的是A、B两路正交脉冲,控制器通过计数得到相对位置变化,再配合方向信号判断旋转方向。它跟绝对式编码器最大的区别是:断电后所有位置信息清零,上电之后只能从当前位置开始数脉冲,不知道转子在哪个角度。

所以每次上电,FOC都需要一个办法来确定转子初始位置,这就是“初始位置检测”的来由。有的方案用高频注入,有的用霍尔传感器,而用增量式编码器的方案里,最实用的就是做“零位校准”:先把编码器的零点或某个计数点,和电机定子绕组的某个固定电气角度建立确定关系,之后每次上电都能用公式换算出来。

这里要特别注意:电角度 = 机械角度 × 极对数 + 偏移角。机械角度由编码器计数算出来,极对数是电机固有参数,偏移角则跟编码器安装位置有关。同一批电机、同样的板子,只要编码器装的时候有微小角度差,偏移角就不一样,这才是必须逐台校准的根本原因。

1.3 不校准直接跑,会是什么后果

很多新手一上来就把电流环参数照着教程抄,发现电流始终调不稳。其实不是参数问题,而是电角度全错了,PI再怎么调都是白费劲。未校准时的典型表现是:

现象可能原因
上电后电机嗡嗡响、抖动、发热电角度严重偏移,d轴电流冲高
电流很大但转矩很小角度偏移导致q轴有效分量变小
正转有力、反转无力偏移角符号反了或相序不对
速度一变就剧烈震荡角度换算有跳变,电角度不连续

这些现象我在调试中基本全见过,踩得多了才明白,零位校准不是“锦上添花”,而是FOC能跑起来的前置条件。增量式编码器方案里,这一步做不好,后面谈什么速度环、位置环都是空中楼阁。

2. 校准前的硬件检查与准备

在校准动作开始之前,先花十分钟检查硬件和软件环境,比直接改代码试错要省事得多。我见过不少朋友上来就写校准程序,结果编码器线序接反、供电电压不够,折腾一整天还以为是算法问题。

2.1 增量式编码器的接线与信号类型

增量式编码器通常输出A、B、Z三路信号,A和B相差90度,用来判向和倍频计数,Z是每转一圈一个脉冲的零点信号。FOC日常控制只需要A、B两路,Z信号一般用来辅助校验“这一圈数有没有丢步”。

接线时重点确认三点。第一,供电电压,常见的有5V和3.3V编码器,看编码器说明书,5V供电的编码器输出到STM32引脚时,电平可能超过3.3V,需要在A、B、Z信号线上串电阻分压或加电平转换芯片,直接进MCU会有烧引脚风险。第二,A、B相是否接入定时器的两个通道,比如STM32的TIM2 CH1和CH2,或TIM3 CH1和CH2,需要使用定时器的编码器模式,不能随便接普通GPIO。第三,部分编码器输出是开漏结构,板子上必须要有上拉电阻,否则信号边沿不干净,计数会发生丢步或误计数。

我在自己做的小板子上就吃过亏,编码器5V供电、AB线直接连到STM32的3.3V引脚,刚开始能用,跑了十几分钟后MCU就复位,后来一测发现引脚已经内伤。所以这块不要省,信号电平匹配是稳定运行的前提。

2.2 母线电压、限流与安全准备

校准过程中最常做的操作是开环给电压矢量,把转子强行拉到指定位置。这个操作如果不限流,转子被拉到位之后电流会一直上升,绕组很快发烫,严重时烧驱动。

建议在直流母线输入端串一个限流电阻或使用可调限流电源,把电流限制在电机额定电流的30%以内,够拉动转子就行。同时准备好串口,方便把编码器计数值、计算出的偏移角实时打印出来。没有串口调试,整个校准过程就像摸黑走路,不方便判断到底发生了什么。

另外一定要养成一个好习惯:第一次上电做任何强拖操作时,手不要离开电源开关。一旦发现电流过大或电机剧烈抖震,立刻断电,先检查硬件连接和软件配置,再继续下一步。

2.3 CubeMX中的关键配置

说一个最基础的配置参考,以STM32F103为例。使用TIM3的编码器模式,A相接PA6(CH1),B相接PA7(CH2),编码器分辨率设为4倍频,计数范围设为0到编码器单圈总计数减1。PWM输出用高级定时器TIM1,六路互补PWM驱动三相全桥,ADC采样两相电流,串口1接调试口,波特率115200。

对于新接触STM32的人,有几点容易遗漏。定时器编码器模式要选“Encoder Mode TI1 and TI2”,不是普通的输入捕获。定时器计数方向默认和电机转向匹配,如果后来发现计数方向和实际旋转方向相反,可以在CubeMX里把“Polarity”改成相反的,也可以在代码里对计数值取反。ADC要配置成双通道规则组采样,触发源选择定时器更新事件,确保电流采样和三路PWM同步,这样采到的电流才是真实绕组电流而不是开关噪声。

外设配置项关键设置
TIM3编码器模式Encoder Mode TI1 and TI2,4倍频
TIM1PWM输出中心对齐模式,带互补输出和死区
ADC1电流采样双通道,定时器触发,采样保持时间适当加大
USART1调试输出115200-8-N-1

配置完之后,先用一个最简单的测试程序确认编码器计数是否随转子转动而增减,方向是否一致。这一步通过之后再进入校准流程,能减少后面很多干扰因素。

3. 5个关键步骤逐一拆解

重头戏来了。增量式编码器零位校准的完整流程,我把它拆成5个步骤:确认方向、开环强拖、锁定测偏移、写入并验证、保存与复验。这5步每步都有明确目的,做完就能让FOC正常出力。

3.1 第1步:确认编码器方向与电机相序

原理很简单:给电机施加一个固定电压矢量,让转子被拉到某个确定位置。然后用手或辅助工具轻轻拨动转子,观察编码器计数的增减方向。如果编码器方向和期望方向相反,后面所有角度换算全都会乱,所以必须在最开始确认。

实操方法如下。先给逆变器输出一个简单的两相通电状态,比如A相接正、B相接地,C相悬空,这样定子会产生一个固定方向的磁场,转子会被拉到一个平衡位置。然后手动缓慢旋转转子,观察串口打印的编码器计数值。记录转子顺时针旋转时计数是递增还是递减。

接下来换C相接正、B相接地的状态,同样手动旋转转子,确认计数变化方向没有因为换相改变。这个操作的目的是排除相序接反而引起的“假方向正常”。正常情况是:相同旋转方向下,计数增减方向始终一致。如果换了相序后计数方向变了,说明有一相或两相绕组的接线顺序有误,要先把UVW三个相的接线理清楚再继续。

相序确认之后,还有一个隐藏点要注意:FOC控制的电压矢量和编码器计数方向必须形成“正反馈”关系。也就是当程序输出的空间矢量使电机正向旋转时,编码器计数也应该朝正向变化。这个关系直接影响第4步验证是否顺利,可以顺手在第1步里一起确认。

3.2 第2步:开环强拖建立初始电角度

这个步骤的目的,是让控制器和电机之间先建立一种“已知的关系”。在还没校准零位时,电角度是未知的,所以不能直接上闭环,得用开环方式把转子“拖”起来,让编码器计数值跟着转子同步变化。

操作时,程序里把q轴电流给定设为0,d轴电流给定设为某个固定正值,配合一个缓慢递增的电角度,通过反Park变换生成期望电压矢量。这个角度不依赖编码器,而是代码里用定时器累加出来的。转子会跟着磁场转动,编码器计数值也会随之变化。

我给一个工程里常用的开环强拖代码框架:

void foc_openloop_drag(float target_speed_rps, float id_command) { float theta_elec = 0.0f; float angle_step = 2.0f * PI * target_speed_rps * POLE_PAIRS * DT_SECONDS; while (1) { // 反Park变换,输入id和iq,iq=0 float u_alpha = -id_command * sinf(theta_elec); float u_beta = id_command * cosf(theta_elec); // 经过SVPWM调制输出到逆变器 svpwm_set(u_alpha, u_beta, DC_BUS_VOLTAGE); theta_elec += angle_step; if (theta_elec > 2.0f * PI) theta_elec -= 2.0f * PI; // 打印编码器原始计数值和当前电角度 printf("enc=%d theta=%.3f\r\n", TIM3->CNT, theta_elec); delay_ms(1); } }

注意这里有一个容易被忽视的点:强拖时给定的id方向决定了零位偏移角计算出来之后的符号。不同FOC框架里,有些以d轴正向励磁,有些以id负向励磁,保存偏移角之前要统一符号规则,否则第4步会发现角度怎么都对不上。

3.3 第3步:锁定测偏移,算出零位偏置角

这是整个校准流程的核心,说白了就是“把转子锁到一个已知电角度,然后读编码器的值,反推出偏移角”。

具体做法是,依次向电机施加六个不同的电压矢量,每个矢量对应一个已知电角度。比如常用的六步换向法,六个矢量分别对应0度、60度、120度、180度、240度、300度的电气角度。每个矢量保持一段时间,让转子稳定下来,然后记录编码器计数值。

为什么要做六个点而不是只做一个点?因为单个锁定点受齿槽转矩、摩擦力、负载偏差影响,可能存在几度误差。六个点均匀分布,取平均之后能把大部分机械不对称误差抵消掉,得到的偏移角更稳。

我用的计算逻辑是:

#define ENCODER_PPR 4096 // 编码器线数×4倍频后的单圈总计数 #define POLE_PAIRS 7 // 电机极对数,写自己的电机实际值 int32_t enc_locked[6]; float angle_locked[6] = {0, 60, 120, 180, 240, 300}; // 单位:度 // 依次锁定6个电角度 for (int i = 0; i < 6; i++) { set_lock_voltage_vector(angle_locked[i]); delay_ms(500); // 等转子完全稳定 enc_locked[i] = TIM3->CNT; } // 计算每个锁定点对应的偏移角,然后取平均 float offset_sum = 0.0f; for (int i = 0; i < 6; i++) { float mech_angle = (float)enc_locked[i] / ENCODER_PPR * 2.0f * PI; float elec_angle = mech_angle * POLE_PAIRS; float offset = angle_locked[i] * PI / 180.0f - elec_angle; // 归一到 0 ~ 2pi while (offset < 0) offset += 2.0f * PI; while (offset >= 2.0f * PI) offset -= 2.0f * PI; offset_sum += offset; } // 6个点取平均 float zero_offset = offset_sum / 6.0f; while (zero_offset < 0) zero_offset += 2.0f * PI; while (zero_offset >= 2.0f * PI) zero_offset -= 2.0f * PI; printf("zero_offset=%.3f rad\r\n", zero_offset);

把这个偏移角保存下来,后续FOC运行时:

float get_elec_angle(void) { float mech_angle = (float)TIM3->CNT / ENCODER_PPR * 2.0f * PI; float elec_angle = mech_angle * POLE_PAIRS + zero_offset; // 归一到 0 ~ 2pi while (elec_angle >= 2.0f * PI) elec_angle -= 2.0f * PI; while (elec_angle < 0) elec_angle += 2.0f * PI; return elec_angle; }

锁定电压矢量的实现方式有讲究。可以直接操作六个开关状态,也可以用SVPWM输出固定的alpha beta分量,后者更接近实际运行状态,建议使用。另外锁定时长要足够,我一般用500毫秒,如果电机转动惯量大就加长到1秒,确保机械振荡衰减完毕再读数。

3.4 第4步:写入偏移值并验证角度同步性

偏移角算出来以后,下一步不是直接跑闭环,而是先做一个低速同步验证。把计算出的zero_offset代进get_elec_angle函数,然后在开环强拖模式下,以非常低的速度(比如每秒1圈)拖动转子,同时打印两个角度:代码里累加的期望电角度,和通过编码器换算出来的实际电角度。

理想情况下,这两个角度之差应该稳定在一个很小的范围内,不会随转子位置变化而出现明显波动。如果差值忽大忽小,说明极对数设置错了,或者编码器倍频配置不对。如果差值基本稳定但相差一个常数,那是偏置角没算准,重新检查第3步的符号。

验证通过后,可以在闭环电流模式下做一次正反转测试。给一个较小的q轴电流指令,比如额定电流的10%,观察电机是否能平稳转动。正转和反转的力矩应该基本一致,电流波动小,没有明显的“一顿一顿”的感觉。

这一步最常见的坑是:强拖同步验证看着没问题,一上闭环就抖。原因往往是电流采样时序不对,或者PWM死区设置不合适,这两个问题跟零位校准没有直接关系,但很容易被误判成校准失败。所以我一般建议,闭环测试时先把d轴电流设为0,只给q轴电流,这样只要角度正确,电机就会以正常方式出力,能更快定位到真正的问题。

3.5 第5步:保存偏移值并做好重复性确认

校准结果不能只留在RAM里,掉电就丢。需要把zero_offset保存到STM32的Flash或外部EEPROM,上电时读取并加载到全局变量中。

保存时注意几个细节。浮点数直接存Flash需要对地址做4字节对齐,或者把角度乘以一个系数转成整数存储。我习惯用角度制的整数,比如把偏移角乘以1000,转换成一个int32_t,读回来再除以1000,这样既能满足精度,又避免了浮点存储的对齐问题。

保存完偏移值后,要断电重新上电,重复做几遍第3步的锁定测量。每遍算出来的偏移角应该非常接近,波动范围最好在±1度以内。如果两次测量差了十几度,大概率是编码器安装松动、联轴器打滑,或者强拖时转子没锁稳就记录了计数值。这种机械问题代码是治不了的,先检查安装再重新标定。

批量生产时,每台电机的偏移角都要单独标定并把结果写入各自的控制器。不要试图“复制粘贴”某台电机标定好的参数到另一台电机上,因为编码器安装角度的微小差异足以导致FOC出力明显变差。

4. 代码实现:与FOC电流环完整对接

这一节给出一个可运行的代码组织方式,帮助你把校准流程嵌入到自己的STM32工程里。代码按模块分文件,逻辑清晰,方便替换硬件相关部分。

4.1 代码模块划分

我把校准和FOC运行拆成三个文件:encoder.c负责编码器计数读取和电角度换算;foc_align.c负责六步锁定和偏移角计算;foc_core.c负责电流环闭环控制。这样校准逻辑和运行逻辑互相独立,调试和以后改成无感FOC都方便。

encoder.c里对外提供三个函数:

void encoder_init(void); float get_elec_angle(void); // 返回电角度,单位rad int32_t get_enc_count(void); // 返回编码器原始计数值

foc_align.c里是对外提供标定入口:

float foc_align_run_zero_offset(void); // 执行6步锁定,返回偏移角 void foc_align_save_offset(float offset); float foc_align_load_offset(void);

foc_core.c里就是常规的电流环,核心循环里直接调用get_elec_angle:

void foc_current_loop(void) { float ia = get_phase_a_current(); float ib = get_phase_b_current(); float ic = -ia - ib; float theta = get_elec_angle(); // Clarke和Park变换 float i_alpha = ia - 0.5f * (ib + ic); float i_beta = 0.866f * (ib - ic); float id = i_alpha * cosf(theta) + i_beta * sinf(theta); float iq = -i_alpha * sinf(theta) + i_beta * cosf(theta); // PI调输出vd、vq,这里略去具体实现 // vd = pid_update(&pid_id, id_ref, id); // vq = pid_update(&pid_iq, iq_ref, iq); // 反Park变换 // v_alpha = vd * cosf(theta) - vq * sinf(theta); // v_beta = vd * sinf(theta) + vq * cosf(theta); // svpwm_set(v_alpha, v_beta, DC_BUS_VOLTAGE); }

4.2 六步锁定测偏移的实现细节

六步锁定的实现,不是简单地把六个开关状态依次设一遍,而是要确保每次锁定都对应一个明确的电角度。工程上我一般用SVPWM函数来生成固定的alpha、beta电压分量,这样锁定方式和后续闭环运行方式完全一样,消除了“开关状态锁定”和“SVPWM运行”之间可能存在的角度映射误差。

对应关系可以这样理解。SVPWM的六个扇区边界矢量分别对应电角度0、60、120、180、240、300度,正好对应六个开关状态。用SVPWM输出这些矢量时,每个矢量的方向就是对应的电角度,然后通过反Park变换得到alpha、beta分量,再交给SVPWM模块去生成PWM占空比。

代码实现如下:

void set_lock_voltage_vector(float angle_deg) { float angle_rad = angle_deg * PI / 180.0f; // 电压小一点,能锁住转子就行 float u_alpha = 0.2f * DC_BUS_VOLTAGE * cosf(angle_rad); float u_beta = 0.2f * DC_BUS_VOLTAGE * sinf(angle_rad); svpwm_set(u_alpha, u_beta, DC_BUS_VOLTAGE); }

锁定时另一个关键项是“等待稳定”。转子是一个机械系统,施加电压矢量后它会有振荡衰减的过程,刚切换状态就立刻读数,往往锁在了一个偏摆位置,偏移角计算出来就不准。等待时间至少要覆盖机械振荡衰减周期,500毫秒通常够用,大惯量电机适当加长。

4.3 Flash存储偏移角的示例

偏移角的保存,我用的Flash存储实现大致是:

#define ALIGN_OFFSET_ADDR 0x0800F000 // 选一个空闲Flash页 void foc_align_save_offset(float offset) { int32_t value = (int32_t)(offset * 1000.0f); HAL_FLASH_Unlock(); FLASH_ErasePage(ALIGN_OFFSET_ADDR); uint32_t address = ALIGN_OFFSET_ADDR; // 注意:F103的Flash编程以16位为单位,这里按32位写两次 *(volatile uint16_t *)address = (uint16_t)(value & 0xFFFF); address += 2; *(volatile uint16_t *)address = (uint16_t)((value >> 16) & 0xFFFF); HAL_FLASH_Lock(); } float foc_align_load_offset(void) { int32_t value = 0; uint32_t address = ALIGN_OFFSET_ADDR; value |= *(volatile uint16_t *)address; address += 2; value |= ((int32_t)*(volatile uint16_t *)address) << 16; return (float)value / 1000.0f; }

注意这个代码是F1系列的Flash操作方式,F4、H7等系列的地址对齐要求和编程单位不同,直接照搬会卡死在写Flash的函数里。用其他型号芯片时先去查参考手册的Flash编程章节,避免踩坑。

4.4 上电初始化顺序

上电时,主函数的初始化顺序建议是:

int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM3_Init(); // 编码器模式 MX_TIM1_Init(); // PWM MX_ADC1_Init(); MX_USART1_Init(); encoder_init(); // 读取标定的零位偏移 float offset = foc_align_load_offset(); if (offset < 0 || offset >= 2.0f * PI) // 简单校验读取结果是否合理 { // 不合理则进入标定模式 offset = foc_align_run_zero_offset(); foc_align_save_offset(offset); } foc_core_init(offset); // 主循环开始执行FOC和速度环 while (1) { // 根据业务逻辑运行 } }

这样的好处是首次烧录程序时,Flash里没有有效偏移值,自动进入标定模式;标定完保存后,下次上电直接加载,不需要每次开机都做一次机械锁定。

实际上,很多量产设备会在产线里做专门的标定工装,通过串口下发“进入标定模式”指令,标定完成后自动保存并退出。调试阶段用一个按键触发标定更顺手,不改主程序结构也能灵活切换。

5. 常见问题与排查技巧实录

这一节的内容全是我自己调试过程中遇到并解决过的真实问题,挑几条经常出现且具有迷惑性的记录下来,能帮大家少走很多弯路。

5.1 现象排查速查表

现象排查方向处理办法
六步锁定时转子乱跳电压矢量方向与电机相序不匹配检查UVW三相接线顺序
锁定后计数值反复变化转子未完全稳定就读取延长锁定等待时间到1秒以上
计算出的偏移角每次差异大编码器联轴器松动或计数方向翻转重新固定联轴器,检查A/B相接线
强拖时电流快速上升PWM占空比太大或限流失效降低锁定电压系数,确认限流保护有效
闭环测试时正反转力矩差很大偏移角符号错误或极对数填错重新核对极对数,检查偏移角符号约定
速度增加后电流振荡加剧电角度换算存在跳变检查编码器计数是否溢出引发不连续

这里面最坑的是“编码器计数溢出”。如果你的定时器计数范围设置为0到ARR值自动回绕,那么机械角度公式里TCNT直接从4095跳到0时,换算出的角度会瞬间变化,如果不做归一化处理,电角度会出现一个负跳变,电流环看到的是一个剧烈变化的输入,自然就不稳定。

我的处理办法是在获取编码器角度时,把计数值转成有符号的浮点累积值,而不是直接用裸TCNT做比例换算。简单做法是每次中断里计算增量和方向,累加到一个int32_t全局变量,用这个累加值换算机械角度,就不会有回绕问题。

5.2 用手转电机来检验偏置角对不对

有个很简单却很实用的手动校验法,分享给大家。把电机从闭环控制中摘出来,只给三相逆变器一个固定开关状态,让电机处于锁定状态,然后用手缓慢转动转子,同时观察d轴电流反馈值的变化。

如果偏移角正确,当你手动旋转转子时,反馈电流会呈现一个标准的正弦或余弦变化,且峰值出现在你预期的机械角度附近。如果波形明显畸变或相位不对,多半是极对数设置错误。如果波形完全跟编码器位置对不上,通常是编码器计数方向反了或者接线有误。

这个方法不需要示波器,串口打印速度放慢一点也能看趋势。我第一次用这个方法时,很快就发现自己其中一个工程把极对数少写了一半,波形频率正好差一半,调整之后一切正常。

5.3 一个真实案例:偏移角符号写反的代价

有一次我调试一台带减速箱的永磁同步电机,编码器装在电机尾部,前后换了三版主板,都出现在某一个方向电流很大但有异响的毛病。按照前面步骤重新校准,校准结果显示偏移角大约在1.9弧度附近,我一开始顺手就写进了代码,结果正转正常、反转明显无力。

反复检查后发现,减速箱在正反转时存在不同方向的回程间隙,导致手动锁定和旋转验证时,转子实际位置跟编码器读数之间有差异。解决办法是标定时让转子先向固定方向转半圈再锁入目标位置,消除减速箱间隙后再读数。修改之后正反转力矩终于一致。

这件事给我最大的启发是:零位校准不只是调一个软件参数,它跟整个传动链的机械特性息息相关。换机械结构、换联轴器、换减速箱之后,偏移角必须重新标定,不要觉得软件没动就能继续用旧参数。

5.4 两点避坑技巧

最后说两个代码层面的小技巧。

第一点,所有三角函数调用尽量使用查表法或者在定点硬件加速支持下计算,否则在STM32F1这种主频不高的芯片上,FOC循环里频繁计算sin/cos会占掉大量CPU时间。我一般用256点的正余弦查找表,精度足够电流环使用,性能余量直接提升一截。

第二点,标定和正式运行使用同一个电角度换算函数,不要在标定时单独写一套角度计算逻辑。任何“标定简单但运行复杂”的做法,都可能引入两套逻辑之间的隐性差异,一旦出现莫名其妙的问题,排查起来非常痛苦。统一函数、统一符号、统一归一化方式,是我在多个项目里一直坚持的约定。

增量式编码器的零位校准,说难不难,说简单也不简单。核心就是建立“编码器读数”和“转子电角度”之间的固定换算关系,核心工具就是六步锁定加平均。这一套方法不挑芯片、不挑电机,换到任何一款自研FOC板上都能直接用。按这个流程走一遍,电机大概率能立刻转得又稳又顺。

返回列表