Cube_Mini_Solution
Dependencies: mbed QEI MPU6050 BLE_API nRF51822 MCP4725 eMPL_MPU6050
main.cpp
- Committer:
- BoulusAJ
- Date:
- 2020-05-29
- Revision:
- 18:3b1aa67e11ca
- Parent:
- 17:04eebb7c29b5
File content as of revision 18:3b1aa67e11ca:
/* Boulus Abu Joudom Cube Mini - Prototype 1 V1 Mbed Version: May 28, 2020. 18:00 ZHAW - IMS Cubiod Balancing Experiment - Control Lab Template Version: 1.2 Seeed Tiny Microcontroller on Cuboid 1.0 */ /* // Physical Model of Cuboid 2.0 % Mass Parameters Cube Mini - Mass = 0.6 + 0.170 + 0.015 = 0.7850 Kg, Mass of the Full Cuboid with Motor and Flywheel Cube Mini - Flywheel Mass = 0.170 kg Cube Mini - Without Flywheel Mass = 0.6150 kg % Dimensions Parameters % Cube Mini Dimensions (10x10x10) cm Cube Mini - Length = 0.90*100e-3 m (The multiplication by 0.9 is to compensate for the rounded edges) Cube Mini - CoM = Cube Mini - Length * 0.5, Center of Mass Location % Inertia Parameters Cube Mini - Inertia (J) of the Body from the edge Edge = 0.003044 Kg.m^2, Inertia about the edge Cube Mini - Inertia (J) of Flyhweel and Rotor from Center = (0.0002104064 + 0.0000181) kg.m^2, % Motor Parameters % Motor: Maxon Flat EC45, Part number: 411812, Specs: 24V, Km 36.9e-3 Cube Mini - Motor.Km = 36.9e-3 % Torque Constant % Unit: Nm/A Cube Mini - Motor.Resistance = 0.608 % Terminal Resistance % Unit: Ohm Cube Mini - Motor.Inductance = 0.463e-3 % Unit: H, Henry % PI Controller Escon Cube Mini - Escon.PI_Tn = 500e-6; %945e-6 Cube Mini - Escon.PI_Kp = 100*(10/2^11);%576*(10/2^11) % RC Physical Velocity Voltage Filter Filter.Velocity.R = 1e3 Ohm Filter.Velocity.C = 10e-6 F Settings for Maxon ESCON controller (upload via ESCON Studio) Escon Studio file Location: ... Hardware Connections Serial PC Communication UART_TX p9 UART_RX p11 IMU SDA and SCL MPU6050_SDA p12 MPU6050_SCL p13 Velocity Input as Analogue Signal from Escon Pin 5 Button Pin Pin 6 Current output to Escon occurs through I2C connection to a DAC (Digital to Analogue Converter). DAC outputs Analogue voltage to Escon Pin SDA p3 Pin SCL P4 Escon Mapping of voltage to current and rotational velocity to voltage Escon Linear Mapping of input current from voltage is 0V is -15A and 5V is 15A, hence 2.5V is 0A !!! Escon Linear Mapping of output rotational velocity (in rpm) to voltage is -4000rpm is 0V and 4000rpm is 3.0V, hence 1.5V is 0 RPM !!! Notes The Maximum output Current allowed is 13A and the Minimum is -13A !!! Escon Linear Mapping of input current from voltage is 0V is -15A and 5V is 15A, hence 2.5V is 0A !!! All needed Libraries for the IMU (Accelerometer and Gyroscope), DAC are included. The PID is to be written by students. Use PID_Cntrl.cpp and PID_Cntrl.h as templates ... */ // Libraries #include <stdbool.h> #include "mbed.h" #include "MPU6050.h" // IMU Library #include "mcp4725.h" // DAC Library #include "IIR_filter.h" // Filter Library (Please go to the Filter Library and add in your Code) #include "LinearCharacteristics.h" // Linear Scaling Library #include "PID_Cntrl.h" // PID Library (Please go to PID Library and add in your PID Code) // Serial PC Communication #define UART_TX p9 #define UART_RX p11 // IMU SDA and SCL #define MPU6050_SDA p12 #define MPU6050_SCL p13 // PI Values #define PI 3.1415927f #define pi 3.1415927f // PC Serial Connection Serial pc(UART_TX, UART_RX); // serial connection via USB - programmer // ------------------------------- // Analog/Digitsl I/O Definitions // ------------------------------- AnalogIn Velocity_Voltage_Input(p5); // Velocity Input as Analogue Signal from Escon DigitalOut Pin_3V3(p30); // Defining P30 as 3V3 Pin for internal use. Please do not modify. InterruptIn Button(p6); // User Button Interrput // ------------------------------- // Initialization of Values and Variables // ------------------------------- // Sample time of main loops float Ts = 0.007; // Around 143 Hz. Do not change. This is the lowest value possible if printing to a serial connection is needed. // MPU 6050 Variables - Acceleration and Gyroscope Raw and Converted Data Variables int16_t AccX_Raw, AccY_Raw, AccZ_Raw; int16_t GyroX_Raw, GyroY_Raw, GyroZ_Raw; double AccX_g, AccY_g, AccZ_g; double GyroX_Degrees, GyroY_Degrees, GyroZ_Degrees, GyroZ_RadiansPerSecond; // printf Variable int k = 0; // ------------------------------- // Variables: Here define variables such as gains etc. // ------------------------------- // Accelerometer and Gyroscope // Cube Angle and Speed Variables double Cuboid_Angle_Radians, Cuboid_Angle_Degrees; double Cuboid_Angle_Speed_Degrees = 0.0; // Low pass filter variables float t = 0.5f; // Flywheel Position and Velocity variables double Velocity_Input_Voltage = 0.0f; double Velocity, Velocity_Voltage, Velocity_rpm, Velocity_Voltage_Read; // User Button int Button_Status = 0; // User Button Status Timer T_Button; // define timer for button // ------------------------------- // Controller Variables: Such as SS Controller Values and Saturation Limits // ------------------------------- // Variables concerning the Controller Design is in reference to the Matlab and Simulink Files .............. // User Input float Desired_input = 0.0f; // Initial System input in Amperes float Sys_input_Amps = 0.0f; // Sate Space Controller Values float K_SS_Controller [2] = {-57.1176*0.3, -2.6398*1.5}; // From Matlab // Controller Variables float Loop1_output; // Loop 1 controller output float Loop2_output; // Loop 2 controller output float PID_Input, PID_Output, PID_Input2, PID_Output2; // PID (PI Parameters) // PID 1 - Velocity control after lift-up float Kp_1 = -0.09; float Ki_1 = -0.09*0.5*0.5; float Kd_1 = 0; // No D-Part float Tf_1 = 1; // No D-Part // Controller Loop (PI-Part) in Case 2 (breaking case) float Kp_2 = 0.25/4.0; float Ki_2 = 0.25/4.0; float Kd_2 = 0; // No D-Part float Tf_2 = 1; // No D-Part // Saturation Parameters // PI Controller Limits const float uMin1 = -13.0f; const float uMax1= 13.0f; // Cuboid Escon Input Limits in Amps const float uMin = -13.0f; // Minimum Current Allowed const float uMax = 13.0f; // Maximum Current Allowe // ------------------------------- // User-Defined Functions // ------------------------------- // USer Button void pressed(void); // user Button pressed void released(void); // user Button released // Controller loop (via interrupt) void updateControllers(void); // controller loop (via interrupt) // Linear Scaler - Follow the mapping mentioned in the Notes above // Linear Scaler LinearCharacteristics CurrentToVoltage(-15.0f, 15.0f, 0.0f, 5.0f); LinearCharacteristics VoltageToVelocity(0.0f, 3.0f, -4000.0f, 4000.0f); // PID (PI) Controller (My PID Controller is fine but needs clarity updates) PID_Cntrl C1(Kp_1,Ki_1,Kd_1,Tf_1,Ts,uMin1,uMax1); // Defining the 1st Loop Controller (PI-Part) PID_Cntrl C2(Kp_2,Ki_2,Kd_2,Tf_2,Ts,uMin1,uMax1); // Defining the PI Controller for Chase (State 2) to keep motor velocity at zero PID_Cntrl C3(Kp_1*2.5,Ki_1*1.5,Kd_2,Tf_2,Ts,uMin1,uMax1); // Safety Implementation in Case 4 PID // Accelerometer and Gyroscope Filters IIR_filter FilterAccX(t, Ts, 1.0f); IIR_filter FilterAccY(t, Ts, 1.0f); IIR_filter FilterGyro(t, Ts, t); // Interrupts Ticker ControllerLoopTimer; // Interrupt for control loop // ------------------------------- // Functions // ------------------------------- // DAC - For Current output MCP4725 VoltageOut(p3, p4, MCP4725::Fast400kHz, 0); // IMU - MPU6050 Functions and I2C Functions I2C i2c(MPU6050_SDA, MPU6050_SCL); MPU6050 mpu(i2c); // Timers Timer Loop; //****************************************************************************** //----------------------------- Main Loop -------------------------------------- //****************************************************************************** int main() { VoltageOut.write(2.5); // Output Zero Current to the Motor // Microcontroller initialization Pin_3V3.write(1); *((unsigned int *)0x40007504) = 0x400A; // Configure the ADC to the internel reference of 1.2V. // IMU - MPU6050 initialization - Do NOT Modify mpu.initialize(); bool mpu6050TestResult = mpu.testConnection(); if(mpu6050TestResult) { pc.printf("MPU6050 test passed \n\r"); } else { pc.printf("MPU6050 test failed \n\r"); } mpu.setDLPFMode(MPU6050_DLPF_BW_42); // Set Low Pass Filter Bandwidth to 44Hz (4.9ms) for the Acc and 42Hz (4.8ms) for the Gyroscope mpu.setFullScaleGyroRange(2u); // Change the scale of the Gyroscope to +/- 1000 degrees/sec mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_4); // Change the scale of the Accelerometer to +/- 4g - Sensitivity: 4096 LSB/mg // Serial Communication pc.baud(115200); // Reset Filters and PID Controller FilterAccX.reset(0.0f); FilterAccY.reset(0.0f); FilterGyro.reset(0.0f); C1.reset(0.0f); C2.reset(0.0f); C3.reset(0.0f); pc.printf("Hello World!\n\r"); wait(5); // Control Loop Interrupt at Ts, 200Hz Rate ControllerLoopTimer.attach(&updateControllers, Ts); Button.fall(&pressed); // attach key pressed function Button.rise(&released); // attach key pressed function } //****************************************************************************** //------------------ Control Loop (called via interrupt) ----------------------- //****************************************************************************** void updateControllers(void) { // Acquire Velocity Velocity_Voltage = 3.0*1.1978917*(Velocity_Voltage_Input.read()); // *1.2V because the Vref is 1.2V // *3 because the prescaling is 1/3 // *1.1978917 is for Calibration purposes instead of 1.2 // Velocity = .... Refer to mapping Velocity_rpm = VoltageToVelocity(Velocity_Voltage); Velocity = Velocity_rpm*2.0*pi/60.0; // Aquire Raw Acceleration and Gyro Data form the IMU mpu.getMotion6(&AccX_Raw, &AccY_Raw, &AccZ_Raw, &GyroX_Raw, &GyroY_Raw, &GyroZ_Raw); // -------------- Convert Raw data to SI Units -------------------- //Convert Acceleration Raw Data to (ms^-2) - (Settings of +/- 4g) AccX_g = AccX_Raw / 8192.0f; AccY_g = AccY_Raw / 8192.0f; AccZ_g = AccZ_Raw / 8192.0f; //Convert Gyroscope Raw Data to Degrees per second GyroX_Degrees = GyroX_Raw / 32.768f; // (2^15/1000 = 32.768) GyroY_Degrees = GyroY_Raw / 32.768f; // (2^15/1000 = 32.768) GyroZ_Degrees = GyroZ_Raw / 32.768f; // (2^15/1000 = 32.768) //Convert Gyroscope Raw Data to Degrees per second GyroZ_RadiansPerSecond = (GyroZ_Raw / 32.768f)* pi/180.0f; // ----- Combine Accelerometer Data and Gyro Data to Get Angle ------ Cuboid_Angle_Radians = -1*atan2(-FilterAccX(AccX_g), FilterAccY(AccY_g)) + 0.7854f + FilterGyro(GyroZ_RadiansPerSecond); Cuboid_Angle_Degrees = Cuboid_Angle_Radians*180.0f/pi; // ------------------------- Controller ----------------------------- // Switch Statement Maybe?...... switch(Button_Status) { case 0: // Output of 0 Amps Sys_input_Amps = 0.0; break; case 1: // Breaking // PI Controller PID_Input = 0.0f - Velocity; PID_Output = C2.update(PID_Input); // System input Sys_input_Amps = PID_Output; break; case 2: // Balancing and lift-up // Current Input Updater - Amperes // Loop 1 Loop1_output = Cuboid_Angle_Radians*K_SS_Controller[0]; // Loop 2 Loop2_output = GyroZ_RadiansPerSecond*K_SS_Controller[1]; // PI Controller PID_Input = Desired_input - Velocity; PID_Output = C1.update(PID_Input); // System input Sys_input_Amps = PID_Output - Loop1_output - Loop2_output; // Do Not Modify - This is implemented to prevent the Cube from continuously speeding up while being still for after falling due to the user interrupting its movement if ( abs(Velocity) > 250.0f && (abs(Cuboid_Angle_Degrees)<50.0f && abs(Cuboid_Angle_Degrees)>40.0f) ) { Button_Status = 4; break; } break; case 3: // Fall Sys_input_Amps = 0.0; // Not implemented Yet :) break; case 4: // Lift up when stuck // Do Not Modify - This is implemented to prevent the Cube from continuously speeding up while being still for after falling due to the user interrupting its movement PID_Input2 = 0.0f - Velocity; PID_Output2 = C3.update(-1*PID_Input2); // System input Sys_input_Amps = PID_Output2; if (Sys_input_Amps > 1.0f) { Sys_input_Amps = 1.0f; } if (Sys_input_Amps < -1.0f) { Sys_input_Amps = -1.0f; } VoltageOut.write(CurrentToVoltage(Sys_input_Amps)); //pc.printf("%0.7f, %0.7f, \n\r", abs(Cuboid_Angle_Degrees), abs(Velocity)); if (abs(Velocity) < 15.0f) { Sys_input_Amps = 0.0; Button_Status = 2; break; } break; } // End of Switch Statement if (Sys_input_Amps > uMax) { Sys_input_Amps = uMax; } if (Sys_input_Amps < uMin) { Sys_input_Amps = uMin; } if (Cuboid_Angle_Degrees > -50.0f && Cuboid_Angle_Degrees < 50.0f) { // Scaling the controller output from -15 A --> 15 A to 0 V --> 5 V VoltageOut.write(CurrentToVoltage(Sys_input_Amps)); } else { Sys_input_Amps = 0.0f; VoltageOut.write(CurrentToVoltage(Sys_input_Amps)); } // ---------------- // Print Data // Printing Rate (in this case) is every Ts*100 = 0.007*100 = every 0.7 seconds if(++k >= 100) { k = 0; pc.printf("Some Outputs: %0.6f, %0.6f, %i\n\r", Sys_input_Amps, Cuboid_Angle_Degrees, Button_Status); } } //****************************************************************************** //------------------ User functions like buttens handle etc. ------------------- //****************************************************************************** //... // start timer as soon as Button is pressed void pressed() { T_Button.start(); } // Falling edge of button: enable/disable controller void released() { // readout, stop and reset timer float ButtonTime = T_Button.read(); T_Button.stop(); T_Button.reset(); if(ButtonTime > 0.05f) { Button_Status = Button_Status + 1; if (Button_Status > 3.0f) { Button_Status = 1.0; } //pc.printf("Button Status: %i\r\n", Button_Status); } }