
Dès les années 30, les avions ont commencé à adopter des formes aérodynamiques afin de diminuer leur résistance à l'air. Cependant, les turbulences de bout d'aile ont longtemps été ignorées. Il s'est avéré avec le temps que les winglets peuvent faire gagner jusqu'à 5% d'efficacité. Le but du CPU-Bird est donc de démontrer l'efficacité du Laminar Feather.


Ce planeur de 120x80x20 cm est équipé du Laminar Feather et de matériel électronique programmé afin de relever des données sur le vol. Il nous permet de visualiser une courbe tri-dimensionnelle affichant la trajectoire exacte du planeur au cm près.
Le Laminar Feather est un winglet inédit qui cherche à imiter le mieux possible le bout des ailes de rapaces, caractérisées par leurs trois grandes plumes. Son but est de diminuer le plus possible les turbulences de bout d'aile afin d'améliorer les performances de l'avion.

Ci-contre, une comparaison entre les turbulences d'une aile basique et celles d'une aile équipée du Laminar Feather. La grosse turbulence d'origine est divisée en trois zones de perturbations qui permettent chacune une réduction supplémentaire des turbulences par l'accompagnement du flux d'air.

Electronical process
Le systeme de motion capture que nous avons inséré dans l'avion, se décortique en trois parties:
(1) : Un microcontroleur ATmega328P qui s'apparente à un arduino nano.
Cette partie est mère de toutes
(2) : Même si caché sur la photo, il y a un gyroscope/accéléromètre/thermomètre MPU6050 monté sur une carte GY-501
(3) : Un lecteur SD afin d'enregistrer les données traduites par le microcontroleur du MPU.
(4) : Une connectique pour une alimentation externe de type lithium polimère.


(1)
(2)
(3)
(4)
La théorie
La plus grosse difficulté de la communiquation avec les differentes partie est la concordance des données car chaquun utilise une unité différente:
- L'ARDUINO utilise une sorte de C qui est simplifié.
- le MPU communique en port I2C avec un ssystème SLAVE IN et SLAVE out.
- La SD comnmunique en UTF-8
- Mon logiciel Java lis en comprend le mieux le Unicode
​
OULÀ mais cela veux dire quoi tout ces termes?
En clair c'est comment un orchestre:
Les composants sont les musiciens et l'arduino c'est le chef d'orchestre.
Mais? le logiciel dans la methaphore c'est qui?
Le logiciel c'est la presse présente lors du spectacle, elle observe puis donne à l'utilisateur son avis.
/*
* THIS CODE WAS WRITTEN BY BAPTISTE GRIMALDI
/* FIRST USABLE VERSION: 17.02.19
* COPYRIGHT: - PUBLIC WITH MENTION OF THE ORIGINAL AUTOR
* - 2019 - © CPU-FLIGHT
*/
#include<Wire.h>
#include <SPI.h>
#include <SD.h>
File myFile;
const int MPU_addr=0x68; // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
int n = 0;
bool keep = false;
bool keep2 = false;
const int sdPin = 6;
void setup(){
Wire.begin();
Wire.beginTransmission(MPU_addr);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
Serial.begin(115200);
while (!Serial) {
;
}
while (!SD.begin(sdPin)) {
;
}
}
void loop(){
myFile = SD.open("MPU_DATA.TXT", FILE_WRITE);
if(n>=6000) {
keep2=true;
while(keep2==true) {
Serial.println("END");
}
}
if (myFile) {
keep = true;
while (keep == true) {
n+=1;
Serial.println(n);
Wire.beginTransmission(MPU_addr);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr,14,true); // request a total of 14 registers
AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
myFile.print(AcX); myFile.print("Y");
myFile.print(AcY); myFile.print("Y");
myFile.print(AcZ); myFile.print("Y");
myFile.print(GyX); myFile.print("Y");
myFile.print(GyY); myFile.print("Y");
myFile.print(GyZ); myFile.print("Y");
myFile.print(Tmp); myFile.print("N");
if(n>=6000) {
keep=false;
myFile.print("END");
myFile.close();
}
}
}
}
Le MPU6050 communique en lb/g/s
se qui est un type d'accélération.
Mais en plus de la conversion, il nous faut mettre en place la correction d'erreur du au placement initial du MPU et aussi soustraire la valeur de la gravité considérée par le capteur: 9,81m/s^2
LA CATAPULTE
Le but de la catapulte est de propulser l'avion à force égale à chaque vol. Il a été nécessaire d'en fabriquer une très grosse pour avoir une stabilité optimale.
Je ne détaillerai pas la construction de l'arbalète car elle nous à pris finalement une journée à construire en raison de l'importante quantité de matériel à transporter afin de la réaliser.


Pour qu'elle soit la plus transportable possible, elle est essentiellement faite de grosses fisses taillées sur mesures et de gros boulons.

LES ESSAIS SUR LE TERRAIN
Nous avons fait les tests sur le terrain d'aéromodélisme de Pfaffenheim, propriété du club d'aéromodélisme "Jean Mermoz" de Colmar. Hélas, le seul jour où nous pouvions y accéder en présence d'un membre du club était un jour de vent fort. Le vent était notre ennemi le plus redouté car sur un petit avion comme le nôtre, il a une influence énorme. Les tests ont donc été peu concluants malgré notre travail.
Notre conclusion
Ce projet a été une expérience très intéressante et enrichissante qui nous a permis de découvrir de nouvelles méthodes de travail et de surmonter des épreuves en groupe, animés par le désir d’apprendre et de comprendre. C’est vraiment un travail que nous avons pris à cœur et en lequel nous voyions une opportunité inédite, quelque chose de vraiment nouveau.

Toutes les vidéos
Le programme gravé dans le microcontroleur:
#define INTERRUPT_PIN 2 // use pin 2 on Arduino Uno & most boards
#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
bool blinkState = false;
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorInt16 aa; // [x, y, z] accel sensor measurements
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
// ================================================================
// === INITIAL SETUP ===
// ================================================================
void setup() {
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
// initialize serial communication
// (115200 chosen because it is required for Teapot Demo output, but it's
// really up to you depending on your project)
Serial.begin(115200);
while (!Serial); // wait for Leonardo enumeration, others continue immediately
// NOTE: 8MHz or slower host processors, like the Teensy @ 3.3V or Arduino
// Pro Mini running at 3.3V, cannot handle this baud rate reliably due to
// the baud timing being too misaligned with processor ticks. You must use
// 38400 or slower in these cases, or use some kind of external separate
// crystal solution for the UART timer.
// initialize device
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT);
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// wait for ready
Serial.println(F("\nSend any character to begin DMP programming and demo: "));
while (Serial.available() && Serial.read()); // empty buffer
while (!Serial.available()); // wait for data
while (Serial.available() && Serial.read()); // empty buffer again
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788); // 1688 factory default for my test chip
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
Serial.println(F(")..."));
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
// configure LED for output
pinMode(LED_PIN, OUTPUT);
}
// ================================================================
// === MAIN PROGRAM LOOP ===
// ================================================================
void loop() {
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize) {
if (mpuInterrupt && fifoCount < packetSize) {
// try to get out of the infinite loop
fifoCount = mpu.getFIFOCount();
}
// other program behavior stuff here
// .
// .
// .
// if you are really paranoid you can frequently test in between other
// stuff to see if mpuInterrupt is true, and if so, "break;" from the
// while() loop to immediately process the MPU data
// .
// .
// .
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & _BV(MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
fifoCount = mpu.getFIFOCount();
Serial.println(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & _BV(MPU6050_INTERRUPT_DMP_INT_BIT)) {
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
#ifdef OUTPUT_READABLE_QUATERNION
// display quaternion values in easy matrix form: w x y z
mpu.dmpGetQuaternion(&q, fifoBuffer);
Serial.print("quat\t");
Serial.print(q.w);
Serial.print("\t");
Serial.print(q.x);
Serial.print("\t");
Serial.print(q.y);
Serial.print("\t");
Serial.println(q.z);
#endif
#ifdef OUTPUT_READABLE_EULER
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetEuler(euler, &q);
Serial.print("euler\t");
Serial.print(euler[0] * 180/M_PI);
Serial.print("\t");
Serial.print(euler[1] * 180/M_PI);
Serial.print("\t");
Serial.println(euler[2] * 180/M_PI);
#endif
#ifdef OUTPUT_READABLE_YAWPITCHROLL
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
#endif
#ifdef OUTPUT_READABLE_REALACCEL
// display real acceleration, adjusted to remove gravity
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetAccel(&aa, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
Serial.print("areal\t");
Serial.print(aaReal.x);
Serial.print("\t");
Serial.print(aaReal.y);
Serial.print("\t");
Serial.println(aaReal.z);
#endif
#ifdef OUTPUT_READABLE_WORLDACCEL
// display initial world-frame acceleration, adjusted to remove gravity
// and rotated based on known orientation from quaternion
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetAccel(&aa, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
Serial.print("aworld\t");
Serial.print(aaWorld.x);
Serial.print("\t");
Serial.print(aaWorld.y);
Serial.print("\t");
Serial.println(aaWorld.z);
#endif
#ifdef OUTPUT_TEAPOT
// display quaternion values in InvenSense Teapot demo format:
teapotPacket[2] = fifoBuffer[0];
teapotPacket[3] = fifoBuffer[1];
teapotPacket[4] = fifoBuffer[4];
teapotPacket[5] = fifoBuffer[5];
teapotPacket[6] = fifoBuffer[8];
teapotPacket[7] = fifoBuffer[9];
teapotPacket[8] = fifoBuffer[12];
teapotPacket[9] = fifoBuffer[13];
Serial.write(teapotPacket, 14);
teapotPacket[11]++; // packetCount, loops at 0xFF on purpose
#endif
// blink LED to indicate activity
blinkState = !blinkState;
digitalWrite(LED_PIN, blinkState);
}
}
