GY-521 (MPU-6050) utilisation des données brute pour obtenir une position

Bonjour,

dans le cadre d'un projet de localisation intérieur, je suis contraint d'utiliser un IMU. J'ai donc choisit un MPU 6050 monté sur la carte Gy-521.

Via l'IDE d'arduino je transorme les six données brutes en une valeur un peu plus parlante à savoir la force en g et la vitesse de rotation en °/s.

Pour cela je divise simplement les valeurs fourni par L'accéléromètre par 16384( LSB sensitivity valeur fourni dans la datasheet pour une echelle de +-2g) et celle fourni par le gyromètre par 131 ( fournie par la datasheet de meme pour une echelle de +-250°/s).

J'envoie les données via le port série sur matlab qui devrait faire le process me permettant d'obtenir les coordonnées spatiale xyz de mon imu.

Maintenant comment dois je faire pour obtenir ces coordonnées? (ok pour double intégration temporelle pour l'accélération avec constante V0 et x0) (ok pour une simple intégration temporelle pour obtenir l'angle de rotation ).

Meme si je ne suis aps sur de bien faire les intégrations, une fois que j'ai disons la distance parcouru ro=racine carré (x2+y2+z2), et le tétaX, tétaY, tétaZ, que dois je faire?

Merci à ceux qui tenterons de m'aider, j'espère avoir été claire sur mes attentes.

Cordialement.

Bonjour,

A priori, ce n'est pas une bonne idée d'utiliser ce genre d'IMU pour faire de la localisation. Les dérives du capteur plus le bruit intégrés une ou deux fois, au bout de 10 minutes tu vas te croire à la Guadeloupe !

Bon j'exagère, bien sûr, mais il faut quand même utiliser une autre technique: que veux-tu localiser au juste ?

Oui je sais que je ne peux pas l'utiliser toute seule!

En gros j'ai une caméra au plafond qui me fourni les coordonnées xy, et j'ai un capteur de distance qui vient faire echo sur le sol qui me fourni le z! Et je comptais venir initialisé mon IMU avec les valeurs fourni par cette localisation pour qu'il prenne le relais pendant maximum une minute ( donc je ne pense pas que les erreurs de dérive soit trop importante bien qu'au final je n'ai pas encore pu faire un test expérimental pour mesurer le temps au bout duquel les erreurs de dérive sont trop importante.

Je veux localiser un objet "volant" (meme si tenu par un utilisateur).

1 minute me semble beaucoup mais bon, il faut essayer.
Il y a cependant un autre problème avec le MPU6050: il n'y a pas de magnétomètre intégré. Hors, sans magnétomètre, tu ne sais pas dans quelle direction pointe ton engin initialement.
Tu as peut-être un magnétomètre séparé ?

A part ça, il existe des exemples de code sur Arduino qui te permettent de savoir comment ton objet est orienté dans l'espace, à partir des mesures qui sortent de l'accéléro et du gyro (le mot-clé est "AHRS").
Attention: quand je dis "orienté dans l'espace", c'est dans un repère qui dépend de l'orientation initiale, pas dans le repère absolu de la pièce. Sauf si tu as un moyen de faire le lien entre les deux (magnétomètre ou caméra).

Oui je vois mais en y réfléchissant, ça peut meme etre seulement 20,30seconde.

Le prototype en question sera développé dans un atelier d'entreprise environnement très perturbant ou la mesure du champ magnétique sera perturbé.

Je n'ai pas compris pour l'orientation. Pour moi, le gyroscope me fournit une données sur la direction de l'imu et l'accéléromètre une données sur la distance parcouru. n'est il pas suffisant pour connaitre tout ce dont j'ai besoin?

Non, parce que l'orientation de l'engin dans l'espace n'est pas toujours la même. Donc il faut faire les intégrations dans un repère qui change en permanence, c'est la raison pour laquelle tu a besoin de connaître en permanence l'orientation de ce repère par rapport à un repère fixe. C'est l'objet des algorithmes de l'AHRS.

Si tu veux mener ton projet à bien, il faut que tu creuses un peu (si ce n'est beaucoup) la théorie de tout ça.

Oui oui je me doute, je commence à être fatigué de bouffé de la doc en anglais ^^ J'ai un peu regardé hier, j'ai pu réussir à me rendre compte que j'étais completement dépasser en maths !

Je ne baisse pas les bras pour autant !

Bon jai lu quelque documentation rien de très intéressant mise a part un cour sur les matrices de rotations.

Voila comment je penser procédé dites moi ce que vous en pensez si cela ne vous gene pas.

J'ai (ax,ay,az) j'intègre une première fois en rajoutant une constante V0 (vitesse initial nulle au départ), puis une seconde fois avec x0 la position du centre de l'origine de mon repère locale (repère définie par les axe xyz de mon IMU (donc en mouvement).

J'obtient ainsi le déplacement selon x,y,z, si je les additionne l'un l'autre selon la direction de laxe ( par exemple si x=2cm y=5cm et z=1cm, j'avance de 1cm selon l'axe z, je me decale de 2cm selon un axe fictif parallèle a x et de meme pour y. J'obtiens ainsi la position du nouveau centre O' du nouveau repère x'y'z'.

A ce stage il me reste juste à définir dans quel direction pointe x'y'z', c'est la que je ferais intervenir le gyroscope. En intégrant la vitesse de rotations , j'obtiendrais l'angle de déviation de chaque axe. ceci me permettra via quelque calcul géométrique plus ou moins compliqué que je n'ai pas encore parfaitement identifier, de connaitre la nouvelle position l'axe x' y' et z'.

J'aurais donc la position du centre de mon repère ainsi que la direction dans laquelle pointe mon nouveau repère.

Cela vous parrait il correcte?

Personnellement, j'aurais plutôt repris un algo AHRS (il y a des versions toutes faites pour Arduino) afin de connaître l'orientation de mes axes. Il ne reste plus alors qu'à faire les projections des accélérations dans le repère inertiel et de les intégrer.
J'aurais fait ça pour limiter les erreurs qui ne vont pas manquer de survenir si tu refais tout à partir de zéro.

Mais avant toute chose, je m'assurerais aussi que ce que je vais faire a un sens. Or, pour l'instant, il ne me semble pas que tu aies résolu le problème de la détermination de l'orientation de ton engin à l'instant initial.

Ta problématique est intéressante, j'y avais pas mal réfléchi mais ça demande pas mal de maths:

Tu peux télécharger les souces ici: Open source IMU and AHRS algorithms – x-io Technologies
(en bas dans download)

Il te faut un gyroscope/accéléromètre/magnétomètre, y a des packages tout en un comme le mpu9250:
https://www.invensense.com/products/motion-tracking/9-axis/mpu-9250/

Ou bien tu peux utiliser par exemple un mpu6050 avec un hmc5883l.

Souvent ce qui est fait c'est de changer le firmware du mpu6050 (ou 9250) pour qu'il compile les données (gyro/accel/magneto) directement en quaternions ou en pitch/roll/yaw, que tu récupère en i2c, il faut des outils spécifiques pour faire ça.

Si tu veux tester j'utilise le bno055 (gyro + magnéto) dans ma bibliothèque (module), il a l'avantage de sortir en i2c des quaternions et des données pitch/roll/yaw directement: Smart Sensor BNO055 | Bosch Sensortec

Les 2 seuls gros soucis de ce gyro c'est qu'il a une fréquence de rafraîchissement des données fusionnées pas assez rapides (100Hz voir moins), et qu'il a non pas une dérive, mais une déviation des valeurs pitch/roll quand le gyro translate de façon continue, voir mon test ici avec ce gyro:

A partir de 3min12, et surtout à 3min16 on voit bien que le pitch du gyro n'est plus tout à fait avec le zéro (gravité de la terre).

Bref, voila en gros quelques idées pour faire ce que tu demandes.

sylvainmahe:
Souvent ce qui est fait c'est de changer le firmware du mpu6050 (ou 9250) pour qu'il compile les données (gyro/accel/magneto) directement en quaternions ou en pitch/roll/yaw, que tu récupère en i2c, il faut des outils spécifiques pour faire ça.

Non, pas besoin de changer le firmware avec le MPU6050 ou le MPU9250, les quaternions et pitch/roll/yaw peuvent être récupérés en standard.

Ok, mais comment? Car le datasheet ne mentionne absolument pas ceci.

Ces calculs sont effectués par le DMP (Digital Motion Processor) de ces deux capteurs.
Les bibliothèques Arduino qui existent pour chacun (i2cdevlib/Arduino/MPU6050 at master · jrowberg/i2cdevlib · GitHub et GitHub - sparkfun/MPU-9250_Breakout) permettent de récupérer ces infos facilement.

J'avais vue cela effectivement, mais les articles que j'avais lu parlaient de modifier le firmware du bordel pour pouvoir récupérer en i2c ces infos.

Je viens de lire ce que tu as posté, apparemment les calculs pitch/roll/yaw sont bien effectués dans la bibliothèque, et non pas dans le DMP, voir par exemple ici:

uint8_t MPU6050::dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity) {
    // yaw: (about Z axis)
    data[0] = atan2(2*q -> x*q -> y - 2*q -> w*q -> z, 2*q -> w*q -> w + 2*q -> x*q -> x - 1);
    // pitch: (nose up/down, about Y axis)
    data[1] = atan(gravity -> x / sqrt(gravity -> y*gravity -> y + gravity -> z*gravity -> z));
    // roll: (tilt left/right, about X axis)
    data[2] = atan(gravity -> y / sqrt(gravity -> x*gravity -> x + gravity -> z*gravity -> z));
    return 0;
}

Et ici le fait qu'il écrit dans la mémoire au démarrage du mpu (a mon avis le bout de programme qui créé ses fameux quaternions):

const unsigned char dmpMemory[MPU6050_DMP_CODE_SIZE] PROGMEM = {

Et encore, j'ai un doute que les quaternions soient faits dans le DMP:

class Quaternion {
    public:
        float w;
        float x;
        float y;
        float z;
        
        Quaternion() {
            w = 1.0f;
            x = 0.0f;
            y = 0.0f;
            z = 0.0f;
        }
        
        Quaternion(float nw, float nx, float ny, float nz) {
            w = nw;
            x = nx;
            y = ny;
            z = nz;
        }

        Quaternion getProduct(Quaternion q) {
            // Quaternion multiplication is defined by:
            //     (Q1 * Q2).w = (w1w2 - x1x2 - y1y2 - z1z2)
            //     (Q1 * Q2).x = (w1x2 + x1w2 + y1z2 - z1y2)
            //     (Q1 * Q2).y = (w1y2 - x1z2 + y1w2 + z1x2)
            //     (Q1 * Q2).z = (w1z2 + x1y2 - y1x2 + z1w2
            return Quaternion(
                w*q.w - x*q.x - y*q.y - z*q.z,  // new w
                w*q.x + x*q.w + y*q.z - z*q.y,  // new x
                w*q.y - x*q.z + y*q.w + z*q.x,  // new y
                w*q.z + x*q.y - y*q.x + z*q.w); // new z
        }

        Quaternion getConjugate() {
            return Quaternion(w, -x, -y, -z);
        }
        
        float getMagnitude() {
            return sqrt(w*w + x*x + y*y + z*z);
        }
        
        void normalize() {
            float m = getMagnitude();
            w /= m;
            x /= m;
            y /= m;
            z /= m;
        }
        
        Quaternion getNormalized() {
            Quaternion r(w, x, y, z);
            r.normalize();
            return r;
        }
};

class VectorInt16 {
    public:
        int16_t x;
        int16_t y;
        int16_t z;

        VectorInt16() {
            x = 0;
            y = 0;
            z = 0;
        }
        
        VectorInt16(int16_t nx, int16_t ny, int16_t nz) {
            x = nx;
            y = ny;
            z = nz;
        }

        float getMagnitude() {
            return sqrt(x*x + y*y + z*z);
        }

        void normalize() {
            float m = getMagnitude();
            x /= m;
            y /= m;
            z /= m;
        }
        
        VectorInt16 getNormalized() {
            VectorInt16 r(x, y, z);
            r.normalize();
            return r;
        }
        
        void rotate(Quaternion *q) {
            // http://www.cprogramming.com/tutorial/3d/quaternions.html
            // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/index.htm
            // http://content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation
            // ^ or: http://webcache.googleusercontent.com/search?q=cache:xgJAp3bDNhQJ:content.gpwiki.org/index.php/OpenGL:Tutorials:Using_Quaternions_to_represent_rotation&hl=en&gl=us&strip=1
        
            // P_out = q * P_in * conj(q)
            // - P_out is the output vector
            // - q is the orientation quaternion
            // - P_in is the input vector (a*aReal)
            // - conj(q) is the conjugate of the orientation quaternion (q=[w,x,y,z], q*=[w,-x,-y,-z])
            Quaternion p(0, x, y, z);

            // quaternion multiplication: q * p, stored back in p
            p = q -> getProduct(p);

            // quaternion multiplication: p * conj(q), stored back in p
            p = p.getProduct(q -> getConjugate());

            // p quaternion is now [0, x', y', z']
            x = p.x;
            y = p.y;
            z = p.z;
        }

        VectorInt16 getRotated(Quaternion *q) {
            VectorInt16 r(x, y, z);
            r.rotate(q);
            return r;
        }
};

class VectorFloat {
    public:
        float x;
        float y;
        float z;

        VectorFloat() {
            x = 0;
            y = 0;
            z = 0;
        }
        
        VectorFloat(float nx, float ny, float nz) {
            x = nx;
            y = ny;
            z = nz;
        }

        float getMagnitude() {
            return sqrt(x*x + y*y + z*z);
        }

        void normalize() {
            float m = getMagnitude();
            x /= m;
            y /= m;
            z /= m;
        }
        
        VectorFloat getNormalized() {
            VectorFloat r(x, y, z);
            r.normalize();
            return r;
        }
        
        void rotate(Quaternion *q) {
            Quaternion p(0, x, y, z);

            // quaternion multiplication: q * p, stored back in p
            p = q -> getProduct(p);

            // quaternion multiplication: p * conj(q), stored back in p
            p = p.getProduct(q -> getConjugate());

            // p quaternion is now [0, x', y', z']
            x = p.x;
            y = p.y;
            z = p.z;
        }

        VectorFloat getRotated(Quaternion *q) {
            VectorFloat r(x, y, z);
            r.rotate(q);
            return r;
        }
};

Car encore une fois le datasheet ne mentionne pas du tout cela, car si c'était le cas j'aurais inclu pitch/roll/yaw à ma classe mpu6050 de mon système: http://sylvainmahe.xyz/howToProgramWidthModule.html
Car c'est quelque chose que pas mal de gens me demandent, mais je ne sais pas faire puisque le datasheet n'indique rien la dessus. J'ai l'impression que cela ressemble à une sorte de "hack" de la mémoire du mpu pour permettre cela.

Tu as raison pour les angles, mais ça (i2cdevlib/MPU6050_6Axis_MotionApps20.h at master · jrowberg/i2cdevlib · GitHub) laisse quand même penser que les quaternions sont calculés par le DMP.

Oui c'est vrai tu as raison. Le code dois donc lui injecter ce qu'il faut dans la mémoire pour qu'il effectue ces quaternions.
Le fait que tout cela n'est pas indiqué dans le datasheet me pose problème, tant sur le plan de la compréhension que le fait que le mpu6050 soit "bricolé" par du code dont on ne comprend rien puisque il est en hexadecimal (il est déjà compilé...).

M'enfin ça à l'air de fonctionner, comment je ne sais pas, mais ça fonctionne...

Merci pour vos infos je vais regarder tout cela.

Par contre il faut garder en tete que je ne peux pas utilisé de magnétomètre.. Etant dans un atelier d'entreprise, ses valeurs seraient trop perturbé pour être exploitable.

Je comptais suivre la procédure suivante:

A partir des angles de rotations fourni par le gyroscope, je comptais trouver les valeurs des trois vecteurs de mon nouveau repère (x'y'z') en multipliant la matrice de rotation R=Rx(teta)Ry(ksi)Rz(phi) par le vecteur xyz.

Une fois le nouveau repère en place, je comptais faire la projection de l'axe z ( l'ancien donc celui ou la gravité s'applique constamment) sur mes trois nouveau axe x'y'z' dont je viendrais soustraire leur valeurs respectivement sur chaque accélération de chaque nouvel axe.

Enfin, je viendrais intégrer deux fois mon accélération pour avoir le déplacement effectué selon chacun des axes.

Je n'ai pas oublié qu'il faut que je connaisse a tout pris l'attitude de départ de mon imu, mais pour l'instant je le placerais dans une position connu au départ de manière a me facilité la tache (j'ai une idée disons gitane de comment trouver cette attitude de départ mais je verrais sa plus tard, si j'arrive à faire fonctionner déja ce joujou qui me rend fou)

J'ai vu que les quaternions était souvent utilisé, mais je ne sais pas quoi en faire, et je n'ai pas trouver de bon document dessus ou du moins je n'ai pas réussi à extirper les informations pertinente sur ce sujet. Si vous arrivez à m'expliquer sa de manière claire et concise, je suis preneur.

Merci de votre aide, et désolé pour le dérangement.

Sans magnétomètre tu auras forcément une dérive à plus ou moins court terme Ce qui n'empêche pas d'écrire l’algorithme de ce que tu dit, et faire des tests.

Il y a le LIDAR sinon qui peut être une solution.

Pour les quaternions j'ai un peu de mal aussi, malgré les quelques articles que j'ai lu. En informatique 3D, notamment animation 3D c'est souvent utilisé car sa créé des rotations plus naturelles et surtout ça évite le blocage de cardan ! (ce que provoque les axes euler)

Par contre de la à comprendre un espace de coordonnées à 4 dimensions...

Les quaternions sont des objets mathématiques qui permettent d'éviter une division par 0 qui peut arriver quand on fait tout avec les angles d'Euler. Ca ne sert à rien d'essayer de chercher une signification physique.

Pour le reste, encore une fois, à ta place je repartirais d'un existant qui fonctionne, genre ça:

Tu n'as plus qu'à rajouter ce qui manque.

Ok ok, du coup j'ai pas forcément besoin des quaternions si c'est pour pouvoir observe les rotatiosn de manière plus fluide!

Moi j'ai juste besoin que mon code me renvoie la position courante et quand j'appuie sur un boutton poussoir connecté il m'autorise a faire ou non une action en fonction de la position retourner.

Mais je ne pense pas que sa va le faire.. J'utilise une caméra par laquel j'obtient une positon en xy via une led accrocher sur l'objet "volant" et une quatre led placé a des endroit connue pour quadriller la zone. Quant au z il est fourni par un hrlvmaxsonar se basant sur le principe de l'echo.

Malheuresement, l'erreur de dérive de mon imu fera que mon système sera très mal localiser a la fin hors il me faut une précision d'au moins 10 mm..

j'aurais surement du partir sur les ultrasons comme prévue initialement.