तेज और स्मृति कुशल चलती औसत गणना


33

मैं सी में एक चलती औसत की गणना करने के लिए एक समय और स्मृति कुशल समाधान की तलाश कर रहा हूं। मुझे विभाजन से बचने की आवश्यकता है क्योंकि मैं एक PIC 16 पर हूं जिसमें कोई समर्पित विभाजन इकाई नहीं है।

फिलहाल, मैं रिंग रिंग बफर में सभी मूल्यों को संग्रहीत करता हूं और हर बार एक नया मूल्य आने पर राशि को स्टोर और अपडेट करता हूं । यह वास्तव में कुशल है, लेकिन दुर्भाग्य से मेरी उपलब्ध स्मृति का सबसे अधिक उपयोग करता है ...


3
मुझे नहीं लगता कि ऐसा करने के लिए कोई और अधिक कुशल तरीका है।
रॉकेटमैग्नेट

4
@JobyTaffey अच्छी तरह से, यह नियंत्रण प्रणालियों पर काफी व्यापक रूप से उपयोग किया जाने वाला एल्गोरिदम है, और इसके लिए सीमित हार्डवेयर संसाधनों से निपटने की आवश्यकता है। इसलिए मुझे लगता है कि वह एसओ की तुलना में यहां अधिक मदद पाएंगे।
clabacchio

3
@ जॉबी: इस सवाल के बारे में कुछ झुर्रियाँ हैं जो छोटे संसाधन-सीमित प्रणालियों के लिए प्रासंगिक हैं। मेरा जवाब देखिए। आप इसे बड़े सिस्टम पर अलग तरह से करेंगे जैसे SO लोगों के लिए उपयोग किया जाता है। इलेक्ट्रॉनिक्स डिजाइन करने के मेरे अनुभव में यह बहुत ऊपर आया है।
ओलिन लेट्रोप

1
मैं सहमत हूँ। यह इस मंच के लिए काफी उपयुक्त है, क्योंकि यह एम्बेडेड सिस्टम से संबंधित है।
राकेटमग्नेट

मैं अपनी आपत्ति को वापस
लेता

जवाबों:


55

जैसा कि दूसरों ने उल्लेख किया है, आपको उस एफआईआर (परिमित आवेग प्रतिक्रिया) के बजाय एक आईआईआर (अनंत आवेग प्रतिक्रिया) फिल्टर पर विचार करना चाहिए जिसे आप अभी उपयोग कर रहे हैं। इसमें और भी बहुत कुछ है, लेकिन पहली नज़र में एफआईआर फ़िल्टर स्पष्ट संकल्प और समीकरणों के साथ IIR फ़िल्टर के रूप में लागू किए जाते हैं।

विशेष रूप से IIR फ़िल्टर जो मैं माइक्रोकंट्रोलर्स में बहुत उपयोग करता है, वह एकल पोल कम पास फिल्टर है। यह एक साधारण आर सी एनालॉग फ़िल्टर के डिजिटल समकक्ष है। अधिकांश अनुप्रयोगों के लिए, आपके पास उपयोग किए जा रहे बॉक्स फ़िल्टर की तुलना में बेहतर विशेषताएं होंगी। मेरे द्वारा सामना किए गए एक बॉक्स फ़िल्टर के अधिकांश उपयोग डिजिटल सिग्नल प्रोसेसिंग क्लास में किसी पर ध्यान नहीं देने के परिणामस्वरूप होते हैं, न कि उनके विशेष विशेषताओं की आवश्यकता के परिणामस्वरूप। यदि आप केवल उच्च आवृत्तियों को देखना चाहते हैं जो आप जानते हैं कि शोर है, तो एक एकल पोल कम पास फिल्टर बेहतर है। एक माइक्रोकंट्रोलर में डिजिटल रूप से लागू करने का सबसे अच्छा तरीका है:

FILT <- FILT + FF (NEW - FILT)

FILT लगातार स्थिति का एक टुकड़ा है। यह एकमात्र स्थिर चर है जिसे आपको इस फ़िल्टर की गणना करने की आवश्यकता है। नया नया मान है कि फ़िल्टर को इस पुनरावृत्ति के साथ अद्यतन किया जा रहा है। एफएफ फिल्टर अंश है , जो फिल्टर के "भारीपन" को समायोजित करता है। इस एल्गोरिथ्म को देखें और देखें कि एफएफ = 0 के लिए फ़िल्टर असीम रूप से भारी है क्योंकि आउटपुट कभी नहीं बदलता है। FF = 1 के लिए, यह वास्तव में कोई फिल्टर नहीं है क्योंकि आउटपुट केवल इनपुट का अनुसरण करता है। बीच में उपयोगी मान हैं। छोटे सिस्टम पर आप 1/2 N होने के लिए FF चुनेंताकि एफएफ द्वारा गुणा को एन बिट्स द्वारा एक सही बदलाव के रूप में पूरा किया जा सके। उदाहरण के लिए, FF 1/16 हो सकता है और FF द्वारा गुणा किया जा सकता है इसलिए 4 बिट्स की एक सही शिफ्ट। अन्यथा इस फिल्टर को केवल एक घटाव और एक ऐड की आवश्यकता होती है, हालांकि संख्याओं को आमतौर पर इनपुट मूल्य (नीचे एक अलग अनुभाग में संख्यात्मक परिशुद्धता पर अधिक) से व्यापक होने की आवश्यकता होती है।

मैं आमतौर पर ए / डी रीडिंग को बहुत तेजी से लेता हूं, जब वे जरूरत से ज्यादा होते हैं और इनमें से दो फिल्टर कैस्केड करते हैं। यह श्रृंखला में दो RC फ़िल्टर का डिजिटल समतुल्य है, और रोलऑफ़ आवृत्ति के ऊपर 12 dB / ऑक्टेव द्वारा क्षीणन करता है। हालांकि, ए / डी रीडिंग के लिए आमतौर पर इसके चरण प्रतिक्रिया पर विचार करके समय डोमेन में फ़िल्टर को देखना अधिक प्रासंगिक है। यह बताता है कि जब आप जिस चीज को माप रहे हैं, उस बदलाव को आपका सिस्टम कितनी तेजी से देखेगा।

इन फिल्टरों को डिजाइन करने की सुविधा के लिए (जिसका अर्थ केवल एफएफ चुनना और यह तय करना है कि उनमें से कितने को कैस्केड करना है), मैं अपने प्रोग्राम FILBBITS का उपयोग करता हूं। आप फिल्टर की कैस्केड श्रृंखला में प्रत्येक एफएफ के लिए शिफ्ट बिट्स की संख्या निर्दिष्ट करते हैं, और यह चरण प्रतिक्रिया और अन्य मूल्यों की गणना करता है। वास्तव में मैं आमतौर पर अपनी आवरण स्क्रिप्ट PLOTFILT के माध्यम से इसे चलाता हूं। यह FILTBITS चलाता है, जो CSV फ़ाइल बनाता है, फिर CSV फ़ाइल प्लॉट करता है। उदाहरण के लिए, यहां "PLOTFILT 4 4" का परिणाम है:

PLOTFILT के दो मापदंडों का मतलब है कि ऊपर वर्णित प्रकार के दो फिल्टर कैस्केड होंगे। 4 के मान एफएफ द्वारा गुणा का एहसास करने के लिए शिफ्ट बिट्स की संख्या को इंगित करते हैं। इस मामले में दो FF मान 1/16 हैं।

लाल ट्रेस इकाई चरण प्रतिक्रिया है, और देखने के लिए मुख्य बात है। उदाहरण के लिए, यह आपको बताता है कि यदि इनपुट तुरंत बदल जाता है, तो संयुक्त फ़िल्टर का आउटपुट 60 पुनरावृत्तियों में नए मूल्य के 90% पर आ जाएगा। यदि आप 95% बसने के समय की परवाह करते हैं तो आपको लगभग 73 पुनरावृत्तियों की प्रतीक्षा करनी होगी, और 50% निपटाने के लिए केवल 26 पुनरावृत्तियों के लिए।

हरे रंग का निशान आपको एकल पूर्ण आयाम स्पाइक से आउटपुट दिखाता है। यह आपको यादृच्छिक शोर दमन का कुछ विचार देता है। ऐसा लगता है कि आउटपुट में 2.5% परिवर्तन से अधिक एकल नमूना नहीं होगा।

नीला ट्रेस एक व्यक्तिपरक एहसास देने के लिए है कि यह फिल्टर सफेद शोर के साथ क्या करता है। यह एक कठोर परीक्षा नहीं है क्योंकि इस बात की कोई गारंटी नहीं है कि PLOTFILT के इस रन के लिए व्हाइट नॉइज़ इनपुट के रूप में चुना गया रैंडम नंबरों का वास्तव में क्या कंटेंट था। यह आपको केवल एक मोटा एहसास देने के लिए है कि इसे कितना स्क्वैश किया जाएगा और यह कितना चिकना है।

PLOTFILT, शायद FILTBITS और बहुत सारे अन्य उपयोगी सामान, विशेष रूप से PIC फर्मवेयर विकास के लिए PIC विकास उपकरण सॉफ़्टवेयर रिलीज़ में मेरे सॉफ़्टवेयर डाउनलोड पृष्ठ पर उपलब्ध है

संख्यात्मक परिशुद्धता के बारे में जोड़ा गया

मैं टिप्पणियों से देखता हूं और अब एक नया उत्तर है कि इस फिल्टर को लागू करने के लिए आवश्यक बिट्स की संख्या पर चर्चा करने में रुचि है। ध्यान दें कि एफएफ द्वारा गुणा बाइनरी पॉइंट के नीचे लॉग 2 (एफएफ) नए बिट्स बनाएगा । छोटी प्रणालियों पर, एफएफ को आमतौर पर 1/2 एन चुना जाता है, ताकि यह वास्तव में एन बिट्स की एक सही शिफ्ट द्वारा महसूस किया जा सके।

FILT इसलिए आमतौर पर एक निश्चित बिंदु पूर्णांक होता है। ध्यान दें कि यह प्रोसेसर के दृष्टिकोण से किसी भी गणित को नहीं बदलता है। उदाहरण के लिए, यदि आप 10 बिट ए / डी रीडिंग और एन = 4 (एफएफ = 1/16) फ़िल्टर कर रहे हैं, तो आपको 10 बिट पूर्णांक ए / डी रीडिंग के नीचे 4 अंश बिट्स की आवश्यकता होती है। सबसे अधिक प्रोसेसर, आप 10 बिट ए / डी रीडिंग के कारण 16 बिट पूर्णांक ऑपरेशन कर रहे हैं। इस स्थिति में, आप अभी भी एक ही 16 बिट पूर्णांक opertions कर सकते हैं, लेकिन 4 बिट्स द्वारा स्थानांतरित ए / डी रीडिंग के साथ शुरू करें। प्रोसेसर अंतर नहीं जानता है और इसकी आवश्यकता नहीं है। पूरे 16 बिट पूर्णांक पर गणित करना यह काम करता है कि क्या आप उन्हें 12.4 निश्चित बिंदु मानते हैं या सही 16 बिट पूर्णांक (16.0 अंक)।

यदि आप संख्यात्मक प्रतिनिधित्व के कारण शोर नहीं जोड़ना चाहते हैं तो सामान्य तौर पर, आपको प्रत्येक फिल्टर पोल में एन बिट्स जोड़ने की आवश्यकता होती है। ऊपर के उदाहरण में, दो के दूसरे फिल्टर में जानकारी न खोने के लिए 10 + 4 + 4 = 18 बिट्स होने चाहिए। 8 बिट मशीन पर अभ्यास करने का मतलब है कि आप 24 बिट मूल्यों का उपयोग करेंगे। तकनीकी रूप से केवल दो के दूसरे पोल के लिए व्यापक मूल्य की आवश्यकता होगी, लेकिन फर्मवेयर सादगी के लिए मैं आमतौर पर एक ही प्रतिनिधित्व का उपयोग करता हूं, और इस तरह एक ही कोड, एक फिल्टर के सभी ध्रुवों के लिए।

आमतौर पर मैं एक फिल्टर पोल ऑपरेशन करने के लिए एक सबरूटीन या मैक्रो लिखता हूं, फिर प्रत्येक पोल पर लागू होता है। क्या सबरूटीन या मैक्रो इस बात पर निर्भर करता है कि उस विशेष परियोजना में साइकिल या प्रोग्राम मेमोरी अधिक महत्वपूर्ण है या नहीं। किसी भी तरह से, मैं सबरूटीन / मैक्रो में नया पास करने के लिए कुछ स्क्रैच स्टेट का उपयोग करता हूं, जो कि FILT को अपडेट करता है, लेकिन यह भी लोड करता है कि एक ही स्क्रैच स्टेट में NEW था। इससे एक पोल के अपडेटेड FILT के बाद से कई पोल लगाना आसान हो जाता है। अगले एक का नया। जब एक सबरूटीन होता है, तो रास्ते में FILT पर एक पॉइंटर पॉइंट होना उपयोगी होता है, जो कि रास्ते में FILT के ठीक बाद में अपडेट किया जाता है। यदि कई बार कॉल किया जाता है तो स्मृति में लगातार फ़िल्टर पर सबट्रीन स्वचालित रूप से काम करता है। मैक्रो के साथ आपको एक सूचक की आवश्यकता नहीं है क्योंकि आप प्रत्येक पुनरावृत्ति पर संचालित करने के लिए पते में पास करते हैं।

कोड उदाहरण

यहाँ एक मैक्रो का उदाहरण दिया गया है जैसा कि पीआईसी 18 के लिए ऊपर वर्णित है:

////////////////////////////////////////////////// //////////////////////////////
//
// मैक्रो फिल्टर फिल्म
//
// NEWVAL में नए मान के साथ एक फ़िल्टर पोल अपडेट करें। NEWVAL को अपडेट किया गया है
// में नए फ़िल्टर किए गए मान हैं।
//
// FILT फ़िल्टर राज्य चर का नाम है। इसे 24 बिट माना जाता है
// विस्तृत और स्थानीय बैंक में।
//
// फिल्टर को अपडेट करने का सूत्र है:
//
// FILT <- FILT + FF (NEWVAL - FILT)
//
// FF द्वारा गुणा करने से FILTBITS बिट्स की एक सही शिफ्ट होती है।
//
/ मैक्रो फ़िल्टर
  /लिखो
         dbankif lbankadr
         Movf [arg 1] +0, w; NEWVAL <- NEWVAL - FILT
         उपवाक्य newval + 0
         Movf [arg 1] +1, w
         उपविभाजन newval + 1
         मूव [arg 1] +2, w
         उपविभाजन newval + 2

  /लिखो
  / लूप एन filtbits; एक बार प्रत्येक बिट के लिए NEWVAL सही स्थानांतरित करने के लिए
         rlcf newval + 2, w; शिफ्ट NEWVAL को एक बिट सही करें
         rrcf newval + 2
         rrcf newval + 1
         rrcf newval + 0
    / endloop

  /लिखो
         movf newval + 0, w, फ़िल्टर में स्थानांतरित मूल्य जोड़ें और NEWVAL में सहेजें
         addwf [arg 1] +0, w
         मूव [आर्गे 1] +0
         मूव न्यूवेल + ०

         Movf newval + 1, w
         addwfc [arg 1] +1, w
         Movwf [arg 1] +1
         मूव न्यूवेल + 1

         मूव न्यूवेल + 2, डब्ल्यू
         addwfc [arg 1] +2, w
         मूवेफ [arg 1] +2
         मूव न्यूवेल + 2
  / endmac

और यहां PIC 24 या dsPIC 30 या 33 के लिए एक समान मैक्रो है:

////////////////////////////////////////////////// //////////////////////////////
//
// मैक्रो फ़िल्टर ffbits
//
// एक कम पास फिल्टर की स्थिति को अपडेट करें। नया इनपुट मान W1: W0 में है
// और अद्यतन की जाने वाली फ़िल्टर स्थिति W2 द्वारा इंगित की जाती है।
//
// अद्यतन फ़िल्टर मान W1: W0 और W2 में भी वापस आ जाएगा
/ / फिल्टर राज्य के पहले स्मृति के लिए। यह स्थूल इसलिए हो सकता है
// कैस्केड कम पास फिल्टर की एक श्रृंखला को अद्यतन करने के लिए उत्तराधिकार में आह्वान किया गया।
//
// फ़िल्टर सूत्र है:
//
// FILT <- FILT + FF (NEW - FILT)
//
// जहां एफएफ द्वारा गुणा किया जाता है, एक अंकगणितीय दाएं शिफ्ट द्वारा किया जाता है
// FFBITS।
//
// चेतावनी: W3 को ट्रैश किया गया है।
//
/ मैक्रो फ़िल्टर
  / var नए ffbits पूर्णांक = [arg 1]; बिट्स की संख्या को स्थानांतरित करने के लिए प्राप्त करें

  /लिखो
  / लिखना "; एक पोल कम पास फ़िल्टरिंग, शिफ्ट बिट्स =" ffbits करना
  /लिखो " ;"

         उप w0, [w2 ++], w0; नया - FILT -> W1: W0
         सब्ब w1, [w2--], w1

         lsr w0, # [v ffbits], w0; परिणाम को W1 में स्थानांतरित करें: W0 सही
         sl w1, # [- 16 ffbits], w3
         ior w0, w3, w0
         asr w1, # [v ffbits], w1

         जोड़ें W0, [w2 ++], w0; W1 में अंतिम परिणाम बनाने के लिए FILT जोड़ें: W0
         addc w1, [w2--], w1

         mov w0, [w2 ++]; फ़िल्टर स्थिति के लिए परिणाम लिखें, अग्रिम सूचक
         Mov w1, [w2 ++]

  /लिखो
  / endmac

इन दोनों उदाहरणों को मेरे PIC असेंबलर प्रीप्रोसेसर का उपयोग करके मैक्रोज़ के रूप में कार्यान्वित किया जाता है , जो कि अंतर्निहित मैक्रो सुविधाओं में से अधिक सक्षम है।


1
+1 - धन पर अधिकार। केवल एक चीज जो मैं जोड़ूंगा, वह यह है कि मूविंग एवरेज फिल्टर्स में अपनी जगह तब होती है जब किसी कार्य के लिए समकालिक रूप से प्रदर्शन किया जाता है (जैसे कि अल्ट्रासाउंड जनरेटर को चलाने के लिए ड्राइव वेवफॉर्म का उत्पादन करना) ताकि वे 1 / T के हार्मोनिक्स को फ़िल्टर करें जहां टी चलती है औसत समय।
जेसन एस

2
अच्छा जवाब, लेकिन सिर्फ दो बातें। पहली: यह जरूरी नहीं कि ध्यान की कमी है जो एक गलत फिल्टर की पसंद की ओर जाता है; मेरे मामले में, मुझे कभी भी अंतर के बारे में नहीं सिखाया गया है, और यह गैर-स्नातक लोगों पर भी लागू होता है। तो कभी-कभी यह केवल अज्ञानता है। लेकिन दूसरा: आप एक उच्च क्रम एक का उपयोग करने के बजाय दो प्रथम-ऑर्डर डिजिटल फ़िल्टर क्यों करते हैं? (बस को समझने के लिए, मैं की आलोचना नहीं कर रहा हूँ)
clabacchio

3
दो cascaded सिंगल पोल IIR फ़िल्टर संख्यात्मक मुद्दों के लिए अधिक मजबूत होते हैं, और एकल 2-ऑर्डर IIR फ़िल्टर की तुलना में आसान होते हैं; व्यापार यह है कि 2 कैस्केड चरणों के साथ आपको कम क्यू (= 1/2?) फ़िल्टर मिलता है, लेकिन ज्यादातर मामलों में यह बहुत बड़ी बात नहीं है।
जेसन एस

1
@clabacchio: मेरे द्वारा उल्लेखित एक और मुद्दा फर्मवेयर कार्यान्वयन है। आप एक बार एक ही पोल कम पास फिल्टर सबरूटीन लिख सकते हैं, फिर इसे कई बार लगा सकते हैं। वास्तव में मैं आमतौर पर इस तरह के सबरूटीन को फ़िल्टर स्थिति में मेमोरी में पॉइंटर लेने के लिए लिखता हूं, तो क्या उसने पॉइंटर को आगे बढ़ाया है ताकि मल्टी-पोल फिल्टर का एहसास करने के लिए इसे उत्तराधिकार में आसानी से बुलाया जा सके।
ओलिन लेथ्रोप

1
1. आपके उत्तर के लिए बहुत बहुत धन्यवाद - उन सभी को। मैंने इस IIR फ़िल्टर का उपयोग करने का निर्णय लिया है, लेकिन इस फ़िल्टर का उपयोग मानक लोपास फ़िल्टर के रूप में नहीं किया गया है, क्योंकि मुझे काउंटर वैल्यू औसत करने और एक निश्चित सीमा में परिवर्तन का पता लगाने के लिए उनकी तुलना करने की आवश्यकता है। चूंकि ये वैल्यू वैन हार्डवेयर के आधार पर बहुत भिन्न आयामों के होते हैं, इसलिए मैं इन हार्डवेयर विशिष्ट परिवर्तनों पर स्वचालित रूप से प्रतिक्रिया करने में सक्षम होने के लिए औसत लेना चाहता था।
सेंसेक्स

18

यदि आप औसतन (यानी २,४,,,१६,३२ आदि) दो मदों की शक्ति के प्रतिबंध के साथ रह सकते हैं, तो विभाजन आसानी से और कुशलता से कम प्रदर्शन वाले सूक्ष्म पर किया जा सकता है जिसमें कोई समर्पित विभाजन नहीं है क्योंकि यह थोड़ा बदलाव के रूप में किया जा सकता है। प्रत्येक शिफ्ट राइट दो की एक शक्ति है जैसे:

avg = sum >> 2; //divide by 2^2 (4)

या

avg = sum >> 3; //divide by 2^3 (8)

आदि।


वह कैसे मदद करता है? ओपी का कहना है कि मुख्य समस्या स्मृति में पिछले नमूनों को बनाए रखना है।
जेसन एस

यह ओपी के सवाल को बिल्कुल भी संबोधित नहीं करता है।
राकेटमग्नेट

12
ओपी ने सोचा कि उसे दो समस्याएं हैं, एक PIC16 में विभाजित करना और अपने रिंग बफर के लिए मेमोरी। यह उत्तर बताता है कि विभाजन मुश्किल नहीं है। माना जाता है कि यह मेमोरी प्रॉब्लम को संबोधित नहीं करता है, लेकिन एसई सिस्टम आंशिक उत्तरों की अनुमति देता है, और उपयोगकर्ता अपने लिए प्रत्येक उत्तर से कुछ ले सकते हैं, या अन्य उत्तरों को संपादित और संयोजित भी कर सकते हैं। चूँकि कुछ अन्य उत्तरों के लिए एक विभाजन ऑपरेशन की आवश्यकता होती है, वे इसी तरह से अपूर्ण हैं क्योंकि वे यह नहीं दिखाते हैं कि PIC16 पर इसे कुशलता से कैसे प्राप्त किया जाए।
मार्टिन

8

वहाँ है , कम स्मृति आवश्यकताओं के साथ एक सच्चे चलती औसत फिल्टर (उर्फ "मालगाड़ी फिल्टर") के लिए एक जवाब है, तो आप downsampling आपत्ति नहीं है। इसे कैस्केड इंटीग्रेटर-कंघी फिल्टर (CIC) कहा जाता है । विचार यह है कि आपके पास एक इंटीग्रेटर है जिसे आप एक समयावधि में अंतर करते हैं, और मुख्य मेमोरी-सेविंग डिवाइस यह है कि डाउनसम्पलिंग द्वारा, आपको इंटीग्रेटर के प्रत्येक मूल्य को स्टोर करने की आवश्यकता नहीं है। इसे निम्नलिखित छद्मकोश का उपयोग करके लागू किया जा सकता है:

function out = filterInput(in)
{
   const int decimationFactor = /* 2 or 4 or 8 or whatever */;
   const int statesize = /* whatever */
   static int integrator = 0;
   static int downsample_count = 0;
   static int ringbuffer[statesize];
   // don't forget to initialize the ringbuffer somehow
   static int ringbuffer_ptr = 0;
   static int outstate = 0;

   integrator += in;
   if (++downsample_count >= decimationFactor)
   {
     int oldintegrator = ringbuffer[ringbuffer_ptr];
     ringbuffer[ringbuffer_ptr] = integrator;
     ringbuffer_ptr = (ringbuffer_ptr + 1) % statesize;
     outstate = (integrator - oldintegrator) / (statesize * decimationFactor);
   }
   return outstate;
}

आपकी प्रभावी चलती औसत लंबाई है, decimationFactor*statesizeलेकिन आपको केवल statesizeनमूनों के आसपास रखने की आवश्यकता है । जाहिर है आप बेहतर प्रदर्शन प्राप्त कर सकते हैं यदि आपकी statesizeऔर decimationFactor2 की शक्तियां हैं, ताकि डिवीजन और शेष ऑपरेटरों को पाली और मास्क-एस द्वारा प्रतिस्थापित किया जाए।


पोस्टस्क्रिप्ट: मैं ओलिन से सहमत हूं कि आपको चलती औसत फिल्टर से पहले हमेशा सरल IIR फिल्टर पर विचार करना चाहिए। यदि आपको बॉक्सर फिल्टर की आवृत्ति-नल की आवश्यकता नहीं है, तो 1-पोल या 2-पोल कम-पास फिल्टर शायद ठीक काम करेगा।

दूसरी ओर, यदि आप डिसिमिनेशन के उद्देश्यों के लिए फ़िल्टरिंग कर रहे हैं (उच्च-नमूना-दर इनपुट ले रहे हैं और इसे कम-दर प्रक्रिया द्वारा उपयोग के लिए औसत करते हैं) तो एक CIC फ़िल्टर वही हो सकता है जो आप देख रहे हैं। (खासकर यदि आप राज्यों = 1 का उपयोग कर सकते हैं और रिंगबफ़र से पूरी तरह से बच सकते हैं, तो केवल एक ही पिछले इंटीग्रेटर मान के साथ)


8

पहले आदेश IIR फ़िल्टर का उपयोग करने के पीछे गणित का कुछ गहन विश्लेषण है जिसे ओलिन लेथ्रोप ने पहले ही डिजिटल सिग्नल प्रोसेसिंग स्टैक एक्सचेंज पर वर्णित किया है (जिसमें बहुत सारे चित्र शामिल हैं।) इस IIR फ़िल्टर के लिए समीकरण है:

y [n] = αx [n] + (1-α) y [n-1]

इसे केवल पूर्णांक और निम्न विभाजन का उपयोग करके कोई विभाजन लागू नहीं किया जा सकता है (कुछ डिबगिंग की आवश्यकता हो सकती है क्योंकि मैं मेमोरी से टाइप कर रहा था)।

/**
*  @details    Implement a first order IIR filter to approximate a K sample 
*              moving average.  This function implements the equation:
*
*                  y[n] = alpha * x[n] + (1 - alpha) * y[n-1]
*
*  @param      *filter - a Signed 15.16 fixed-point value.
*  @param      sample - the 16-bit value of the current sample.
*/

#define BITS 2      ///< This is roughly = log2( 1 / alpha )

short IIR_Filter(long *filter, short sample)
{
    long local_sample = sample << 16;

    *filter += (local_sample - *filter) >> BITS;

    return (short)((*filter+0x8000) >> 16);     ///< Round by adding .5 and truncating.
}

यह फिल्टर अल्फा के मान को 1 / K पर सेट करके अंतिम K नमूनों की एक चलती औसत का अनुमान लगाता है। पूर्ववर्ती कोड में ऐसा करें #defineing BITSकश्मीर = 16 सेट के लिए यानी LOG2 लिए (के), BITS4, कश्मीर के लिए = 4 सेट BITSकरने के लिए 2, आदि

(जैसे ही मुझे बदलाव मिलेगा और जरूरत पड़ने पर इस उत्तर को संपादित कर दूँगा, मैं यहाँ सूचीबद्ध कोड सत्यापित कर दूँगा।


6

यहां सिंगल-पोल कम-पास फिल्टर (चलती औसत, कटऑफ आवृत्ति = कटऑफफ्रीक्वेंसी के साथ) है। बहुत सरल, बहुत तेज, बढ़िया काम करता है, और लगभग कोई मेमोरी नहीं है।

नोट: सभी वेरिएबल्स में फ़िल्टर फ़ंक्शन से परे स्कोप है, सिवाय newInput में पास के

// One-time calculations (can be pre-calculated at compile-time and loaded with constants)
DecayFactor = exp(-2.0 * PI * CutoffFrequency / SampleRate);
AmplitudeFactor = (1.0 - DecayFactor);

// Filter Loop Function ----- THIS IS IT -----
double Filter(double newInput)
{
   MovingAverage *= DecayFactor;
   MovingAverage += AmplitudeFactor * newInput;

   return (MovingAverage);
}

नोट: यह सिंगल स्टेज फिल्टर है। फ़िल्टर के तीखेपन को बढ़ाने के लिए कई चरणों को एक साथ कैस्केड किया जा सकता है। यदि आप एक से अधिक चरण का उपयोग करते हैं, तो आपको क्षतिपूर्ति करने के लिए आपको DecayFactor (कटऑफ-फ्रीक्वेंसी से संबंधित) को समायोजित करना होगा।

और जाहिर है आप सभी की जरूरत है उन दो लाइनों कहीं भी रखा है, वे अपने स्वयं के समारोह की जरूरत नहीं है। मूविंग एवरेज इनपुट सिग्नल का प्रतिनिधित्व करने से पहले इस फ़िल्टर में रैंप-अप समय होता है। यदि आपको उस रैंप-अप समय को बायपास करने की आवश्यकता है, तो आप बस 0 के बजाय newInput के पहले मान में मूविंग एवरेज को इनिशियलाइज़ कर सकते हैं, और उम्मीद करते हैं कि पहला newInput आउटलाइवर नहीं है।

(कटऑफफ्रीक्वेंसी / सैंपलरेट) की सीमा 0 से 0.5 के बीच है। DecayFactor 0 और 1 के बीच का एक मूल्य है, जो आमतौर पर 1 के करीब होता है।

एकल-सटीक फ़्लोट्स अधिकांश चीजों के लिए पर्याप्त हैं, मैं सिर्फ युगल पसंद करता हूं। यदि आपको पूर्णांक के साथ चिपके रहने की आवश्यकता है, तो आप DecayFactor और Amplitude Factor को आंशिक पूर्णांक में बदल सकते हैं, जिसमें अंश को पूर्णांक के रूप में संग्रहीत किया जाता है, और भाजक 2 की पूर्णांक शक्ति है (इसलिए आप दाईं ओर बिट-शिफ्ट कर सकते हैं फ़िल्टर लूप के दौरान विभाजित होने के बजाय हर)। उदाहरण के लिए, यदि DecayFactor = 0.99, और आप पूर्णांक का उपयोग करना चाहते हैं, तो आप DecayFactor = 0.99 * 65536 = 64881 पर सेट कर सकते हैं। और फिर कभी भी आप अपने फ़िल्टर लूप में DecayFactor से गुणा करें, बस परिणाम >> 16 को स्थानांतरित करें।

इस बारे में अधिक जानकारी के लिए, एक उत्कृष्ट पुस्तक जो ऑनलाइन है, पुनरावर्ती फिल्टर पर अध्याय 19: http://www.dspguide.com/ch19.htm

PS मूविंग एवरेज प्रतिमान के लिए, DecayFactor और AmplitudeFactor को सेट करने के लिए एक अलग दृष्टिकोण जो आपकी आवश्यकताओं के लिए अधिक प्रासंगिक हो सकता है, मान लें कि आप पिछले चाहते हैं, लगभग 6 आइटम एक साथ औसतन हैं, इसे विवेकपूर्ण रूप से करते हुए, आप 6 आइटम जोड़ेंगे और विभाजित करेंगे 6, ताकि आप 1/6 के लिए एम्प्लिट्यूफ़ैक्टर को सेट कर सकें, और डेकेफ़ैक्टर को (1.0 - एम्प्लिटफ़ैक्टर को)।


4

आप एक सरल IIR फ़िल्टर के साथ कुछ अनुप्रयोगों के लिए एक चलती हुई अव्यवस्था का अनुमान लगा सकते हैं।

वजन 0..255 मान है, उच्च मूल्य = छोटे समय के लिए avaraging

मान = (newvalue * वजन + मूल्य * (256-वज़न)) / 256

गोलाई की त्रुटियों से बचने के लिए, मूल्य सामान्य रूप से एक लंबा होगा, जिसमें से आप केवल अपने 'वास्तविक' मान के रूप में उच्च-सीमा वाले बाइट्स का उपयोग करते हैं।


3

बाकी सभी ने आईआईआर बनाम एफआईआर की उपयोगिता और शक्ति-दो विभाजन पर पूरी तरह से टिप्पणी की है। मैं कुछ कार्यान्वयन विवरण देना चाहूंगा। नीचे कोई एफपीयू के साथ छोटे माइक्रोकंट्रोलर पर अच्छी तरह से काम करता है। कोई गुणन नहीं है, और यदि आप N को दो की शक्ति रखते हैं, तो सभी विभाजन एकल-चक्र बिट-शिफ्टिंग हैं।

बेसिक एफआईआर रिंग बफर: अंतिम एन मानों का एक रनिंग बफर, और बफर में सभी मानों का एक रनिंग एसयूएम रखें। जब भी कोई नया नमूना आता है, तो SUM से बफर में सबसे पुराने मूल्य को घटाएं, इसे नए नमूने से बदलें, SUM में नया नमूना जोड़ें और SUM / N आउटपुट करें।

unsigned int Filter(unsigned int sample){
    static unsigned int buffer[N];
    static unsigned char oldest = 0;
    static unsigned long sum;

    sum -= buffer[oldest];
    sum += sample;
    buffer[oldest] = sample;
    oldest += 1;
    if (oldest >= N) oldest = 0;

    return sum/N;
}

संशोधित IIR रिंग बफर: अंतिम N मानों का एक चल रहा SUM रखें। हर बार एक नया सैंपल आता है, SUM - = SUM / N, नया सैंपल, और आउटपुट SUM / N में जोड़ें।

unsigned int Filter(unsigned int sample){
    static unsigned long sum;

    sum -= sum/N;
    sum += sample;

    return sum/N;
}

अगर मैं आपको सही पढ़ा रहा हूँ, तो आप पहले क्रम IIR फ़िल्टर का वर्णन कर रहे हैं; वह मूल्य जो आप घटा रहे हैं वह सबसे पुराना मूल्य नहीं है जो गिर रहा है, बल्कि इसके बजाय पिछले मानों का औसत है। प्रथम-क्रम IIR फ़िल्टर निश्चित रूप से उपयोगी हो सकते हैं, लेकिन मुझे यकीन नहीं है कि जब आप सुझाव देते हैं कि आउटपुट सभी आवधिक संकेतों के लिए समान है। 10KHz नमूना दर पर, 20-चरण बॉक्स फ़िल्टर में 100Hz वर्ग तरंग खिलाने से एक संकेत मिलेगा जो 20 नमूनों के लिए समान रूप से उगता है, 30 के लिए उच्च बैठता है, 20 नमूनों के लिए समान रूप से गिरता है, और 30 के लिए कम बैठता है। IIR फ़िल्टर ...
सुपरकैट

... एक लहर पैदा करेगा जो तेजी से बढ़ना शुरू कर देता है और धीरे-धीरे इनपुट अधिकतम के पास (लेकिन नहीं) के स्तर को बंद कर देता है, फिर तेजी से गिरना शुरू हो जाता है और धीरे-धीरे इनपुट न्यूनतम के करीब (लेकिन नहीं) के स्तर को बंद कर देता है। बहुत अलग व्यवहार।
सुपरकैट

आप सही कह रहे हैं, मैं दो प्रकार के फिल्टर को भ्रमित कर रहा था। यह वास्तव में एक प्रथम-क्रम IIR है। मैं मैच के लिए अपना जवाब बदल रहा हूं। धन्यवाद।
स्टीफन कॉलिंग्स

एक मुद्दा यह है कि एक साधारण चलती औसत उपयोगी हो सकती है या नहीं। IIR फ़िल्टर के साथ, आप अपेक्षाकृत कुछ बछड़ों के साथ एक अच्छा फ़िल्टर प्राप्त कर सकते हैं। आपके द्वारा वर्णित एफआईआर आपको केवल समय में एक आयत दे सकती है - फ्रीक में एक सिनैक - और आप साइड लॉब्स का प्रबंधन नहीं कर सकते। यह अच्छी तरह से लायक हो सकता है कि कुछ पूर्णांक गुणकों में फेंकने के लिए यह एक अच्छा सममित ट्यून करने योग्य प्राथमिकी बना सकता है यदि आप घड़ी की टिक को खाली कर सकते हैं।
स्कॉट सेडमन

@ScottSeidman: यदि किसी व्यक्ति के पास एफआईआर के प्रत्येक चरण में या तो इनपुट का औसत उस चरण और उसके पिछले संग्रहित मूल्य के लिए आउटपुट है, और फिर इनपुट को संग्रहीत करें (यदि किसी के पास संख्यात्मक सीमा है, तो कोई भी राशि का उपयोग कर सकता है औसत के बजाय)। चाहे वह बॉक्स फ़िल्टर से बेहतर हो, यह एप्लिकेशन पर निर्भर करता है (1ms की कुल देरी के साथ बॉक्स फ़िल्टर की चरण प्रतिक्रिया, उदाहरण के लिए, इनपुट बदलने पर एक गंदा d2 / dt स्पाइक होगा, और फिर 1ms बाद में, लेकिन होगा कुल 1ms देरी के साथ एक फिल्टर के लिए न्यूनतम संभव d / dt)।
सुपरकैट

2

जैसा कि मिसेइलेक्ट्रिकस्टफ ने कहा, अगर आपको वास्तव में अपनी मेमोरी की जरूरतों को कम करने की आवश्यकता है, और आप अपने आवेग की प्रतिक्रिया को एक घातीय (आयताकार नाड़ी के बजाय) होने का बुरा नहीं मानते हैं, तो मैं एक घातीय चलती औसत फिल्टर के लिए जाऊंगा । मैं उनका बड़े पैमाने पर उपयोग करता हूं। उस प्रकार के फ़िल्टर के साथ, आपको किसी भी बफर की आवश्यकता नहीं है। आपको N पिछले नमूनों को संग्रहीत करने की आवश्यकता नहीं है। बस एक ठो। तो, आपकी स्मृति आवश्यकताओं को एन के एक कारक द्वारा काट दिया जाता है।

इसके अलावा, आपको उसके लिए किसी विभाजन की आवश्यकता नहीं है। केवल गुणा। यदि आपके पास फ्लोटिंग-पॉइंट अंकगणित तक पहुंच है, तो फ़्लोटिंग-पॉइंट गुणन का उपयोग करें। अन्यथा, पूर्णांक गुणन करें और दाईं ओर शिफ्ट करें। हालांकि, हम 2012 में हैं, और मैं आपको संकलक (और एमसीयू) का उपयोग करने की सलाह दूंगा जो आपको फ्लोटिंग-पॉइंट संख्याओं के साथ काम करने की अनुमति देता है।

अधिक स्मृति कुशल और तेज होने के अलावा (आपको किसी भी परिपत्र बफर में आइटम को अपडेट करने की आवश्यकता नहीं है), मैं कहूंगा कि यह अधिक प्राकृतिक भी है , क्योंकि एक घातीय आवेग प्रतिक्रिया बेहतर तरीके से मेल खाती है, जो कि ज्यादातर मामलों में प्रकृति का व्यवहार करती है।


5
मैं आपके साथ फ्लोटिंग पॉइंट नंबरों का उपयोग करने की सिफारिश से सहमत नहीं हूं। ओपी संभवतः एक कारण के लिए 8-बिट माइक्रोकंट्रोलर का उपयोग करता है। हार्डवेयर फ़्लोटिंग-पॉइंट समर्थन के साथ 8-बिट माइक्रोकंट्रोलर ढूंढना एक मुश्किल काम हो सकता है (क्या आप कोई जानते हैं?)। और हार्डवेयर समर्थन के बिना फ्लोटिंग-पॉइंट नंबरों का उपयोग करना एक बहुत ही संसाधन गहन कार्य होगा।
पेटपल्सेन

5
यह कहना कि आपको हमेशा फ्लोटिंग पॉइंट क्षमता वाली प्रक्रिया का उपयोग करना चाहिए, बस मूर्खतापूर्ण है। इसके अलावा, कोई भी प्रोसेसर फ्लोटिंग पॉइंट कर सकता है, यह सिर्फ गति का सवाल है। एम्बेडेड दुनिया में, निर्माण लागत में कुछ सेंट सार्थक हो सकते हैं।
ओलिन लेट्रोप

@ ओलिन लेट्रोप और पेटपल्सेन: मैंने कभी नहीं कहा कि उन्हें हार्डवेयर एफपीयू के साथ एमसीयू का उपयोग करना चाहिए। मेरे उत्तर को फिर से पढ़ें। "(और MCUs)" से मेरा मतलब है कि MCUs एक फ्लोटिंग तरीके से सॉफ्टवेयर फ्लोटिंग-पॉइंट अंकगणित के साथ काम करने के लिए पर्याप्त शक्तिशाली है, जो सभी MCUs के लिए ऐसा नहीं है।
तेलक्लेवियो

4
केवल 1-पोल कम-पास फिल्टर के लिए फ़्लोटिंग-पॉइंट (हार्डवेयर या सॉफ़्टवेयर) का उपयोग करने की आवश्यकता नहीं है।
जेसन एस

1
यदि उनके पास फ्लोटिंग पॉइंट ऑपरेशंस हैं, तो वे पहले स्थान पर विभाजन के लिए आपत्ति नहीं करेंगे।
फेडेरिको रुसो

0

IIR फिल्टर के साथ एक मुद्दा जैसा कि @olin और @supercat द्वारा लगभग छुआ गया है, लेकिन जाहिर है कि दूसरों द्वारा अवहेलना की गई है कि राउंडिंग डाउन कुछ अशुद्धता (और संभावित पूर्वाग्रह / छंटनी) का परिचय देता है: यह मानते हुए कि एन दो की शक्ति है, और केवल पूर्णांक अंकगणितीय है। उपयोग किया जाता है, शिफ्ट राइट व्यवस्थित रूप से नए नमूने के एलएसबी को समाप्त करता है। इसका मतलब है कि श्रृंखला कब तक हो सकती है, औसत उन लोगों को कभी भी ध्यान में नहीं रखेगा।

उदाहरण के लिए, मान लीजिए कि धीरे-धीरे कम होने वाली श्रृंखला (8,8,8, ..., 8,7,7,7, ... 7,6,6), और मान लीजिए कि शुरुआत में औसत 8 है। मुट्ठी "7" का नमूना औसत को 7 तक लाएगा, जो फिल्टर की ताकत है। सिर्फ एक नमूने के लिए। 6 के लिए एक ही कहानी, आदि अब इसके विपरीत के बारे में सोचो: सीरी ऊपर जाता है। यह औसत 7 तक रहेगा, जब तक नमूना इसे बदलने के लिए पर्याप्त बड़ा न हो जाए।

बेशक, आप 1/2 ^ एन / 2 जोड़कर "पूर्वाग्रह" के लिए सही कर सकते हैं, लेकिन यह वास्तव में सटीक समस्या को हल नहीं करेगा: उस मामले में घटती श्रृंखला हमेशा 8 पर रहेगी जब तक कि नमूना 8-1 न हो। / 2 ^ (एन / 2)। उदाहरण के लिए N = 4 के लिए, शून्य से ऊपर कोई भी नमूना औसत को अपरिवर्तित रखेगा।

मेरा मानना ​​है कि इसके लिए एक समाधान खोए हुए LSBs के संचायक को धारण करना होगा। लेकिन मैंने इसे कोड तैयार करने के लिए पर्याप्त नहीं बनाया है, और मुझे यकीन नहीं है कि यह श्रृंखला के कुछ अन्य मामलों में IIR शक्ति को नुकसान नहीं पहुंचाएगा (उदाहरण के लिए कि 7,9,7,9 तब औसतन 8 होगा) ।

@ ओलिन, आपके दो-चरण के कैस्केड को भी कुछ स्पष्टीकरण की आवश्यकता होगी। क्या आपका मतलब है कि प्रत्येक पुनरावृत्ति में पहले खिलाए गए दूसरे के परिणाम के साथ दो औसत मान रखना? इसका क्या फायदा है?

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.