第1步:基本知識-時序,定時器,中斷。..
一般思想
金屬探測器的基本原理是,線圈中的電感/信號會隨著目標靠近線圈而改變。識別這些變化的常用方法是測量頻移,衰減時間,選定時間點的電壓變化等。基于基于ATmega328的Arduino的功能,這些輸入可以通過模擬讀取,數字讀取來測量
此指令將覆蓋這些輸入。
要獲得靈敏的檢測器,不幸的是,需要非常快速地測量輸入。以16MHz運行的ATmega似乎運行得很快,但是在許多情況下,這太慢了,無法使用基于標準Arduino的例程。這里將提供各種“外部”標準Arduino例程“外部”的方法,以提供盡可能高的速度
定時,定時,定時。..。.
基于線圈的金屬探測器的關鍵要素是時候了。通常,信號是快速的,因此時序上的小誤差會導致讀數錯誤,從而難以獲得穩定的讀數。
Arduino時序的標準功能是諸如millis(),micros(), delay()和delaymicroseconds()。在大多數應用中,這些例程運行良好,對于金屬探測器設計,它們的性能并不理想。
根據參考,millis()的分辨率為4μS。另一方面,當查看Arduino的振蕩頻率時,兩個脈沖在16MHz處的延遲為0,0625μS,是分辨率的64倍。為了獲得對此16MHz頻率的訪問以進行計時,最方便的方法是使用ATmega內部定時器。共有三個計時器(timer0,timer1和timer2)。
可以通過預分頻器(分頻器)將定時器設置為以16MHz或更低的頻率運行。定時器具有不同的模式,但在最簡單的模式下,它們僅從0計數到給定值,觸發中斷服務程序(ISR –小程序)并重新開始計數。這種操作模式稱為比較時清除定時器(CTC)。在計時器計數期間,在程序的任何時間都可以訪問計數器值并將其復制到變量中。
在Arduino運行的大多數時間里都是如此。不幸的是,不僅定時器觸發中斷,而且許多其他事件也觸發中斷。因此,即使在執行ISR期間,中斷也可能在相似的時間發生。由于每個中斷都會延遲當前代碼的執行,因此會再次導致較小的誤讀甚至完全丟失值。為了使ATmega本身具有某種順序,可以對優先級不同的中斷進行處理。
Vector Addr Source Interrupts definition
1 0x0000 RESET External Pin, Power-on Reset, Brown-out Reset and Watchdog System Reset
2 0x0002 INT0 External Interrupt Request 0
3 0x0004 INT1 External Interrupt Request 0
4 0x0006 PCINT0 Pin Change Interrupt Request 0
5 0x0008 PCINT1 Pin Change Interrupt Request 1
6 0x000A PCINT2 Pin Change Interrupt Request 2
7 0x000C WDT Watchdog Time-out Interrupt
8 0x000E TIMER2_COMPA Timer/Counter2 Compare Match A
9 0x0010 TIMER2_COMPB Timer/Coutner2 Compare Match B
10 0x0012 TIMER2_OVF Timer/Counter2 Overflow
11 0x0014 TIMER1_CAPT Timer/Counter1 Capture Event
12 0x0016 TIMER1_COMPA Timer/Counter1 Compare Match A
13 0x0018 TIMER1_COMPB Timer/Coutner1 Compare Match B
14 0x001A TIMER1_OVF Timer/Counter1 Overflow
15 0x001C TIMER0_COMPA Timer/Counter0 Compare Match A
16 0x001E TIMER0_COMPB Timer/Coutner0 Compare Match B
17 0x0020 TIMER0_OVF Timer/Counter0 Overflow
18 0x0022 SPI STC SPI Serial Transfer Complete
19 0x0024 USART_RX USART Rx Complete
20 0x0026 USART_UDRE USART Data Register Empty
21 0x0028 USART_TX USART Tx Complete
22 0x002A ADC ADC Conversion Complete
23 0x002C EE READY EEPROM Ready
24 0x002E ANALOG COMP Analog Comparator
25 0x0030 TWI 2-wire Serial Interface (I2C)
26 0x0032 SPM READY Store Program Memory Ready
穩定時序編程的目標是盡可能使用最高優先級的中斷并找到解決方法防止在ISR期間發生其他中斷。這導致了處理中斷的第一條規則:減小ISR! (。.我將定期違反該規則……)。
由于標準Arduino函數也了解這些計時器,因此許多例程和庫都在使用它們。這意味著,如果我們使用計時器并更改其預設值,則某些標準例程將不再起作用。
延遲,音調,傳感器,步進,PWM功能以及通信可能不再正常工作。
步驟2:如何使用計時器
有關如何使用計時器的說明非常棒。我使用了這個Instructable的參考,這里只會給出一些摘要。如果您想更深入地了解計時器,請參閱說明“ Arduino計時器中斷:6個步驟(帶有圖片)”。
基本原理是,首先您要告訴timer0,timer1或timer2
它應該運行多快(寄存器TCCR0B,TCCR2B,TCCR2B中的預分頻器)
要計算的值(寄存器OCR0A,OCR1A,OCR2A中的值)
此時要執行的操作(TCCR0A,TCCR1A,TCCR2A中的設置-》重置并重新啟動,觸發PWM信號等…)
將計數器設置為一個給定的值(從TCNT0,TCNT1,TCNT2中的值開始)
啟用相關中斷(在TIMSK0,TIMSK1, TIMSK2)→這將調用ISR
,然后使用中斷向量名稱e定義ISR。 G。 ISR(TIMER0_COMPA_vect)并定義在此ISR期間應執行的操作。再次,此代碼應保持簡短,因為此代碼可隨時被其他中斷打斷,這將破壞值或導致程序崩潰。
另一部分信息是,計時器0和計時器2可以計數到255,計時器1可以計數到65535。
如何將計時器用于金屬探測器
如前所述,計時對于金屬探測器至關重要。因此,所有與時間相關的工作都將通過使用計時器來完成。
通常,向線圈提供一個初始脈沖。之后,測量線圈的反應(可以是相同的線圈,也可以是不同的線圈)。在測量期間,應避免所有其他中斷!在這段時間內中斷會導致值略有下降,值損壞,值丟失。
我將計時器用于3種不同目的:
timer0用于主事件(例如用于脈沖感應檢測器的脈沖)
timer1用于數據采集(例如,頻移檢測,模數轉換的時序)
timer2用于音調/音量生成。
timer0
脈沖感應(PI)檢測器的主周期包括兩個階段。首先是為線圈供電的脈沖,然后是進行數據采集的靜音狀態。對于PI檢測器,脈沖的正常持續時間約為250μS,脈沖后的靜音應足以進行數據采集,處理和更新輸出。
因此,首先要一方面將timer0設置為所需的“事件速度”,以提供接近250μS的脈沖和可用的靜音時間。對于200Hz PI檢測器,這將是1024的預分頻器(aka每周期16MHz/1024 = 15.625kHz→64μS),脈沖的比較計數器為“ 4”,而靜默計數器為“ 72”。
// set timer0 - taking care of the pulse to the coil and the pause between two pulses
// Pulse composes of 4.672ms “off” (72) and 0.32ms “on”(4)
// separate times for OCR0A will be set in the interrupt routine
// Resulting frequency is 200Hz
cli();
TCCR0A = 0; // set entire TCCR0A register to 0
TCCR0B = 0; // same for TCCR0B
TCNT0 = 0; // initialize counter value to 0
// set compare match register to required pulse with
OCR0A = 72; // = (4672μS/(0.0625μS * 1024)) - 1 (must be 《256)
// turn on Clear Timer on Compare (CTC) mode
TCCR0A |= (1 《《 WGM01);
// Set CS01 and CS00 bits for 1024 prescaler
TCCR0B |= (1 《《 CS02) | (1 《《 CS00);
// enable timer compare interrupt
TIMSK0 |= (1 《《 OCIE0A); sei();
由于信號非常不對稱,因此可以在每次中斷時將值設置為4和72。
ISR基本看起來像這樣(帶有全局易失性布爾“ toggle”):
ISR(TIMER0_COMPA_vect){
if(toggle){
cli();
OCR0A = 72; // set for a long “off-time” -》 next interrupt timer0 in 4.672ms
sei();
}
else{
cli();
OCR0A = 4; // set for a short “on-time” -》 next interrupt timer0 in 320μS
sei();
}
toggle=!toggle;
}
當然,在if和else期間,還有一些其他代碼可用于
timer1
由于timer1的計數器值最高(65535),因此可以用來高精度地測量“長”時間段。以最大速度(16MHz),超限之前的最長事件為4.1ms。如果事件的時間變化很小,則超限甚至可以忽略。然后,最大定時分辨率為0.0625μs!那就是后臺timer1用于數據采集。
首先將計時器設置為最大速度
cli();
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
// set compare match register
OCR1A = timer1MaxValue; // just a value to start with -》 set to a long period
// to prevent unwanted interrupt interference
// turn on Clear Timer on Compare (CTC) mode
TCCR1B |= (1 《《 WGM12);
// Set CS10 no prescaler → running at full 16MHz
TCCR1B |= (1 《《 CS10);
// enable timer compare interrupt → calling the ISR
TIMSK1 |= (1 《《 OCIE1A);
sei();
要開始數據采集,timer0重新啟動通過將timer1的計數器設置為0(TCNT1 = 0),在每個脈沖后將timer1設置為1。現在有兩個數據采集選項:
等待事件發生,并通過讀取計數器TCNT1,使用timer1查看事件發生的時間。
事件,例如在脈沖后的特定時間(使用timer1的預設比較值)讀取模擬信號。
在第一種情況下,ISR(例如,模擬比較器或引腳變化)將讀取TCNT1,例如g。
ISR(ANALOG_COMP_vect){ // for analog comparator @ pin D6 and D7
Toggles[toggleCounter]=TCNT1;
toggleCounter++;
}
在第二種情況下,比較值OCR1A設置為將調用timer1比較ISR并執行模擬讀取的值。在ISR期間,可以將比較值OCR1A更改為新值,以重復執行模數轉換(ADC)周期e。 g。
ISR(TIMER1_COMPA_vect){
cli();
OCR1A = timer1PauseValue; // set the next interrupt to the value where a
// next ADC will give a usefull value
ADCSRA |= (1 《《 ADSC); // ADSC -》 start the cycle -》 will be cleared
// after the conversion is done
sei();
}
timer2
指示目標的最簡單方法是通過聲音。在尋找寶藏時,您的眼睛通常會集中在尋找地點。因此,它們不能用于顯示或LED。使用聲音可以輕松地在觀看線圈并同時收聽時找到確切的位置。為了給目標提供精確度和某種感覺,音調應根據信號強度改變其音量。因此,我認為應該為揚聲器實現音量調制。
這只是通過向揚聲器發送32kHz PWM信號來完成的。標準揚聲器應緩慢將32kHz信號轉換為音調。 32kHz被“解釋”,而不是模擬值。通過提供一個“開”脈沖由32kHz信號組成的可聽頻率,該信號具有不同的PWM比例,可改變可聽音的音量。
Timer2的優點是它是硬連線的D3和D11引腳的PWM功能
可以通過設置寄存器TCCR2A的COM2x1(COM2A1和COM2B1)中的位將這些引腳激活為PWM。寄存器/輸出A為D11,寄存器/輸出B為D3。通過將“如何表現”設置為“快速PWM”,可以將PWM設置為直接驅動引腳,而無需任何ISR!比較值OCR2A設置PWM比(OCR2A = 0→0%正波→0V; OCR2A = 255→100%正波→5V)。
OCR2A,因此PWM比可以設置為任意值在代碼中放置以更改音量。
現在有了音量,我們仍然需要創建可聽到的聲音/頻率。可以使用其ISR中的其他定時器之一來完成此操作(例如,在啟動脈沖時將OCR2A設置為“ volume”,將OCR2A = 0設置為“ 0”;在啟動脈沖時會產生200Hz的可聽音)。
第3步:使用數字和模擬引腳
模擬讀取,但速度非常快。
通過使用AnalogRead()可以以良好的精度和穩定性讀取10Bit值。不幸的是,最大采樣率約為10kHz。這部分歸因于AnalogRead()中的一些附加代碼,部分歸因于AD轉換的時鐘預分頻器。
幸運的是,可指導的“ Girino-快速Arduino示波器”提供了有關如何進行獲得更高的采樣率。只需取消相關行的注釋,就可以設置模數轉換(ADC)速度的采樣。
// ADCSRA |= (1 《《 ADPS2) | (1 《《 ADPS0); // 32 prescaler for 38.5 KHz
ADCSRA |= (1 《《 ADPS2); // 16 prescaler for 76.9 KHz
// ADCSRA |= (1 《《 ADPS1) | (1 《《 ADPS0); // 8 prescaler for 153.8 KHz
要使用ADC,有以下三種基本方法:
自由運行模式–每次轉換完成后都會調用特定的ISR。
帶中斷的單次轉換–轉換完成后將調用ISR
單次轉換和“延遲”直到值可用(例如在analogRead()中)
盡管自由運行模式在給定時間內可以最終獲得最多樣本,但我不建議使用用于金屬檢測。為什么?由于中斷的優先級較低(請參見中斷優先級表):優先級為22!前幾個讀數很可能會精確計時。此后,其他中斷將開始產生干擾并稍微延遲ADC。
所以我真正建議使用的是通過帶有中斷的單次轉換觸發的timer1觸發測量。
》
因此,timer1的每個ISR只需設置
ADCSRA |= (1 《《 ADSC);
大約270個(理論上為208個)xtal周期即可觸發ADC,“ I-am-finish-with-the-Analog”到數字轉換” ISR(ADC_vect)被調用,并且可以讀取該值。在16位預分頻器上,只能使用10位分辨率中的8位,這是因為較低的兩個字節不會在高速下給出精確值(請參見數據手冊)。
因為ADC將在在硬件級別上具有背景知識,因此此時間范圍可用于在ISR期間執行一些命令以存儲值和限制測量值的數量等
對于某些應用,信號的動態范圍與預期會產生什么樣的效果(在一次AD轉換中看到完整的波形)。但這似乎并不是一個大問題。這很可能是由于ATmega芯片內部的內部采樣和保持電路工作得很好!
數字端口的嚴重位限制
在某些情況下,必須設置Arduino。這可以通過使用digitalWrite()輕松完成。仍然,這些函數還有一些額外的開銷代碼,這使它們變慢,因此需要相當長的周期。
由于某些輸出在ISR期間發生了變化,因此對端口進行位沖擊是一個更好的選擇
基本功能是:
PORTD = PORTD | B00000100;//將D2設置為高電平,而不更改其他端口
PORTD = PORTD&B11111011;//將D2設置為低電平而不更改其他端口
PIND = PIND | B00000100;//如果從低到高切換D2;如果為高→低
端口分配按位分配
PORTD D7 D6 D5 D4 D3 D2 D1(TXD) D0(RXD)
PORTB N.A. N.A. D13 D12 D11 D10 D9 D8
PORTC A0 A1 A2 A3 A4 A5 Reset N.A.
為線圈供電,應將端口直接設置為“高”或“低” ,對于音頻輸出,PIND命令(切換)提供了一個簡潔的功能:多音目標識別。
如果在一個循環中有兩個點切換了揚聲器的引腳,我們可以決定是否根據目標,在兩個點或僅在一個點之間切換銷釘。這樣,我們可以達到高音(每個周期200Hz的兩個切換)或低音(每個周期100Hz的一個切換)。
使用引腳進行測試
尤其是在測試新開發的電路和新代碼非常有用,它可以驅動一個額外的引腳來指示代碼中正在發生的事情。它既可以用來觸發示波器,也可以用來顯示代碼中某個例程花費多長時間。通過在例程開始時將引腳設置為高電平,然后在例程之后將其設置為低電平,可以很好地看出這段代碼是否干擾了中斷或其他信號,或者可以比較哪個版本的代碼更快或更慢。
附帶的是模擬信號(紅色)和表示AD轉換需要多長時間的引腳信號的圖片。
步驟4:接口和數據輸出
探測器的主要目標是找到一個目標,然后向用戶提供有關目標的一些信息。指示目標的一種方式是使用揚聲器,如計時器2所述。
串行輸出。
這主要用于測試和實驗,但是它是無與倫比的!
Serial.print ()和兄弟姐妹的速度非常快(如果設置為Serial.begin(115200))。因此,在嘗試電路并獲得讀數的感覺時,Serial.print()可用于向計算機發送大量數據。如果以適當的方式格式化(例如,值之間的“空格”,循環之間的“返回”)格式,則可以通過將串行監視器的輸出復制到Excel或類似的電子表格程序中來傳輸大量數據,以便以后進行分析(我正在使用Libre Office )。
我廣泛使用了此功能,一個探測器項目將合并使用此功能,以便在以后的某個時間將數據打印到16x2 LCD。
16x2 LCD
我認為這是提供有關目標的其他信息的好方法。這可以合并信號強度,但同時提供菜單以瀏覽電位設置(靈敏度,自動平衡,功率,辨別力)。
有兩種簡單的方法來驅動典型的16x2 LCD
使用I2C背包傳輸4位直接接線
我知道,也有UART封裝,但是它們不像I2C背包那樣常見。
直接接線是直接的,但是很麻煩,因為要使用許多端口,并且需要一些接線。因此,使用16x2顯示屏的最簡單方法是使用I2C背包。應該!!不幸的是,這里有一些真實的話題:
它似乎在啟動過程中正在使用timer0
I2C的運行速度非常慢!
它
1。。在lcd.begin(16,2)期間;顯然,使用了delay()和同級(很有趣,僅在那里)。這意味著,如果我們希望像其他操作一樣將這些計時器用于其他目的,則必須在設置計時器之前調用lcd.begin(16,2)(這花了我一點時間。..)。 LCD上的其他呼叫不使用計時器,或者至少可以在修改的計時器設置下使用。
2。 最大的缺點絕對是I2C的速度。在將所有信息發送到LCD的第一個實現過程中,LCD根本不顯示任何內容,而只是掛起。我意識到發送包括lcd.clear()在內的32個字符太多了,所以我減少了2個字符的數量。但是,即使發送兩個字符也要花費大約3毫秒,這太長了。
在200Hz的工作頻率下,該周期包括一個300μs的脈沖和4.7ms的靜音。這種“靜音”用于數據采集(在我的情況下約為2.5毫秒),數據處理(在我的情況下為1毫秒)為LCD輸出留出約1.1毫秒的時間。實驗表明,即使是一個字符也要花費1.5毫秒。為了解決此問題,例程Wire.setClock(400000)非常有用。
通常,默認情況下,I2C時鐘設置為100kHz。通過使用Wire.setClock(400000),可以將時鐘設置為400kHz。在wire.begin()中設置了默認的100kHz。在lcd.begin()期間調用此例程。因此必須在lcd.begin()之后調用Wire.setClock(400000)(…。再受挫幾個小時)。
仍然必須解決該問題,即每個字符只能發送一個字符周期。為了解決這個問題,創建了一個數組(總共16x2個字符→總共32個字符),并填充了所有需要顯示的信息。然后,該數組每個周期讀取一個字符并發送到顯示器。
3。。默認情況下,I2C連接到A4和A5。在測試過程中顯而易見的是,驅動I2C總線正在使模擬引腳上的電壓不穩定(可能是所有引腳的電壓都不穩定,但是模擬引腳對ADC的影響非常敏感)。這就導致了時序問題,所有I2C傳輸都應與任何敏感的ADC周期完全隔離。
因此,通過將I2C時鐘速度設置為400kHz并在每個周期內僅發送一個字符來進行通信。在將下一個脈沖發送到線圈之前,可以將LCD縮小為完成狀態。因此,在脈沖后從引腳A0開始數據采集時,A4和A5相當不錯。因此,LCD的刷新率是200Hz/32個字符→6.25Hz
聲音
如有關計時器的步驟中所述,timer2用于為顯示器產生動態聲音輸出。探測器。一個簡單的揚聲器通過100歐姆電阻連接到端口A(D11)或端口B(D3),可以使您感覺到信號強度和目標類型(如果使用了多音目標識別)。要實現此目的,代碼并不是很好,但是可以完成工作。
在代碼中的兩個位置,由timer0計時,例如在dataCrunching之后,有兩個由布爾“聲音”驅動的代碼片段和“音高”
if((sound)&&(highPitch)) { // if the speaker should sound and at high pitch
if(OCR2A)
OCR2A=0;
else
OCR2A=volume;
} if(sound){ // if the speaker should make noise
if(OCR2A)
OCR2A=0;
else
OCR2A=volume;
}
else
OCR2A=0;
設置布爾值“ sound”和“ pitch”應該在代碼中的其他位置進行,因為它們不是時間緊迫的。
將變量“ volume”作為無符號字符或字節進行相同計數。
LED
由于通常在所有Arduino板上D13處都存在一個LED LED墊片,通過上一步中所述的位敲擊來設置銷釘。在測試過程中,這給人留下了很好的印象,供以后使用,該引腳可用于驅動外部LED。
附有來自不同時序的屏幕截圖,可通過外部引腳看到。可以看到初始脈沖,而不是一些劇烈的振蕩。在振蕩期間,進行數據采集。數據采集后,數據處理開始(黃色信號設置為高電平)。在所有數據處理和數據傳輸結束時,黃色信號設置為低電平。圖片顯示了以下持續時間的差異:
僅數據處理
數據處理,并通過Serial.print()發送50個值
數據處理并通過I2C @ 100kHz將僅1個字符發送到LCD
步驟5:數據處理
在理想情況下,接收到的信號將非常清晰,與參考值相比最小的變化將指示目標。不幸的是,這個世界并不理想,并且金屬探測器中的信號嘈雜,骯臟(尤其是當我使用很少的外部組件時)。為了過濾掉數據的相關部分,嘗試了一些方法來過濾接收到的數據,甚至找到了一些有用的方法。
優化的第一要點是電路!
如果電路和線圈是報廢的,那么最好的濾波算法就無法為您提供幫助。
有一些通用規則,例如使用大型電容器。屏蔽電路也將有所幫助,應進行試驗。
真正使事情變得困難的一件事是信號尖峰。如果信號高于5V或低于0V,則有一些內部電路可防止ATmega爆炸。僅當電流保持較低時,此方法才有效。為了保護起見,這很好用,對于程序的穩定性而言,則不是那么好。
已經處理了相當“骯臟”的信號,代碼產生了錯誤,ADC周期出現了嚴重的誤讀。這可能會導致讀數延遲小,讀數遺漏或無法解釋的原始值。優化電路是獲得合格數據的第一步。
這里需要對電路進行一些單獨的測試。方法是:
使連接保持短路
防止Arduino引腳上的大負載
防止靠近敏感零件/連接的大電流
防止組件加熱/冷卻
屏蔽組件,連接,完整電路
雙絞線
防止移動/丟失電線
知道自己在做什么……。.
如何解釋測量值
最簡單的方法識別被測信號變化的方法是將信號與信號的參考值進行比較。如果測量值不同于該參考值,則您發現了一些東西。
事實證明與眾不同。
實際上,您的讀數會有很小的偏差。
這些偏差是由于來自主電網的50/60Hz信號的信號干擾,屏蔽不良的設備(交流適配器,計算機)的高頻信號,對其他無線信號(Wifi,GSM)的干擾引起的或僅僅是由于電路設計而產生的振蕩。這些干擾可能會偏離參考值,幅度如此之大,以致使檢測器的靈敏度變得毫無用處。
在接下來的部分中,將介紹用于處理噪聲信號的不同方法。
創建平均值
在最后幾個值上創建平均值是一個不錯的起點。這樣,可以消除正負方向上的小偏差。根據噪聲的大小和外觀,可以選擇不同的值來創建平均值。
最簡單的方法是定義一個數組,然后將獲得的值填充到該數組中,并按每個周期遞增。在每個循環中,您將所有值相加,然后將它們與參考值進行比較。小偏差將被消除。這可以通過全局計數器輕松實現,全局計數器每個周期遞增一次。如果達到“ maxValue”,則將其設置為0。
每個循環,for循環從0到“ maxValue”計數以求和。可以將總和除以maxValue確實沒有意義。參考值可以直接與總和進行比較。
優點:易于實現,快速代碼,易于閱讀。
缺點:延遲小(可能無關緊要) ),僅適用于噪音很小
#define maxValue 20
int valueCounter=0;
int valueSamples[maxValue];
void dataCrunching(void ){
int i;
double average;
valueSamples[valueCounter]=nextValue;
valueCounter++;
if(valueCounter》(maxValue-1))
valueCounter=0;
average=0;
for(i=0; i average=average+valueSamples[i];
}
觀察邊界
如果信號太不穩定,則可以采用其他方法查看最小值或最大值在一個數組中。如果信號傾向于在一個方向上特別偏離,這將特別有用。再次使用數組,每個周期填充一個值。每個循環檢查數組的e。 G。最低值。然后將該值與參考值進行比較。
創建e。 G。可以通過給變量一個高的值開始(例如255),然后在for循環中檢查給定的數組值是否較小
if(minValueminValue=array[i];
循環minValue具有數組的最小值。
優點:即使嘈雜的信號也可以很好地分析
缺點:數組需要足夠大(可以減慢反應速度)速度)
#define maxValue 20
int valueCounter=0;
int valueSamples[maxValue];
void dataCrunching(void){
int i;
int minValue;
valueSamples[valueCounter]=nextValue;
valueCounter++;
if(valueCounter》(maxValue-1))
valueCounter=0;
minValue=32767;
for(i=0;i if(minValueminValue=valueSamples[i];
}
}
忽略值
有時信號中會出現一些毛刺或明顯的誤讀。如果將這些值添加到數組,它們將完全破壞對數組的分析。為了避免這種情況,可以通過設置“期望邊界”來濾除“不可信”的值。最簡單的方法是查看數組中的平均值,并且僅接受平均值的+/-范圍內的值。我不建議這樣做!為什么不?可能導致平均值“卡住”的情況。如果讀數緩慢上升,然后突然回到“正常”,則平均值將停留在較高的值,因為正常值可能超出+/-范圍。
如果相對于數組中的參考值信號不在+/-范圍內,則舊值將保留在數組中。這將導致在相同范圍內充滿值的數組。優點:將陣列的平均值與參考值進行比較將對微小的變化非常敏感。
優點:過濾掉毛刺和嚴重誤讀的好方法,即使在噪聲信號中也可以檢測到很小的變化。
缺點:足夠的值需要在該范圍內,參考值和范圍應謹慎選擇。
#define maxValue 20
int valueCounter=0;
int valueSamples[maxValue];
int limit=10;
int referenceValue; // needs to be created somewhere in the program!!!
void dataCrunching(void){
int i;
int diff;
diff=abs(nextValue-referenceValue) // create the difference
if(diff valueSamples[valueCounter]=nextValue;
valueCounter++;
if(valueCounter》(maxValue-1))
valueCounter=0;
average=0;
for(i=0;i average=average+valueSamples[i];
}
平均單個值
一種濾除小偏差的方法是使用前一個值乘以一個因子,再加上新值,然后除以(factor + 1)。
優點:非常易于實現,濾除細小的噪聲
缺點:即使細微變化是持久的,也忽略不了細微變化(新值需要偏差超過“因素”以影響除法后的結果)
#define maxValue 20
int valueCounter=0;
int valueSamples[maxValue];
int factor=3;
void dataCrunching(void){
int i;
double filterValue;
filterValue=valueSamples[valueCounter];
filterValue=filterValue*factor;
filterValue=filterValue+nextValue;
valueSamples[valueCounter]=filterValue/(factor+1);
valueCounter++;
if(valueCounter》(maxValue-1))
valueCounter=0;
average=0;
for(i=0; i average=average+valueSamples[i];
}
使用較高/較低的計數器
在這種情況下,會將測量值與先前創建的平均值進行比較。如果該值較大,則平均值diffdiffer將增加。如果該值較小,則diffCounter將減少。如果diffCounter達到最大值maxDiffCounter,則將平均值增加,并將diffCounter設置為0。如果diffCounter降至0以下,則將減小平均值,并將diffCounter設置為maxDiffCounter。
優點:甚至
缺點:高/低的分布應該“穩定”,偏差的指示可能很慢
#define maxDiffCounter 30
int diffCounter=0;
int referenceValue; // needs to be created elsewhere in the program!!!
int sampleValue;
int average=0;
void dataCrunching(void){
int i;
double average;
if(nextValue》referenceValue) // if larger than the reference
diffCounter++; // increase the diffCounter
else if(nextValue diffCounter--; // decrease the difCounter
if(diffCounter》maxDiffCounter){ // maximum value reached
average++; // correct the average
diffCounter=0; // restart at 0
}
else if(diffCounter《0){ // minimum value reached
average--; // correct the average
diffCounter=maxDiffCounter; // restart at maximum value
}
}
創建參考值
數據處理中最簡單的部分是采用不同的方法來濾除噪聲。
-
編程
+關注
關注
88文章
3679瀏覽量
94863 -
金屬探測器
+關注
關注
19文章
79瀏覽量
24920 -
Arduino
+關注
關注
188文章
6491瀏覽量
190091
發布評論請先 登錄
VirtualLab Fusion應用:用于光波導系統的均勻性探測器
用于光波導系統的均勻性探測器
金屬探測器的常見故障及解決方法
雷達探測器的工作原理 雷達探測器與激光探測器區別
探測器選擇指導

評論