Bonjour,
Je voulais savoir s'il existait une librairie où la fonction "sin()" serait optimisé en temps par rapport à celle de base de l'iDE.
Je suis sur une Arduino Due et sur l'iDE arduino 1.5.2.
Merci
Bonjour,
Je voulais savoir s'il existait une librairie où la fonction "sin()" serait optimisé en temps par rapport à celle de base de l'iDE.
Je suis sur une Arduino Due et sur l'iDE arduino 1.5.2.
Merci
Bonjour,
C'est pour faire quoi exactement ?
Le plus simple pour optimiser (en temps) ta fonction c'est des tables de correspondance ("lookup tables" ou "wave tables").
Comme suggéré par Skywodd suivant la précision dont tu as besoin, le plus efficace c'est de faire un tableau rempli avec les valeurs dont tu as besoin correspondant au sinus des nombres. Tu n'as ensuite plus qu'à aller chercher la valeur dont tu as besoin. Tableau en const évidemment pour ne pas bouffer la RAM
Exactement, la table. Selon le nombre de pas que tu veux (résolution), sachant qu'il te faut 1/4 du nombre de pas sur un tour (avec les sin x = -sin -x, sin -x = -sin x, sin (x+2pi) = sin x...)
Mais tout dépend pourquoi. Dans un vieux projet pas fini, j'ai une aiguille à faire tourner selon une valeur. j'obtiens les coordonnées du bout de l'aiguille par deux tables (x et y), de mémoire, chaque table contient 30 octets (occupation mémoire 60 octets), soit la possibilité de faire un tour complet sur 256 valeurs, le rayon de l'aiguille fait 51 pixels, dans l'extrait ci-dessous, je dessine 4 traits pour la grossir un peu, et comme sin x = cos (x + 2pi)... j'ai donc besoin de seulement 30 données pour décrire les 256 valeurs d'un sinus et d'un cosinus...
// constantes de dessin d'aiguille
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 29 20 21 22 23 24 25 26 27 28 29
byte a0_x[30] = {51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 49, 49, 49, 48, 48, 47, 47, 46, 45, 45, 44, 43, 43, 42, 41, 40, 40, 39, 38, 37};
byte a0_y[30] = { 0, 1, 3, 4, 5, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 34, 35};
void vitesse_affiche(){
byte v_index;
if(vitesse != vitesse_old){ // ne change l'affichage que si besoin
lcd_line(x0, y0, x00, y00, UNPUT);
lcd_line(x0 - v_e, y0 - v_e, x00, y00, UNPUT); // effacer l'ancienne aiguille
lcd_line(x0 + v_e, y0 + v_e, x00, y00, UNPUT);
lcd_line(x0 - v_e, y0 + v_e, x00, y00, UNPUT);
lcd_line(x0 + v_e, y0 - v_e, x00, y00, UNPUT);
// recherche du cadran dans lequel se trouve l'aiguille, et calcul des nouvelles coordonnées.
if (vitesse < 30) { // cadran 0
v_index = 29 - vitesse;
x00 = x0 - a0_x[v_index];
y00 = y0 + a0_y[v_index];
} else if (vitesse < 60) { // cadran 1
v_index = vitesse - 30;
x00 = x0 - a0_x[v_index];
y00 = y0 - a0_y[v_index];
} else if (vitesse < 90) { // cadran 2
v_index = 89 - vitesse;
x00 = x0 - a0_y[v_index];
y00 = y0 - a0_x[v_index];
} else if (vitesse < 120) { // cadran 3
v_index = vitesse - 90;
x00 = x0 + a0_y[v_index];
y00 = y0 - a0_x[v_index];
} else if (vitesse < 150) { // cadran 4
v_index = 149 - vitesse;
x00 = x0 + a0_x[v_index];
y00 = y0 - a0_y[v_index];
} else if (vitesse < 180) { // cadran 5
v_index = vitesse - 150;
x00 = x0 + a0_x[v_index];
y00 = y0 + a0_y[v_index];
} else if (vitesse < 210) { // cadran 6
v_index = 209 - vitesse;
x00 = x0 + a0_y[v_index];
y00 = y0 + a0_x[v_index];
} else { // cadran 7
v_index = vitesse - 210;
x00 = x0 - a0_y[v_index];
y00 = y0 + a0_x[v_index];
}
v_draw_point(); // dessine le rond central de l'aiguille (effacé par l'effacement de l'ancienne aiguille)
lcd_line(x0 - v_e, y0 - v_e, x00, y00, PUT); // aiguille
lcd_line(x0 + v_e, y0 + v_e, x00, y00, PUT);
lcd_line(x0 - v_e, y0 + v_e, x00, y00, PUT);
lcd_line(x0 + v_e, y0 - v_e, x00, y00, PUT);
lcd_line(x0, y0, x00, y00, PUT);
vitesse_old = vitesse; // sauvegarde de la valeur actuelle
}
}
Mais ce n'est peut-être pas cela que tu cherchais... mais si ça peut te donnée une idée...
Grilled par batto, mais attention au const, car l'accès à la flash est plus long qu'en ram direct, on ne gagnera peut-être pas grand chose...
Merci pour vos reponses
En fait je genere des fonctions sinusoidales qui s'enchainent. Par exemple une sinus redressée (avec un angle, un temps, une amplitude) suivie d'une sinus simple etc...
Pour faire ça je prépare un tableau de 5000 points que je genere sur 20ms autant de fois que nécessaire avant de passer à la fonction suivante.
Seulement le temps pour preparer ma courbe prends 110ms et je dois arriver à moins de 50ms.
Sachant que j'ai mesuré à l'oscilloscope que avec et sans la fonction sinus j'ai un écart de 80ms. Ce qui me fait 5000 * sin() = 80ms.
Je vais voir pour faire un tableau comme vous m'avez indiqué pour ne plus avoir que des multiplications ensuite mais une constante de 5000 c'est long XD
Excel est très pratique pour ça, et en jouant avec des concaténations, ça peut te générer le code de déclaration de la table tout seul!
Touf2638:
En fait je genere des fonctions sinusoidales qui s'enchainent. Par exemple une sinus redressée (avec un angle, un temps, une amplitude) suivie d'une sinus simple etc...
Pour faire ça je prépare un tableau de 5000 points que je genere sur 20ms autant de fois que nécessaire avant de passer à la fonction suivante.
Seulement le temps pour preparer ma courbe prends 110ms et je dois arriver à moins de 50ms.
Sachant que j'ai mesuré à l'oscilloscope que avec et sans la fonction sinus j'ai un écart de 80ms. Ce qui me fait 5000 * sin() = 80ms.
Ça nous dit toujours pas à quoi va servir les résultats au final.
Je balance des idées en vrac, elles te seront peut être utiles :
Pour faire ça je prépare un tableau de 5000 points que je genere sur 20ms autant de fois que nécessaire avant de passer à la fonction suivante.
20 ms = 50Hz
50 * 5000 = 250kHz je doute que l'ATmega puisse sortir les points à cette vitesse
Salut Skywood,
C'est un generateur de signaux pour tester des disjoncteurs magnetiques avec des angles d'enclenchements et des formes specifiques.
L'arduino est là pour générer un signal qui est amplifié en courant par la suite.
Le projet est presque finalisé et je me concentre actuellement sur les cadences. Je jette un oeil à tes mots clefs.
Salut fdufnews,
Je suis sur une Due et ca passe pour l'instant si j'ai pas trop d'autres algo à intégrer dans l'interruption.
Merci
Je vais être un peu plus précis quand même.
J'ai une page Web ou on peut choisir des fonctions. Par exemple une fonction "A" qui est une sinus simple
Sur cette page je peux remplir les champs de cette fonction (Angle, amplitude, temps d'envoi)
J'ajoute ensuite une fonction "B" qui est une sinus redressée
Sur cette page je peux remplir les champs de cette fonction (Angle, amplitude, temps d'envoi)
Je clique sur start et ça génère les deux fonctions à la suite. Le temps qui me pose soucis est le temps ou je recalcule mon tableau pour la deuxieme fonction.
Si je pars sur ce que vous m'indiquez, je vais faire un tableau avec sin(PI/50001), sin(PI/50002),sin(PI/50003) ... ,sin(PI/50005000)
Je n'aurais plus ensuite qu'à faire des multiplications par l'amplitude en gros ce qui me prendra moins de temps.
Par contre en progmem pas le droit aux flottants si j'ai bien lu ?
Et dans un post de skywood [prog] Déclaration de variables et mémoire, const volatile static... - Français - Arduino Forum si je pige bien un tableau de constante est stocké par défaut en SRAM ?
Si je suis pas clair ou si je comprends mal n'hesitez pas
Touf2638:
Si je pars sur ce que vous m'indiquez, je vais faire un tableau avec sin(PI/50001), sin(PI/50002),sin(PI/50003) ... ,sin(PI/50005000)
Je n'aurais plus ensuite qu'à faire des multiplications par l'amplitude en gros ce qui me prendra moins de temps.
Mauvaise idée de faire des tableaux aussi grand.
Regarde mon code Cheaptune et le principe de DDS, c'est exactement ça qu'il te faut.
Touf2638:
Par contre en progmem pas le droit aux flottants si j'ai bien lu ?
Tu peut mais c'est 8 octets par flottant.
Touf2638:
Et dans un post de skywood [prog] Déclaration de variables et mémoire, const volatile static... - Français - Arduino Forum si je pige bien un tableau de constante est stocké par défaut en SRAM ?
Oui :
const -> RAM
const PROGMEM -> mémoire flash
Dans Cheaptune pour ce qui m'interresse ca se resumerait à "SinusWaveform.cpp" qui vient lire des fichiers include avec tes points de la sinusoide au demarrage et les stocke en flash
Ensuite tu lis avec PROGMEM_READWORD
Le principe c'est bien ça ?
Ca équivaut à un tableau quand même non ?
Touf2638:
Dans Cheaptune pour ce qui m'interresse ca se resumerait à "SinusWaveform.cpp" qui vient lire des fichiers include avec tes points de la sinusoide au demarrage et les stocke en flash
La classe SinusWaveform seule ne sert strictement à rien.
Il faut au minimum les classes : SinusWaveform, Waveform, Oscillator, et StandardOscillator.
PS: dans ton cas tu pourrais fusionner le code des classes SinusWaveform et Waveform, de même pour Oscillator et StandardOscillator.
Touf2638:
Ensuite tu lis avec PROGMEM_READWORD
Attention : c'est une macro (définie dans defines.h) qui permet au code d'être compatible avec plusieurs plateformes (de base c'est pour PC).
De base cette macro travaille en RAM (sur PC le principe de PROGMEM n'existe pas).
Touf2638:
Le principe c'est bien ça ?
Ca équivaut à un tableau quand même non ?
Non pas tout à fait.
Regarde bien comment fonctionne le DDS, c'est très subtile.
Tu ne lit pas le tableau de l'index 0 à Nmax, tu lit le tableau par pas de taille fixe calculé à partir de la fréquence voulu et de la fréquence d'échantillonnage.
(dans la limite de Féchantillonnage / 2, c'est le théorème de la fréquence de nyquist)
Ok merci Skywood
J'ai bien regardé le DDS sur google et je vais regarder en detail cheaptune et comment tu modifies l'amplification sans repreparer le tableau puis essayer d'integrer le code.
Par contre je n'ai pas l'utilité de modifier la frequence je suis à 50Hz fixe mais c'est pas important.
PS: Vu la difference de niveau entre nous c'est possible que je te repose quelques questions plus tard @+
Le progmem n'est pas un peu long en lecture?
Pour générer un sinus de 50Hz, je pense que 16 points te suffiraient, soit 64 points sur une période (chaque valeur se trouve 4 fois sur une période), soit changer ton registre OCR toutes les 312,5µs, et ça, ton arduino saura faire en moins d'une ms...
Déclare un tableau RAM de 16 word (surtout pas INT!!! le timer ne connait pas les nombres négatifs). Si tu veux gagner du temps, il faudra que tu joues avec les registres et non les fonctions du core arduino. c'est pas bien long à comprendre ni à faire.
Salut Super_Cinci,
Mon soucis n'est pas au niveau de la generation. Ca ca marche déjà sur 5OOO points.
Par contre c'est avant de générer, quand je prépare mon tableau. Je recuperer l'amplitude venant de la page Web et je remplis mon tableau de 5000 points. Pour cela j'utilise la fonction sin() qui elle prend pas mal de temps surtout 5000 fois.
Je pensais passer une partie de la formule fixe qui comprend le sinus en données brutes. Cela me permettra de ne faire qu'une multiplication par l'amplitude.
Pour ce qui est de la resolution c'est ce qui m'est imposé pour le test. =(
A moi de voir ce qui m'a été indiqué par Skywood en esperant comme tu dis que le temps d'accès progmem est inferieur à sin()
Je reprecise que la génération des 5000 points du tableau sur 20ms ca ne pose pas de soucis.
Touf2638:
Sachant que j'ai mesuré à l'oscilloscope que avec et sans la fonction sinus j'ai un écart de 80ms. Ce qui me fait 5000 * sin() = 80ms.
Touf2638:
Je reprecise que la génération des 5000 points du tableau sur 20ms ca ne pose pas de soucis.
Là en fait j'ai un doute. Parce que moi aussi j'ai une Due.
Je n'avais pas eu le temps ces derniers jours mais ce soir je l'ai ressortie et pour faire un tableau de 5000 sinus il me faut 2µs. Alors même en admettant que tu fasses plus que calculer des sinus tu me vois quand même un peu surpris.
N'aurais-tu pas quelques opérations inutiles dans la boucle de calcul.
Le programme en question. Je calcule le sinus et je normalise sur 12 bits comme si j'utilisais un CAN 12 bits.
void setup() {
Serial.begin(9600);
}
void loop() {
int tab[5000];
int i;
float val=0,inc;
unsigned long temps1,temps2;
temps1 = micros();
inc = 2*3.1415926/5000;
for(i=0; i<5000;i++){
tab[i]=(int)(sin(val)*2047.0+2048.0);
val+=inc;
}
temps2 = micros();
Serial.print("temps1=");
Serial.println(temps1);
Serial.print("temps2=");
Serial.println(temps2);
Serial.println(temps2-temps1);
delay(100);
}
Bah j'ai vérifié pas mal de fois à l'oscillo... Je vais poster mon code dès que possible si tu vois une erreur.
La différence est que mon tableau est une variable globale et que je fais pas exactement ce calcul mais bon de la à avoir un écart pareil ...
Par contre j'ai plus un truc comme
val = PI/Resolution;
for(i=0; i<5000;i++)
{
tab[i]=sin(i*val)*amplitude);
}
C'est de tête mais je poste le vrai code dès que possible. Si ça pouvait être une erreur de ma part ca serait bien sympa
En y réfléchissant, 2µs !! cela me parait un peu trop rapide.
Il faudrait que je vérifie s'il n'y a pas une embrouille avec la fonction micros() sur le Due.
Demain je mettrai un coup d'oscilloscope pour vérifier.