本帖最后由 radio2radio 于 2018-11-26 15:51 编辑 1 L' X: U$ g- M v/ C1 q: D
2 H+ p9 n( [6 q A
虽然这个话题是程序员的基本功,但是,每一次实际使用时都要“重新编程+除错”折腾一番。
: j5 U* O- g/ ?4 W于是,萌发了搞一个“通用的”,目的是,下次用到时,拿过来就用。3 v+ T, f/ i4 N( K2 Z) P8 L' V
大家看看我的方法是不是最佳的,欢迎提出改进方案。
& b2 I. z! e7 Q; l% @( {+ ]- Q- E" s' W. ~5 `$ k* N
由MCU的ADC读到的“真实世界”的数据,0~1023对应10bit的ADC,0~4095对应12bit,0~65535对应16bit。通常,这些数据都要通过简单的数据处理,变换成电压/电流/温度/压力/等等意义明确的数值,用于传输或者显示。
# `, z {2 x1 y* i! V2 w" L+ S$ e8 }) u; h% }# O
线性插值,就是最最常用的数据处理方法。 直线函数公式:
/ c: G& c" C- k: r
; w2 ~ B5 [4 [" C
0 d0 I9 B* K n8 U9 P: @: l/ o' M/ Q5 L( h2 `& u% e
应用实例,某电池的“电压—容量”测量估算结果,大致如下图所示:
& `3 |7 q& n3 R+ o; l( `( `, H7 U# {
$ s: O$ i: {" Z2 i% e
, R3 Y& `/ `' {1 M7 o由曲线上面可以看出,虽然已经有了10组实测数据,但是只要取其中黄色的4组数据,也就是用3段直线进行线性插值,就可以得到很好的“近似结果”。6 l$ {/ o" ^' y9 ~
下图,就是用我的“通用线性插值程序”得到的计算结果,看图形,基本上一样的。
( X( F. H8 W W) b6 `- u( f/ S* u% V(注意,ADC数据所对应的电池电压值,只是测量ADC数据时用可调电源代替电池的外加电压,它们并不参与运算。 直接由ADC数据插值出电池的剩余电量。)$ M1 e& C$ w: A! T5 _ F6 r
+ p5 s2 W @8 v2 c2 v6 w5 {& U! N1 _: V: K f% C
- _8 s x) O* b+ J; ~7 N
R+ d. E7 ^. O" g9 x正弦曲线测试实例,使用37个X轴等间隔的数据(每10度一个),线性插值出0-360度的范围一段正弦曲线的结果:
' q: ~6 E% f! Q8 Q5 }9 r: y(为了提高转换精度,合理的做法是在曲线变化剧烈的部分,密集取点;线性好的部分,少量取点。而不是采用等间隔的方式取点。)
2 P6 ~ R3 t/ \5 F. ~) x5 p
) j% O7 s: D. Q8 r) y/ K0 i
8 z @: `5 F! T1 g: z7 x# _; U2 l* o4 y5 q% T5 ~
$ I6 t( l. ~/ ^
最后,给出子程序:# N) E/ h5 w6 {" P4 X4 N* g
需要说明的是,笔者对有符号的整数(int)和浮点数(float)有“原始的抵触”,喜欢使用无符号整数类型(uint)。所以只做出了整数类型的。% P B$ n# ], n9 |
* X8 `* d. ?* B8 A- k& R9 H3 q8 ~* ~+ U
- //General integer Linear Interpolation
C! `+ W0 x% h4 M, D - //$ O6 ]- e# h" @6 _; J6 H& M
- //setup: uint16_t Y[size] = {Y0, Y1, Y2, , , Ysize-1};& \4 e9 ^# i! X" o6 J L% i
- //setup: uint16_t X[size] = {X0, X1, X2, , , Xsize-1};* p) B" R8 n% q, K
- // - Two Arrays in integer format, Unsigned, no negative values.
$ k1 D9 _3 T) ?. N! {/ B$ f# W - // - X[0]<X[1]<X[2]......<Xn, must increasing.
9 V9 I( ?+ ]; t
$ A# @/ E7 I1 z- //Test data-1, battery capacity:3 j9 w A# _" C
- const uint16_t X[] = {31778, 33442, 39398, 40421};$ S0 ~5 C) [& w R- Z" G9 {
- const uint16_t Y[] = {0, 29, 94, 100 };
+ _' {: q! L/ H- ?8 {
; S$ z, R6 ~, W. e8 x3 \- //Test data-2, sine wave:
- R/ B v9 Q+ h; d* P3 E - //const uint16_t X[] = {0,10,20,30,40,50,60,70,80,90,% T' [# H" F. b% }8 O8 b2 f3 O
- // 100,110,120,130,140,150,160,170,180,190,
) ^) u* r: J8 M2 d. V% w - // 200,210,220,230,240,250,260,270,280,290,
( f ] k( l: B4 u: G* m - // 300,310,320,330,340,350,360};5 c8 c( M4 F |' g
- //const uint16_t Y[] = {2000,2174,2342,2500,2643,2766,2866,2940,2985,3000,' k- ~9 h, G/ Z! n, ^7 o- J
- // 2985,2940,2866,2766,2643,2500,2342,2174,2000,1826,
2 [! i' n3 k/ L3 Z, r" k1 I - // 1658,1500,1357,1234,1134,1060,1015,1000,1015,1060,+ ^ p9 ^1 A* l& t
- // 1134,1234,1357,1500,1658,1826,2000};9 l0 q# v& M8 |* \
, G9 i6 S7 U( ^6 ? T! z- : r! h: w, ?0 G/ y7 n# I4 G
- uint16_t u16LinearInterpolation(uint16_t xdata)/ [$ [( z7 u/ n
- {# {7 c" p ~& o
- uint32_t u32Temp;
, ^, G/ h1 U3 Y* f/ a1 { - int i,size;/ y. O/ `5 n& M5 U* ^
-
1 Z. y; V0 U, k$ U* w1 j. r' L - size = sizeof(X)/2; //get the array numbers, uint16_t occupied 2 bytes.2 U4 ] ], M; i4 g
-
: M. G( p2 u6 Y6 ` - if (xdata <= X[0]) return Y[0];
9 _7 m9 s6 V8 G - if (xdata >= X[size-1]) return Y[size-1];, d4 { I; T* ?7 W) z J( R4 u0 Y
- . @ }+ g. T* {
- for (i=0; i<(size-1); i++)
' q% i; w" r% Y: F% C6 N( s$ D - {; j' Z5 N( {# O# r; g& N! m
- if ((xdata >= X[i]) && (xdata < X[i+1])) break; . B$ ~: e1 j* k; b8 x0 }
- }
$ V) h" K- [" g( i! V - " `* t9 Z- L! ~9 A) W9 i
- if ((xdata == X[i]) || (Y[i] == Y[i+1])) return Y[i]; {# a' S+ |- q3 h. D
- 0 j) p9 J1 \9 Y% X1 J& [2 x6 ~1 w
- //Y(x)=[Yi*(Xi+1-x)+Yi+1*(x-Xi)]/(Xi+1-Xi); A( @. ?2 D% l/ w
- u32Temp = Y[i]*(X[i+1]-xdata);
7 c9 d& @) P/ T2 B- h0 e5 q1 R - u32Temp += Y[i+1]*(xdata-X[i]);
5 h, T: Q- @; c$ }' c - u32Temp /= X[i+1]-X[i];
9 `; b- \! `: l, ]& D+ t; ^; ^ - ( C3 g5 a/ R) m5 K0 @! F
- return (uint16_t) u32Temp;7 K7 ?* i! M& p7 f* {" [- h
- }
' e. a9 H1 t4 ~7 x) z
复制代码 , \4 Y- |. F; h, M) W6 [
r' t% Z1 [) K* j% U
% ~5 B3 Y9 w% L( Q* h: l$ c: `+ Q4 _ G/ I0 Q
* H, ^5 e' r& A: ^0 c; e |