简介面向新能源汽车驱动电机软件开发与嵌入式控制学习者的旋变软解码MCU代码包围绕旋变励磁信号生成与输出信号软件解码展开适合需要理解位置反馈闭环、并在STM32平台上实践PWM励磁与ADC采样解码的开发者参考。包内共1269个文件以724个C源文件、341个头文件为主辅以97个汇编文件、73个文本说明及24个ICF链接配置等覆盖底层驱动、外设初始化与工程构建配置压缩包约7.1MB。已有1402人学习说明该主题在电机控制方向具有较高关注度。代码基于STM32G4系列外设资源组织涉及HRTIM、TIM、ADC、I2C、UART、SPI等模块可用于梳理励磁波形参数设置、模拟信号采集、滤波解调与角度位置计算的实现路径并观察工程目录划分和HAL库调用方式。对源码学习与问题排查较有帮助。1. 伺服上电角度先在原地跳了 ±3°伺服驱动器上电做闭环电机还没转上位机的角度曲线先在原地跳了 ±3°。查了两天不是编码器干扰也不是地线没接好问题出在旋变解码这条路上励磁和采样各跑各的定时器相位漂一点解出来的包络就跟着飘最后 atan2 出来的角度自然不稳。这类问题在低成本伺服、云台、机器人关节、电驱台架里反复出现专用解码芯片能解决但 BOM 成本和差分走线都上去了。这份旋变软解码 MCU 代码走的是另一条路MCU 自己发 10 kHz 励磁同一套定时器触发 sin/cos 双通道 ADC 采样同步解调出包络再用跟踪环算出角度和速度全程不依赖外部解码芯片。适合已经上手电机控制、想把旋变这套信号链接进自己板子、并且愿意为一两个参数调半天的开发者。2. 技术路线与整体架构为什么把解码从芯片搬回 MCU2.1 旋变的信号本质是两条被调制的正弦波先把数学前提摆清楚不然后面代码看不懂。旋变转子绕组上加一路高频励磁常见是 10 kHz、2~4 Vrms 的正弦V_exc(t) A · sin(ωt)定子的 sin / cos 两路绕组感应出来的信号幅度被转子角度 θ 调制载波还是那路励磁V_sin(t) k · A · sin(ωt) · sin(θ) V_cos(t) k · A · sin(ωt) · cos(θ)k 是变压比多数旋变在 0.5 左右。注意这里的关键点角度信息藏在包络的幅度比里不在频率里。所以解码的本质是两步——先把包络从载波上剥下来再对两个包络做反正切或跟踪。剥包络的办法就是同步解调。把采样到的 V_sin 乘上同相的励磁参考 sin(ωt)用积化和差展开V_sin · sin(ωt) kA·sin(θ)·sin²(ωt) (kA/2)·sin(θ)·[1 - cos(2ωt)]结果是一个直流分量加一个二倍频分量。低通滤掉二倍频剩下的 (kA/2)·sin(θ) 就是纯包络。cos 通道同理。这一步用模拟乘法器做就是硬件解调用 ADC 采样后在代码里乘就是软解码两条路的数学完全一样差别只在实现位置。提示励磁参考的相位必须和实际加到旋变上的励磁对齐。中间隔了 DAC、功放、隔离变压器、RC 滤波相位一定会偏这个偏移量在 3.3 节和 4.3 节都会再遇到。2.2 三条实现路线的取舍做旋变解码的路子基本就三条选哪条取决于你的成本红线、转速范围和板子空间。我在不同项目上三条都用过下面这张表是踩过坑之后的实际体感不是理论值。维度专用解码芯片硬件解调 ADC全软件同步解调本项目BOM 成本高一颗芯片占大头中需要乘法器/模拟开关 运放低只吃 MCU 的 ADC 和算力角度延迟小芯片内部硬件流水中取决于低通截止频率可调靠滤波器带宽和跟踪环带宽换低速精度好好好前提是采样和励磁严格同步高速表现受芯片内部带宽限制受模拟低通限制受采样率和解调延迟限制需要相位补偿参数灵活性低靠外围电阻定中高全在代码里改抗干扰好差分输入做得完善中取决于布线和采样时刻适合场景批量大、要过认证、不想调算法老平台改造、MCU 算力紧张成本敏感、需要自定义带宽和诊断选软解码最实际的动因往往不是省钱而是可控。角度延迟、带宽、故障阈值、零点偏移全在代码里换一款电机不用动硬件调参数就行。代价是采样时刻、相位补偿、通道一致性这些细节全得自己兜住兜不住就是玄学抖动。2.3 代码的数据流与模块划分整个工程按信号流切成五块从定时器一路串到角度输出TIM 励磁定时器 ── 正弦查表 ── DAC/PWM ── 功放 ── 旋变 │ └── 同一定时器触发 ── ADC 双通道sin/cos── 偏置/增益标定 │ v 同步解调乘参考 累加 │ v 包络 sinθ / cosθ │ v Type-II 跟踪环 ── θ、ω关键在左半边那条虚线励磁生成和 ADC 触发必须共用同一个定时器基准。这是整个方案能不能稳的地基。如果励磁走 TIM1、采样走 TIM3两个定时器哪怕配置成同一个频率晶振和分频的微小差异也会让相对相位慢慢漂移表现出来的现象就是角度误差按拍频周期来回摆非常像干扰但示波器上看信号干干净净。这类问题我后面第 5 章单独列了一条。模块划分上代码分成四层方便移植bsp_resolver.c定时器、DAC、ADC 的初始化和底层读写换 MCU 只改这一层exc_gen.c正弦表、相位累加器、励磁幅值控制demod.c同步解调、低通/整周期累加、包络输出angle_track.c跟踪环、角度归一化、速度估计、故障诊断层与层之间只传结构体不传全局变量。这个习惯在做多轴控制的时候特别值钱一套代码能同时跑三路旋变而不用复制粘贴。3. 核心模块拆解从励磁波形到一个稳定的角度值3.1 励磁生成查表法 相位累加器点数要对齐采样励磁波形不用实时算 sin太费时间标准做法是查表。表长取 2 的整数次幂相位累加器溢出的位就是查表索引这样连取模都省了。/* 励磁参数10 kHz 励磁每周期固定 64 个点DAC 更新率 640 kHz */ #define EXC_TABLE_BITS 8 /* 256 点正弦表 */ #define EXC_POINTS_PER_CYCLE 64 /* 一个励磁周期采/发 64 点 */ #define EXC_PHASE_STEP ((1u EXC_TABLE_BITS) / EXC_POINTS_PER_CYCLE) static const int16_t sin_table[1u EXC_TABLE_BITS] { /* 生成好的 256 点 */ }; static uint16_t exc_phase; /* 相位累加器0~255 循环 */ static uint16_t exc_amp 2048; /* 励磁幅值对应 DAC 中点偏置 */ /* 定时器中断里调用频率 励磁频率 × EXC_POINTS_PER_CYCLE */ void exc_isr(void) { uint16_t idx (exc_phase 0) ((1u EXC_TABLE_BITS) - 1); int32_t v (int32_t)sin_table[idx] * exc_amp 12; dac_write(DAC_CH_EXC, (uint16_t)(2048 v)); /* 12 位 DAC中点 2048 */ exc_phase EXC_PHASE_STEP; }这段代码有三个参数值得拎出来说。EXC_POINTS_PER_CYCLE决定了一个励磁周期被切成多少份也决定了 ADC 的采样节奏。取 64 意味着 DAC 更新率 640 kHz对多数电机控制 MCU 的 DAC 来说偏高实际更常见的是 32 点320 kHz或者把励磁降到 5 kHz。点数越多整周期累加的抑制效果越好但对 DAC 和 ADC 的吞吐要求也越高得按你手上芯片的规格倒推。exc_amp是在线可调的。这个变量存在的意义是标定不同旋变的变压比、不同板子的功放增益都不一样靠改电阻太笨直接在代码里乘一个系数最省事。调到输出包络峰值占 ADC 量程的 60%~70% 比较合适太低了信噪比差太高了削顶。exc_phase必须和 ADC 触发严格同源。常见做法是不用两个定时器而是让这个定时器在更新 DAC 的同时用同一个计数器的比较事件去触发 ADC。这样从物理上保证了每个励磁周期的第 n 个点采样点的相位永远一致不会漂。3.2 ADC 同步采样与通道标定ADC 这边要采两路差分信号sin 和 cos。差分转单端在片上运放或者外部运放完成代码只需要处理偏置和增益。typedef struct { int32_t offset; /* 通道偏置ADC 码值 */ int32_t gain_q15; /* 增益归一化系数Q15 */ } chan_cal_t; static chan_cal_t cal_sin { 2048, 32768 }; static chan_cal_t cal_cos { 2048, 32768 }; /* 在励磁周期固定相位点被 ADC 转换完成中断调用 */ void adc_isr(void) { int32_t raw_s adc_read(ADC_CH_SIN); int32_t raw_c adc_read(ADC_CH_COS); /* 去偏置 增益归一化输出范围约 ±2048 */ int32_t s ((raw_s - cal_sin.offset) * cal_sin.gain_q15) 15; int32_t c ((raw_c - cal_cos.offset) * cal_cos.gain_q15) 15; demod_push(s, c, exc_phase); /* 连同当前励磁相位一起送进解调器 */ }offset是零位偏置。旋变的 sin/cos 差分输出理论上零点在电源中点但运放的失调、ADC 的零点误差会让实际零点偏几十个码值。这几十个码值在 atan2 里会变成固定角度误差低速时尤其明显——电机指令给 0 速实测一直在 ±0.5° 来回蹭很多驱动器抖动的锅其实是这个偏置没标。gain_q15是两路增益失配的补偿。旋变两路绕组的匝数、运放电阻的精度都不可能完全一致增益差 1% 就意味着角度误差里有二次谐波成分误差曲线一个电周期内波动两次。这个系数靠 4.3 节的标定流程测出来不用手工算。把exc_phase一起送进解调器是为了让解调用的参考相位和发出去的波形对齐。如果你用的是半周期积分那种方案这一步可以省但用点乘解调就必须带上。3.3 同步解调整周期累加比 IIR 低通更稳解调有两种写法我都写过最后基本都换成了整周期累加。第一种是逐点乘参考再走低通/* 逐点解调 一阶 IIR 低通截止频率 励磁频率的 1/10 左右 */ static int32_t lp_s, lp_c; void demod_point(int32_t s, int32_t c, uint16_t phase) { int32_t ref sin_table[(phase 0) 0xFF]; /* 与励磁同相的参考 */ int32_t d_s (s * ref) 12; /* 乘参考含二倍频 */ int32_t d_c (c * ref) 12; /* 一阶低通alpha 越小越平滑、延迟越大 */ lp_s ((d_s - lp_s) * DEMOD_ALPHA) 8; lp_c ((d_c - lp_c) * DEMOD_ALPHA) 8; }DEMOD_ALPHA是低通系数。这个参数调起来很别扭取小了二倍频滤不干净包络上叠着 20 kHz 的纹波跟踪环跟着抖取大了包络的相位延迟上去了转速一高角度就滞后。而且一阶 IIR 对二倍频的抑制只有 -20 dB/dec想滤干净就得把截止频率压得很低延迟根本受不了。所以我一般用第二种——整周期累加。原理很简单把一整个励磁周期的采样值乘上对应的参考值再累加二倍频分量在整周期上积分正好为零天然不需要滤波器。/* 整周期同步解调N 个点累加完输出一次包络 */ #define DEMOD_N 64 /* 必须等于每周期采样点数 */ static int32_t acc_s, acc_c; static uint16_t acc_cnt; void demod_push(int32_t s, int32_t c, uint16_t phase) { int32_t ref sin_table[(phase 0) 0xFF]; acc_s (s * ref) 12; acc_c (c * ref) 12; acc_cnt; if (acc_cnt DEMOD_N) { /* 整周期到输出包络 */ int32_t env_s acc_s / (DEMOD_N / 2); /* 归一化恢复幅值量纲 */ int32_t env_c acc_c / (DEMOD_N / 2); angle_track_update(env_s, env_c); /* 送跟踪环 */ acc_s acc_c 0; acc_cnt 0; } }DEMOD_N必须严格等于一个励磁周期内的采样点数这是整个算法成立的前提。如果采样率和励磁频率不是整数倍关系累加窗口里就凑不齐整周期二倍频残留会直接漏到包络上表现为角度误差按差频周期波动。这也是为什么 3.1 节强调采样触发要和励磁同源——不同源就保证不了这个整数关系。env_s / (DEMOD_N / 2)这个归一化系数是从前面的推导来的点乘之后直流分量是幅值的一半再乘点数所以要除以 N/2 才能还原成原始幅值量纲。写成右移也行但 N/2 不一定是 2 的幂编译器优化不了除法在这里一个周期只执行一次开销可以忽略。整周期累加还有一个附带好处它天然是一个矩形窗 FIR对励磁频率整数倍的干扰有很强的抑制工频和开关频率的耦合只要不在整数倍上衰减都很可观。代价是输出率等于励磁频率10 kHz 励磁就是 10 kHz 更新一次包络对电流环 10~20 kHz 的控制周期来说够用再快就得上更高的励磁频率。3.4 角度跟踪环从包络到 θ 和 ω拿到 sinθ、cosθ 两个包络之后最直接的办法是atan2(env_s, env_c)。能出结果但我一般不这么用原因有两个一是 atan2 在包络过零附近对噪声极其敏感噪声被放大成角度尖峰二是速度只能靠角度差分10 kHz 的更新率下差分噪声很大低速时速度环基本没法用。标准做法是 Type-II 跟踪环本质是一个二阶锁相环typedef struct { int32_t theta; /* 估计角度Q15 定点满量程 2π */ int32_t omega; /* 估计速度每个控制周期的角度增量 */ int32_t kp; /* 比例增益 */ int32_t ki; /* 积分增益 */ } pll_t; static pll_t pll; void angle_track_update(int32_t env_s, int32_t env_c) { /* 叉积求相位误差sin(θ-θ̂) ≈ θ-θ̂小误差下线性 */ int32_t sin_hat sin_q15(pll.theta); int32_t cos_hat cos_q15(pll.theta); int32_t err ((env_s * cos_hat) - (env_c * sin_hat)) 15; /* 二阶环积分项出速度速度再积分出角度 */ pll.omega (err * pll.ki) 15; pll.theta pll.omega ((err * pll.kp) 15); /* 角度归一化到 [0, 2π)用掩码实现避免取模 */ pll.theta 0x7FFF; }err这一行是整个环的核心。用叉积而不是直接减角度是因为它天然带 sin 特性大误差时自动限幅不会像直接相减那样在跨零时产生 2π 的跳变。误差小的时候 sin(Δθ) ≈ Δθ环就退化成一个线性二阶系统好分析也好调。kp和ki决定环的带宽和阻尼。有个经验公式先定带宽 ω_b单位 rad/s再取 kp ≈ 2ζω_b·T、ki ≈ ω_b²·T²T 是包络更新周期ζ 取 0.707。实际调的时候我一般从 200 Hz 带宽起步看阶跃响应有没有过冲再往上加。带宽越高动态越好但包络上的残余纹波会直接进速度输出低速时速度反馈会毛。伺服应用里 100~300 Hz 是常见区间。pll.theta 0x7FFF这个掩码要求角度用 15 位表示满量程 2π也就是 32768 对应 360°。定点的好处是三角函数查表和归一化都变成移位坏处是所有增益系数都要按 Q15 换算写的时候容易把小数点位置搞错。我建议在代码里统一约定凡是角度都用 int32 存 Q15凡是增益都用 Q15凡是幅值都用 ADC 码值三层量纲不要混。速度输出pll.omega的单位是每包络周期转过的 Q15 角度要换算成 rpm 得乘上更新率和 60/(2π)再除以电机极对数。这个换算建议单独写一个函数别散在业务代码里否则改励磁频率的时候会漏改。4. 跑起来工程结构、参数配置与上电标定4.1 工程目录与编译入口工程按可移植的思路分层目录结构大致这样resolver_sw_decode/ ├── bsp/ │ ├── bsp_resolver.c # 定时器/DAC/ADC 初始化与读写 │ ├── bsp_resolver.h │ └── board_cfg.h # 引脚、通道、时钟配置 ├── algo/ │ ├── exc_gen.c # 正弦表、相位累加器 │ ├── demod.c # 同步解调与整周期累加 │ ├── angle_track.c # Type-II 跟踪环 │ └── resolver_cal.c # 偏置/增益/相位标定 ├── app/ │ ├── main.c # 初始化、主循环、上位机通信 │ └── shell_cmd.c # 参数在线修改与波形输出 └── tools/ ├── sin_table_gen.py # 生成正弦表头文件 └── cal_collect.py # 采集标定数据、拟合补偿系数编译入口只有一个main.c上电顺序有讲究不能乱int main(void) { clock_init(); /* 先定系统时钟后面所有分频都依赖它 */ bsp_gpio_init(); exc_gen_init(); /* 建正弦表、复位相位累加器 */ bsp_resolver_init(); /* 定时器/DAC/ADC采样触发与励磁同源 */ angle_track_init(200.0f); /* 跟踪环带宽 200 Hz */ resolver_cal_load(); /* 从 Flash 读上次标定的偏置和增益 */ bsp_timer_start(); /* 一旦启动励磁和采样同时跑起来 */ while (1) { shell_poll(); /* 上位机命令读角度、改参数、触发标定 */ fault_check(); /* 包络幅值、超速、断线诊断 */ } }顺序里最容易出错的是resolver_cal_load()的位置。它必须在定时器启动之前完成否则前几个周期的解调用的是默认系数跟踪环会先被带偏虽然之后能收敛回来但如果你的故障检测在启动瞬间就使能会直接误报一次故障。我在一个项目上就因为这个顺序问题上电偶尔报旋变信号异常复位又好了查了很久。sin_table_gen.py这个工具别省。正弦表手工写容易错而且不同项目可能需要不同的表长和幅值用脚本生成头文件一行命令搞定比复制粘贴靠谱。4.2 参数配置表一次改对这几个数软解码调不起来八成是这几个参数里有一个不对。下面这张表是我每次上新板子必对的清单参数典型值改动影响出错现象励磁频率10 kHz越高动态越好但对 DAC/ADC 要求越高太低导致包络更新慢电流环跟不上每周期点数32 / 64决定累加窗口和采样率与采样率不匹配时二倍频残留角度周期性波动励磁幅值量程的 60%~70%影响信噪比太低噪声大太高削顶后角度畸变跟踪环带宽100~300 Hz越高动态越好、噪声越大太高速度毛刺太低高速相位滞后阻尼系数0.707影响阶跃响应过冲太小过冲振荡太大响应迟钝相位补偿1~3 个采样点补偿模拟通路延迟未补偿时角度误差随转速线性增大电机极对数按电机填机械角/电角度换算填错导致换相完全错乱电机堵转初始角偏移标定得出电角度零点对齐未标定时电机可能反转或启动无力相位补偿这一项值得单独说。从 DAC 输出到 ADC 采样信号穿过了功放、可能还有隔离变压器、RC 抗混叠滤波每一级都有群延迟。10 kHz 下一个 2 kHz 截止的 RC 滤波器就能贡献十几度的相位偏移。如果不补解调出来的包络不是纯 sinθ 和 cosθ而是带固定相移的混合等价于角度上叠了一个随转速变化的误差。补偿的办法很简单在解调时给参考相位加一个固定偏移/* 相位补偿根据实测标定值调整参考相位 */ #define PHASE_COMP 3 /* 补偿 3 个采样点按实测填 */ void demod_push(int32_t s, int32_t c, uint16_t phase) { uint16_t ref_phase phase PHASE_COMP * EXC_PHASE_STEP; int32_t ref sin_table[(ref_phase 0) 0xFF]; /* ... 后续累加逻辑同 3.3 节 ... */ }PHASE_COMP的单位是采样点不是度这样换励磁频率的时候不用重算。标定方法在 4.3 节。4.3 上电标定偏置、增益、相位延迟三步走标定是软解码能不能达到标称精度的分水岭。工厂里用转台当然最准但多数人手上没有我用的是手动匀速转 数据拟合这套土办法精度够用。第一步标偏置。让电机静止在任意位置采集 1000 个包络点求平均。理论上静止时包络是常数平均值就是偏置和真实包络的叠加所以要转几个不同位置分别采取最小值作为偏置估计——因为包络是正弦最小值出现在信号过零附近此时读数基本就是纯偏置。更稳的做法是转一整圈对每个通道的数据做正弦拟合拟合出的直流分量就是偏置。第二步标增益。同样转一整圈对 sin 和 cos 的包络分别求峰峰值。理想情况下两路峰峰值应该相等都等于 kA实际会有偏差。把 cos 通道的增益系数设成sin_pp / cos_ppQ15 换算让两路幅值对齐。这一步做完角度误差里的二次谐波应该明显下去。第三步标相位。这一步最容易被跳过但对高速精度影响最大。方法是用外部设备拖动电机匀速转记录角度误差随转速的变化曲线。如果误差和转速成正比就是相位补偿不够逐步加大PHASE_COMP直到误差曲线在正反转下基本对称。没有拖动设备的话用驱动器自己开速度环让电机在 10%~30% 额定转速下正反转各跑一遍看角度误差的符号也能粗调。注意三步标定的顺序不能换。偏置没标就标增益拟合出来的峰峰值是偏的增益没对齐就标相位误差曲线会被二次谐波污染根本看不出线性趋势。标定结果存 Flash上电加载。resolver_cal.c里建议加一个版本号和 CRC防止 Flash 擦写异常导致加载了半截数据——这种问题现象是昨天还好好的今天角度全乱了非常难查。4.4 联调验证三个动作判断解调链路是否正常代码跑起来之后按下面三个动作验证基本能定位问题在哪一层。第一个动作不接电机只看励磁输出。示波器测 DAC 输出和功放输出确认频率、幅值、波形失真度。如果功放输出削顶或者有明显交越失真后面的解调全是白搭。这一步花五分钟能省掉后面几小时的排查。第二个动作手转电机看包络。把env_s和env_c通过 DA 或者串口打到上位机手动匀速转一圈。正常现象是两路包络都是干净的正弦相位差 90°幅值基本相等转一圈正好一个周期。如果包络上有高频毛刺查采样时刻和 ADC 配置如果两路幅值差很多回 4.3 节标增益如果包络不是纯正弦而是有平顶查励磁幅值是不是太大导致削顶。第三个动作看角度线性度。把解算出的角度和一路参考比如电机自带的增量编码器或者干脆用手转 分度头对比画误差曲线。理想的误差曲线是一条平直的线实际会看到一次、二次谐波成分。一次谐波主要来自初始角偏移和相位补偿二次谐波主要来自两路增益失配和偏置。这条曲线的形状就是你的调参指南针。5. 避坑与排查软解码最容易翻车的五个点5.1 角度低频摆动周期几秒到几十秒现象电机静止或者匀速运行时角度读数以一个很慢的周期来回摆幅度可能有 1°~3°示波器上看 sin/cos 信号干干净净找不到干扰源。原因励磁和采样走了两个独立定时器。哪怕两个定时器配成同一频率只要时钟源分频后有微小差异相对相位就会持续漂移漂一圈的时间就是拍频周期。这时候解调参考和实际载波的相位差在变解出来的包络幅值就跟着变。解决把 ADC 触发改成由励磁定时器的比较事件产生物理上同源。如果硬件上确实做不到比如 ADC 触发源被别的外设占了退而求其次用同一个定时器的不同通道或者定期用励磁过零点重新同步采样相位累加器。5.2 转速越高角度误差越大正反转不对称现象低速时角度挺准一到中高速角度就滞后而且正转和反转的误差方向不一样。原因模拟通路的相位延迟没补偿。DAC、功放、隔离、RC 滤波加起来有固定群延迟转速越高同样的时间延迟折算成的角度误差越大误差 ≈ 转速 × 延迟。正反转时误差符号相反所以不对称。解决按 4.3 节第三步标PHASE_COMP。补到正反转误差曲线基本对称就行。如果补偿后高速还是差检查跟踪环带宽是不是太低环本身的相位滞后也会叠加进来。5.3 角度在特定扇区跳变误差曲线有明显二次谐波现象角度整体是线性的但在某些角度附近会突然跳一下误差曲线一个电周期内波动两次。原因sin 和 cos 两路增益不一致或者其中一路有残余偏置。增益失配会让包络的圆变成椭圆atan2 或者跟踪环在椭圆上解出来的角度自然带二次谐波残余偏置会把圆心挪走误差曲线出现一次和二次的混合。解决重做 4.3 节的第一步和第二步。偏置用整圈正弦拟合的直流分量别用单点采样增益用峰峰值比。标完之后再看误差曲线二次谐波应该降到 0.1° 以内。5.4 上电角度随机电机启动时抖动或者反转现象每次上电读到的电角度都不一样闭环启动时电机抖一下甚至反转开环强拖能转起来切闭环就炸。原因初始角偏移没标定或者极对数配错了。旋变本身是机械角电机控制要的是电角度两者差一个极对数倍率和初始偏移。另外还有一种可能是上电时跟踪环还没收敛就使能了闭环。解决先确认极对数填对再用给 U 相通直流、转子定位到已知角度的办法标初始偏移。软件上上电后先让跟踪环空跑 50~100 ms等pll.omega稳定在零附近再使能闭环。5.5 角度噪声大电机有高频啸叫现象角度读数上叠着明显的高频噪声电流波形毛刺多电机有啸叫尤其在低速轻载时明显。原因ADC 采样时刻撞上了功率管的开关沿。软解码的采样是周期性的如果采样点正好落在 PWM 开关瞬间共模干扰会直接进 ADC差分也压不住。解决把 ADC 触发时刻挪到 PWM 周期的中点避开开关沿硬件上检查差分走线是否等长、共模电容是否合理软件上可以在解调前加一级限幅或者中值滤波把明显的野值剔掉。如果这些都不行考虑在开关频率上做同步让采样率和 PWM 频率成整数倍关系。6. 二次开发把这套解码改成你自己平台的模块移植这套代码最忌讳的是直接改bsp_resolver.c里的寄存器。我的做法是先抽一层薄接口把硬件相关的东西全收口到五个函数里/* 移植层换平台只实现这五个函数 */ typedef struct { void (*dac_write)(uint16_t ch, uint16_t val); uint16_t (*adc_read)(uint16_t ch); void (*timer_start)(void); uint32_t (*get_tick_us)(void); void (*cal_save)(const void *buf, uint32_t len); } resolver_port_t;这五个函数之外的所有代码——解调、跟踪环、标定、诊断——都是纯算法不碰寄存器可以直接复用。get_tick_us看着多余其实是给故障诊断用的幅值异常要判断持续了多久才报故障没有时间基准就只能数中断次数换平台又得改。移植完之后建议做两件事。第一件是加诊断。软解码相比硬件解码芯片最大的短板是没有现成的故障标志位得自己判。至少加三条包络幅值低于阈值旋变断线或者励磁丢了、包络幅值超上限短路或者增益异常、角度跳变率超限跟踪环失锁。每条诊断都要有确认计数和恢复计数别用单次采样就报故障否则电机一震动就报警。第二件是用信号注入做验证。这套代码最大的好处是解耦——解调器和跟踪环的输入是两个 int32。你可以写个测试函数直接给angle_track_update喂理想的正弦包络扫一遍 0~360°看输出角度误差。这一步能把算法问题和硬件问题彻底分开注入测试过了说明代码没问题剩下的都是板子和信号链的事注入测试不过说明参数或者定点换算写错了别去动硬件。我在一个项目上就是靠这个测试发现kp的 Q15 换算少移了一位环带宽实际是设想的 32 倍难怪一直抖。还有一个小技巧把励磁频率、采样点数、跟踪环带宽、极对数这几个参数集中到一个resolver_cfg.h里用宏定义别散落在各个 .c 文件。换电机的时候只改这一个文件编译一次就能验证。我吃过亏有次改励磁频率漏改了速度换算里的常数电机跑起来转速显示只有实际的一半现场调了半天才想起来。从那以后我每次上电调试都强制走一遍流程先看励磁波形再看包络正弦度最后看角度线性度三步都过了才敢使能闭环。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?