Comme d'autres l'ont mentionné, vous devriez envisager un filtre IIR (réponse impulsionnelle infinie) plutôt que le filtre FIR (réponse impulsionnelle finie) que vous utilisez actuellement. Il y a plus que cela, mais à première vue, les filtres FIR sont implémentés sous forme de convolutions explicites et de filtres IIR avec équations.
Le filtre IIR que j'utilise beaucoup dans les microcontrôleurs est un filtre passe-bas unipolaire. C'est l'équivalent numérique d'un simple filtre analogique RC. Pour la plupart des applications, celles-ci auront de meilleures caractéristiques que le filtre de boîte que vous utilisez. La plupart des utilisations d’un filtre de boîte que j’ai rencontrées résultent de l’absence de vigilance de la part de la classe de traitement du signal numérique et non du fait qu’elles ont besoin de leurs caractéristiques particulières. Si vous souhaitez simplement atténuer les hautes fréquences que vous savez être du bruit, un filtre passe-bas unipolaire est préférable. La meilleure façon d’implémenter numériquement l’un dans un microcontrôleur est généralement:
FILT <- FILT + FF (NOUVEAU - FILT)
FILT est un morceau d'état persistant. C'est la seule variable persistante dont vous avez besoin pour calculer ce filtre. NEW est la nouvelle valeur que le filtre est mis à jour avec cette itération. FF est la fraction de filtre qui ajuste la "lourdeur" du filtre. Regardez cet algorithme et voyez que pour FF = 0 le filtre est infiniment lourd puisque la sortie ne change jamais. Pour FF = 1, il n’ya vraiment aucun filtre car la sortie ne fait que suivre l’entrée. Les valeurs utiles sont entre les deux. Sur les petits systèmes, vous choisissez FF égal à 1/2 N sorte que la multiplication par FF puisse être accomplie comme un décalage à droite de N bits. Par exemple, FF pourrait être 1/16 et multiplié par FF, donc un décalage à droite de 4 bits. Sinon, ce filtre ne nécessite qu'une soustraction et une addition, bien que les nombres doivent généralement être plus larges que la valeur d'entrée (plus de précision numérique dans une section séparée ci-dessous).
Je prends habituellement les lectures A / D bien plus rapidement que nécessaire et applique deux de ces filtres en cascade. Il s’agit de l’équivalent numérique de deux filtres RC en série et s’atténue de 12 dB / octave au-dessus de la fréquence de décélération. Cependant, pour les lectures A / D, il est généralement plus pertinent d'examiner le filtre dans le domaine temporel en tenant compte de sa réponse progressive. Cela vous indique à quelle vitesse votre système verra un changement lorsque ce que vous mesurez change.
Pour faciliter la conception de ces filtres (c’est-à-dire choisir le FF et choisir le nombre de filtres à utiliser en cascade), j’utilise mon programme FILTBITS. Vous spécifiez le nombre de bits de décalage pour chaque FF dans la série de filtres en cascade, qui calcule la réponse à l'étape et les autres valeurs. En fait, je le fais habituellement via mon script wrapper PLOTFILT. Cela lance FILTBITS, qui crée un fichier CSV, puis trace le fichier CSV. Par exemple, voici le résultat de "PLOTFILT 4 4":
Les deux paramètres à PLOTFILT signifient qu'il y aura deux filtres en cascade du type décrit ci-dessus. Les valeurs de 4 indiquent le nombre de bits de décalage pour réaliser la multiplication par FF. Les deux valeurs FF sont donc 1/16 dans ce cas.
La trace rouge correspond à la réponse pas à pas de l'unité et constitue l'élément principal à examiner. Par exemple, cela vous indique que si l'entrée change instantanément, la sortie du filtre combiné s'installera à 90% de la nouvelle valeur en 60 itérations. Si vous vous souciez du temps de stabilisation de 95%, vous devez attendre environ 73 itérations, et pour le temps de stabilisation de 50%, seulement 26 itérations.
Le tracé vert vous montre le résultat d’un pic unique d’amplitude complète. Cela vous donne une idée de la suppression du bruit aléatoire. Il semble qu'aucun échantillon ne provoque plus de 2,5% de changement dans la sortie.
La trace bleue donne une impression subjective de ce que fait ce filtre avec le bruit blanc. Ce test n’est pas rigoureux, car rien ne garantit exactement le contenu exact des nombres aléatoires choisis comme entrée de bruit blanc pour cette série de PLOTFILT. C'est seulement pour vous donner une idée approximative de la quantité de poudre que vous allez écraser et de sa douceur.
PLOTFILT, peut-être FILTBITS, et de nombreux autres éléments utiles, en particulier pour le développement de microprogrammes PIC, sont disponibles dans la version logicielle des outils de développement PIC sur la page Téléchargements de logiciels .
Ajouté à propos de la précision numérique
Je vois dans les commentaires et maintenant une nouvelle réponse qu'il y a un intérêt à discuter du nombre de bits nécessaires pour implémenter ce filtre. Notez que la multiplication par FF créera de nouveaux bits de Log 2 (FF) en dessous du point binaire. Sur les petits systèmes, FF est généralement choisi égal à 1/2 N, de sorte que cette multiplication est effectivement réalisée par un décalage à droite de N bits.
FILT est donc généralement un nombre entier à point fixe. Notez que cela ne change en rien le calcul du point de vue du processeur. Par exemple, si vous filtrez les lectures A / D 10 bits et que N = 4 (FF = 1/16), vous avez besoin de 4 bits de fraction inférieurs aux lectures A / D de 10 bits d'entiers. Dans la plupart des processeurs, vous feriez des opérations sur les entiers 16 bits en raison des lectures A / N 10 bits. Dans ce cas, vous pouvez toujours effectuer exactement les mêmes opérations sur les entiers 16 bits, mais commencez par les lectures A / D décalées de 4 bits. Le processeur ne connaît pas la différence et n'en a pas besoin. Faire le calcul sur des entiers 16 bits entiers fonctionne que vous considériez qu’il s’agit d’entiers de 12,4 points fixes ou de vrais entiers 16 bits (16,0 points fixes).
En général, vous devez ajouter N bits à chaque pôle de filtrage si vous ne souhaitez pas ajouter de bruit en raison de la représentation numérique. Dans l'exemple ci-dessus, le deuxième filtre de deux aurait 10 + 4 + 4 = 18 bits pour ne pas perdre d'informations. En pratique, sur une machine 8 bits, cela signifie que vous utiliseriez des valeurs 24 bits. Techniquement, seul le deuxième pôle sur deux aurait besoin de la valeur la plus large, mais pour simplifier les microprogrammes, j'utilise généralement la même représentation, et donc le même code, pour tous les pôles d'un filtre.
En général, j’écris un sous-programme ou une macro pour effectuer une opération de filtrage, puis l’applique à chaque pôle. Que ce soit un sous-programme ou une macro dépend de l'importance des cycles ou de la mémoire programme dans ce projet particulier. Quoi qu’il en soit, j’utilise un état de travail pour passer NEW dans le sous-programme / macro, qui met à jour FILT, mais le charge également dans le même état de travail de NEW. Cela facilite l’application de plusieurs pôles puisque le FILT mis à jour d’un pôle est le NOUVEAU du suivant. Lorsqu’un sous-programme, il est utile d’avoir un pointeur sur FILT à l’entrée, qui est mis à jour juste après FILT à la sortie. De cette façon, le sous-programme fonctionne automatiquement sur des filtres consécutifs en mémoire s’il est appelé plusieurs fois. Avec une macro, vous n'avez pas besoin d'un pointeur puisque vous transmettez l'adresse pour pouvoir l'utiliser à chaque itération.
Exemples de code
Voici un exemple de macro décrite ci-dessus pour un PIC 18:
////////////////////////////////////////////////////////// ////////////////////////////////////
//
// Macro FILTER filt
//
// Met à jour un pôle de filtrage avec la nouvelle valeur dans NEWVAL. NEWVAL est mis à jour pour
// contient la nouvelle valeur filtrée.
//
// FILT est le nom de la variable d'état du filtre. Il est supposé être de 24 bits
// large et dans la banque locale.
//
// La formule pour mettre à jour le filtre est la suivante:
//
// FILT <- FILT + FF (NEWVAL - FILT)
//
// La multiplication par FF est réalisée par un décalage à droite des bits FILTBITS.
//
/ filtre macro
/écrire
dbankif lbankadr
movf [arg 1] +0, w; NEWVAL <- NEWVAL - FILT
subwf newval + 0
movf [arg 1] +1, w
souswfb newval + 1
movf [arg 1] +2, w
souswfb newval + 2
/écrire
/ loop n filtbits; une fois pour chaque bit pour déplacer NEWVAL à droite
rlcf newval + 2, w; décale NEWVAL d'un bit à droite
rrcf newval + 2
rrcf newval + 1
rrcf newval + 0
/ endloop
/écrire
movf newval + 0, w; ajoute une valeur décalée dans le filtre et enregistre dans NEWVAL
addwf [arg 1] +0, w
movwf [arg 1] +0
movwf newval + 0
movf newval + 1, w
addwfc [arg 1] +1, w
movwf [arg 1] +1
movwf newval + 1
movf newval + 2, w
addwfc [arg 1] +2, w
movwf [arg 1] +2
movwf newval + 2
/ endmac
Et voici une macro similaire pour un PIC 24 ou dsPIC 30 ou 33:
////////////////////////////////////////////////////////// ////////////////////////////////////
//
// Macro FILTER ffbits
//
// Met à jour l'état d'un filtre passe-bas. La nouvelle valeur d'entrée est dans W1: W0
// et l'état du filtre à mettre à jour est pointé par W2.
//
// La valeur de filtre mise à jour sera également renvoyée dans W1: W0 et W2 indiqueront
// vers la première mémoire après l'état du filtre. Cette macro peut donc être
// invoqué successivement pour mettre à jour une série de filtres passe-bas en cascade.
//
// La formule de filtre est:
//
// FILT <- FILT + FF (NOUVEAU - FILT)
//
// où la multiplication par FF est effectuée par un décalage arithmétique à droite de
// FFBITS.
//
// ATTENTION: W3 est mis à la corbeille.
//
/ filtre macro
/ var new ffbits entier = [arg 1]; récupère le nombre de bits à décaler
/écrire
/ write "; Effectue un filtrage passe-bas à un pôle, bits de décalage =" ffbits
/écrire " ;"
sous w0, [w2 ++], w0; NOUVEAU - FILT -> W1: W0
subb w1, [w2--], w1
lsr w0, # [v ffbits], w0; décale le résultat dans W1: W0 à droite
sl w1, # [- 16 ffbits], w3
ior w0, w3, w0
asr w1, # [vffbits], w1
ajouter w0, [w2 ++], w0; ajouter FILT pour obtenir le résultat final dans W1: W0
addc w1, [w2--], w1
mov w0, [w2 ++]; écrit le résultat dans l'état du filtre, avance le pointeur
mov w1, [w2 ++]
/écrire
/ endmac
Ces deux exemples sont implémentés sous forme de macros à l'aide de mon préprocesseur d'assembleur PIC , qui est plus capable que l'une ou l'autre des installations de macro intégrées.