proteus仿真micropython_基于micropython的滑动平均滤波器
滑動(dòng)平均濾波也叫遞推平均濾波。
把連續(xù)取得的N個(gè)采樣值看成一個(gè)隊(duì)列,隊(duì)列的長度固定為N,每次采樣到一個(gè)新數(shù)據(jù)放入隊(duì)尾,并扔掉原來隊(duì)首的一次數(shù)據(jù)(先進(jìn)先出原則),把隊(duì)列中的N個(gè)數(shù)據(jù)進(jìn)行算術(shù)平均運(yùn)算,獲得新的濾波結(jié)果。
從這個(gè)原理中可以感覺出這種濾波方法的平滑度會(huì)很高,所以會(huì)適用于高頻震蕩系統(tǒng),對(duì)周期性的干擾有很好的抑制作用。但是也是有缺點(diǎn)的,靈敏度低,
對(duì)偶然出現(xiàn)的脈沖性干擾的抑制作用較差不易消除由于脈沖干擾所引起的采樣值偏差不適用于脈沖干擾比較嚴(yán)重的場(chǎng)合,由于他要保存過去N個(gè)樣本的數(shù)據(jù)所以說會(huì)比較浪費(fèi)RAM。
(N值的選取:流量,N=12;壓力,N=4;液面,N=4-12;溫度,N=1-4。)
以上引用來自互聯(lián)網(wǎng),由于出處太多已分不清是誰的原創(chuàng),如果原作者見了需版權(quán)聲明請(qǐng)通知
本次實(shí)驗(yàn)是基于pyboard的,原碼作者是英國一個(gè)大牛用匯編寫的https://github.com/peterhinch/micropython-filters.git
這個(gè)帖子主要對(duì)其進(jìn)行了測(cè)試以及用python寫了個(gè)可以移植到其他micropython平臺(tái)的代碼。后續(xù)會(huì)貼出FIR濾波器,傅立葉變換等帖子,之前一直想發(fā)帖,無奈總是提示我有敏感詞匯發(fā)不了
下圖為官網(wǎng)pyboard的圖以及本次測(cè)試用的板子圖
板子是我自己畫板掏錢打板手工焊的,完全兼容其他pyboard的開發(fā)板,所以盡管放心測(cè)試
截圖201712261006377680.png (500.06 KB, 下載次數(shù): 22)
2017-12-26 10:06 上傳
截圖201712261334343967.png (312.66 KB, 下載次數(shù): 17)
2017-12-26 13:34 上傳
英國牛人代碼如下,
[AppleScript] 純文本查看 復(fù)制代碼# Implementation of moving average filter in Arm Thumb assembler
# Author: Peter Hinch
# 15th Feb 2015
# Updated to reflect support for sdiv instruction
# Timing: 27uS on MicroPython board (independent of data)
# Function arguments:
# r0 is an integer scratchpad array. Must be of length 3 greater than
# the number of values to be averaged.
# On entry array[0] must hold the array length, other elements must be zero
# r1 holds new data value
# Return value: the current moving average
# array[0] is array length, array[1] is the current sum, array[2] the insertion point
# r2 holds the length of the coefficient array
# Pointers (byte addresses)
# r3 start of ring buffer
# r4 insertion point (post increment)
# r5 last location of ring buffer
# Other registers
# r7 temporary store for result
@micropython.asm_thumb
def avg(r0, r1):
mov(r3, r0)
add(r3, 12) # r3 points to ring buffer start
ldr(r2, [r0, 0]) # Element count
sub(r2, 4) # Offset in words to buffer end
add(r2, r2, r2)
add(r2, r2, r2) # convert to bytes
add(r5, r2, r3) # r5 points to ring buffer end (last valid address)
ldr(r4, [r0, 8]) # Current insertion point address
cmp(r4, 0) # If it's zero we need to initialise
bne(INIT)
mov(r4, r3) # Initialise: point to buffer start
label(INIT)
ldr(r7, [r0, 4]) # get current sum
ldr(r6, [r4, 0])
sub(r7, r7, r6) # deduct oldest value
add(r7, r7, r1) # add newest value
str(r7, [r0, 4]) # put sum back
str(r1, [r4, 0]) # put in buffer and post increment
add(r4, 4)
cmp(r4, r5) # Check for buffer end
ble(NOLOOP)
mov(r4, r3) # Incremented past end: point to start
label(NOLOOP)
str(r4, [r0, 8]) # Save the insertion point for next call
ldr(r1, [r0, 0]) # Element count
sub(r1, 3) # No. of data points
mov(r0, r7) # The sum
sdiv(r0, r0, r1) # r0 = r0//r1
為了提高效率代碼是由匯編寫的直接操作寄存器。
這個(gè)函數(shù)入口主要有兩個(gè)參數(shù),第一個(gè)參數(shù)為隊(duì)列的緩存,第二參數(shù)為最新的數(shù)據(jù),關(guān)于第一個(gè)參數(shù)源碼作者有說明,這個(gè)參數(shù)的長度必須比隊(duì)列長度的長3,這個(gè)參數(shù)第一個(gè)元素存儲(chǔ)著這個(gè)參數(shù)的總長度,第二元素為N個(gè)樣本數(shù)據(jù)的和,第三個(gè)元素是數(shù)據(jù)的插入點(diǎn)(對(duì)于第二個(gè)第三個(gè)元素不需要去操心,用來給匯編提供便利)后面的元素為采樣值的緩存。
作者提供了一個(gè)測(cè)試程序,主要測(cè)試了一下這個(gè)用匯編寫的代碼的執(zhí)行用時(shí),跑一下試試
[Python] 純文本查看 復(fù)制代碼# Demo program for moving average filter
# Author: Peter Hinch
# 12th Feb 2015
import array, pyb
from avg import avg
data = array.array('i', [0]*13) # Average over ten samples
data[0] = len(data)
def test():
for x in range(12):
print(avg(data, 1000))
for x in range(12):
print(avg(data, 0))
def timing():
t = pyb.micros()
avg(data, 10)
t1 = pyb.elapsed_micros(t) # Time for one call with timing overheads
t = pyb.micros()
avg(data, 10)
avg(data, 10)
t2 = pyb.elapsed_micros(t) # Time for two calls with timing overheads
print(t2-t1,"uS") # Time to execute the avg() call
test()
print("Timing test")
timing()
測(cè)試結(jié)果
截圖201712261336544902.png (83.56 KB, 下載次數(shù): 13)
2017-12-26 13:36 上傳
隊(duì)列長度為10,執(zhí)行用時(shí)8微秒,效率挺高的了
那么接下來看看直接接設(shè)備進(jìn)行實(shí)時(shí)測(cè)試看效果怎么樣。
思路:外接一個(gè)三軸加速度計(jì),取一個(gè)軸的數(shù)據(jù)進(jìn)行濾波,同時(shí)打印原始數(shù)據(jù)和濾波后的數(shù)據(jù)做對(duì)比(當(dāng)然板載一個(gè)MMA7660三軸加速度計(jì),但是其輸出的數(shù)值范圍太小了,并不是說小數(shù)值不能用只是為了提高觀察效果我采用了外置的加速度計(jì))
代碼如下
[Python] 純文本查看 復(fù)制代碼import MPU6050
import array
from protocol import *
from avg import *
from pyb import I2C,UART,delay
#golbal data
accx_data = array.array('i', [0]*8)
#f_acc_data = avg_filiter(accx_data)
accx_data[0] = len(accx_data)
#hardwareobject
uart_port = UART(4,57600)
iic = I2C(1,I2C.MASTER)
acc_dev = MPU6050.MPU6050(iic)
while True:
raw_data = acc_dev.read_Accel_z()
filter_data = avg(accx_data,raw_data)
send_data = data_send(raw_data,filter_data,0,0,0,0,0,0,0)
uart_port.write(send_data)
delay(20)
用串口示波器把數(shù)據(jù)顯示出來效果如圖
截圖201712261349484095.png (145.63 KB, 下載次數(shù): 16)
2017-12-26 13:49 上傳
局部圖
截圖201712261350306731.png (176.56 KB, 下載次數(shù): 15)
2017-12-26 13:50 上傳
繼續(xù)放大
截圖201712261351009137.png (217.63 KB, 下載次數(shù): 14)
2017-12-26 13:51 上傳
紅色是原始數(shù)據(jù),藍(lán)色是濾波后的數(shù)據(jù),本次實(shí)驗(yàn)滑動(dòng)的N為5,從圖像來看確實(shí)對(duì)波形的毛刺有了很好的平滑效果,波形有輕微的延時(shí)
。
大致思路明白了,那么現(xiàn)在用python自己來寫一個(gè)滑動(dòng)濾波的函數(shù),捋一下大致步驟就是,建立一個(gè)先入先出的隊(duì)列,然后每次新的數(shù)據(jù)入列先把最前面的元素出列然后對(duì)所有的數(shù)據(jù)求平均值,由于對(duì)樣本的和是一個(gè)反復(fù)的過程,所以為了提高效率,采納原作者的思路,找一個(gè)位置來存放所求的和,每次計(jì)算只需減去最前面的樣本在加上最新的樣本即可,這樣就省去N-2次的加法計(jì)算。(我之前用最簡單最笨的方法寫的試了,實(shí)在是耗時(shí)太長了,最后借鑒了源作者的方法,換句話說就是對(duì)原作者代碼的匯編到python的翻譯,這樣也可以在其他不支持匯編的平臺(tái)上無縫移植了)
代碼如下,但是用時(shí)太久了,樣本長度為10的情況下測(cè)試的。
[Python] 純文本查看 復(fù)制代碼import array, pyb
class avg_filiter():
def __init__(self,cache_data):
self.cache = cache_data
self.len = len(cache_data)
self.cache[0] = self.len
self.sum = 0
for item in cache_data[3:]:
self.sum += item
self.cache[1] = self.sum
def avg(self,new_data):
self.cache[1] = self.cache[1] - self.cache[3]
self.cache[1] = self.cache[1] + new_data
self.cache[3:-1] = self.cache[4:]
self.cache[-1] = new_data
return self.cache[1]//(self.len - 3)
data = array.array('i', [0]*13) # Average over ten samples
fdata = avg_filiter(data)
def test():
for x in range(12):
print(fdata.avg(1000))
for x in range(12):
print(fdata.avg(0))
def timing():
t = pyb.micros()
fdata.avg(10)
t1 = pyb.elapsed_micros(t) # Time for one call with timing overheads
t = pyb.micros()
fdata.avg(10)
fdata.avg(10)
t2 = pyb.elapsed_micros(t) # Time for two calls with timing overheads
print(t2-t1,"uS") # Time to execute the avg() call
test()
print("Timing test")
timing()
但是程序耗時(shí)整整翻了十倍
截圖201712261340208536.png (76.89 KB, 下載次數(shù): 17)
2017-12-26 13:40 上傳
不過雖然時(shí)間相對(duì)匯編來說有點(diǎn)長,但是對(duì)于一般的AD采集的場(chǎng)合這個(gè)時(shí)間完全夠用了,現(xiàn)在可以開心的移植到esp8266上了
滑動(dòng)濾波相對(duì)簡單一點(diǎn),大概就這里吧。后面附件貼上代碼,里面有mpu6050的庫
mpu6050 滑動(dòng)平均濾波.zip
(4.74 KB, 下載次數(shù): 70)
2017-12-26 10:53 上傳
點(diǎn)擊文件名下載附件
代碼
總結(jié)
以上是生活随笔為你收集整理的proteus仿真micropython_基于micropython的滑动平均滤波器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTTP(S)协议详解
- 下一篇: 程序员,也需要学习分析与设计的方法?!