4、ADC采样与信号调理:ADC模块配置、电流/电压采样、过采样与滤波
ADC采样,说白了就是电机控制的眼睛。眼睛不好使,算法再牛也白搭。我见过不少工程师,花大把时间调PI参数,结果问题出在ADC采样上——噪声大、精度差、时序不对。嗯,今天咱们就把这块彻底聊透。
4.1 ADC模块配置——别小看这几步
英飞凌的ADC模块,我个人觉得设计得挺灵活的。但灵活也意味着坑多。配置时,有几个关键点你必须盯死。
核心配置项:
- 分辨率选择:12位是标配,但有些场景用10位反而更好(比如高速采样时)。我习惯在低速电机上用12位,高速电机切到10位。
- 采样时间:这个参数很多人忽略。采样时间太短,电容充不满,精度直接崩。太长呢?又影响吞吐率。
- 触发源:PWM同步触发是电机控制的标配。千万别用软件触发,那玩意儿抖动大,电流环会抖得你怀疑人生。
举个例子,我配置一个典型的双电阻电流采样:
// 伪代码:ADC模块初始化
void ADC_Init(void)
{
// 1. 使能ADC时钟
ADC_ClockEnable(ADC_MODULE_0);
// 2. 设置分辨率12位,采样时间100ns
ADC_SetResolution(ADC_MODULE_0, ADC_RES_12BIT);
ADC_SetSampleTime(ADC_MODULE_0, ADC_CH_V, 100); // 单位ns
// 3. 配置触发源为PWM同步信号
ADC_SetTriggerSource(ADC_MODULE_0, ADC_TRIGGER_PWM_SYNC);
// 4. 配置结果对齐方式(右对齐)
ADC_SetResultAlignment(ADC_MODULE_0, ADC_ALIGN_RIGHT);
}
我的小技巧:采样时间别死磕手册上的最小值。我一般留20%余量。比如手册说80ns够,我就设100ns。多出来的20ns,换来的是稳定的精度,值不值?你自己算。
4.2 电流/电压采样——实战中的那些坑
电流采样,电机控制的命门。我做过一个项目,电流采样一直有直流偏置,查了三天,最后发现是PCB布局问题——采样电阻离大功率管太近,热噪声耦合进去了。
常见的采样方式有三种:
| 采样方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 单电阻采样 | 成本低,电路简单 | 重构算法复杂,低速性能差 | 低成本风机、水泵 |
| 双电阻采样 | 两相电流直接测量,算法简单 | 需要两个运放,成本略高 | 通用伺服、工业驱动 |
| 三电阻采样 | 三相独立测量,精度最高 | 成本高,PCB面积大 | 高端伺服、机器人关节 |
电压采样相对简单,但要注意母线电压的纹波。我曾经遇到过母线电压采样不准,导致过压保护误触发。后来加了RC滤波,问题解决。
避坑指南:我曾经在双电阻采样中,忽略了死区时间对采样窗口的影响。PWM死区期间,电流采样结果完全是垃圾数据。一定要确保采样窗口避开死区。怎么避?用PWM定时器的比较值来精确控制采样触发点。
4.3 过采样——用时间换精度
过采样,说白了就是用多个采样点取平均,来提升有效分辨率。你想想看,12位ADC不够用怎么办?换16位的?成本高啊。过采样就是个经济实惠的方案。
原理很简单:每提高1位有效分辨率,需要4倍的过采样率。比如从12位提到14位,需要16倍过采样。
// 过采样实现示例
uint16_t Oversample_Current(uint16_t *raw_data, uint8_t oversample_ratio)
{
uint32_t sum = 0;
uint8_t i;
// 累加过采样数据
for(i = 0; i < oversample_ratio; i++)
{
sum += raw_data[i];
}
// 右移得到最终结果(oversample_ratio = 4^n)
// 比如16倍过采样,右移2位得到14位结果
return (uint16_t)(sum >> 2); // 假设oversample_ratio=16
}
注意:过采样不是万能的。它只能降低白噪声,对周期性噪声效果有限。而且会降低采样率。我一般只在低速或稳态工况下用过采样,高速时关掉。
4.4 滤波——别让噪声毁了你的控制
滤波这块,我踩过的坑最多。很多人上来就上低通滤波,结果相位滞后一大截,电流环带宽直接砍半。
常用的滤波方法:
- 硬件RC滤波:简单粗暴,但会引入相位滞后。截止频率一般设在开关频率的1/10左右。
- 软件均值滤波:适合过采样场景,计算量小。但会平滑掉真实变化。
- 一阶低通滤波:代码简单,但要注意截止频率的选择。我习惯用截止频率=电流环带宽的5倍。
- 卡尔曼滤波:效果好,但计算量大。我一般只在高端伺服上用。
// 一阶低通滤波实现
typedef struct {
float output; // 滤波输出
float alpha; // 滤波系数 (0~1)
} LowPassFilter_t;
float LowPassFilter_Update(LowPassFilter_t *filter, float input)
{
// 一阶低通:output = alpha * input + (1-alpha) * output_prev
filter->output = filter->alpha * input + (1.0f - filter->alpha) * filter->output;
return filter->output;
}
// 初始化:alpha = Ts / (Ts + Tau)
// Ts: 采样周期, Tau: 时间常数
void LowPassFilter_Init(LowPassFilter_t *filter, float Ts, float cutoff_freq)
{
float Tau = 1.0f / (2.0f * 3.14159f * cutoff_freq);
filter->alpha = Ts / (Ts + Tau);
filter->output = 0.0f;
}
我的经验:滤波不是越强越好。我见过有人把电流信号滤得干干净净,结果电流环响应慢得像蜗牛。记住一个原则:滤波器的截止频率,至少要比电流环带宽高3-5倍。这样既能滤掉开关噪声,又不影响控制性能。
4.5 实战总结——ADC采样的黄金法则
做了这么多年电机控制,我总结了几条ADC采样的黄金法则:
- 采样时序必须与PWM同步:这是底线。不同步?电流环抖给你看。
- 采样窗口避开死区:死区期间的采样数据,直接丢弃或标记无效。
- 滤波要适度:能硬件滤波就别用软件,能简单滤波就别上复杂算法。
- 校准不能省:每次上电做一次ADC偏移校准和增益校准。我见过有人省了这一步,结果电流误差5%以上。
- 留足裕量:采样时间、滤波截止频率、过采样率,都留20%的裕量。别卡着极限值设计。
嗯,ADC采样这块就聊这么多。说白了,它就是个信号链的问题——从物理量到数字量,每一步都有坑。但只要把原理搞透,配置做对,滤波选好,你的电机控制就成功了一半。下一章咱们聊聊电流环的PI参数整定,那才是真正见功夫的地方。