4. 信号处理:模拟信号滤波与数字信号处理
做力矩控制,最头疼的是什么?
我个人觉得,不是算法写不出来,而是传感器回来的信号太「脏」了。你想想看,电机一转,PWM一开,整个电路板上的噪声就跟开了派对一样。这时候你要是直接拿原始信号去算力矩,那结果肯定没法看。
所以,信号处理这块,说白了就是「去伪存真」。把有用的力信号留下来,把没用的噪声滤掉。今天我就把我在项目里用过的几种滤波手段,掰开揉碎了讲给你听。
4.1 模拟信号滤波:硬件是第一道防线
我习惯在ADC采样之前,先做一级硬件滤波。为什么?因为有些高频噪声,一旦进了ADC,混叠到低频段,那就再也去不掉了。这叫「抗混叠滤波」,是基本功。
核心原则:模拟滤波负责干掉高频噪声,数字滤波负责处理低频波动。两者分工明确,别指望一个打全场。
4.1.1 一阶RC低通滤波
最简单的方案,一个电阻加一个电容。截止频率公式:
f_c = 1 / (2 * π * R * C)
举个例子,我常用的参数:R=10kΩ,C=100nF,算下来截止频率大概159Hz。对于力矩传感器这种几十赫兹的带宽来说,够用了。
实战技巧:电阻别选太大,否则信号会衰减。我一般控制在10kΩ以内。电容用C0G或者NP0材质,温度漂移小。
4.1.2 二阶有源滤波
如果一阶滤波不够陡,那就上二阶。用运放搭一个Sallen-Key结构,衰减斜率能达到-40dB/decade。
// 典型二阶低通参数(巴特沃斯响应)
// 截止频率 100Hz
R1 = R2 = 15.8kΩ
C1 = 0.1μF
C2 = 0.22μF
// 运放推荐:OPA2188 或 MCP6002
我在做一款协作机器人关节模组时,就用了这个配置。效果很明显,PWM带来的20kHz噪声直接被压下去了60dB以上。
注意:运放选型要看GBW(增益带宽积)。对于100Hz的信号,随便一个运放都行。但如果你要处理更高频率,记得算一下。
4.2 数字信号处理:软件才是灵魂
硬件滤完,剩下的就是低频噪声和随机噪声了。这时候轮到数字滤波上场。
4.2.1 一阶数字低通滤波(指数移动平均)
这个我几乎每个项目都在用。简单、高效、资源占用少。
// 一阶低通数字滤波
float lowpass_filter(float input, float prev_output, float alpha) {
return alpha * input + (1.0f - alpha) * prev_output;
}
// alpha取值建议:0.1 ~ 0.3
// alpha越大,响应越快,但滤波效果越差
// alpha越小,响应越慢,但滤波越平滑
嗯,这里要注意:alpha的取值是个trade-off。我一般先设0.2,然后看波形调。如果噪声还是大,就降到0.1。如果响应太慢,就升到0.3。
4.2.2 卡尔曼滤波:当精度要求很高时
说实话,卡尔曼滤波在嵌入式上跑,有点「杀鸡用牛刀」的感觉。但有些场景不得不用——比如精密力控装配,要求0.01N的精度。
卡尔曼滤波的核心思想:把系统状态和测量值融合起来,各自取长补短。
// 一维卡尔曼滤波(简化版)
typedef struct {
float Q; // 过程噪声协方差
float R; // 测量噪声协方差
float P; // 估计误差协方差
float K; // 卡尔曼增益
float X; // 状态估计值
} KalmanFilter;
void kalman_init(KalmanFilter *kf, float init_val) {
kf->Q = 0.01f; // 根据系统调整
kf->R = 0.1f; // 根据传感器噪声调整
kf->P = 1.0f;
kf->X = init_val;
}
float kalman_update(KalmanFilter *kf, float measurement) {
// 预测
kf->P = kf->P + kf->Q;
// 更新
kf->K = kf->P / (kf->P + kf->R);
kf->X = kf->X + kf->K * (measurement - kf->X);
kf->P = (1.0f - kf->K) * kf->P;
return kf->X;
}
关键点:Q和R的调参决定了滤波效果。Q设大了,滤波器更相信测量值,响应快但噪声大。R设大了,滤波器更相信预测值,平滑但滞后。
我曾经在一个力控打磨项目里,花了整整两天调这两个参数。最后发现,Q=0.005,R=0.2的时候效果最好。这东西没有标准答案,全靠试。
4.3 噪声抑制实战技巧
理论说完了,来点干货。这些都是我踩过的坑。
4.3.1 采样时机很重要
别在PWM开关瞬间采样。你想想看,MOS管一开一关,电流突变,地平面都在抖。这时候采回来的数据,神仙都滤不干净。
我的做法:用定时器触发ADC,采样时刻避开PWM边沿。比如PWM周期是50μs,我就在PWM高电平中间位置采样。
4.3.2 多次采样取平均
这个技巧简单粗暴,但非常有效。我一般采16次或32次,然后取平均。注意,次数要是2的幂,这样除法可以用移位操作,快得很。
// 16次采样取平均
uint16_t adc_buf[16];
uint32_t sum = 0;
for(int i=0; i<16; i++) {
sum += adc_buf[i];
}
uint16_t result = sum >> 4; // 除以16
4.3.3 中值滤波对付野值
有时候传感器会突然跳一个离谱的值,比如力从5N瞬间蹦到50N。这种野值,平均滤波搞不定,得用中值滤波。
// 简单中值滤波(取3个值的中位数)
float median_filter_3(float a, float b, float c) {
if(a > b) { float t = a; a = b; b = t; }
if(b > c) { float t = b; b = c; c = t; }
if(a > b) { float t = a; a = b; b = t; }
return b;
}
我的习惯:先做中值滤波去掉野值,再做低通滤波平滑波形。顺序不能反,否则野值会被低通滤波「记住」,影响好几拍。
4.4 知识体系总览
下面这张图,是我自己总结的信号处理流程。每次做新项目,我都会先按这个框架走一遍。
从这张图可以看出来,信号处理不是单一环节的事。从传感器出来,到最终给到控制算法,中间每一步都在跟噪声作斗争。少一环,效果都会打折扣。
4.5 总结一下
做力矩控制的信号处理,我总结了三条经验:
- 硬件滤波不能省。哪怕只是一个RC,也能挡住大部分高频噪声。别指望全靠软件擦屁股。
- 数字滤波要分层。先中值去野值,再低通平滑。顺序搞反了,效果差很多。
- 采样时机要讲究。避开PWM开关时刻,选在信号最稳定的时候下手。
这些技巧,每一个都是我拿实际项目换来的教训。你照着做,至少能少走一半弯路。
最后说一句:滤波参数没有万能公式。同样的参数,换一个电机、换一个负载,效果可能天差地别。所以,调参的时候耐心点,多看看波形,多试试不同组合。这活儿,急不来。