bldc driver firmware based on hobbyking cheetah compact

Dependencies:   BLDC_V2 mbed-dev-f303 FastPWM3

Dependents:   BLDC_V2

Committer:
Wooden
Date:
Wed Apr 07 10:12:43 2021 +0000
Revision:
48:a74e401a6d84
Parent:
47:f4ecf3e0576a
wooden_bldc_test

Who changed what in which revision?

UserRevisionLine numberNew contents of line
benkatz 22:60276ba87ac6 1 /// Calibration procedures for determining position sensor offset,
benkatz 22:60276ba87ac6 2 /// phase ordering, and position sensor linearization
benkatz 22:60276ba87ac6 3 ///
benkatz 22:60276ba87ac6 4
benkatz 22:60276ba87ac6 5 #include "calibration.h"
benkatz 23:2adf23ee0305 6 #include "foc.h"
benkatz 23:2adf23ee0305 7 #include "PreferenceWriter.h"
benkatz 23:2adf23ee0305 8 #include "user_config.h"
benkatz 44:efcde0af8390 9 #include "motor_config.h"
benkatz 44:efcde0af8390 10 #include "current_controller_config.h"
benkatz 22:60276ba87ac6 11
Wooden 47:f4ecf3e0576a 12 extern Serial pc;
Wooden 47:f4ecf3e0576a 13
benkatz 23:2adf23ee0305 14 void order_phases(PositionSensor *ps, GPIOStruct *gpio, ControllerStruct *controller, PreferenceWriter *prefs){
benkatz 28:8c7e29f719c5 15
benkatz 22:60276ba87ac6 16 ///Checks phase order, to ensure that positive Q current produces
benkatz 22:60276ba87ac6 17 ///torque in the positive direction wrt the position sensor.
Wooden 47:f4ecf3e0576a 18 pc.printf("\n\r Checking phase ordering\n\r");
benkatz 22:60276ba87ac6 19 float theta_ref = 0;
benkatz 22:60276ba87ac6 20 float theta_actual = 0;
benkatz 44:efcde0af8390 21 float v_d = .15f; //Put all volts on the D-Axis
Wooden 48:a74e401a6d84 22 // float v_d = .08f;
benkatz 44:efcde0af8390 23 float v_q = 0.0f;
benkatz 22:60276ba87ac6 24 float v_u, v_v, v_w = 0;
benkatz 44:efcde0af8390 25 float dtc_u, dtc_v, dtc_w = .5f;
benkatz 22:60276ba87ac6 26 int sample_counter = 0;
benkatz 22:60276ba87ac6 27
benkatz 22:60276ba87ac6 28 ///Set voltage angle to zero, wait for rotor position to settle
benkatz 25:f5741040c4bb 29 abc(theta_ref, v_d, v_q, &v_u, &v_v, &v_w); //inverse dq0 transform on voltages
benkatz 25:f5741040c4bb 30 svm(1.0, v_u, v_v, v_w, &dtc_u, &dtc_v, &dtc_w); //space vector modulation
benkatz 22:60276ba87ac6 31 for(int i = 0; i<20000; i++){
benkatz 27:501fee691e0e 32 TIM1->CCR3 = (PWM_ARR>>1)*(1.0f-dtc_u); // Set duty cycles
benkatz 27:501fee691e0e 33 TIM1->CCR2 = (PWM_ARR>>1)*(1.0f-dtc_v);
benkatz 27:501fee691e0e 34 TIM1->CCR1 = (PWM_ARR>>1)*(1.0f-dtc_w);
benkatz 22:60276ba87ac6 35 wait_us(100);
benkatz 22:60276ba87ac6 36 }
benkatz 22:60276ba87ac6 37 //ps->ZeroPosition();
benkatz 45:aadebe074af6 38 ps->Sample(DT);
benkatz 23:2adf23ee0305 39 wait_us(1000);
benkatz 38:67e4e1453a4b 40 //float theta_start = ps->GetMechPositionFixed(); //get initial rotor position
benkatz 26:2b865c00d7e9 41 float theta_start;
benkatz 23:2adf23ee0305 42 controller->i_b = I_SCALE*(float)(controller->adc2_raw - controller->adc2_offset); //Calculate phase currents from ADC readings
benkatz 23:2adf23ee0305 43 controller->i_c = I_SCALE*(float)(controller->adc1_raw - controller->adc1_offset);
benkatz 23:2adf23ee0305 44 controller->i_a = -controller->i_b - controller->i_c;
benkatz 23:2adf23ee0305 45 dq0(controller->theta_elec, controller->i_a, controller->i_b, controller->i_c, &controller->i_d, &controller->i_q); //dq0 transform on currents
benkatz 23:2adf23ee0305 46 float current = sqrt(pow(controller->i_d, 2) + pow(controller->i_q, 2));
Wooden 47:f4ecf3e0576a 47 pc.printf("\n\rCurrent\n\r");
Wooden 47:f4ecf3e0576a 48 pc.printf("%f %f %f\n\r\n\r", controller->i_d, controller->i_q, current);
benkatz 22:60276ba87ac6 49 /// Rotate voltage angle
benkatz 25:f5741040c4bb 50 while(theta_ref < 4*PI){ //rotate for 2 electrical cycles
benkatz 25:f5741040c4bb 51 abc(theta_ref, v_d, v_q, &v_u, &v_v, &v_w); //inverse dq0 transform on voltages
benkatz 25:f5741040c4bb 52 svm(1.0, v_u, v_v, v_w, &dtc_u, &dtc_v, &dtc_w); //space vector modulation
benkatz 22:60276ba87ac6 53 wait_us(100);
benkatz 27:501fee691e0e 54 TIM1->CCR3 = (PWM_ARR>>1)*(1.0f-dtc_u); //Set duty cycles
benkatz 27:501fee691e0e 55 TIM1->CCR2 = (PWM_ARR>>1)*(1.0f-dtc_v);
benkatz 27:501fee691e0e 56 TIM1->CCR1 = (PWM_ARR>>1)*(1.0f-dtc_w);
benkatz 45:aadebe074af6 57 ps->Sample(DT); //sample position sensor
benkatz 38:67e4e1453a4b 58 theta_actual = ps->GetMechPositionFixed();
benkatz 26:2b865c00d7e9 59 if(theta_ref==0){theta_start = theta_actual;}
benkatz 22:60276ba87ac6 60 if(sample_counter > 200){
benkatz 22:60276ba87ac6 61 sample_counter = 0 ;
Wooden 47:f4ecf3e0576a 62 pc.printf("%.4f %.4f\n\r", theta_ref/(NPP), theta_actual);
benkatz 22:60276ba87ac6 63 }
benkatz 22:60276ba87ac6 64 sample_counter++;
benkatz 22:60276ba87ac6 65 theta_ref += 0.001f;
benkatz 22:60276ba87ac6 66 }
benkatz 38:67e4e1453a4b 67 float theta_end = ps->GetMechPositionFixed();
benkatz 22:60276ba87ac6 68 int direction = (theta_end - theta_start)>0;
Wooden 47:f4ecf3e0576a 69 pc.printf("Theta Start: %f Theta End: %f\n\r", theta_start, theta_end);
Wooden 47:f4ecf3e0576a 70 pc.printf("Direction: %d\n\r", direction);
Wooden 47:f4ecf3e0576a 71 if(direction){pc.printf("Phasing correct\n\r");}
Wooden 47:f4ecf3e0576a 72 else if(!direction){pc.printf("Phasing incorrect. Swapping phases V and W\n\r");}
benkatz 23:2adf23ee0305 73 PHASE_ORDER = direction;
benkatz 22:60276ba87ac6 74 }
benkatz 22:60276ba87ac6 75
benkatz 22:60276ba87ac6 76
benkatz 23:2adf23ee0305 77 void calibrate(PositionSensor *ps, GPIOStruct *gpio, ControllerStruct *controller, PreferenceWriter *prefs){
benkatz 22:60276ba87ac6 78 /// Measures the electrical angle offset of the position sensor
benkatz 22:60276ba87ac6 79 /// and (in the future) corrects nonlinearity due to position sensor eccentricity
Wooden 47:f4ecf3e0576a 80 pc.printf("Starting calibration procedure\n\r");
benkatz 22:60276ba87ac6 81
benkatz 25:f5741040c4bb 82 const int n = 128*NPP; // number of positions to be sampled per mechanical rotation. Multiple of NPP for filtering reasons (see later)
benkatz 25:f5741040c4bb 83 const int n2 = 50; // increments between saved samples (for smoothing motion)
benkatz 25:f5741040c4bb 84 float delta = 2*PI*NPP/(n*n2); // change in angle between samples
benkatz 25:f5741040c4bb 85 float error_f[n] = {0}; // error vector rotating forwards
benkatz 25:f5741040c4bb 86 float error_b[n] = {0}; // error vector rotating backwards
benkatz 28:8c7e29f719c5 87 const int n_lut = 128;
benkatz 28:8c7e29f719c5 88 int lut[n_lut]= {0}; // clear any old lookup table before starting.
benkatz 28:8c7e29f719c5 89 ps->WriteLUT(lut);
benkatz 22:60276ba87ac6 90 int raw_f[n] = {0};
benkatz 22:60276ba87ac6 91 int raw_b[n] = {0};
benkatz 22:60276ba87ac6 92 float theta_ref = 0;
benkatz 22:60276ba87ac6 93 float theta_actual = 0;
Wooden 48:a74e401a6d84 94 float v_d = .25f; // Put volts on the D-Axis
Wooden 48:a74e401a6d84 95 // float v_d = .08f;
benkatz 44:efcde0af8390 96 float v_q = 0.0f;
benkatz 22:60276ba87ac6 97 float v_u, v_v, v_w = 0;
benkatz 44:efcde0af8390 98 float dtc_u, dtc_v, dtc_w = .5f;
benkatz 22:60276ba87ac6 99
benkatz 22:60276ba87ac6 100
benkatz 22:60276ba87ac6 101 ///Set voltage angle to zero, wait for rotor position to settle
benkatz 25:f5741040c4bb 102 abc(theta_ref, v_d, v_q, &v_u, &v_v, &v_w); // inverse dq0 transform on voltages
benkatz 25:f5741040c4bb 103 svm(1.0, v_u, v_v, v_w, &dtc_u, &dtc_v, &dtc_w); // space vector modulation
benkatz 22:60276ba87ac6 104 for(int i = 0; i<40000; i++){
benkatz 27:501fee691e0e 105 TIM1->CCR3 = (PWM_ARR>>1)*(1.0f-dtc_u); // Set duty cycles
benkatz 24:58c2d7571207 106 if(PHASE_ORDER){
benkatz 27:501fee691e0e 107 TIM1->CCR2 = (PWM_ARR>>1)*(1.0f-dtc_v);
benkatz 27:501fee691e0e 108 TIM1->CCR1 = (PWM_ARR>>1)*(1.0f-dtc_w);
benkatz 22:60276ba87ac6 109 }
benkatz 22:60276ba87ac6 110 else{
benkatz 27:501fee691e0e 111 TIM1->CCR1 = (PWM_ARR>>1)*(1.0f-dtc_v);
benkatz 27:501fee691e0e 112 TIM1->CCR2 = (PWM_ARR>>1)*(1.0f-dtc_w);
benkatz 22:60276ba87ac6 113 }
benkatz 22:60276ba87ac6 114 wait_us(100);
benkatz 22:60276ba87ac6 115 }
benkatz 45:aadebe074af6 116 ps->Sample(DT);
benkatz 23:2adf23ee0305 117 controller->i_b = I_SCALE*(float)(controller->adc2_raw - controller->adc2_offset); //Calculate phase currents from ADC readings
benkatz 23:2adf23ee0305 118 controller->i_c = I_SCALE*(float)(controller->adc1_raw - controller->adc1_offset);
benkatz 23:2adf23ee0305 119 controller->i_a = -controller->i_b - controller->i_c;
benkatz 23:2adf23ee0305 120 dq0(controller->theta_elec, controller->i_a, controller->i_b, controller->i_c, &controller->i_d, &controller->i_q); //dq0 transform on currents
benkatz 23:2adf23ee0305 121 float current = sqrt(pow(controller->i_d, 2) + pow(controller->i_q, 2));
Wooden 47:f4ecf3e0576a 122 pc.printf(" Current Angle : Rotor Angle : Raw Encoder \n\r\n\r");
benkatz 25:f5741040c4bb 123 for(int i = 0; i<n; i++){ // rotate forwards
benkatz 22:60276ba87ac6 124 for(int j = 0; j<n2; j++){
benkatz 22:60276ba87ac6 125 theta_ref += delta;
benkatz 25:f5741040c4bb 126 abc(theta_ref, v_d, v_q, &v_u, &v_v, &v_w); // inverse dq0 transform on voltages
benkatz 25:f5741040c4bb 127 svm(1.0, v_u, v_v, v_w, &dtc_u, &dtc_v, &dtc_w); // space vector modulation
benkatz 27:501fee691e0e 128 TIM1->CCR3 = (PWM_ARR>>1)*(1.0f-dtc_u);
benkatz 25:f5741040c4bb 129 if(PHASE_ORDER){ // Check phase ordering
benkatz 27:501fee691e0e 130 TIM1->CCR2 = (PWM_ARR>>1)*(1.0f-dtc_v); // Set duty cycles
benkatz 27:501fee691e0e 131 TIM1->CCR1 = (PWM_ARR>>1)*(1.0f-dtc_w);
benkatz 22:60276ba87ac6 132 }
benkatz 22:60276ba87ac6 133 else{
benkatz 27:501fee691e0e 134 TIM1->CCR1 = (PWM_ARR>>1)*(1.0f-dtc_v);
benkatz 27:501fee691e0e 135 TIM1->CCR2 = (PWM_ARR>>1)*(1.0f-dtc_w);
benkatz 22:60276ba87ac6 136 }
benkatz 22:60276ba87ac6 137 wait_us(100);
benkatz 45:aadebe074af6 138 ps->Sample(DT);
benkatz 22:60276ba87ac6 139 }
benkatz 45:aadebe074af6 140 ps->Sample(DT);
benkatz 38:67e4e1453a4b 141 theta_actual = ps->GetMechPositionFixed();
benkatz 22:60276ba87ac6 142 error_f[i] = theta_ref/NPP - theta_actual;
benkatz 22:60276ba87ac6 143 raw_f[i] = ps->GetRawPosition();
Wooden 47:f4ecf3e0576a 144 pc.printf("%.4f %.4f %d\n\r", theta_ref/(NPP), theta_actual, raw_f[i]);
benkatz 22:60276ba87ac6 145 //theta_ref += delta;
benkatz 22:60276ba87ac6 146 }
benkatz 26:2b865c00d7e9 147
benkatz 25:f5741040c4bb 148 for(int i = 0; i<n; i++){ // rotate backwards
benkatz 22:60276ba87ac6 149 for(int j = 0; j<n2; j++){
benkatz 22:60276ba87ac6 150 theta_ref -= delta;
benkatz 25:f5741040c4bb 151 abc(theta_ref, v_d, v_q, &v_u, &v_v, &v_w); // inverse dq0 transform on voltages
benkatz 25:f5741040c4bb 152 svm(1.0, v_u, v_v, v_w, &dtc_u, &dtc_v, &dtc_w); // space vector modulation
benkatz 27:501fee691e0e 153 TIM1->CCR3 = (PWM_ARR>>1)*(1.0f-dtc_u);
benkatz 24:58c2d7571207 154 if(PHASE_ORDER){
benkatz 27:501fee691e0e 155 TIM1->CCR2 = (PWM_ARR>>1)*(1.0f-dtc_v);
benkatz 27:501fee691e0e 156 TIM1->CCR1 = (PWM_ARR>>1)*(1.0f-dtc_w);
benkatz 22:60276ba87ac6 157 }
benkatz 22:60276ba87ac6 158 else{
benkatz 27:501fee691e0e 159 TIM1->CCR1 = (PWM_ARR>>1)*(1.0f-dtc_v);
benkatz 27:501fee691e0e 160 TIM1->CCR2 = (PWM_ARR>>1)*(1.0f-dtc_w);
benkatz 22:60276ba87ac6 161 }
benkatz 22:60276ba87ac6 162 wait_us(100);
benkatz 45:aadebe074af6 163 ps->Sample(DT);
benkatz 22:60276ba87ac6 164 }
benkatz 45:aadebe074af6 165 ps->Sample(DT); // sample position sensor
benkatz 38:67e4e1453a4b 166 theta_actual = ps->GetMechPositionFixed(); // get mechanical position
benkatz 22:60276ba87ac6 167 error_b[i] = theta_ref/NPP - theta_actual;
benkatz 22:60276ba87ac6 168 raw_b[i] = ps->GetRawPosition();
Wooden 47:f4ecf3e0576a 169 pc.printf("%.4f %.4f %d\n\r", theta_ref/(NPP), theta_actual, raw_b[i]);
benkatz 22:60276ba87ac6 170 //theta_ref -= delta;
benkatz 22:60276ba87ac6 171 }
benkatz 22:60276ba87ac6 172
benkatz 22:60276ba87ac6 173 float offset = 0;
benkatz 22:60276ba87ac6 174 for(int i = 0; i<n; i++){
benkatz 25:f5741040c4bb 175 offset += (error_f[i] + error_b[n-1-i])/(2.0f*n); // calclate average position sensor offset
benkatz 22:60276ba87ac6 176 }
benkatz 25:f5741040c4bb 177 offset = fmod(offset*NPP, 2*PI); // convert mechanical angle to electrical angle
benkatz 23:2adf23ee0305 178
benkatz 22:60276ba87ac6 179
benkatz 25:f5741040c4bb 180 ps->SetElecOffset(offset); // Set position sensor offset
benkatz 23:2adf23ee0305 181 __float_reg[0] = offset;
benkatz 23:2adf23ee0305 182 E_OFFSET = offset;
benkatz 22:60276ba87ac6 183
benkatz 22:60276ba87ac6 184 /// Perform filtering to linearize position sensor eccentricity
benkatz 22:60276ba87ac6 185 /// FIR n-sample average, where n = number of samples in one electrical cycle
benkatz 22:60276ba87ac6 186 /// This filter has zero gain at electrical frequency and all integer multiples
benkatz 25:f5741040c4bb 187 /// So cogging effects should be completely filtered out.
benkatz 22:60276ba87ac6 188
benkatz 22:60276ba87ac6 189 float error[n] = {0};
benkatz 23:2adf23ee0305 190 const int window = 128;
benkatz 22:60276ba87ac6 191 float error_filt[n] = {0};
benkatz 23:2adf23ee0305 192 float cogging_current[window] = {0};
benkatz 22:60276ba87ac6 193 float mean = 0;
benkatz 25:f5741040c4bb 194 for (int i = 0; i<n; i++){ //Average the forward and back directions
benkatz 22:60276ba87ac6 195 error[i] = 0.5f*(error_f[i] + error_b[n-i-1]);
benkatz 22:60276ba87ac6 196 }
benkatz 22:60276ba87ac6 197 for (int i = 0; i<n; i++){
benkatz 22:60276ba87ac6 198 for(int j = 0; j<window; j++){
benkatz 25:f5741040c4bb 199 int ind = -window/2 + j + i; // Indexes from -window/2 to + window/2
benkatz 22:60276ba87ac6 200 if(ind<0){
benkatz 25:f5741040c4bb 201 ind += n;} // Moving average wraps around
benkatz 22:60276ba87ac6 202 else if(ind > n-1) {
benkatz 22:60276ba87ac6 203 ind -= n;}
benkatz 22:60276ba87ac6 204 error_filt[i] += error[ind]/(float)window;
benkatz 22:60276ba87ac6 205 }
benkatz 23:2adf23ee0305 206 if(i<window){
benkatz 23:2adf23ee0305 207 cogging_current[i] = current*sinf((error[i] - error_filt[i])*NPP);
benkatz 23:2adf23ee0305 208 }
benkatz 22:60276ba87ac6 209 //printf("%.4f %4f %.4f %.4f\n\r", error[i], error_filt[i], error_f[i], error_b[i]);
benkatz 22:60276ba87ac6 210 mean += error_filt[i]/n;
benkatz 22:60276ba87ac6 211 }
benkatz 25:f5741040c4bb 212 int raw_offset = (raw_f[0] + raw_b[n-1])/2; //Insensitive to errors in this direction, so 2 points is plenty
benkatz 28:8c7e29f719c5 213
benkatz 28:8c7e29f719c5 214
Wooden 47:f4ecf3e0576a 215 pc.printf("\n\r Encoder non-linearity compensation table\n\r");
Wooden 47:f4ecf3e0576a 216 pc.printf(" Sample Number : Lookup Index : Lookup Value : Cogging Current Lookup\n\r\n\r");
benkatz 25:f5741040c4bb 217 for (int i = 0; i<n_lut; i++){ // build lookup table
benkatz 22:60276ba87ac6 218 int ind = (raw_offset>>7) + i;
benkatz 22:60276ba87ac6 219 if(ind > (n_lut-1)){
benkatz 22:60276ba87ac6 220 ind -= n_lut;
benkatz 22:60276ba87ac6 221 }
benkatz 22:60276ba87ac6 222 lut[ind] = (int) ((error_filt[i*NPP] - mean)*(float)(ps->GetCPR())/(2.0f*PI));
Wooden 47:f4ecf3e0576a 223 pc.printf("%d %d %d %f\n\r", i, ind, lut[ind], cogging_current[i]);
benkatz 26:2b865c00d7e9 224 wait(.001);
benkatz 22:60276ba87ac6 225 }
benkatz 23:2adf23ee0305 226
benkatz 25:f5741040c4bb 227 ps->WriteLUT(lut); // write lookup table to position sensor object
benkatz 23:2adf23ee0305 228 //memcpy(controller->cogging, cogging_current, sizeof(controller->cogging)); //compensation doesn't actually work yet....
benkatz 25:f5741040c4bb 229 memcpy(&ENCODER_LUT, lut, sizeof(lut)); // copy the lookup table to the flash array
Wooden 47:f4ecf3e0576a 230 pc.printf("\n\rEncoder Electrical Offset (rad) %f\n\r", offset);
benkatz 22:60276ba87ac6 231
benkatz 23:2adf23ee0305 232 if (!prefs->ready()) prefs->open();
benkatz 25:f5741040c4bb 233 prefs->flush(); // write offset and lookup table to flash
benkatz 25:f5741040c4bb 234 prefs->close();
benkatz 23:2adf23ee0305 235
benkatz 23:2adf23ee0305 236
benkatz 22:60276ba87ac6 237 }