Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed Servo MC33926 encoder
Fork of FlexV1 by
main.cpp
- Committer:
- fgagey
- Date:
- 2021-03-27
- Revision:
- 8:3c174bef1dbd
- Parent:
- 7:fe796ee2f59f
- Child:
- 9:7b0f9fd4586b
File content as of revision 8:3c174bef1dbd:
// Flex balancing robot using MPU6050 DMP (MotionApps v2.0)
// 26/03/2021 by Ferréol Gagey <ferreol.gagey@ens-paris-saclay.fr>
// Changelog:
// 26/03/2021 - simple pid without encoder
//===============================================
// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include "mbed.h"
#include <math.h>
#include "MC33926.h"
#include "QEI.h"
//DigitalOut leds[] = {(LED1), (LED2),(LED3),(LED4)};
#include "MPU6050_6Axis_MotionApps20.h" // works
MPU6050 mpu;
#ifndef M_PI
#define M_PI 3.1415
#endif
// uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/
// pitch/roll angles (in degrees) calculated from the quaternions coming
// from the FIFO. Note this also requires gravity vector calculations.
// Also note that yaw/pitch/roll angles suffer from gimbal lock (for
// more info, see: http://en.wikipedia.org/wiki/Gimbal_lock)
#define OUTPUT_READABLE_YAWPITCHROLL
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[15] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n',0 };
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
/* Motors*/
//MC33926 motorRight(D8,D9,A2);
MC33926 motorLeft(D4,D6,A3);
/* Encoders*/
QEI rightEncoder(D3,D2,NC,360);
QEI leftEncoder(A4,A5,NC,360);
int pulseleftcoder;
int pulserightcoder;
/* bluetooth module*/
Serial blue(D1, D0); // Bluetooth TX, RX
/* Variable declarations */
float pitchAngle = 0;
float rollAngle = 0;
bool dir; // direction of movement
float Kp = 0.5;
float Ki = 0.00001;
float Kd = 0.01;//0.01;//0.05;
float set_point = -0.8; // Angle for staying upright
float errorPID = 0; // Proportional term (P) in PID
float last_errorPID =0; // Previous error value
float integral = 0; // Integral (I)
float derivative = 0; // Derivative (D)
float outputPID = 0; // Output of PID calculations
int phone_char; //char returned by th bluetooth module
Ticker toggler1; // Ticker for led toggling
Ticker gyro; // Ticker for periodic call to compFilter funcçs
Ticker balance; // Periodic routine for PID control of balance system
Ticker speed; // Periodic routine for speed control
Ticker bluetooth; // Ticker for navigation
void toggle_led1();
void toggle_led2();
void getAngle();
void balancePID();
void balanceControl();
void Forward(float);
void Reverse(float);
void Stop(void);
void Navigate();
void TurnRight(float);
void TurnLeft(float);
// ================================================================
// === INITIAL SETUP ===
// ================================================================
int main()
{
//Pin Defines for I2C Bus
#define D_SDA D14
#define D_SCL D15
//#define D_SDA p28
//#define D_SCL p27
I2C i2c(D_SDA, D_SCL);
// mbed Interface Hardware definitions
//DigitalOut myled1(LED1);
//DigitalOut myled2(LED2);
//DigitalOut myled3(LED3);
//DigitalOut heartbeatLED(LED4);
// initialize serial communication
// (115200 chosen because it is required for Teapot Demo output, but it's
// really up to you depending on your project)
//Host PC Baudrate (Virtual Com Port on USB)
#define D_BAUDRATE 115200
// Host PC Communication channels
Serial pc(USBTX, USBRX); // tx, rx
pc.baud(D_BAUDRATE);
// initialize device
pc.printf("Initializing I2C devices...\n");
mpu.initialize();
// verify connection
pc.printf("Testing device connections...\n");
bool mpu6050TestResult = mpu.testConnection();
if(mpu6050TestResult){
pc.printf("MPU6050 test passed \n");
} else{
pc.printf("MPU6050 test failed \n");
}
// wait for ready
pc.printf("\nSend any character to begin DMP programming and demo: ");
//while(!pc.readable());
//pc.getc();
//pc.printf("\n");
// load and configure the DMP
pc.printf("Initializing DMP...\n");
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(62);
mpu.setYGyroOffset(1);
mpu.setZGyroOffset(63);
mpu.setZAccelOffset(16282); // 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
pc.printf("Enabling DMP...\n");
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
pc.printf("Enabling interrupt detection (Arduino external interrupt 0)...\n");
// attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
pc.printf("DMP ready! Waiting for first interrupt...\n");
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)
pc.printf("DMP Initialization failed (code ");
pc.printf("%u",devStatus);
pc.printf(")\n");
}
//gyro.attach(&getAngle, 0.005); // Call the complementaryFilter func. every 5 ms (200 Hz sampling period)
balance.attach(&balancePID, 0.010); // Same period with balancePID
speed.attach(&balanceControl, 0.01);
bluetooth.attach(&Navigate, 0.05);
// ================================================================
// === MAIN PROGRAM LOOP ===
// ================================================================
while(1)
{
if(blue.readable())
{
phone_char = blue.getc();
pc.putc(phone_char);
pc.printf("Bluetooth Start\r\n");
//myled = !myled;
}
pulseleftcoder=leftEncoder.getPulses();
pc.printf("compteur = %d ",pulseleftcoder);
pc.printf("Roll Angle: %.1f, Pitch Angle: %.1f ",rollAngle,pitchAngle);
pc.printf("Error = %f\r\n",outputPID);
// if programming failed, don't try to do anything
if (!dmpReady) ;//continue;
//myled2=0;
// wait for MPU interrupt or extra packet(s) available
// while (!mpuInterrupt && fifoCount < packetSize) {
// while (!mpuIntStatus && fifoCount < packetSize)
{
// 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
// .
// .
// .
// fifoCount= mpu.getFIFOCount();
// mpuIntStatus = mpu.getIntStatus();
}
wait_us(500);
// 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 & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
//pc.printf("FIFO overflow!");
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) {
// 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_YAWPITCHROLL
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
rollAngle=ypr[1] * 180/M_PI;
pitchAngle=ypr[2] * 180/M_PI;
//pc.printf("ypr\t");
// pc.printf("%f3.2",ypr[0] * 180/M_PI);
// pc.printf("\t");
// pc.printf("%f3.2",ypr[1] * 180/M_PI);
// pc.printf("\t");
// pc.printf("%f3.2\n",ypr[2] * 180/M_PI);
#endif
// blink LED to indicate activity
//blinkState = !blinkState;
//myled1 = blinkState;
}
}
}
void getAngle(){
}
void balancePID()
{
errorPID = set_point - pitchAngle; //Proportional (P)
integral += errorPID; //Integral (I)
derivative = errorPID - last_errorPID; //Derivative (D)
last_errorPID = errorPID; //Save current value for next iteration
outputPID = (Kp * errorPID) + (Ki * integral) + (Kd * derivative); //PID calculation
/* errorPID is restricted between -1 and 1 */
if(outputPID > 0.8)
outputPID = 0.8;
else if(outputPID < -0.8)
outputPID = -0.8;
}
void balanceControl()
{
int direction=0; // Variable to hold direction we want to drive
if (outputPID>0)direction=1; // Positive speed indicates forward
if (outputPID<0)direction=2; // Negative speed indicates backwards
if((abs(pitchAngle)>10.00))direction=0;
switch( direction) { // Depending on what direction was passed
case 0: // Stop case
Stop();
break;
case 1: // Forward case
Forward(abs(outputPID));
break;
case 2: // Backwards
Reverse(abs(outputPID));
break;
default: // Catch-all (Stop)
Stop();
break;
}
}
void Stop(void)
{
//motorRight.SetPWMPulsewidth(2,0);
motorLeft.SetPWMPulsewidth(2,0);
}
void Forward(float s)
{
//motorRight.SetPWMPulsewidth(1,s);
motorLeft.SetPWMPulsewidth(1,s);
}
void TurnRight(float s)
{
//motorRight.SetPWMPulsewidth(0,s);
motorLeft.SetPWMPulsewidth(1,s);
}
void TurnLeft(float s)
{
//motorRight.SetPWMPulsewidth(1,s);
motorLeft.SetPWMPulsewidth(0,s);
}
void Reverse(float s)
{
//motorRight.SetPWMPulsewidth(0,s);
motorLeft.SetPWMPulsewidth(0,s);
}
void Navigate()
{
switch (phone_char)
{
case 'f':
Forward(0.7);
break;
case 'F':
Forward(0.7);
break;
case 'b':
Reverse(0.7);
break;
case 'B':
Reverse(0.7);
break;
case 'r':
TurnRight(0.7);
break;
case 'R':
TurnRight(0.7);
break;
case 'l':
TurnLeft(0.7);
break;
case 'L':
TurnLeft(0.7);
break;
default:
Stop();
}
}
