Accéléromètre MPU6050 : OS Error : [Errorno 121] Remote I/O error

Bonjour,
Je suis étudiant et j’essaie de mettre en place un gimbal asservie sur 1 axe avec des composants bon marché, pour un projet. Pour ce faire, j’ai acheté un MPU6050 et un motoréducteur. J’ai réussi à obtenir un angle sur l’axe X. Cependant, lorsque j’ai essayé d’utiliser ces valeurs pour asservir mon moteur, et lorsqu’il commence à tourner, l’erreur 121 se produit.

Traceback (most recent call last): File "/home/pi/Documents/TIPE/Programme de stabilisation.py", line 224, in <module> accel_data=mpu.get_accel_data() File "/usr/local/lib/python3.7/dist-packages/mpu6050_raspberrypi-1.1 py3.7.egg/mpu6050/mpu6050.py", line 139, in get_accel_data x = self.read_i2c_word(self.ACCEL_XOUT0) File "/usr/local/lib/python3.7/dist-packages/mpu6050_raspberrypi-1.1-py3.7.egg/mpu6050/mpu6050.py", line 72, in read_i2c_word low = self.bus.read_byte_data(self.address, register + 1) OSError: [Errno 121] Remote I/O error

J’ai essayé de réduire le taux d’échantillonnage à la sortie du MPU6050, et de réduire la longueur des fils comme je l’ai vu sur d’autres plateformes, mais cela ne fonctionne pas.

Faites-moi savoir si vous avez des solutions !

hello,

sans le schéma du montage, ni le code du programme on a juste la confirmation que quand le moteur démarre ça coupe la liaison i2c …

Oui en effet, on ne peut pas dire grand chose de plus, désolé…
Voici ci-dessous le branchement (j’ai fait comme j’ai pu) :

Et voici la partie du code, il y a une partie initialisation que je n’ai pas mis car il n’y a aucune erreur provenant de cette partie. De plus j’utilise un filtrage par convolution avec scipy via un filtre RIF afin d’avoir des valeurs assez potables.( Si jamais vous avez d’autres moyens je suis preneur)

import smbus
import math
import time
from mpu6050 import mpu6050
import scipy.signal
#Fonctions
def dist(a,b):
    return math.sqrt(a**2 +b**2)
def get_y_angle(x,y,z):
    radians=math.atan2(x,dist(y,z))
    return -math.degrees(radians)
def get_x_angle(x,y,z):
    radians=math.atan2(y,dist(x,z))
    return math.degrees(radians)
def get_z_angle(x,y,z):
    radians=math.atan2(dist(x,y),z)
    return math.degrees(radians)
def filtrage_convolution(x,b):
    N=len(b)
    ne=len(x)
    filtre=0
    for k in range(N):
        filtre+=b[k]*x[(ne-1)-k]
    return filtre
def moyenne(L):
    s=0
    for elt in L:
        s+=elt
    return s/(len(L))
#Définitions des pins moteurs
M1_En=21
M1_In1=20
M1_In2=16

M2_En=18
M2_In1=23
M2_In2=24

#Création d'une liste de pins pour chaque moteurs
Pins=[[M1_En, M1_In1,M1_In2],[M2_En, M2_In1,M2_In2]]

#Setup
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(True)

GPIO.setup(M1_En,GPIO.OUT)
GPIO.setup(M1_In1,GPIO.OUT)
GPIO.setup(M1_In2,GPIO.OUT)

GPIO.setup(M2_En,GPIO.OUT)
GPIO.setup(M2_In1,GPIO.OUT)
GPIO.setup(M2_In2,GPIO.OUT)

M1_Vitesse=GPIO.PWM(M1_En,100)
M2_Vitesse=GPIO.PWM(M2_En,100)
M1_Vitesse.start(100)
M2_Vitesse.start(100)

def sens1(moteurNum):
    GPIO.output(Pins[moteurNum-1][1],GPIO.HIGH)
    GPIO.output(Pins[moteurNum-1][2],GPIO.LOW)

def sens2(moteurNum):
    GPIO.output(Pins[moteurNum-1][1],GPIO.LOW)
    GPIO.output(Pins[moteurNum-1][2],GPIO.HIGH)

def arret(moteurNum):
    GPIO.output(Pins[moteurNum-1][1],GPIO.LOW)
    GPIO.output(Pins[moteurNum-1][2],GPIO.LOW)
    print("Moteur",moteurNum,"arret.")
def arretComplet():
    GPIO.output(Pins[1][1],GPIO.LOW)
    GPIO.output(Pins[1][2],GPIO.LOW)
    GPIO.output(Pins[0][1],GPIO.LOW)
    GPIO.output(Pins[0][2],GPIO.LOW)
    print("Moteur arretes.")
#......
#Listes des variables
b1=scipy.signal.firwin(nutmaps=5,cutoff=[0.04],window='han',pass_zero='lowpass',fs=8000)#8000 Hz est la fréquence d'échantillonnage en sortie de l'accéléro
Y=[]
Yfiltre=[]
X=[]
Xfiltre=[]
Z=[]
Zfiltre=[]
RotX=[]
RotXfiltre=[]
RotY=[]
RotYfiltre=[]
ordre=len(b1)

while True :
    #filtrage de l'angle donné par les valeurs de translations
    accel_data=mpu.get_accel_data()
    Ax=accel_data['x']/16384
    Ay=accel_data['y']/16384
    Az=accel_data['z']/16384
    angle_y=get_y_angle(Ax,Ay,Az)/1.3
    angle_x=get_x_angle(Ax,Ay,Az)/1.3
    angle_z=get_z_angle(Ax,Ay,Az)/1.3
    X.append(angle_x)
    Xfiltre.append(filtrage_convolution(X,b1))#seul angle qui nous intéresse
    print("")
    print(Xfiltre[len(Xfiltre)-1])
    print("")
    if Xfiltre[len(Xfiltre)-1]<X0: #X0 est la valeur trouvée lors de l'initialisation
        #cas selon si l'angle est supérieur ou inférieur à X0, ici, l'angle diminue quand le poignet  se casse vers l'avant avec l'accéléro face à soit
        while abs(Xfiltre[len(Xfiltre)-1]-X0)<1.5: #tolérance de +/- 1.5 degré
            sens2(2)#va dans le sens inverse du poignet i.e : la gauche ( vu en face de l'arbre moteur)
            accel_data=mpu.get_accel_data()
            Ax=accel_data['x']/16384
            Ay=accel_data['y']/16384
            Az=accel_data['z']/16384
            angle_x=get_x_angle(Ax,Ay,Az)/1.3
            X.append(angle_x)
            Xfiltre.append(filtrage_convolution(X,b1))
            print("")
            print(Xfiltre[len(Xfiltre)-1])
            print("")
            time.sleep(0.04)
        arret(2)
    else:
        while abs(Xfiltre[len(Xfiltre)-1]-X0)<1.5:
            sens1(2)
            accel_data=mpu.get_accel_data()
            Ax=accel_data['x']/16384
            Ay=accel_data['y']/16384
            Az=accel_data['z']/16384
            angle_x=get_x_angle(Ax,Ay,Az)/1.3
            X.append(angle_x)
            Xfiltre.append(filtrage_convolution(X,b1))
            print("")
            print(Xfiltre[len(Xfiltre)-1])
            print("")
            time.sleep(0.04)
        arret(2)

P.S: Le motoréducteur vient de la marque ICQUANZX

bien que ce type de manip dépasse largement mon domaine de compétence … le time sleep(0.04) est peut être trop court; dans les quelques tuto web que j’ai pu trouvé il colle au minimum 500ms (sleep(0.5)) entre deux accès au périphérique i2c

Pouvez-vous envoyer les liens des tutos dont vous parler ?

Cependant, ce qui est bizarre, c’est que le time.sleep(0.04) ne pose pas de soucis lorsque le périphérique i2c est tout seul à fonctionner.

Mais merci beaucoup en tout cas, je vais essayer avec cette durée, je vous tiens au courant !

a la réflexion cette histoire de sleep() me semble pas une si bonne idée…

. si la line 224 est une des deux lectures dans le deuxième while on peut imaginer que les deux lectures sont trop proche ou que la vitesse du i2c est trop lente ou que ça le laisse dans un état « instable » !

Augmenter la vitesse du bus i2c du Raspberry-pi ( Utilisez le MPU6050 sur Raspberry-Pi – Openest)

. si la ligne 224 est la première lecture on peut imaginer que c’est l’action sur le l98n qui bug…

pour etre fixer tu pourrais aussi mettre en commentaire les action (sens1, sens2 arret) ou les remplacer par des print pour être fixer sur le fait que ton montage du l298N n’interfère pas sur le mpu6050

Je viens d’effectuer la manip en remplaçant sens1(2) et sens2(2) par des prints.

  • Après avoir modifié quelques conditions sur mes while, le sens de rotation correspond bien à la condition fixée et le moteur est bien sensé s’arrêter une fois qu’on est dans une tolérance de +/- 2° de l’angle initial. (donc pas forcément besoin de modifier la vitesse du bus ?)

  • Cependant, une fois que j’enlève les print pour faire réellement tourner le moteur, l’erreur apparaît après 2 ou 3 lectures des données de l’accéléromètre

J’imagine donc que cela vient surement du module L298N

Excusez ma curiosité, mais votre module à L298 a-t-il des condensateurs

  • chimiques (disons 47/4700µF) pour manger les pointes de courant

  • ceramiques (100-470nF) pour deparasiter (les chimiques ne gèrent « que » la basse fréquence)

Pas de soucis, merci beaucoup de votre aide !

Je ne suis pas assez calé pour pouvoir remarquer cela désolé…

Je joins la datasheet du module si cela peut vous permettre de le vérifier, à la page 3 il y a une photo assez nette du composant.

Et s’ils ne sont pas présents où devrais-je les placer ?

Lien : https://components101.com/sites/default/files/component_datasheet/L298N-Motor-Driver-Datasheet.pdf

Pour les condensateurs d’antiparasitage: trouvez deux condensateurs de 330-470nF non polarisés (à moins de 0.1E$ en prix de gros) ; les visser (ils sont livrés avec des fils; le montage en surface est abominable) en parallele de chaque moteur -c’est très simple; sur J2 et J4) moteur.
Il est aussi recommandé de découpler en haute fréquence le régulateur -même si ça ne sert pas toujours) par des condensateurs de 100 à 470 nF: pour l’entrée, par bornier -J8-, c’est tout aussi facile; pour le 5v c’est aussi tout aussi facile -entre la masse et le 5v de J6-
En tout, ça vous fait 4 condensateurs à visser (pas besoin de souder) pour tenter de déparasiter et voir si ça suffit.

D’accord merci beaucoup !

J’ai testé avec des fils encore plus courts et l’erreur apparaît moins souvent voire plus du tout.

Il y a ensuite une instabilité due à mon programme.

Mais le programme n’est que rarement arrêté par cette erreur.

Je pourrais retester cette expérience la semaine prochaine.

Je vous tiens au courant, merci beaucoup !

Bon, une recherche google avec votre code d’horreur ramène … vers des problèmes de cablage (aggravés / activés par les vibrations du moteur: le mettre sur un support en caoutchouc doit le resoudre, joint à un recablage -ce que vous avez fait lors du racourcissement des fils-. Mettre des condensateurs de découplage est recommandé -au moins pour le 7805: certains oscillent -Spannungsregler – Wikipedia donne une explication détaillée -la version anglaise donne, sans justification, un schéma sécurisé: votre fournisseur n’a pas sécurisé le 7805.
Quant au code:
je pense que votre prof (qui a choisi un sujet interessant) vous a recommandé scipy (ça ne s’invente pas) Je parie que scipy a tout ce qu’il faut pour calculer une moyenne (plus rapidement que votre solution, qui donne lentement un résultat juste) un produit scalaire (une fois réordonné votre filtre, vous avez un produit scalaire -dot product en anglais) ou un filtre. Ces solutions sont plus rapides que de le faire dans une boucle (ça peut être genant si vous passez à du traitement d’image -dépend de votre cursus- ou si votre asservissement de baballe est handicapé par des calculs pas assez rapides pour suive…)
Sans vouloir déflorer votre travail, python - What is the pythonic way to calculate dot product? - Stack Overflow m’a semblé interessant …pour les produits scalaires rapides…

Merci beaucoup pour toutes ces précisions !

  • J’ai suivi une piste qui consistait à d’enrouler les fils du moteur et de données de l’accéléromètre (SCL et SDA) autour d’une ferrite.
    ( https://fr.rs-online.com/web/c/passifs/noyaux-de-ferrite/anneaux-de-ferrite/ pour en acheter)

  • Cela a marché, l’erreur n’apparaît plus lorsque le système est stable (i.e : quand le moteur ne fais pas faire aux câbles de l’accéléromètre un tour sur eux-mêmes) .

  • J’ai également essayé de « blinder » les fils de l’accéléromètre, en les entourant d’un papier aluminium relié à la masse, mais cela n’a pas marché.

Je pense donc que ce problème est résolu, merci à tous pour votre aide !

Bonjour,
Excuses-moi, qu’appelles-tu ainsi?

Je voulais juste dire un stabilisateur vidéo sur un axe, je me suis mal exprimé dsl.