//========================================================= //Inverted Pendulum with Kalman Filter //MPU board: NUCLEO-F401RE //Accelerometer + Gyro sensor: BMX055 //Motor driver: BD6212HFP x 2 //2020/06/18 N. Beppu //========================================================= #include "mbed.h" #include "math.h" //========================================================= //Port Setting DigitalOut led1(LED1); //LED on the NUCLEO board I2C i2c(PB_9, PB_8); // Gyro + ACC (SDA, SCLK) BusIn encoder_bus(PC_11, PD_2); //Encoder (LSB to MSB) PwmOut motor_in1(PC_8); //BD6212 FIN PwmOut motor_in2(PA_11); //BD6212 RIN DigitalOut led_r(PC_0); //Red LED DigitalOut led_g(PC_1); //Green LED DigitalOut led_y(PC_2); //Yellow LED //========================================================= //Ticker Ticker timer1; //for rotary encoder Ticker timer2; //for Kalman filter (angle) //========================================================= //Accelerometer and gyro statistical data int sample_num = 100; float meas_interval = 0.01; float theta_mean; float theta_variance; float theta_dot_mean; float theta_dot_variance; //========================================================= //Rotary encoder variables int rotary_encoder_update_rate = 25; //usec int rotary_encoder_resolution = 100; int encoder_value = 0; int table[16] = {0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0}; float pre_theta2 = 0; //========================================================= //Kalman filter (for angle estimation) variables //Update rate float theta_update_freq = 400; //Hz float theta_update_interval = 1.0f/theta_update_freq; //State vector //[[theta(degree)], [offset of theta_dot(degree/sec)]] float theta_data_predict[2][1]; float theta_data[2][1]; //Covariance matrix float P_theta_predict[2][2]; float P_theta[2][2]; //"A" of the state equation float A_theta[2][2] = {{1, -theta_update_interval}, {0, 1}}; //"B" of the state equation float B_theta[2][1] = {{theta_update_interval}, {0}}; //"C" of the state equation float C_theta[1][2] = {{1, 0}}; //========================================================= //Kalman filter (for all system estimation) variables //State vector //[[theta1(rad)], [theta1_dot(rad/s)], [theta2(rad)]. [theta2_dot(rad/s)]] float x_data_predict[4][1]; float x_data[4][1]; //Covariance matrix float P_x_predict[4][4]; float P_x[4][4]; //"A" of the state equation (update freq = 100 Hz) float A_x[4][4] = { {1.00209e+00, 1.00070e-02, 0.00000e+00, 7.76911e-05}, {4.18058e-01, 1.00209e+00, 0.00000e+00, 1.52693e-02}, {-1.09229e-03, -3.67296e-06, 1.00000e+00, 9.47937e-03}, {-2.14678e-01, -1.09229e-03, 0.00000e+00, 8.97709e-01} }; //"B" of the state equation (update freq = 100 Hz) float B_x[4][1] = { {-4.99557e-04}, {-9.81822e-02}, {3.34770e-03}, {6.57733e-01} }; //"C" of the state equation (update freq = 100 Hz) float C_x[4][4] = { {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1} }; //measurement noise float measure_variance_mat[4][4]; //System noise float voltage_error = 0.01; float voltage_variance = voltage_error * voltage_error; //========================================================= //Motor control variables float feedback_rate = 0.01; //sec float motor_value = 0; int pwm_width = 0; float motor_offset = 0.096; //volt float vdd_voltage = 5.0; //volt //========================================================= //Gain vector for the state feedback //(R=1000, Q = diag(1, 1, 10, 10), f=100Hz) float Gain[4] = {2.41954e+01, 3.84444e+00, 9.26969e-02, 3.64506e-01}; //========================================================= // Matrix common functions //========================================================= //Matrix addition void mat_add(float *m1, float *m2, float *sol, int row, int column) { for(int i=0; i pivot ) { pivot = temp[j*(2*row) + i]; pivot_index = j; } } if(pivot_index != i) { for(int j=0; j<2*row; j++) { pivot_temp = temp[ pivot_index * (2*row) + j ]; temp[pivot_index * (2*row) + j] = temp[i*(2*row) + j]; temp[i*(2*row) + j] = pivot_temp; } } //division for(int j=0; j<2*row; j++) { temp[i*(2*row) + j] /= pivot; } //sweep for(int j=i+1; j> 4; y_temp_L = y_temp_L & 0x0f; //read RATE_Y_MSB registor (0x05) int y_temp_H = i2c_mem_read(0x19, 0x05); //calculate Y acceleration int y_data = y_temp_L + 16 * y_temp_H; if(y_data > 2047) { y_data = -1 * (4096 - y_data); } //----------------------------------------------------- //read ACCD_Z_LSB registor (0x06) int z_temp_L = i2c_mem_read(0x19, 0x06); z_temp_L = z_temp_L >> 4; z_temp_L = z_temp_L & 0x0f; //read RATE_Z_MSB registor (0x07) int z_temp_H = i2c_mem_read(0x19, 0x07); //calculate Z acceleration int z_data = z_temp_L + 16 * z_temp_H; if(z_data > 2047) { z_data = -1 * (4096 - z_data); } //----------------------------------------------------- //calculate theta float theta_deg = atan( float(z_data) / float(y_data) ); return (float)theta_deg * 57.29578f; //degree } //statistical data of accelerometer void acc_init() { //initialize ACC register 0x0F (range) //Full scale = +/- 2 G i2c_mem_write(0x19, 0x0f, 0x03); //initialize ACC register 0x10 (band width) //Filter bandwidth = 1000 Hz i2c_mem_write(0x19, 0x10, 0x0f); //get data float theta_array[sample_num]; for(int i=0; i 32767) { x_data = -1 * (65536 - x_data); } x_data = -1 * x_data; // +1000 (deg/sec) / 2^15 = 0.0305176 return float(x_data) * 0.0305176f; // deg/sec } //statistical data of gyro void gyro_init() { //initialize Gyro register 0x0F (range) //Full scale = +/- 1000 deg/s i2c_mem_write(0x69, 0x0f, 0x01); //initialize Gyro register 0x10 (band width) //Data rate = 1000 Hz, Filter bandwidth = 116 Hz i2c_mem_write(0x69, 0x10, 0x02); //get data float theta_dot_array[sample_num]; for(int i=0;i vdd_voltage) { Vin = vdd_voltage; } if(motor_value < -vdd_voltage) { Vin = -vdd_voltage; } mat_mul(A_x[0], x_data[0], A_x_x[0], 4, 4, 4, 1);//Ax_hat mat_mul_const(B_x[0], Vin , B_x_Vin[0], 4, 1);//Bu mat_add(A_x_x[0], B_x_Vin[0], x_data_predict[0], 4, 1);//Ax+Bu //predict covariance matrix: P'=APA^T + BUB^T mat_tran(A_x[0], tran_A_x[0], 4, 4);//A^T mat_mul(A_x[0], P_x[0], AP[0], 4, 4, 4, 4);//AP mat_mul(AP[0], tran_A_x[0], APAT[0], 4, 4, 4, 4);//APA^T mat_tran(B_x[0], tran_B_x[0], 4, 1);//B^T mat_mul(B_x[0], tran_B_x[0], BBT[0], 4, 1, 1, 4);//BB^T mat_mul_const(BBT[0], voltage_variance, BUBT[0], 4, 4);//BUB^T mat_add(APAT[0], BUBT[0], P_x_predict[0], 4, 4);//APA^T+BUB^T //--------------------------------------- //Motor control //--------------------------------------- //reset motor_value = 0; //calculate motor voltage for(int i=0; i<4; i++) { motor_value += Gain[i] * x_data[i][0]; } //offset if(motor_value > 0) { motor_value += motor_offset ; } if(motor_value < 0) { motor_value -= motor_offset; } //calculate PWM pulse width //max pulse width = 50 usec (20 kHz) pwm_width = int( (motor_value/vdd_voltage) * 50.0f ); //drive the motor in forward if(pwm_width>=0) { //over voltage if(pwm_width>50) { pwm_width = 50; } led_g = 1; motor_in1.pulsewidth_us(pwm_width); motor_in2.pulsewidth_us(0); } //drive the motor in reverse else { //calculate the absolute value pwm_width = -1 * pwm_width; //over voltage if(pwm_width>50) { pwm_width = 50; } led_r = 1; motor_in1.pulsewidth_us(0); motor_in2.pulsewidth_us(pwm_width); } // prepare for the next calculation of theta2_dot pre_theta2 = y[2][0]; // start the angle update process timer2.attach(&update_theta, theta_update_interval); // wait wait(feedback_rate); } //=========================================== //Main loop (end) //=========================================== }