Aller au contenu


Photo
- - - - -

IMU : lequel choisir ?


25 réponses à ce sujet

#1 dakota99

dakota99

    Habitué

  • Membres
  • PipPip
  • 228 messages
  • Gender:Male

Posté 28 décembre 2021 - 09:52

Bonjour

 

Sur un robot roulant je souhaiterais installer un IMU principalement pour :

- lui permettre de garder un cap précis à +/- 1° pendant une dizaine de mètres.

- faire des corrections de trajectoire.

- de faire des virages précis : 45° 90° 180° (c'est déjà fait actuellement par 2 encodeurs mais peut-être que l'IMU peut apporter un plus)

 

- 2 roues motrices

- vitesse d'avancement 6 à 10 m/min

- Arduino Mega avec Shield grove.

 

Y a-t-il un IMU qui convient particulièrement bien avec l'Arduino Mega et qui est couramment utilisé dans vos projets  ?

 

Merci



#2 Sandro

Sandro

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 1 260 messages
  • Gender:Male

Posté 28 décembre 2021 - 12:34

Bonjour,

j'ai pas un model en particulier à suggérer, mais quelques éléments à prendre en compte :

- prends un IMU 3D : même si le sol est "plat", une inclinaison d'un degré verticalement crée une erreur énorme (surtout sur l'accélération)

- avec un IMU, la distance parcourue ou la rotation effectuée ne jouent quasiment pas sur l'erreur, c'est surtout la durée qui compte

- si tu es en extérieur, et que tu peux placer l'IMU sufisement loin des moteurs/cables de puissance/métaux ferreux, alors un IMU intégrant une boussole permet de garder un cap absolut. En intérieur, oublie, c'est inexploitable

- vérifie les niveaux logiques

- si l'IMU gère le calcul de l'orientation en interne, ça te facilite la vie

- vérifie qu'il existe une librairie arduino, ça te facilitera la vie


Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...

Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.


#3 dakota99

dakota99

    Habitué

  • Membres
  • PipPip
  • 228 messages
  • Gender:Male

Posté 28 décembre 2021 - 12:49

Bonjour Sandro, merci pour ta réponse.

 

J'étais parti sur ce modèle.

https://www.gotronic...20252-25310.htm

 

Je suppose que un IMU 3D veut dire un IMU 3 axes.

Je ne sais pas s'il gère le calcul de l'orientation en interne...

Mais il a une librairie Arduino et est bien documenté.

 

Merci



#4 Sandro

Sandro

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 1 260 messages
  • Gender:Male

Posté 28 décembre 2021 - 01:28

Par IMU 3D, j'entends 3 axes pour chaque capteur (accéléromètre, gyro, et éventuellement magnétomètre). Je précise de cette manière car parfois le nombre d'axes correspond au nombre de mesures (par exemple un IMU "6 axes" peut être un accéléromètre 3D et un gyro 3D) : dans ce cas, un IMU 3 axes peut aussi être un IMU 2D (accélérations en x et y, et rotation autour de l'axe Z)

 

 

Ton lien est basé sur le MPU-9250 :

- pour l'IMU de base, la documentation est correcte, et il y a plusieurs librairies. Par contre, il n'y a pas de calcul en interne de l'orientation

- par contre, il y a un "digital motion processor" (DMP) inclus, qui sert en théorie à faire des calculs plus complexes (par exemple l'orientation) en interne. Cette partie est pour ainsi dire pas documentée : d'après ce que j'ai compris, c'est un FPGA (en gros un circuit logique programmable) qu'il faut "programmer" à chaque démarrage, et qui fait ensuite ce qu'on a programmé. Il semblerait qu'il y ait quelques librairies qui contiennent un tel "programme" pour le FPGA, et qui l'exploitent. J'ai l'impression que c'est le cas de celle suggérée via ton lien, et qu'elle contient des fonctions pour obtenir l'orientation sous forme de quaternions. Par contre, s'il y a le moindre problème, trouver de la documentation sur cette partie risque d'être très difficile

- le MPU-9250 est un IMU qui semble assez répondu. En revanche, il n'est plus produit, donc je ne sais pas pour combien de temps en on trouvera encore (en tant que composant, il est déjà devenu quasi introuvable, mais il reste encore pas mal de petits PCBs comme celui de ton lien à acheter)


Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...

Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.


#5 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 766 messages
  • Gender:Male

Posté 28 décembre 2021 - 02:38

Perso, j'ai acheté celui-là, https://www.amazon.f...0?ie=UTF8&psc=1, en raison de ce tutorial de 26 vidéos qui est vraiment complet et qui m'a fait comprendre beaucoup de choses alors que c'est largement hors de ma portée.

 



#6 quadmean

quadmean

    Membre

  • Membres
  • 27 messages

Posté 28 décembre 2021 - 02:44

Bonjour Dakota,

 

Avant tout, bon courage avec les IMUs. Moi ça m'a pris pas mal de temps pour les utiliser à fond.

 

Il faut savoir que d'une façon générale, les IMUs  ne sont pas très  précis, ou plutôt ils sont précis mais sensibles à leur environnement : perturbations electromagnétiques, etc...

C'est pour cela qu'il est recommandé de réestimer les valeurs en combinant différents instruments (gyroscope, accéléromètre, GPS, ...)

c'est ce recalcul qui n'est pas simple.

 

Le MPU 9250 a été trés populaire car il combinait plusieurs instruments, mais malheureusement il valait mieux réécrire les algorithmes permettant d'obtenir les angles d'orientation.

Son grand frère est arrivé, c'est le ICM 20948 que l'on trouve à 5€ sur les sites chinois.

C'est celui que j'utilise sur Alan. (j'ai deux IMU)

Son intérêt c'est qu'il possède une unité de calcul qui à partir de ses instruments embarqués te recalcule les angles, et effectue une auto calibration.

Ca fonctionne très bien, et ça permet de se décharger des calculs puisque embarqués dans l'IMU.

 

En revanche pour calculer les vitesses, encore une fois l'déal serait de recombiner les données de l'IMU avec un GPS.

En utilisant seulement l'IMU c'est possible mais moins fiable. Il faut intégrer deux fois les accélérations communiquées par l'IMU.

J'ai quelques codes sources à la fois en python et Arduino si ça vous intéresse.



#7 dakota99

dakota99

    Habitué

  • Membres
  • PipPip
  • 228 messages
  • Gender:Male

Posté 28 décembre 2021 - 03:42

Merci merci pour vos commentaires.

Cela semble indispensable de maîtriser ce sujet mais apparemment pas évident :)

 

 

Il me semble raisonnable de laisser tomber le MPU-9250 : je risque de ne pas vivre assez longtemps pour arriver au résultat souhaité :)

 

Je suis très sensible aux arguments d'Oracid : tutoriel de 26 vidéos et apparemment orientées vers l'Arduino. Ca me parle.Je dois freiner pour ne pas le commander tout de suite.

 

Le ICM 20948

https://www.gotronic...tm#complte_desc

me fait un peu peur car je ne vois pas d'exemples de code pour Arduino mais il fait une partie du calcul, donc très bien aussi.

 

Je vais approfondir toutes ces informations.

Encore merci pour votre temps.



#8 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 766 messages
  • Gender:Male

Posté 28 décembre 2021 - 03:51

tutoriel de 26 vidéos et apparemment orientées vers l'Arduino. 

C++, avec Arduino et VPython sur PC, à partir de la leçon 11.

Et communication entre PC et Arduino.

What else ?



#9 quadmean

quadmean

    Membre

  • Membres
  • 27 messages

Posté 28 décembre 2021 - 05:05

Je connais très bien cette série de vidéos, et elle m'a beaucoup inspirée.

Il y a effectivement pas mal de concurrence entre ces deux MPU.

Très honnêtement je ne regrette pas mon choix.

Et une recherche sur github te montrera des exemples avec le 20948 ou on exploite le DMP, et c'est plutôt simple, car il y a une librairie écrite exprès.

Moi pour la compilation j'utilise platformIO, dans studio code ça permet d'avoir accès à plein d'exemples et d'utiliser un debugger intégré....

Et clairement ce qui fait pencher la balance c'est le DMP et le coût. 5€ avec un DMP intégré.... Je ne sais pas si on fait mieux



#10 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 766 messages
  • Gender:Male

Posté 28 décembre 2021 - 06:00

Le BNO085 est identique au BNO-055, mais le firmware est très différent. Voir les détails ici, https://www.adafruit.com/product/4754

 

Voici 2 vidéos comparant le BNO-085 et le ICM-20948. C'est toujours bon de pouvoir comparer.

 



#11 quadmean

quadmean

    Membre

  • Membres
  • 27 messages

Posté 28 décembre 2021 - 07:30

Hello Oracid,

Super vidéo très intéressante. Merci! Je progresse dans ma quête de l'IMU ultime, et ce n'est pas gagné....

 

Ce que j'ai compris en regardant la vidéo et en lisant les commentaires c'est que les deux ont l'air de se valoir, mais dans les commentaires, on parle éventuellement  d'une plus grande sensibilité de l'IMU 20948 aux vibrations. Donc au premier abord, de ce côté avantage au BNO.

Néanmois balle au centre! la vidéo a été faite à partir d'une très vielle librairie de l'IMU 20948. On peut le constater par la date de la vidéo. Hors depuis Avril 2021 et la version 1.2.5 , des évolutions significatives ont été apportées à chaque version sur le paramétrage du DMP pour améliorer la précision (qui était déjà pas mal) et supprimer le drift.

 

Le réel avantage du BNO mais qui explique aussi son prix (l'IMU 20948 est à moins de 5€), c'est qu'il possède une mémoire flash ce qui permet de stocker sa configuration dans le BNO, ce qui n'est pas possible avec l'IMU, mais ça se contourne en paramétrant l'IMU à l'initialisation.  La librairie de l'IMU embarque maintenant une fonction qui fait tout le travail...

 

Je vais ressortir mon banc de tests et recompiler le code avec la dernière version de la librairie pour regarder tout ça...

 

Très intéressant ces évolutions ... Merci mille fois, pour ces éléments Oracid. Je vais reprendre mon code pour voir si maintenant on peut avoir plus de qualité sur la lecture d'une vitesse moyenne.



#12 dakota99

dakota99

    Habitué

  • Membres
  • PipPip
  • 228 messages
  • Gender:Male

Posté 28 décembre 2021 - 09:11

Oui merci pour les vidéos.

 

Il y a un délai pour le BNO055. Et comme j'aimerais l'utiliser avant janvier, je vais opter pour le 20948.

 

Par contre le 20948 est de stock. Plusieurs modèles. Librairie et exemple de code fournis.

 

https://www.gotronic...a4554-33453.htm

 

https://www.gotronic...15335-33508.htm



#13 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 766 messages
  • Gender:Male

Posté 28 décembre 2021 - 10:17

Dans tous les cas, et quel que soit l'IMU choisi, il est important de bien comprendre le fonctionnement de base d'un IMU 9 axes.

Pour cela, je n'ai pas trouvé de tutorial plus pédagogique que celui que je vous ai proposé.



#14 quadmean

quadmean

    Membre

  • Membres
  • 27 messages

Posté 28 décembre 2021 - 11:04

Dakota,

j'avoue avoir réagi car pour moi c'était 5€ sur ce type de capteurs...

18€ ça me parait trop cher.



#15 dakota99

dakota99

    Habitué

  • Membres
  • PipPip
  • 228 messages
  • Gender:Male

Posté 29 décembre 2021 - 07:25

@Oracid : oui absolument les tutoriels sont très explicites. Je vais m'efforcer de les suivre jusqu'au bout. Encore merci pour ce lien.

 

@Quadmean.

Je vais voir si je le trouve moins cher ailleurs. Mais chez les chinois il faut probablement un délai considérable.

Et puis faisons travailler les entreprises européennes ... (c'est un autre débat)



#16 dakota99

dakota99

    Habitué

  • Membres
  • PipPip
  • 228 messages
  • Gender:Male

Posté 03 janvier 2022 - 04:30

Bonjour

J'ai finalement acheté un BNO055 et un ICM20948

J'ai suivi les tutos du Monsieur au café froid sans sucre... Merci à Oracid pour ce lien.

J'ai utilisé le code proposé à la leçon 10.

 

Les objectifs poursuivis :

- de garder un cap précis à +/- 1° pendant une dizaine de mètres.

- faire des corrections de trajectoire.

- de faire des virages précis : 45° 90° 180° (c'est déjà fait actuellement par 2 encodeurs mais peut-être que l'IMU peut apporter un plus)

 

- En laissant l'IMU en position fixe, le valeurs du magnétomètre varient très fort parfois plus de 10 degrés en moins d'une minute.

- si l'IMU est tourné à 90° la différence entre l'angle avant et après la rotation n'est jamais de 90°. La différence est souvent supérieure à 10°

 

Bref il y a peut-être un problème localisé entre la chaise et le clavier, c'est fort possible.

Mais jusqu'à présent, je ne suis pas convaincu par les résultats obtenus par ces IMU

 

 

 

 

 

 



#17 Sandro

Sandro

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 1 260 messages
  • Gender:Male

Posté 03 janvier 2022 - 04:52

Bonjour,

est-ce que tu as une boussole (toute simple, avec l'aiguille aimantée qui tourne)? Si oui, faits le même test au même endroit, en alternant entre l'IMU et la bousolle, et regarde si la boussole est aussi mauvaise que l'IMU.

Si comme je le supposes tu as fait les tests à l'intérieur, sur ton bureau, alors il est bien possible que les perturbations du champ magnétique suffisent à expliquer ces problèmes.

 

Si tu peux, refaits les tests à l'extérieur, loin de toute ferraille (surtout acier et autres alliages ferreux), avec l'arduino aussi loin que possible de l'ordinateur portable. Si le problème persiste, alors c'est un problème de capteur ou de code. S'il disparaît, alors c'est juste qu'à l'intérieur tu avais un champ magnétique très perturbé (globalement, en intérieur, le champ magnétique est inexploitable : au mieux il est constant mais dévié différemment selon l'endroit, au pire il varie en plus dans le temps)


Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...

Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.


#18 quadmean

quadmean

    Membre

  • Membres
  • 27 messages

Posté 03 janvier 2022 - 07:31

Bonjour Dakota,

Hé oui c'est pas simple les IMU...

Il faudrait que tu précises avec quel IMU tu as fais tes tests.

A la lecture de tes commentaires, je suppose que tu n'utilises pas le DMP, ou alors que tu n'utilises pas les quaternions.

les quaternions sont des objets mathématiques indispensables dans les calculs d'angle des IMUs car ils evitent le drift et le gimball lock (je ne sais plus comment ça se dit)

 

Sans DMP, une calibration manuelle est nécessaire, ainsi que la fusion des données car aucun des instruments seul n'est assez fiable pour avoir une information pertinente.

 

C'est pour cela qu'il est vivement conseillé d'utiliser le DMP.  il y a une autocalibration, mais ce n'est pas instantané : l'autocalibration s'apprend avec les mouvements reçus. Egalement le DMP fait la fusion des données et génére les quaternions pour les calculs.

 

Bon... De mon côté pour pouvoir t'aider j'ai refais mon banc de test avec l'affichage 3D de l'IMU et en utilisant la dernière librairie de Sparkfun pour le 20948.

Là je n'ai pas des bonnes nouvelles : le résultat est incohérent, et l'IMU chauffe comme pas possible, ce qui n'était pas le cas avec la version 'Alan'

Comme il y a pas mal de nouveautés sur ce banc de tests, je dois décortiquer un par un les points pour savoir d'ou vient le problème.

 

Ci-dessous le code source que j'ai utilisé, qui est celui de sparkfun modifié.

L'important c'est le début de la boucle loop, tu as le détail du calcul des 3 angles à partir des quaternions.

#include <Arduino.h>
#include "ICM_20948.h" // Click here to get the library: http://librarymanager/All#SparkFun_ICM_20948_IMU

ICM_20948_SPI icm20948;

#define CS_PIN PA4
#define SPI_FREQ 100000

#define SLAVE_SDA PB9
#define SLAVE_SCL PB8
#define SLAVE_ADR 0x21

TwoWire slaveI2C(SLAVE_SDA, SLAVE_SCL);

void receiveEvent(int);
void requestEvent();

void setup()
{
  // put your setup code here, to run once:

  Serial.begin(115200);
  Serial.println("Reseting");

  SPI.begin();

  slaveI2C.begin(SLAVE_ADR);
  // slaveI2C.setClock(SPI_FREQ);
  slaveI2C.onReceive(receiveEvent);
  slaveI2C.onRequest(requestEvent);

  bool initialized = false;
  while (!initialized)
  {

    icm20948.begin(CS_PIN, SPI, SPI_FREQ); // Here we are using the user-defined SPI_FREQ as the clock speed of the SPI bus

    Serial.print(F("Initialization of the sensor returned: "));
    Serial.println(icm20948.statusString());
    if (icm20948.status != ICM_20948_Stat_Ok)
    {
      Serial.println("Trying again...");
      delay(500);
      initialized = true;
    }
    else
    {
      initialized = true;
    }
  }

  // In this advanced example we'll cover how to do a more fine-grained setup of your sensor
  Serial.println("Device connected!");

  // Here we are doing a SW reset to make sure the device starts in a known state
  icm20948.swReset();
  if (icm20948.status != ICM_20948_Stat_Ok)
  {
    Serial.print(F("Software Reset returned: "));
    Serial.println(icm20948.statusString());
  }
  delay(250);

  // Now wake the sensor up
  icm20948.sleep(false);
  icm20948.lowPower(false);

  bool success = true; // Use success to show if the DMP configuration was successful

  // Initialize the DMP. initializeDMP is a weak function. In this example we overwrite it to change the sample rate (see below)
  success &= (icm20948.initializeDMP() == ICM_20948_Stat_Ok);

  // DMP sensor options are defined in ICM_20948_DMP.h
  //    INV_ICM20948_SENSOR_ACCELEROMETER               (16-bit accel)
  //    INV_ICM20948_SENSOR_GYROSCOPE                   (16-bit gyro + 32-bit calibrated gyro)
  //    INV_ICM20948_SENSOR_RAW_ACCELEROMETER           (16-bit accel)
  //    INV_ICM20948_SENSOR_RAW_GYROSCOPE               (16-bit gyro + 32-bit calibrated gyro)
  //    INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED (16-bit compass)
  //    INV_ICM20948_SENSOR_GYROSCOPE_UNCALIBRATED      (16-bit gyro)
  //    INV_ICM20948_SENSOR_STEP_DETECTOR               (Pedometer Step Detector)
  //    INV_ICM20948_SENSOR_STEP_COUNTER                (Pedometer Step Detector)
  //    INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR        (32-bit 6-axis quaternion)
  //    INV_ICM20948_SENSOR_ROTATION_VECTOR             (32-bit 9-axis quaternion + heading accuracy)
  //    INV_ICM20948_SENSOR_GEOMAGNETIC_ROTATION_VECTOR (32-bit Geomag RV + heading accuracy)
  //    INV_ICM20948_SENSOR_GEOMAGNETIC_FIELD           (32-bit calibrated compass)
  //    INV_ICM20948_SENSOR_GRAVITY                     (32-bit 6-axis quaternion)
  //    INV_ICM20948_SENSOR_LINEAR_ACCELERATION         (16-bit accel + 32-bit 6-axis quaternion)
  //    INV_ICM20948_SENSOR_ORIENTATION                 (32-bit 9-axis quaternion + heading accuracy)

  // Enable the DMP Game Rotation Vector sensor (Quat6)
  success &= (icm20948.enableDMPSensor(INV_ICM20948_SENSOR_GAME_ROTATION_VECTOR) == ICM_20948_Stat_Ok);

  // Enable additional sensors / features
  // success &= (icm20948.enableDMPSensor(INV_ICM20948_SENSOR_RAW_GYROSCOPE) == ICM_20948_Stat_Ok);
  //success &= (icm20948.enableDMPSensor(INV_ICM20948_SENSOR_RAW_ACCELEROMETER) == ICM_20948_Stat_Ok);
  // success &= (icm20948.enableDMPSensor(INV_ICM20948_SENSOR_MAGNETIC_FIELD_UNCALIBRATED) == ICM_20948_Stat_Ok);

  // Configuring DMP to output data at multiple ODRs:
  // DMP is capable of outputting multiple sensor data at different rates to FIFO.
  // Setting value can be calculated as follows:
  // Value = (DMP running rate / ODR ) - 1
  // E.g. For a 225Hz ODR rate when DMP is running at 225Hz, value = (225/225) - 1 = 0.
  success &= (icm20948.setDMPODRrate(DMP_ODR_Reg_Quat6, 0) == ICM_20948_Stat_Ok); // Set to 225Hz
  //success &= (icm20948.setDMPODRrate(DMP_ODR_Reg_Accel, 0) == ICM_20948_Stat_Ok); // Set to 225Hz
  // success &= (icm20948.setDMPODRrate(DMP_ODR_Reg_Gyro, 0) == ICM_20948_Stat_Ok); // Set to 225Hz
  // success &= (icm20948.setDMPODRrate(DMP_ODR_Reg_Cpass, 0) == ICM_20948_Stat_Ok); // Set to 225Hz

  // Enable the FIFO
  success &= (icm20948.enableFIFO() == ICM_20948_Stat_Ok);

  // Enable the DMP
  success &= (icm20948.enableDMP() == ICM_20948_Stat_Ok);

  // Reset DMP
  success &= (icm20948.resetDMP() == ICM_20948_Stat_Ok);

  // Reset FIFO
  success &= (icm20948.resetFIFO() == ICM_20948_Stat_Ok);

  // Check success
  if (success)
  {
    Serial.println(F("DMP enabled!"));
  }
  else
  {
    Serial.println(F("Enable DMP failed!"));
    while (1)
      ; // Do nothing more
  }
}

double roll=0, pitch=0, yaw=0;
uint16_t droll=0,dpitch=0,dyaw=0 ;


void loop()
{
  // put your main code here, to run repeatedly:
  icm_20948_DMP_data_t data;
  icm20948.readDMPdataFromFIFO(&data);
  if ((icm20948.status == ICM_20948_Stat_Ok) || (icm20948.status == ICM_20948_Stat_FIFOMoreDataAvail)) // Was valid data available?
  {
    if ((data.header & DMP_header_bitmap_Quat6) > 0) // Check for GRV data (Quat6)
    {
      double q1 = ((double)data.Quat6.Data.Q1) / 1073741824.0; // Convert to double. Divide by 2^30
      double q2 = ((double)data.Quat6.Data.Q2) / 1073741824.0; // Convert to double. Divide by 2^30
      double q3 = ((double)data.Quat6.Data.Q3) / 1073741824.0; // Convert to double. Divide by 2^30

      double q0 = sqrt(1.0 - ((q1 * q1) + (q2 * q2) + (q3 * q3)));

      double q2sqr = q2 * q2;

      // roll (x-axis rotation)
      double t0 = +2.0 * (q0 * q1 + q2 * q3);
      double t1 = +1.0 - 2.0 * (q1 * q1 + q2sqr);
      double roll = atan2(t0, t1) * 180.0 / PI;

      // pitch (y-axis rotation)
      double t2 = +2.0 * (q0 * q2 - q3 * q1);
      t2 = t2 > 1.0 ? 1.0 : t2;
      t2 = t2 < -1.0 ? -1.0 : t2;
      double pitch = asin(t2) * 180.0 / PI;

      // yaw (z-axis rotation)
      double t3 = +2.0 * (q0 * q3 + q1 * q2);
      double t4 = +1.0 - 2.0 * (q2sqr + q3 * q3);
      double yaw = atan2(t3, t4) * 180.0 / PI;
    
      droll=roll ;
      dpitch=pitch ;
      dyaw=yaw ;
    
    }

    if ((data.header & DMP_header_bitmap_Accel) > 0) // Check for Accel
    {
      float acc_x = (float)data.Raw_Accel.Data.X; // Extract the raw accelerometer data
      float acc_y = (float)data.Raw_Accel.Data.Y;
      float acc_z = (float)data.Raw_Accel.Data.Z;
    }
  }
  if (icm20948.status != ICM_20948_Stat_FIFOMoreDataAvail) // If more data is available then we should read it right away - and not delay
  {
    delay(10);
  }
}

uint16_t command = 0;

void receiveEvent(int howMany)
{
  if (slaveI2C.available() < 1)
    return;
  uint8_t c = slaveI2C.read();
  if (c < 2)
    command = c;
  else
    command = 0;
}

void requestEvent()
{
  switch (command)
  {
  case 0:
    slaveI2C.write(0);
    break;
  case 1:
  {
    slaveI2C.write((uint8_t*)&dyaw,2) ;
    slaveI2C.write((uint8_t*)&dpitch,2) ;
    slaveI2C.write((uint8_t*)&droll,2) ;
  }
  }

}

// initializeDMP is a weak function. Let's overwrite it so we can increase the sample rate
ICM_20948_Status_e ICM_20948::initializeDMP(void)
{
  // The ICM-20948 is awake and ready but hasn't been configured. Let's step through the configuration
  // sequence from InvenSense's _confidential_ Application Note "Programming Sequence for DMP Hardware Functions".

  ICM_20948_Status_e result = ICM_20948_Stat_Ok; // Use result and worstResult to show if the configuration was successful
  ICM_20948_Status_e worstResult = ICM_20948_Stat_Ok;

  // Normally, when the DMP is not enabled, startupMagnetometer (called by startupDefault, which is called by begin) configures the AK09916 magnetometer
  // to run at 100Hz by setting the CNTL2 register (0x31) to 0x08. Then the ICM20948's I2C_SLV0 is configured to read
  // nine bytes from the mag every sample, starting from the STATUS1 register (0x10). ST1 includes the DRDY (Data Ready) bit.
  // Next are the six magnetometer readings (little endian). After a dummy byte, the STATUS2 register (0x18) contains the HOFL (Overflow) bit.
  //
  // But looking very closely at the InvenSense example code, we can see in inv_icm20948_resume_akm (in Icm20948AuxCompassAkm.c) that,
  // when the DMP is running, the magnetometer is set to Single Measurement (SM) mode and that ten bytes are read, starting from the reserved
  // RSV2 register (0x03). The datasheet does not define what registers 0x04 to 0x0C contain. There is definitely some secret sauce in here...
  // The magnetometer data appears to be big endian (not little endian like the HX/Y/Z registers) and starts at register 0x04.
  // We had to examine the I2C traffic between the master and the AK09916 on the AUX_DA and AUX_CL pins to discover this...
  //
  // So, we need to set up I2C_SLV0 to do the ten byte reading. The parameters passed to i2cControllerConfigurePeripheral are:
  // 0: use I2C_SLV0
  // MAG_AK09916_I2C_ADDR: the I2C address of the AK09916 magnetometer (0x0C unshifted)
  // AK09916_REG_RSV2: we start reading here (0x03). Secret sauce...
  // 10: we read 10 bytes each cycle
  // true: set the I2C_SLV0_RNW ReadNotWrite bit so we read the 10 bytes (not write them)
  // true: set the I2C_SLV0_CTRL I2C_SLV0_EN bit to enable reading from the peripheral at the sample rate
  // false: clear the I2C_SLV0_CTRL I2C_SLV0_REG_DIS (we want to write the register value)
  // true: set the I2C_SLV0_CTRL I2C_SLV0_GRP bit to show the register pairing starts at byte 1+2 (copied from inv_icm20948_resume_akm)
  // true: set the I2C_SLV0_CTRL I2C_SLV0_BYTE_SW to byte-swap the data from the mag (copied from inv_icm20948_resume_akm)
  result = i2cControllerConfigurePeripheral(0, MAG_AK09916_I2C_ADDR, AK09916_REG_RSV2, 10, true, true, false, true, true);
  if (result > worstResult)
    worstResult = result;
  //
  // We also need to set up I2C_SLV1 to do the Single Measurement triggering:
  // 1: use I2C_SLV1
  // MAG_AK09916_I2C_ADDR: the I2C address of the AK09916 magnetometer (0x0C unshifted)
  // AK09916_REG_CNTL2: we start writing here (0x31)
  // 1: not sure why, but the write does not happen if this is set to zero
  // false: clear the I2C_SLV0_RNW ReadNotWrite bit so we write the dataOut byte
  // true: set the I2C_SLV0_CTRL I2C_SLV0_EN bit. Not sure why, but the write does not happen if this is clear
  // false: clear the I2C_SLV0_CTRL I2C_SLV0_REG_DIS (we want to write the register value)
  // false: clear the I2C_SLV0_CTRL I2C_SLV0_GRP bit
  // false: clear the I2C_SLV0_CTRL I2C_SLV0_BYTE_SW bit
  // AK09916_mode_single: tell I2C_SLV1 to write the Single Measurement command each sample
  result = i2cControllerConfigurePeripheral(1, MAG_AK09916_I2C_ADDR, AK09916_REG_CNTL2, 1, false, true, false, false, false, AK09916_mode_single);
  if (result > worstResult)
    worstResult = result;

  // Set the I2C Master ODR configuration
  // It is not clear why we need to do this... But it appears to be essential! From the datasheet:
  // "I2C_MST_ODR_CONFIG[3:0]: ODR configuration for external sensor when gyroscope and accelerometer are disabled.
  //  ODR is computed as follows: 1.1 kHz/(2^((odr_config[3:0])) )
  //  When gyroscope is enabled, all sensors (including I2C_MASTER) use the gyroscope ODR.
  //  If gyroscope is disabled, then all sensors (including I2C_MASTER) use the accelerometer ODR."
  // Since both gyro and accel are running, setting this register should have no effect. But it does. Maybe because the Gyro and Accel are placed in Low Power Mode (cycled)?
  // You can see by monitoring the Aux I2C pins that the next three lines reduce the bus traffic (magnetometer reads) from 1125Hz to the chosen rate: 68.75Hz in this case.
  result = setBank(3);
  if (result > worstResult)
    worstResult = result;      // Select Bank 3
  uint8_t mstODRconfig = 0x04; // Set the ODR configuration to 1100/2^4 = 68.75Hz
  result = write(AGB3_REG_I2C_MST_ODR_CONFIG, &mstODRconfig, 1);
  if (result > worstResult)
    worstResult = result; // Write one byte to the I2C_MST_ODR_CONFIG register

  // Configure clock source through PWR_MGMT_1
  // ICM_20948_Clock_Auto selects the best available clock source – PLL if ready, else use the Internal oscillator
  result = setClockSource(ICM_20948_Clock_Auto);
  if (result > worstResult)
    worstResult = result; // This is shorthand: success will be set to false if setClockSource fails

  // Enable accel and gyro sensors through PWR_MGMT_2
  // Enable Accelerometer (all axes) and Gyroscope (all axes) by writing zero to PWR_MGMT_2
  result = setBank(0);
  if (result > worstResult)
    worstResult = result;  // Select Bank 0
  uint8_t pwrMgmt2 = 0x40; // Set the reserved bit 6 (pressure sensor disable?)
  result = write(AGB0_REG_PWR_MGMT_2, &pwrMgmt2, 1);
  if (result > worstResult)
    worstResult = result; // Write one byte to the PWR_MGMT_2 register

  // Place _only_ I2C_Master in Low Power Mode (cycled) via LP_CONFIG
  // The InvenSense Nucleo example initially puts the accel and gyro into low power mode too, but then later updates LP_CONFIG so only the I2C_Master is in Low Power Mode
  result = setSampleMode(ICM_20948_Internal_Mst, ICM_20948_Sample_Mode_Cycled);
  if (result > worstResult)
    worstResult = result;

  // Disable the FIFO
  result = enableFIFO(false);
  if (result > worstResult)
    worstResult = result;

  // Disable the DMP
  result = enableDMP(false);
  if (result > worstResult)
    worstResult = result;

  // Set Gyro FSR (Full scale range) to 2000dps through GYRO_CONFIG_1
  // Set Accel FSR (Full scale range) to 4g through ACCEL_CONFIG
  ICM_20948_fss_t myFSS; // This uses a "Full Scale Settings" structure that can contain values for all configurable sensors
  myFSS.a = gpm4;        // (ICM_20948_ACCEL_CONFIG_FS_SEL_e)
                         // gpm2
                         // gpm4
                         // gpm8
                         // gpm16
  myFSS.g = dps2000;     // (ICM_20948_GYRO_CONFIG_1_FS_SEL_e)
                         // dps250
                         // dps500
                         // dps1000
                         // dps2000
  result = setFullScale((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), myFSS);
  if (result > worstResult)
    worstResult = result;

  // The InvenSense Nucleo code also enables the gyro DLPF (but leaves GYRO_DLPFCFG set to zero = 196.6Hz (3dB))
  // We found this by going through the SPI data generated by ZaneL's Teensy-ICM-20948 library byte by byte...
  // The gyro DLPF is enabled by default (GYRO_CONFIG_1 = 0x01) so the following line should have no effect, but we'll include it anyway
  result = enableDLPF(ICM_20948_Internal_Gyr, true);
  if (result > worstResult)
    worstResult = result;

  // Enable interrupt for FIFO overflow from FIFOs through INT_ENABLE_2
  // If we see this interrupt, we'll need to reset the FIFO
  // result = intEnableOverflowFIFO( 0x1F ); if (result > worstResult) worstResult = result; // Enable the interrupt on all FIFOs

  // Turn off what goes into the FIFO through FIFO_EN_1, FIFO_EN_2
  // Stop the peripheral data from being written to the FIFO by writing zero to FIFO_EN_1
  result = setBank(0);
  if (result > worstResult)
    worstResult = result; // Select Bank 0
  uint8_t zero = 0;
  result = write(AGB0_REG_FIFO_EN_1, &zero, 1);
  if (result > worstResult)
    worstResult = result;
  // Stop the accelerometer, gyro and temperature data from being written to the FIFO by writing zero to FIFO_EN_2
  result = write(AGB0_REG_FIFO_EN_2, &zero, 1);
  if (result > worstResult)
    worstResult = result;

  // Turn off data ready interrupt through INT_ENABLE_1
  result = intEnableRawDataReady(false);
  if (result > worstResult)
    worstResult = result;

  // Reset FIFO through FIFO_RST
  result = resetFIFO();
  if (result > worstResult)
    worstResult = result;

  // Set gyro sample rate divider with GYRO_SMPLRT_DIV
  // Set accel sample rate divider with ACCEL_SMPLRT_DIV_2
  ICM_20948_smplrt_t mySmplrt;
  // mySmplrt.g = 19; // ODR is computed as follows: 1.1 kHz/(1+GYRO_SMPLRT_DIV[7:0]). 19 = 55Hz. InvenSense Nucleo example uses 19 (0x13).
  // mySmplrt.a = 19; // ODR is computed as follows: 1.125 kHz/(1+ACCEL_SMPLRT_DIV[11:0]). 19 = 56.25Hz. InvenSense Nucleo example uses 19 (0x13).
  mySmplrt.g = 4; // 225Hz
  mySmplrt.a = 4; // 225Hz
  // mySmplrt.g = 8; // 112Hz
  // mySmplrt.a = 8; // 112Hz
  result = setSampleRate((ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr), mySmplrt);
  if (result > worstResult)
    worstResult = result;

  // Setup DMP start address through PRGM_STRT_ADDRH/PRGM_STRT_ADDRL
  result = setDMPstartAddress();
  if (result > worstResult)
    worstResult = result; // Defaults to DMP_START_ADDRESS

  // Now load the DMP firmware
  result = loadDMPFirmware();
  if (result > worstResult)
    worstResult = result;

  // Write the 2 byte Firmware Start Value to ICM PRGM_STRT_ADDRH/PRGM_STRT_ADDRL
  result = setDMPstartAddress();
  if (result > worstResult)
    worstResult = result; // Defaults to DMP_START_ADDRESS

  // Set the Hardware Fix Disable register to 0x48
  result = setBank(0);
  if (result > worstResult)
    worstResult = result; // Select Bank 0
  uint8_t fix = 0x48;
  result = write(AGB0_REG_HW_FIX_DISABLE, &fix, 1);
  if (result > worstResult)
    worstResult = result;

  // Set the Single FIFO Priority Select register to 0xE4
  result = setBank(0);
  if (result > worstResult)
    worstResult = result; // Select Bank 0
  uint8_t fifoPrio = 0xE4;
  result = write(AGB0_REG_SINGLE_FIFO_PRIORITY_SEL, &fifoPrio, 1);
  if (result > worstResult)
    worstResult = result;

  // Configure Accel scaling to DMP
  // The DMP scales accel raw data internally to align 1g as 2^25
  // In order to align internal accel raw data 2^25 = 1g write 0x04000000 when FSR is 4g
  const unsigned char accScale[4] = {0x04, 0x00, 0x00, 0x00};
  result = writeDMPmems(ACC_SCALE, 4, &accScale[0]);
  if (result > worstResult)
    worstResult = result; // Write accScale to ACC_SCALE DMP register
  // In order to output hardware unit data as configured FSR write 0x00040000 when FSR is 4g
  const unsigned char accScale2[4] = {0x00, 0x04, 0x00, 0x00};
  result = writeDMPmems(ACC_SCALE2, 4, &accScale2[0]);
  if (result > worstResult)
    worstResult = result; // Write accScale2 to ACC_SCALE2 DMP register

  // Configure Compass mount matrix and scale to DMP
  // The mount matrix write to DMP register is used to align the compass axes with accel/gyro.
  // This mechanism is also used to convert hardware unit to uT. The value is expressed as 1uT = 2^30.
  // Each compass axis will be converted as below:
  // X = raw_x * CPASS_MTX_00 + raw_y * CPASS_MTX_01 + raw_z * CPASS_MTX_02
  // Y = raw_x * CPASS_MTX_10 + raw_y * CPASS_MTX_11 + raw_z * CPASS_MTX_12
  // Z = raw_x * CPASS_MTX_20 + raw_y * CPASS_MTX_21 + raw_z * CPASS_MTX_22
  // The AK09916 produces a 16-bit signed output in the range +/-32752 corresponding to +/-4912uT. 1uT = 6.66 ADU.
  // 2^30 / 6.66666 = 161061273 = 0x9999999
  const unsigned char mountMultiplierZero[4] = {0x00, 0x00, 0x00, 0x00};
  const unsigned char mountMultiplierPlus[4] = {0x09, 0x99, 0x99, 0x99};  // Value taken from InvenSense Nucleo example
  const unsigned char mountMultiplierMinus[4] = {0xF6, 0x66, 0x66, 0x67}; // Value taken from InvenSense Nucleo example
  result = writeDMPmems(CPASS_MTX_00, 4, &mountMultiplierPlus[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(CPASS_MTX_01, 4, &mountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(CPASS_MTX_02, 4, &mountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(CPASS_MTX_10, 4, &mountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(CPASS_MTX_11, 4, &mountMultiplierMinus[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(CPASS_MTX_12, 4, &mountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(CPASS_MTX_20, 4, &mountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(CPASS_MTX_21, 4, &mountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(CPASS_MTX_22, 4, &mountMultiplierMinus[0]);
  if (result > worstResult)
    worstResult = result;

  // Configure the B2S Mounting Matrix
  const unsigned char b2sMountMultiplierZero[4] = {0x00, 0x00, 0x00, 0x00};
  const unsigned char b2sMountMultiplierPlus[4] = {0x40, 0x00, 0x00, 0x00}; // Value taken from InvenSense Nucleo example
  result = writeDMPmems(B2S_MTX_00, 4, &b2sMountMultiplierPlus[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(B2S_MTX_01, 4, &b2sMountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(B2S_MTX_02, 4, &b2sMountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(B2S_MTX_10, 4, &b2sMountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(B2S_MTX_11, 4, &b2sMountMultiplierPlus[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(B2S_MTX_12, 4, &b2sMountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(B2S_MTX_20, 4, &b2sMountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(B2S_MTX_21, 4, &b2sMountMultiplierZero[0]);
  if (result > worstResult)
    worstResult = result;
  result = writeDMPmems(B2S_MTX_22, 4, &b2sMountMultiplierPlus[0]);
  if (result > worstResult)
    worstResult = result;

  // Configure the DMP Gyro Scaling Factor
  // @param[in] gyro_div Value written to GYRO_SMPLRT_DIV register, where
  //            0=1125Hz sample rate, 1=562.5Hz sample rate, ... 4=225Hz sample rate, ...
  //            10=102.2727Hz sample rate, ... etc.
  // @param[in] gyro_level 0=250 dps, 1=500 dps, 2=1000 dps, 3=2000 dps
  result = setGyroSF(4, 3);
  if (result > worstResult)
    worstResult = result; // 4 = 225Hz (see above), 3 = 2000dps (see above)

  // Configure the Gyro full scale
  // 2000dps : 2^28
  // 1000dps : 2^27
  //  500dps : 2^26
  //  250dps : 2^25
  const unsigned char gyroFullScale[4] = {0x10, 0x00, 0x00, 0x00}; // 2000dps : 2^28
  result = writeDMPmems(GYRO_FULLSCALE, 4, &gyroFullScale[0]);
  if (result > worstResult)
    worstResult = result;

  // Configure the Accel Only Gain: 15252014 (225Hz) 30504029 (112Hz) 61117001 (56Hz)
  // const unsigned char accelOnlyGain[4] = {0x03, 0xA4, 0x92, 0x49}; // 56Hz
  const unsigned char accelOnlyGain[4] = {0x00, 0xE8, 0xBA, 0x2E}; // 225Hz
  // const unsigned char accelOnlyGain[4] = {0x01, 0xD1, 0x74, 0x5D}; // 112Hz
  result = writeDMPmems(ACCEL_ONLY_GAIN, 4, &accelOnlyGain[0]);
  if (result > worstResult)
    worstResult = result;

  // Configure the Accel Alpha Var: 1026019965 (225Hz) 977872018 (112Hz) 882002213 (56Hz)
  // const unsigned char accelAlphaVar[4] = {0x34, 0x92, 0x49, 0x25}; // 56Hz
  const unsigned char accelAlphaVar[4] = {0x3D, 0x27, 0xD2, 0x7D}; // 225Hz
  // const unsigned char accelAlphaVar[4] = {0x3A, 0x49, 0x24, 0x92}; // 112Hz
  result = writeDMPmems(ACCEL_ALPHA_VAR, 4, &accelAlphaVar[0]);
  if (result > worstResult)
    worstResult = result;

  // Configure the Accel A Var: 47721859 (225Hz) 95869806 (112Hz) 191739611 (56Hz)
  // const unsigned char accelAVar[4] = {0x0B, 0x6D, 0xB6, 0xDB}; // 56Hz
  const unsigned char accelAVar[4] = {0x02, 0xD8, 0x2D, 0x83}; // 225Hz
  // const unsigned char accelAVar[4] = {0x05, 0xB6, 0xDB, 0x6E}; // 112Hz
  result = writeDMPmems(ACCEL_A_VAR, 4, &accelAVar[0]);
  if (result > worstResult)
    worstResult = result;

  // Configure the Accel Cal Rate
  const unsigned char accelCalRate[4] = {0x00, 0x00}; // Value taken from InvenSense Nucleo example
  result = writeDMPmems(ACCEL_CAL_RATE, 2, &accelCalRate[0]);
  if (result > worstResult)
    worstResult = result;

  // Configure the Compass Time Buffer. The I2C Master ODR Configuration (see above) sets the magnetometer read rate to 68.75Hz.
  // Let's set the Compass Time Buffer to 69 (Hz).
  const unsigned char compassRate[2] = {0x00, 0x45}; // 69Hz
  result = writeDMPmems(CPASS_TIME_BUFFER, 2, &compassRate[0]);
  if (result > worstResult)
    worstResult = result;

  // Enable DMP interrupt
  // This would be the most efficient way of getting the DMP data, instead of polling the FIFO
  // result = intEnableDMP(true); if (result > worstResult) worstResult = result;

  return worstResult;
}


#19 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 6 766 messages
  • Gender:Male

Posté 03 janvier 2022 - 09:08

A partir de la vidéo n°21, Paul McWhoerter explique comment utiliser les quaternions.

Bon courage !  :unsure:

 



#20 dakota99

dakota99

    Habitué

  • Membres
  • PipPip
  • 228 messages
  • Gender:Male

Posté 03 janvier 2022 - 09:14

Bonsoir Sandro et Quadmean,

Merci pour vos réponses et votre temps...

 

- Je n'ai pas de boussole sous la main mais j'ai testé avec 2 téléphones. Le cap magnétique est parfaitement stable. Peut-être pas juste car il y a une différence de 20° entre les 2 téléphones.

- Quand je tourne le téléphone de 90° vers la droite le cap magnétique de 214° devient 300° au lieu de 304°, encore de 90° le cap devient 021 au lieu de 034°. Donc le téléphone n'est pas précis non plus mais la lecture est parfaitement stable. Si je remets le téléphone dans sa position initiale, je reviens à la lecture de 214° et après 5 minutes il affiche 212°

 

J'ai fait plusieurs essais avec le BNO055 et l'ICM20948 et c'est apparemment pour le moment l'ICM20948 qui donne des valeurs plus stables (autrement dit qui m'énerve le moins :) ).

Je suis parti de l'exemple fourni avec la librairie pensant que si le fabricant le propose c'est que ça doit fonctionner. J'ai ajouté le calcul du cap magnétique puisque c'est cela qui m'intéresse.

Quand l'ICM20948 est placé dans le même sens que le téléphone (mais retourné composants sur la table car ses axes X et Y sont différents par rapport au BNO055)  il indique des valeurs comprises entre -174.52 et -177.12

S'il est tourné à 90° il donne des valeurs comprises entre -139.29 et -152.63. :crazy:

Ci-dessous le code qui ne fait pas appel aux DMP et Quaternions (dont j'ignorais l'existence jusqu'à présent)

// Basic demo for accelerometer readings from Adafruit ICM20948

#include <Adafruit_ICM20X.h>
#include <Adafruit_ICM20948.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Adafruit_ICM20948 icm;
uint16_t measurement_delay_us = 65535; // Delay between measurements for testing
// For SPI mode, we need a CS pin
#define ICM_CS 10
// For software-SPI mode we need SCK/MOSI/MISO pins
#define ICM_SCK 13
#define ICM_MISO 12
#define ICM_MOSI 11
float Xm;
float Ym;
float psi;

void setup(void) {
  Serial.begin(115200);
  while (!Serial)
    delay(10); // will pause Zero, Leonardo, etc until serial console opens
  Serial.println("Adafruit ICM20948 test!");
  // Try to initialize!
  if (!icm.begin_I2C()) {
    // if (!icm.begin_SPI(ICM_CS)) {
    // if (!icm.begin_SPI(ICM_CS, ICM_SCK, ICM_MISO, ICM_MOSI)) {

    Serial.println("Failed to find ICM20948 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("ICM20948 Found!");
  icm.setAccelRange(ICM20948_ACCEL_RANGE_16_G);
  icm.setGyroRange(ICM20948_GYRO_RANGE_2000_DPS);
  icm.setAccelRateDivisor(4095);
  uint16_t accel_divisor = icm.getAccelRateDivisor();
  float accel_rate = 1125 / (1.0 + accel_divisor);
  icm.setGyroRateDivisor(255);
  uint8_t gyro_divisor = icm.getGyroRateDivisor();
  float gyro_rate = 1100 / (1.0 + gyro_divisor);
  icm.setMagDataRate(AK09916_MAG_DATARATE_10_HZ);
}

void loop() {
  sensors_event_t accel;
  sensors_event_t gyro;
  sensors_event_t mag;
  sensors_event_t temp;
  icm.getEvent(&accel, &gyro, &temp, &mag);
  Xm = mag.magnetic.x;
  Ym = mag.magnetic.y;
  psi = atan2(Ym,Xm);
  psi = psi * 180 / 3.1415;
  Serial.print("psi : ");
  Serial.println(psi);
  delay(1000);
}

 

 

 

 

 

 

 

Image(s) jointe(s)

  • IMG_20220103_204532.jpg




Répondre à ce sujet



  


0 utilisateur(s) li(sen)t ce sujet

0 members, 0 guests, 0 anonymous users