|
是个啥坑?9 Q7 [. X/ f6 i; [ J" z 一个项目中用到一个传感器测量一物理量,这里假定测量温度吧。需要判断其变化趋势,利用这个变化趋势去做一些应用。 那么要怎么判断一个物理量的变化趋势呢?我们能自然能想到去求取该随机序列的变化率。这里涉及到一些数序定义。随机序列有很多可能的来源,最为常见是我之前在<<模数采样知多少>>中介绍的模数采样。5 I% J; e, q& V' @9 R2 P, M 这样将S(t)信号转换为离散信号序列S(n),那么对于当前时刻其斜率怎么求取呢?(这里忽略中间的过度态,仅将其看为线段相连,当然现实应用中如果有更高要求,可以做曲线拟合)( Q$ a" U0 s' r8 V
$ t4 Q7 b: W. d/ D; v 但是如果只判断,斜率极容易误判,比如下面这样的情况:3 Y9 Y, D1 W0 F7 F
其斜率一会儿正,一会儿负,但是其总体趋势又是在增加的,所以只考察斜率显然不可取,获取需要在代码在加各种复杂的条件或者限值去判断。即使加这么多条件系统仍然可能表现的非常不健壮。8 A# `, X% A' k" h+ G7 ?: i
对于模拟信号2而言,趋势又在不断变化。那么怎么做才能稳定呢?先卖个关子?
函数的凹凸性; r( p, |+ A9 J9 F; r: |, | ! s. h4 d1 O8 F% U' L* D 凹函数6 H& C+ `- s6 C 9 ]- ?; m% ^" ~/ S' h& j& J7 C 凹函数是一个定义在某个向量空间的凸集C(区间)上的实值函数f。设f为定义在区间I上的函数,若对I上的任意两点x1<x2和任意的实数t属于(0,1),总有,
则称函数f为l上凹函数,有的书上也称为下凸函数。
如果把上述条件中的“≥”改成“>”,则叫做严格上凹函数,或叫做严格下凸函数。: R) q! J& {+ n; c. _! s! W# C 上面是一维函数情况,这里来个2维函数的图,方便理解。
凸函数, v Q3 N% {# T # `6 t) R1 k" g" G. W" H 设f为定义在区间I上的函数,若对I上的任意两点x1<x2和任意的实数t属于(0,1),上面不等式变成大于等于,则在该区间为凸函数。( G; ~' B' k0 ?8 }3 a. h
可见,凹凸是相对的,如f(x)在某区间为凹,则-f(x)则在该区间为凸。 性质4 p8 ]3 X/ ~9 [+ V7 U % Z e( B) h! N 若一个函数在某区间二阶可导且大于0,则函数在该区间为凹函数" m3 r+ T2 O1 ]! E' @ 若一个函数在某区间二阶可导且小于0,则函数在该区间为凸函数 证明,这里就不推导了,可以利用拉格朗日中值定理可以推导出上面这个性质。* g% F5 o# S: @! n6 {+ j. o 来看一下会动的图,加深一下理解:
函数
sin(2x)的一阶导数为:
sin(2x)的二阶导数为:
; Z" o( j: K1 p# C 装逼结束,也可能没装对~~~4 H6 b" C( `: d* A, |2 ~: b. _& b$ b
回到坑里; M" M; V( a+ [" h F1 g$ T 通过上面装逼,是否可以利用离散序列的求导数来判断传感器的变化趋势。啥?导数?又要开始表演了?' }" C! R- |! @8 n$ a
前面说了一阶导数是这样的:
那么二阶导数是哪样捏?- S2 }; H. C; E6 ^ T
化简一下:
其中S[n]表示当前测量点,S[n-1]表示前一个测量点,S[n-2]表示前第2个测量点。3 `# H* t% s; e8 s1 @! \$ V8 N 上代码 #include <stdio.h> #include <math.h>$ G0 o+ _ C& t" e4 v# z5 d& n4 E$ m #include <string.h>, P' X6 U, X w typedef struct _T_2ND_DRV { float xn1;* C9 t/ W% |9 q4 m f float xn2;9 R# S+ V4 J s( q' F }t_2ND_DRV; typedef struct _T_1ST_DRV { float xn1; }t_1ST_DRV; void init_second_derivative(t_2ND_DRV *pSndDrv)6 \* [/ L+ V; X4 o0 \ {, L, \: P0 b+ w) {! p" K, e5 ? pSndDrv->xn1 = 0; pSndDrv->xn2 = 0; } float second_derivative(t_2ND_DRV *pSndDrv, float xn,float T) {' F9 d4 X% g) j( } float result=0.0f; if(T<=0) return 0x7FBFFFFF; /*非法数据*/ result = (xn-2*pSndDrv->xn1+pSndDrv->xn2)/T/T; pSndDrv->xn2 = pSndDrv->xn1; pSndDrv->xn1 = xn;9 B3 V4 O3 Z" V; I% } return result; }; Q0 D w" [9 t- A4 R' \- j ! ~) v$ k7 s& \ void init_fisrt_derivative(t_1ST_DRV *p1stDrv) { p1stDrv->xn1 = 0;9 U6 I; P) X% H1 K1 S+ m } float fisrt_derivative(t_1ST_DRV *p1stDrv, float xn,float T)1 X; J: V' d& J% N s {' T$ Y" d/ t6 [, a float result=0.0f; if(T<=0); s; g. Z! |5 r7 i5 `3 e2 p& {* v return 0x7FBFFFFF; /*非法数据*/ result = (xn-p1stDrv->xn1)/T; 0 r5 L; y3 v! K6 o0 U p1stDrv->xn1 = xn;3 U( F S6 j: C4 `1 t5 a return result; } #define PI 3.1415f t" g# s3 Q9 z8 |; |$ S #define SAMPLE_RATE 500.0f #define SAMPLE_T (1/SAMPLE_RATE) #define SAMPLE_SIZE (100) int main()% p* _, Z" Q' o+ ~ { float sim1[SAMPLE_SIZE]; q$ |# ?' ~" z$ R float sim2[SAMPLE_SIZE];5 e- {2 L( r& J float out1[SAMPLE_SIZE];8 L6 C5 c7 I+ Q3 Q6 f( G. B float out2[SAMPLE_SIZE];3 j& h/ v) _9 L, E t_2ND_DRV sndDrv; t_1ST_DRV frtDrv; init_fisrt_derivative(&frtDrv);7 f6 t3 @( x" j* T: {- G$ ^1 m- z init_second_derivative(&sndDrv); ! |" A0 D; d( [+ q0 C; i FILE *pFile=fopen("./simulationSin.csv","wt+");7 ]" S0 e" r' t9 d2 k. |1 l# ? if(pFile==NULL) {; q& I9 l) [* f5 l printf("simulationSin.csv opened failed"); return -1;" Z! c \& B0 x- f6 }$ |1 q1 I7 i }+ D. j" o$ D* F, x6 n8 _ * |2 h& W+ }! |* ^* ~# b for(int i=0;i<SAMPLE_SIZE;i++) {* X4 ?6 a5 X- u1 | sim1=10*sin(2*PI*10*i/500);# d: C* V( X! j+ c+ h } for(int i=0;i<SAMPLE_SIZE;i++) { out1=fisrt_derivative(&frtDrv,sim1,SAMPLE_T);/ ]0 e2 D% q/ r' q, O+ I out2=second_derivative(&sndDrv,sim1,SAMPLE_T);- M: v8 i! J c, W' T fprintf(pFile,"%f,%f,%f\n",sim1,out1,out2);9 T$ U* }- h& g% I, b) Q }3 b9 S1 T0 M3 E! V5 M: X fclose(pFile); return 0;3 v: w( v+ ^$ k5 [! M0 Z" @, E( n } 忽略前两个点,利用excel生成曲线:
从图中可看出:2 k$ T$ k/ Z( i. H 一阶导数为正时,函数递增趋势;2 r) l4 f1 [3 I3 }5 N# t, [$ ^9 g 一阶导数为负时,函数递减趋势;+ H" b3 y7 D$ x4 _4 N 二阶导数为0时,出现拐点,趋势改变;此时如果左右两侧的一阶导符号相反,则出现极值。 二阶导数为负时,其一阶导数也即原函数斜率规律单调减,二阶导数为正时,其一阶导数也即原函数斜率规律单调增。 再进一步:9 k C3 C0 J& D5 H! e, i 一阶导数与二阶导数结合起来看,就可以看出测量值变化趋势的趋势,比如在前1/4周期,此区间变换趋势为增,也即一阶导数为正,而其二阶导数为负,也可以看出递增的趋势是逐渐减小到0的。0 ^$ Q V5 c) F& d 代码优化 如果只是做定性判断,上述函数,完全没必要与采样周期做除法,只需要考察其增量即可,代码可优化如下: typedef struct _T_2ND_DRV, e" y% ?; d; w5 ? { float xn1;6 J u" i; r0 B' c float xn2; }t_2ND_DRV; typedef struct _T_1ST_DRV! C" |$ C4 w! i* V { float xn1; }t_1ST_DRV;$ m1 v3 C" k0 h# e void init_second_derivative(t_2ND_DRV *pSndDrv)5 |4 g7 D0 c) R" z4 ~ { pSndDrv->xn1 = 0;& M( V8 X) Q6 q4 Y. S: a pSndDrv->xn2 = 0; }- V4 Z( J3 B; ~# Z; e& K$ `, | float second_derivative(t_2ND_DRV *pSndDrv, float xn) {4 L; C& g% u: j$ m float result=0.0f; result = xn-2*pSndDrv->xn1+pSndDrv->xn2;1 u4 p7 o; ~0 U, b, b2 M! g% W pSndDrv->xn2 = pSndDrv->xn1;2 D# ]& z2 T( E* T% u8 M# j4 U( ~ pSndDrv->xn1 = xn; , } {1 ~0 D# D: k; a+ a return result;" V$ A' U' b/ c$ T } " ?3 i3 }3 V: P5 B" k6 b void init_fisrt_derivative(t_1ST_DRV *p1stDrv) { p1stDrv->xn1 = 0; }9 K: t, e( L' {5 F. @. {% W float fisrt_derivative(t_1ST_DRV *p1stDrv, float xn) {) W0 }: U9 y o float result=0.0f;' J6 q3 H( v+ B0 P$ v$ J7 T result = xn-p1stDrv->xn1; p1stDrv->xn1 = xn; 1 ?4 e; E( T0 e1 M2 e6 I return result; } 意外收获: G" T0 \2 Q% t: `: t' |# I 这里意外引入一个可能很多人没注意的知识点NaN,在计算中,NaN代表非数字,是数字数据类型的成员,可以将其解释为不确定的或无法表示的值,尤其是在浮点运算中。1985年,IEEE 754浮点标准引入了NaN的系统使用,并表示了其他无限量(如无穷大)。9 ~1 }) k7 e9 `2 k" p C6 z 前述函数返回0x7FBFFFFF,也就是表示无穷大。 不同的操作系统和编程语言可能具有NaN的不同字符串表示形式:4 w9 H. T+ I0 w, S: v4 k9 O, O nan( x6 T- I; y, s9 h1 u8 a" e NaN" y O& R5 L0 k; x& F0 y G NaN%1 S: e. P, r' \$ r4 s2 _ NAN NaNQ8 s' n9 T5 k- e; {. n NaNS6 x/ m$ j: Z7 ?4 p5 D- w, g qNaN sNaN6 v9 S& E7 n B' _0 Y# h 1.#SNAN4 M# G9 ?+ U3 Q 1.#QNAN -1.#IND2 r) K( e' u2 E. p. F7 R 实际上,由于编码的NaN具有符号,因此通常也可以在NaN的字符串表示中找到它们,例如:1 k/ `( e0 R7 G8 p' \; }0 w+ f -NaN NaN12345 -sNaN12300 -NaN(s1234) 工程应用9 u$ V2 h6 s ?: P4 p) [ 8 R, y/ f- J) N4 S 这里给出我的建议方案:1 Y( F0 ~+ t6 L6 S( v U
将传感器信号经由电路处理,模数采样,在进入前级数字滤波器,滤除不必要的噪声,在进行一阶/二阶求导。对于一阶和二阶求导再做一级移动平均滤波,最后在按照上面描述进行判别变化趋势,基本比较健壮了。实际移动均值滤波长度不宜选择过长,否则响应就比较滞后了。不能对传感器的变化趋势做出实时的判别。加了后级均值滤波器,则会消除由于波形忽上忽下的随机噪声干扰影响,使得系统判别更为健壮,实际滤波器长度需根据不同的场合进行调试优化。或者也可以选择别的IIR/FIR滤波器形式实现。 总结一下 1 J4 f% w" |2 `" g1 k* m 做为嵌入式er编程,有时候有必要去看看数学书,了解一下数学原理的背后故事,可能会给你带来意想不到的作用哦。 |
微信公众号
手机版
期望能够分享更多的知识!