Mon premier multirotor avec raspberry

Présentez ici vos machines en détails

Modérateur : Dehas

Avatar du membre
Ovopack
Bicopter
Messages : 37
Enregistré le : dim. 30 nov. 2014 13:16
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Ovopack »

PID : commande = Kp * erreur + Ki * somme_erreurs + Kd * (erreur - erreur_précédente)

Tu veux un super site?
http://www.ferdinandpiette.com/blog/201 ... e-calculs/" onclick="window.open(this.href);return false;
C'est ici que j'ai pu comprendre la transformation en programme de la régulation. Bien sûr j'ai fait beaucoup d'adaptation sur cette formule pour mes besoins :
Par exemple, sur un tri, quand un veut un roll (roulis), on ne peut pas se contenter de ne faire marcher que le moteur avant droit ou gauche, sinon le tri par en arrière (120°). Donc avec un foule de calcul que j'ai du faire tiré à partir de schéma 2D que j'ai réalisé moi-même, pour un roulis:

Moteur de coté : consigne - coefpidpitch - coefpidroll/2/cos(30) ATTENTION COS(30) a convertir en degrees
Moteur arrière : consigne + coefpidpitch + abs(coefpidroll)*tan(30) IDEM

Dans la partie initialisation, je calculs la moyenne des 200 premières valeurs puis la déduit des relevés de l'IMU. J'ai du faire ca car l'IMU n'est pas forcément fixé bien droit sur le TRI. Problème : si je fais l'initisalisation sur un sol bancale.... je l'ai dans l'os! Je ne sais pas encore résoudre ce pb. Je pense pas la suite fixer solidement l'imu et régler son inclination manuelement sur le programme.
Si tu dois utiliser la même librarie que moi (la richards electronic). Elle est très bien mais demande quelques petites modifs mineurs dans ses .py


Avatar du membre
Ovopack
Bicopter
Messages : 37
Enregistré le : dim. 30 nov. 2014 13:16
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Ovopack »

Ah et pour le filtrage... tout est fait par la librairie. Pas de gyro ni d'accéléromètre... directement les 3 angles corrigés!!
+ pression + estimation de l'altitude + température.
Par contre, le yaw va de -180 à 180 :gene: , le roll de -90 à 90 :cote: , idem pour le pitch. Il y a juste à ajouter 360° si yaw < 0
Le 0° est toujours au nord, je suppose que la librairie utilise le magnétomètre mais il y a la déclinaison à regler. Pour ma part, inutile pour le moment. Déja parce que je n'ai pas compris la procédure mais surtout que mettre un +/- x° de déclinaison dans le python devrait normalement suffire!


Avatar du membre
Vex
Bicopter
Messages : 12
Enregistré le : mar. 30 déc. 2014 23:27
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Vex »

Oui, j'ai déjà codé mon PID (du moins le PID en lui même, pas la classe toute entière...). J'avais le lien de ton site dans mon onenote ;)
Ce que je voulais dire, c'est que je compte faire du PID position (angle), et du PID taux de correction (rate). On reçoit de l'IMU les positions angulaires, et les vitesses angulaires. Ca va ressembler à ça:
Control_stable.jpg
Sauf que j'ai pas de Kalman pour le moment (complementary filter à la place).

Tu as un lien vers ta lib IMU ? J'ai codé la mienne (pitch, roll, altitude, filtre, temperature, etc...). Ca a l'air d'être OK, mais ... à voir :] Ici un scrennshot du pitch et du roll:
pitchroll.png
Le code ci-dessous (que je vais devoir changer entièrement pour 1) la communication 2) spliter en plusieurs threads pour accélérer la lecture I2C 3) il est brut de pomme, surmement des coquilles ).

Code : Tout sélectionner

#-*- coding: utf-8 -*- 
#
from Adafruit_I2C import Adafruit_I2C
import socket, sys, threading
import time
import commands
import math
from datetime import datetime

class IMU(threading.Thread):
    def __init__(self,com=False):
        threading.Thread.__init__(self)
        
        self.running = True
        
        #communication object
        self.com = com
        print "IMU Started"
        
        # Gyro init L3GD20H 
        print "Attempt to connect to the Gyro..."
        self.L3GD20H_I2C = Adafruit_I2C(0x6B)
        x = self.L3GD20H_I2C.readS8(0x0F)
        print x
        if x == 215: #0xD7
            print "Beagle Bone is connected with the Gyro"
        else:
            print "Cannot connect to the Gyro"
        self.L3GD20H_I2C.write8(0x23, 0x20) # 2000 dps full scale. deg / s
        self.L3GD20H_I2C.write8(0x20, 0xCF) # normal power mode, all axes enabled, 100 Hz
        
        # Compass LSM303D (accelerometer and magnetometer)
        print "Attempt to connect to the Compass..."
        self.LSM303D_I2C = Adafruit_I2C(0x1D)
        x = self.LSM303D_I2C.readS8(0x0F)       
        if x == 73:
            print "Beagle Bone is connected with the Compass"
        else:
            print "Cannot connect to the Compass"
        self.LSM303D_I2C.write8(0x21, 0x00) #CTRL2 --> 2g. 0.061 mg / LSB
        self.LSM303D_I2C.write8(0x20, 0x57) #CTRL1
        self.LSM303D_I2C.write8(0x24, 0x64) #CTRL5
        self.LSM303D_I2C.write8(0x25, 0x20) #CTRL6
        self.LSM303D_I2C.write8(0x26, 0x00) #CTRL7
       
        # for complementary filter
        self.old_time = int(round(time.time() * 1000))
        self.gyro_total_x = 0
        self.gyro_total_y = 0
        self.K1 = 0.98
        self.K2 = 0.02
        self.last_x = 0
        self.last_y = 0
        
    def complementary_filter(self, gyrX, gyrY, gyrZ, accX, accY, accZ):
        
        now = int(round(time.time() * 1000)) 
        dt =  (now - self.old_time)/1000.
        #print dt
        self.old_time = now
        
        gyro_x_delta = gyrX * dt
        gyro_y_delta = -gyrY * dt
        
        self.gyro_total_x += gyro_x_delta
        self.gyro_total_y += gyro_y_delta
        
        rotation_x = math.degrees(math.atan2(accY, math.sqrt(accX ** 2 + accZ ** 2)))
        rotation_y = math.degrees(math.atan2(accX, math.sqrt(accY ** 2 + accZ ** 2)))
        
        self.last_x = self.K1 * (self.last_x + gyro_x_delta) + self.K2 * rotation_x
        self.last_y = self.K1 * (self.last_y + gyro_y_delta) + self.K2 * rotation_y
        
        return [self.last_x, self.last_y]
        
    def stop(self):
        self.running = False
        
    def run(self):
        while self.running:
            
            #time.sleep(0.05)
            
            time_start = datetime.now()
            
            time_1 = datetime.now()
            
            #read x-axis compass
            x = self.LSM303D_I2C.readU8(0x08) #Low
            y = self.LSM303D_I2C.readS8(0x09) #High
            RAW_X = (y << 8) + x
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#103, "SERVER", magX : '+str(round(RAW_X, 1))+', 4, False')
            
            #read y-axis compass
            x = self.LSM303D_I2C.readU8(0x0A) #Low
            y = self.LSM303D_I2C.readS8(0x0B) #High
            RAW_Y = (y << 8) + x
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#103, "SERVER", magY : '+str(round(RAW_Y, 1))+', 4, False')
            
            #read z-axis compass
            x = self.LSM303D_I2C.readU8(0x0C) #Low
            y = self.LSM303D_I2C.readS8(0x0D) #High
            RAW_Z = (y << 8) + x
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#103, "SERVER", magZ : '+str(round(RAW_Z, 1))+', 4, False')
            
            heading = (math.atan2(RAW_Y, RAW_X) * 180.) / math.pi
            if heading < 0: heading += 360
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#103, "SERVER", head : '+str(round(heading, 1))+', 4, False')
            #accelerometer:
            # 2g (2000 mg) = 90°
            
            #read x-axis accelerometer
            x = self.LSM303D_I2C.readU8(0x28) #Low
            y = self.LSM303D_I2C.readS8(0x29) #High
            RAW_X = (y << 8) + x
            SCALED_X = RAW_X * 4000 / 32768.
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#104, "SERVER", accX : '+str(round(SCALED_X, 3))+', 4, False')
            
            #read y-axis accelerometer
            x = self.LSM303D_I2C.readU8(0x2A) #Low
            y = self.LSM303D_I2C.readS8(0x2B) #High
            RAW_Y = (y << 8) + x
            SCALED_Y = RAW_Y * 4000 / 32768.
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#104, "SERVER", accY : '+str(round(SCALED_Y, 3))+', 4, False')
                
            #read z-axis accelerometer
            x = self.LSM303D_I2C.readU8(0x2C) #Low
            y = self.LSM303D_I2C.readS8(0x2D) #High
            RAW_Z = (y << 8) + x
            SCALED_Z = RAW_Z * 4000 / 32768.
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#104, "SERVER", accZ : '+str(round(SCALED_Z, 3))+', 4, False')          
            
            accX = SCALED_X
            accY = SCALED_Y
            accZ = SCALED_Z
            
            #print (RAW_X, RAW_Y, RAW_Z)
            #print (SCALED_X, SCALED_Y, SCALED_Z)
            
                               
            #gyro
            # 1 digit = 70 mdps (mili degree per second) 
            #read x-axis angular
            x = self.L3GD20H_I2C.readU8(0x28) #Low
            y = self.L3GD20H_I2C.readS8(0x29) #High
            RAW_X = (y << 8) + x 
            SCALED_X = (RAW_X - 10) * 0.07
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#105, "SERVER", gyroX : '+str(round(SCALED_X, 3))+', 4, False')
            #read y-axis angular
            x = self.L3GD20H_I2C.readU8(0x2A) #Low
            y = self.L3GD20H_I2C.readS8(0x2B) #High
            RAW_Y = (y << 8) + x
            SCALED_Y = (RAW_Y - 2) * 0.07
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#105, "SERVER", gyroY : '+str(round(SCALED_Y, 3))+', 4, False')
            #read z-axis angular
            x = self.L3GD20H_I2C.readU8(0x2C) #Low
            y = self.L3GD20H_I2C.readS8(0x2D) #High
            RAW_Z = (y << 8) + x
            SCALED_Z = (RAW_Z - 39) * 0.07
            if self.com:
                if self.com.client:
                    self.com.sendToClient('#105, "SERVER", gyroZ : '+str(round(SCALED_Z, 3))+', 4, False')
            
            #print "gyro: "+str(SCALED_X)+" - "+str(SCALED_Y)+" - "+str(SCALED_Z)
            #print (RAW_X, RAW_Y, RAW_Z)
            #print (SCALED_X, SCALED_Y, SCALED_Z)
           
            rotation_x = math.degrees(math.atan2(accY, math.sqrt(accX ** 2 + accZ ** 2)))
            rotation_y = math.degrees(math.atan2(accX, math.sqrt(accY ** 2 + accZ ** 2)))
            
            #print (rotation_x, rotation_y)
            
            output_ = self.complementary_filter(SCALED_X, SCALED_Y, SCALED_Z, accX, accY, accZ)
            
            #print output_
            
            #output_[0] = rotation_x
            #output_[1] = rotation_y
            
            if self.com:
                 if self.com.client:
                     self.com.sendToClient('#104, "SERVER", pitch : '+str(round(output_[0], 1))+', 4, False')
            if self.com:
                 if self.com.client:
                     self.com.sendToClient('#104, "SERVER", roll : '+str(round(output_[1], 1))+', 4, False')
            
            time_end = datetime.now()
            #print "loop : "+str(time_end - time_start)
            
if __name__ == "__main__":
    myIMU = IMU()
    myIMU.start()
J'ai pas trop trop compris l'histoire de l'init de l'IMU. Normallement il n'y a rien à faire. Ce doit être surement des problèmes d'offset de callibration. Si les "200" que tu mentionne est une moyenne glissante, fait attention! Il y aura un retard sur la lecture réelle de l'IMU qui va s'additioner à celui du filtre.

Je suis curieux de regarder la librairie. Je ne sais pas comment il fait pour réduire le bruit. Je suis sceptique si il n'utilise ni le gyro, ni l'accéléromètre 8|
Vous n’avez pas les permissions nécessaires pour voir les fichiers joints à ce message.


Mon Quadricopter: The PyFly Project
Avatar du membre
Ovopack
Bicopter
Messages : 37
Enregistré le : dim. 30 nov. 2014 13:16
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Ovopack »

l'init est juste l'initialisation de la frequence, de l'envoi d'1ms à chaque ECS, de l'initialisation de certaines variables, etc...
Les 200 premières valeurs ne sont pas une moyenne glissante. Je prends ces 200 premières valeurs, j'en fais simplement la moyenne que j'additionne ou retranche à la lecture de l'IMU. Cette moyenne reste stockée dans une variable, elle ne change pas. PS : une moyenne par axe

Pour la librairie, je n'ai pas dis qu'elle n'utilisait pas le gyro ni l'accéléromètre! J'ai juste dis qu'elle me sortait directement les angles filtrés!

Voila la librairie : https://github.com/richards-tech/RTIMULib" onclick="window.open(this.href);return false;

Je pense de plus en plus à un chassis tout fait pour mon tri mais dans cette taille... c'est vraiment galère!! 40cm de rayon (sans les pales)
Le mien est pas fameux. J'espère ne pas avoir à me rabattre sur un quad à cause de ca. Je suis un peu perdu là


Avatar du membre
Vex
Bicopter
Messages : 12
Enregistré le : mar. 30 déc. 2014 23:27
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Vex »

J'avoue, le chassis... Pour ma part, c'est la partie la moins sexy. J'ai prix un truc tout fait à 10€ sur hobbyking. Le faire soi même, c'est cool mais... wow 8|


Mon Quadricopter: The PyFly Project
Avatar du membre
Ovopack
Bicopter
Messages : 37
Enregistré le : dim. 30 nov. 2014 13:16
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Ovopack »

Un résumé vidéo des dernières modifs

[BBvideo=560,315]www.youtube.com/watch?v=y6AmnkykoV8[/BBvideo]


Avatar du membre
Vex
Bicopter
Messages : 12
Enregistré le : mar. 30 déc. 2014 23:27
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Vex »

Coool, et a quand le vol ? :-P


Mon Quadricopter: The PyFly Project
Avatar du membre
Ovopack
Bicopter
Messages : 37
Enregistré le : dim. 30 nov. 2014 13:16
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Ovopack »

Ohhh un lyonnais quelle coïncidence!

bientôt le test en vidéo


Avatar du membre
Ovopack
Bicopter
Messages : 37
Enregistré le : dim. 30 nov. 2014 13:16
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Ovopack »

Est-ce que quelqu'un pourrait me dire à quoi correspondent ces écritures sur mon chargeur : 0:3SER R:3SER.
Il me demande de confirmer ca quand je selectionner "balance charge". je confirme mais bon... je suppose qu'il parle des accus en serie et paralèlle

Les batteries sont chargées, je vais continuer le réglage du PID, sans poids de retenu
J'ai mis un cut-off de 7° cette fois-ci sur le programme dans le cas où il s'envolerai.... (et aussi une bride logiciel sur la puissance max)


Avatar du membre
Ovopack
Bicopter
Messages : 37
Enregistré le : dim. 30 nov. 2014 13:16
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Ovopack »

C'est curieux, à grande puissance, j'ai la led rouge du Pi qui s'éteint. Elle est relié directement au 3.3V et la seule chose qui pompe sur ce circuit c'est le VCC de l'adafruit.
Apparemement c'est possible de l'alimenter en 5V mais outre les problèmes de parasitage sur le circuit, je pense qu'il envera du 5V sur I2C et donc griller le Pi.
Ca serait un peu bête de racheter un régulateur pour avoir du 3.3V... Je comprend pas pourquoi l'adafruit pompe autant de jus, c'est uniquement pour le cicruit de commande pourtant!


Avatar du membre
Ovopack
Bicopter
Messages : 37
Enregistré le : dim. 30 nov. 2014 13:16
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Ovopack »

Hello,
Déjà, comme vu sur la vidéo, j'ai remi en état le chassis en changant tous les rivets, après le crash.

Ensuite, après recherches, sur les nouvelles versions des raspberrys, le circuit 5V est "mesuré" par un chipset qui coupe le 3.3V si la tension du 5V descend en dessous de 4.63V (de mémoire). Le régulateur, montre pourtant 5V en sortie mais il fait une mesure sur une moyenne de 1s donc il doit y avoir des chutes de tension au moment du démarrage du servomoteur et de la commande des ESCs. Je n'ai pas d'oscilloscope donc impossible de confirmer. Ca semble pourtant le plus logique, d'où la led rouge qui s'éteint.
Sur des sites, ils recommandent un condo de 100µF par servo soit ~400-500µF pour moi (3 ESCs + gros servo). Il a même déjà l'emplacement vide pour le souder sur la carte adafruit...

Autre chose, je réfléchi à changer le chassis car j'ai un problème de fixation : les bras sont maintenus par le centre et les fixations extérieures mais ils ont tendences à tourner sur eux-même à cause d'un jeu trop grand dans les fixations. Je dois donc soit renforcer les fixations avec un plus grand de bras de levier (mais plus lourd) soit changer la forme du chassis. Je me suis pas mal renseigné et un T-copter (tri en forme de T) serait identique en terme de vol par rapport à un Y. Le centre de gravité, les calculs, les moments d'inertie, etc... sont les mêmes. Il n'y aurait plus qu'un seul angle et surtout un de 90° donc tout simplement des equerres pour le renforcer...
Je parle de poids, mais je n'ai pas encore fais les calculs de distance : il est possible qu'il y ai plus de longueur de bras sur un T que sur un Y. Et les bras... Alu en U 3mm d'épaisseur.... ca pèsent! Alors renfort ou T-copter?
C'est dommage, j'aimais bien la forme en Y. Quelqu'un à des retours d'expériences entre les T et les Y?


Avatar du membre
OpT!Mu$
Tricopter
Messages : 113
Enregistré le : lun. 13 mai 2013 20:14
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par OpT!Mu$ »

Tout d'abord les écritures sur ton chargeur 0:3SER R:3SER.
Cela signifie que tu demandes de charger une 3S et que le chargeur détecte bien une 3S, donc feu vert pour charger :)

Pour ton problème d'alim, comment tu alimentes ton système ? Via un BEC d'un ESC ? UN BEC externe ? ou directement via la LIPO ?
Si tu alimentes directement par la LIPO le phénomène est normal, lorsque tu as besoin de puissance sur les moteurs cela créer une chute de tension au niveau de tes éléments. Cette chute peut être limitée en fonction de ton choix de lipo (capacité stockée et capacité de décharge).
La meilleur solution pour moi reste le BEC externe 5V / 2A


https://www.facebook.com/B.DroniK" onclick="window.open(this.href);return false;
Autorisation de poster n°00910051
Avatar du membre
Ovopack
Bicopter
Messages : 37
Enregistré le : dim. 30 nov. 2014 13:16
Réputation : 0

Re: Mon premier multirotor avec raspberry

Message non lu par Ovopack »

J'utilise un UBEC externe. J'en parle pas mal dans les précédents posts.
Le problème ici vient en fait du démarrage du moteurs du servo qui créer des chutes de tensions dans le circuit d'alim. Il y a bien un condo en sortie sur le BEC pour lisser la tension carré qui sort du régulateur mais chaque condo est capable de filtrer une certaine fréquence en fonction de sa capacité et ce condo de lissage doit être trop gros pour filtrer des fréquences aussi rapides que des démarrages moteurs. Je vais donc ajouter en parallèle un second condensateur, pour limiter ces chutes de tensions.


Répondre

Retourner vers « Présentation de vos machines »