C语言两行代码实现RC高通/低通滤波,拿来即用,不需要移植

最近做心电监测项目,发现信号干扰很严重,图像完全是干扰信号,根本看不出心电信号,公司给了滤波函数,但是高通滤波不知道什么原因不能用。百度只找到了低通滤波代码(Uo=k*Ui+(1-k)*Uo),k值也没給计算公式,最主要的是没有我需要的高通滤波。数学太菜,搜出来的其他答案大量公式看不懂,符号都不认识,也移植不了。只能自己摸索,花了大量时间,终于搞定高通滤波,把k值计算公式也推导出来了,放出来给需要的童鞋用,免得你们走弯路。

RC滤波电路图:
在这里插入图片描述

1.代码:

//k值的计算方法。
//符号:Sr-采样率(sampling rate,次/秒),f-截止频率(Hz),Pi-圆周率(3.14...)
//k=(2*Pi*f)/Sr
typedef struct
{
  float k;//滤波系数
  float lVal;//上次计算值
}rcPara_t;

//低通滤波:
//rcPara-指向滤波参数
//val-采样值
//返回值-滤波结果
float rcLfFiter(rcPara_t *rcPara, float val)
{
    rcPara->lVal=((float)val*rcPara->k+rcPara->lVal*(1-rcPara->k));
    return rcPara->lVal;
}

//高通滤波:
float rcHpFiter(rcPara_t *rcPara, float val)
{
    rcPara->lVal=((float)val*rcPara->k+rcPara->lVal*(1-rcPara->k));
    //    return -(val-rcPara->lVal);//滤波结果
    return (val-rcPara->lVal);//如果直接返回滤波结果,滤波后图像是倒转的,在心电图等一些场合,需要将图像再镜像过来
}

2.低通滤波测试代码

typedef struct
{
  float freq;//频率
  int ratio;//占空比(%)
  int cycle;//采样率
  int range;//取值范围
  uint32_t count;//数据计数
}fsPara_t;

//正弦信号
#define PI 3.141592653f
float sinSignal(fsPara_t *sgn)
{
  float cnum = (float)sgn->cycle/sgn->freq;
  float unit = 2.0F*PI/(float)cnum;
  float val = 0;
  val += unit*(float)sgn->count++;
  return (sin(val)*sgn->range);
}

//正弦信号频率1Hz,采样率500,幅值100
fsPara_t hsPara = {.freq=1, .cycle=500, .range=100};
rcPara_t rcParaLp = {.k=(2*3.14f*1/500)};
void testRcLfFiterLevel1(void)
{
  float buff[2000];
  float inValMax=0, outValMax=0;

  for(int i=0; i<2000; i++)
  {
    buff[i]=sinSignal(&hsPara);
    if(buff[i]>inValMax) inValMax=buff[i];
    //printf("%0.2f ", buff[i]);
    buff[i]=rcLfFiter(&rcParaLp, buff[i]);
    if(buff[i]>outValMax && i>1000) outValMax=buff[i];
    //printf("%0.2f\n", buff[i]);

  }
  printf("inValMax:%0.2f outValMax:%0.2f\n", inValMax, outValMax);
}

调用testRcLfFiterLevel1();输出结果:inValMax:100.00 outValMax:70.92
70.92/100=0.7092
截止频率下滤波后信号幅值衰减约为0.707(为什么是这个值,自行百度),很接近了。

信号频率改为0.1:
fsPara_t hsPara = {.freq=0.1, .cycle=500, .range=100};
输出结果:inValMax:100.00 outValMax:99.51
可见低频几乎没有衰减。

信号频率改为10:
fsPara_t hsPara = {.freq=10, .cycle=500, .range=100};
输出结果:inValMax:100.00 outValMax:10.01
高频信号大部分被过滤掉了

多阶低通滤波多次调用即可,测试代码(3阶低通滤波):
ps:多阶滤波截止频率公式f=1/(2PiR*C)已经不适用了,自行推导,我也不不会.
慢慢试也能试出来,幅值衰减到0.707(或功率降到0.5)就是截止

//频率1Hz,采样率500,幅值100
fsPara_t hsParaMuti = {.freq=1, .cycle=500, .range=100};
#define RC_F    1.0f
#define RC_K    (2*3.14f*RC_F/500)
rcPara_t rcParaLpMutiL1 = {.k=RC_K};
rcPara_t rcParaLpMutiL2 = {.k=RC_K};
rcPara_t rcParaLpMutiL3 = {.k=RC_K};
void testRcLfFiterLevel3(void)
{
  float buff[2000];
  float inValMax=0, outValMax=0;

  for(int i=0; i<2000; i++)
  {
    buff[i]=sinSignal(&hsParaMuti);
    if(buff[i]>inValMax) inValMax=buff[i];
    //printf("%0.2f ", buff[i]);
    buff[i]=rcLfFiter(&rcParaLpMutiL1, buff[i]);
    buff[i]=rcLfFiter(&rcParaLpMutiL2, buff[i]);
    buff[i]=rcLfFiter(&rcParaLpMutiL3, buff[i]);
    if(buff[i]>outValMax && i>1000) outValMax=buff[i];
    //printf("%0.2f\n", buff[i]);

  }
  printf("inValMax:%0.2f outValMax:%0.2f\n", inValMax, outValMax);
}

调用testRcLfFiterLevel3();输出结果:inValMax:100.00 outValMax:35.66
35.66/100=0.3566
3阶滤波幅值衰减为0.707^3=0.35339…,也很接近。

3.高通滤波测试代码

fsPara_t lsPara = {.freq=1, .cycle=500, .range=100};
rcPara_t rcParaHp = {.k=(2*3.14f*1/500)};
void testRcHpFiterLevel1(void)
{
  float buff[2000];
  float inValMax=0, outValMax=0;

  for(int i=0; i<2000; i++)
  {
    buff[i]=sinSignal(&lsPara);
    if(buff[i]>inValMax) inValMax=buff[i];
    //printf("%0.2f ", buff[i]);
    buff[i]=rcHpFiter(&rcParaHp, buff[i]);
    if(buff[i]>outValMax && i>1000) outValMax=buff[i];
    //printf("%0.2f\n", buff[i]);

  }
  printf("inValMax:%0.2f outValMax:%0.2f\n", inValMax, outValMax);
}

调用testRcHpFiterLevel1();输出结果:inValMax:100.00 outValMax:70.06,
很接近0.707。

信号频率改为0.1,输出结果:inValMax:100.00 outValMax:3.94
低频几乎被过滤掉了.

信号频率改为10,输出结果:inValMax:100.00 outValMax:98.80
高频几乎不受影响.

4.接下来是低通滤波公式推导(高通滤波公式可以用相同的思路推导出来)
PS:非专业分析,仅供参考,如有错误请指正。

符号意义:
Q-电荷量(库伦),C-电容(F),R-电阻(欧姆),U-电压(V),Sr-采样率(sampling rate, 次/秒)
f-截止频率(Hz),Pi-圆周率(3.14…), t-时间

根据RC低通电路图可知,输出电压等于电容电压
如果采样电压与电容电压不一致,电容就会充/放电,
假设电容充电后电压增加Ur(放电则Ur为负数):

符号:Uo-输出电压,U-电容电压, Ui-输入电压
1 Uo=U+Ur

因为C=Q/U, 所以U=Q/C
根据U=Q/C, Q=I*t,I=U/R, t=1/Sr得:
2 Ur=(Ui-U)/(R*C*Sr)

代入1得:
3 Uo=U+(Ui-U)/(R*C*Sr)

由于我们是软件滤波,不需要真正的电容电阻,所以令k=1/(R*C*Sr),代入得:
Uo=U+(Ui-U)*k 简化:
4 Uo=k*Ui+(1-k)*U

到这里已经得出滤波公式,如何通过截止频率得出k值呢

根据截止频率公式f=1/(2*Pi*R*C)得:
RC=1/(2*Pi*f)
代入k=1/(R*C*Sr)得:
5 k=(2*Pi*f)/Sr

  • 18
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值