motor_controller.c

Go to the documentation of this file.
00001 
00066 #include <includes.h>
00067 
00068 //Variables
00069 static volatile signed long int mc_pwm = 0; //the current pwm of the controller
00070 static volatile fixed mc_target_current = 0; //the target current of the pid current controller
00071 //static int mc_target_position = 0; // the target position of the pid position controller
00072 static volatile fixed mc_c1 = 0;
00073 static volatile fixed mc_new_c1 = 0;
00074 static volatile fixed mc_c2 = 0;
00075 static volatile fixed mc_new_c2 = 0;
00076 static volatile fixed mc_command_current = 0;
00077 
00078 /* State Variables */
00079 static volatile unsigned long int mc_therm_off = 0; //whether the motor was turned off due to the thermal limit
00080 static volatile unsigned long int mc_mech_off = 0; //whether the motor was turned off due to the mechanical limit
00081 static volatile unsigned long int mc_off = 0; //whether the motor controller is off (1) or not (0)
00082 static volatile unsigned long int mc_sleep = 0; //whether the motor controller is sleeping (1) or not (0)
00083 static volatile unsigned long int mc_shutdown = 0; //whether the motor controller is shutdown (1) or not (0)
00084 static volatile unsigned long int mc_direction = 0;
00085 
00086 static MC_DATA mc_data; //parameters of the motor controller
00087 static long int mc_positive = 1; //whether the motor is allowed to turn in the positive direction
00088 static long int mc_negative = 1; //whether the motor is allowed to turn in the negative direction
00089 static unsigned long int mc_phase; //what phase we are in (x:0 or 1:1-x)
00090 
00091 static float mc_save_stiff;
00092 static float mc_save_damp;
00093 static float mc_save_comm;
00094 
00116 void mc_init(float max_volts, 
00117     float max_current, 
00118     float min_current,
00119     float max_target, // dont see this in soft_setup_init
00120     float min_target,  // dont see this in soft_setup_init
00121     float thermal_current_limit, //from spec sheet
00122     float thermal_time_constant, //tau, from spec sheet
00123     float kp, 
00124     float ki, 
00125     float kd, 
00126     int max_pwm, 
00127     int error_limit, // set 4 now
00128     float smart_error_limit, // set 10 now
00129     MC_OPERATION op,        // I dont see this in soft_setup_init
00130     FIXED_VOID_F current, 
00131     FLOAT_VOID_F position,
00132     FLOAT_VOID_F velocity) //initializes the motor controller
00133 {
00134   mc_data.operation = op;
00135   
00136   mc_data.motor_voltage_max = max_volts;  //Volts 
00137   mc_data.current_max = float_to_fixed(max_current);  //Amps
00138   mc_data.current_min = float_to_fixed(min_current);  //Amps
00139   mc_data.target_max = float_to_fixed(max_target);
00140   mc_data.target_min = float_to_fixed(min_target);
00141   
00142   mc_data.thermal_limit = float_to_fixed((thermal_current_limit*thermal_current_limit)*thermal_time_constant);
00143   mc_data.tau_inv = float_to_fixed(1.0/thermal_time_constant);
00144   mc_data.thermal_safe = fixed_mult(float_to_fixed(0.75), mc_data.thermal_limit);
00145   mc_data.thermal_diff = float_to_fixed(1.0/(thermal_current_limit - fixed_to_float(mc_data.thermal_safe)));
00146   mc_data.dt = float_to_fixed(1.0/(float)(1000*SCHED_SPEED));
00147   
00148   mc_data.kp = float_to_fixed(kp);
00149   mc_data.ki = float_to_fixed(ki);
00150   mc_data.kd = float_to_fixed(kd);  
00151   mc_data.integral = 0;
00152   mc_data.max_pwm = max_pwm;
00153   mc_data.max_integral = float_to_fixed((((float)max_pwm)/ki));
00154   mc_data.error_limit  = error_limit;
00155   mc_data.smart_error_limit = float_to_fixed(smart_error_limit);
00156   mc_data.smart_error_limit_inverse = float_to_fixed(1.0*10.0/smart_error_limit); 
00157   mc_data.get_current = current;
00158   mc_data.get_position = position;
00159   mc_data.get_velocity = velocity;
00160 }
00161 
00167 void mc_set_target_current(float new_current) 
00168 {
00169   fixed curr = float_to_fixed(new_current);
00170   mc_set_target_current_fixed(curr);
00171 }
00172 
00179 static void mc_set_target_current_fixed(fixed current){
00180 //  fixed curr = mc_data.operation * current;
00181   fixed curr = current;
00182   if (curr < mc_data.target_min){
00183     error_occurred(ERROR_MC_TCURR_OOB);
00184     curr = mc_data.target_min;
00185   }
00186   if (curr > mc_data.target_max){
00187     error_occurred(ERROR_MC_TCURR_OOB);
00188     curr = mc_data.target_max;
00189   }
00190   mc_target_current = curr;
00191 }
00192 
00197 void mc_set_unsafe_target_current(float new_current) 
00198 {
00199   fixed curr = float_to_fixed(new_current);
00200   mc_target_current = /*mc_data.operation * */curr;
00201 }
00202   
00213 void mc_compliant_control(void){
00214   float pos_rads = mc_data.get_position();
00215   fixed pos = float_to_fixed(pos_rads);
00216   fixed vel = float_to_fixed(mc_data.get_velocity());
00217   fixed control = mc_command_current - fixed_mult(mc_c1, pos) - fixed_mult(mc_c2, vel);
00218   mc_set_target_current_fixed(control);
00219   mc_pid_current();
00220 }
00221 
00227 void mc_set_command_current(float new_command)
00228 {
00229   mc_command_current = float_to_fixed(new_command);
00230   mc_save_comm = new_command;
00231   //if (new_command != 7.2){mcu_led_red_blink(10);}
00232   mc_c1 = mc_new_c1;
00233   mc_c2 = mc_new_c2;
00234 }
00235 float mc_get_command_current(void){
00236   return mc_save_comm;
00237 }
00238 
00244 void mc_set_stiffness(float new_c1){
00245   mc_new_c1 = float_to_fixed(new_c1); 
00246   mc_save_stiff = new_c1;
00247   //if (new_c1 != 4.0){mcu_led_red_blink(10);}
00248 }
00249 float mc_get_stiffness(void){
00250   return mc_save_stiff;
00251 }
00252 
00258 void mc_set_dampness(float new_c2){
00259   mc_new_c2 = float_to_fixed(new_c2); 
00260   mc_save_damp = new_c2;
00261   //if (new_c2-0.2 > 0.01 || new_c2-0.2 < -0.01){mcu_led_red_blink(10);}else{mcu_led_green_blink(10);}
00262 }
00263 float mc_get_dampness(void){
00264   return mc_save_damp;
00265 }
00266 
00278 void mc_direction_control(MOTOR_DIRECTION direction, MOTOR_CONTROL command) 
00279 {
00280   int dir = direction/* * mc_data.operation*/; //if running backwards, opposite direction
00281   if (dir == MC_POSITIVE){
00282     mc_positive = command;
00283   } else if (dir == MC_NEGATIVE){
00284     mc_negative = command;
00285   }
00286 }
00287 
00295 static int mc_is_running(void){
00296   int running = (!mc_off) && (!mc_sleep) && (!mc_shutdown) && (!mc_therm_off) && (!mc_mech_off);
00297   if (!running)error_occurred(ERROR_MC_NOT_RUNNING);
00298   return running;
00299 }
00300 
00308 MC_DATA* mc_get_parameters(void) 
00309 {
00310   return &mc_data;
00311 } 
00312 
00317 int mc_get_pwm(void) 
00318 {
00319   return mc_pwm;
00320 }
00321 
00330 void mc_update_watchdog(void) 
00331 {
00332   if (!mc_shutdown && !mc_off && !mc_therm_off && !mc_mech_off){
00333     if(FIO0PIN & (1<<6)){
00334       FIO0CLR = (1<<6); //make watchdog low
00335     }
00336     else {
00337       FIO0SET = (1<<6); //set watchdog hi
00338     }
00339   }
00340 }
00341 
00349 void mc_set_shutdown(int shutdown){
00350   unsigned long sd = ((unsigned long)shutdown) & 0x7FFF;
00351   mc_shutdown = sd;
00352 }
00353 
00360 void mc_set_sleep(int sleep){
00361   unsigned long sl = ((unsigned long)sleep) & 0x7FFF;
00362   mc_sleep = sl;
00363 }
00364 
00372 static void mc_turnoff(MC_SAFETY_CAUSE reason){
00373   switch(reason){ //set appropriate state and cause error
00374     case MC_THERM: mc_therm_off = 1;
00375       error_occurred(ERROR_MC_TEMP_OFF);
00376       break;
00377     case MC_MECH: mc_mech_off = 1;
00378       error_occurred(ERROR_MC_MECH_OFF);
00379       break;
00380     default: mc_off = 1;
00381       error_occurred(ERROR_MC_SHUTOFF);
00382       break;
00383   }
00384   
00385   mc_pwm = 0;
00386   PWMMR2 = 0;   //Both PWMs set to 0
00387   PWMMR6 = 0;
00388   PWMLER = (1<<6)|(1<<2);
00389   
00390   mcu_led_green_on();
00391 }
00392 
00400 static void mc_turnon(MC_SAFETY_CAUSE reason){
00401   switch(reason){ //set appropriate state and cause error
00402     case MC_THERM: mc_therm_off = 0;
00403       error_occurred(ERROR_MC_TEMP_ON);
00404       break;
00405     case MC_MECH: mc_mech_off = 0;
00406       error_occurred(ERROR_MC_MECH_ON);
00407       break;
00408     default: //shouldn't get here
00409       break;
00410   }
00411   
00412   mcu_led_green_off();
00413 }
00414 
00419 void mc_set_pwm(int pwm)
00420 {
00421   const unsigned long int full = PWMMR0;
00422   const unsigned long int offset = 14;
00423 
00424   if (!mc_is_running()){ //controller is asleep, shutdown, or off
00425     pwm = 0;
00426   } else if (pwm < 0) { //trying to go in negative direction
00427     pwm = pwm * mc_negative;
00428   } else if (pwm > 0) { //trying to go in positive direction, but not allowed
00429     pwm = pwm * mc_positive;
00430   }  
00431   
00432   pwm = mc_data.operation * pwm;
00433 
00434   mc_pwm = pwm;
00435   
00436   mc_update_watchdog();
00437   
00438   //check bounds
00439   if (pwm > mc_data.max_pwm){
00440     pwm = mc_data.max_pwm;
00441     error_occurred(ERROR_MC_PWM_LIMIT);
00442   }
00443   else if (pwm < -1*mc_data.max_pwm){
00444     pwm = -1*mc_data.max_pwm;
00445     error_occurred(ERROR_MC_PWM_LIMIT);
00446   }
00447   
00448   if (mc_phase == 0){ //Normal operation: PWMA = x%, PWMB = 0%
00449     if(pwm < 0) { //Negative current, clockwise  
00450       PWMMR2 = 0;       //Set duty cycle of PWM2 to 0 so that only one motor direction is on
00451       PWMMR6 = -1 * pwm;        //Set duty cycle of PWM6 (out of 600 max)
00452       PWMLER = (1<<2)|(1<<6);   //Transfer new PWMMR2 & PWMMR6 values to reg at next PWM match
00453     }
00454     else { //Positive current, counter-clockwise
00455       PWMMR2 = pwm;  //Set duty cycle of PWM2 (out of 600 max)  
00456       PWMMR6 = 0;   //Set duty cycle of PWM6 to 0 so that only one motor direction is on    
00457       PWMLER = (1<<2)|(1<<6);     //Transfer new PWMMR2 & PWMMR6 values to reg at next PWM match
00458     }
00459     mc_phase = 1;
00460   } else { //Alternate Operation: PWMA = 100%, PWMB = 100-x%
00461     if(pwm < 0) {//negative current, clockwise
00462       PWMMR2 = full - (pwm * -1) - offset;
00463       PWMMR6 = full;
00464       PWMLER = (1<<2)|(1<<6);
00465     } else { //positive current, counter-clockwise
00466       PWMMR2 = full;
00467       PWMMR6 = full - (pwm) - offset;
00468       PWMLER = (1<<2)|(1<<6);
00469     }
00470     mc_phase = 0;
00471   }
00472 }
00473 
00474 static float mc_mult;
00476 float mc_get_mult(void){ return mc_mult;}
00486 void mc_pid_current(void) 
00487 {
00488   volatile fixed mc_error = 0;  
00489   static unsigned char start_count = 2;
00490   signed long int curr_pwm = 0;
00491   long long int pid_sum, p_component, i_component;
00492   fixed thermal_multiplier, mechanical_multiplier;
00493   
00494   mc_update_watchdog(); //feed watchdog if running or sleeping
00495 
00496   if (!start_count){ //wait a few iterations to allow the adcx to get meaningful data
00497     
00498     fixed current = mc_data.get_current(); //motor current on the robot
00499     fixed target_current = mc_target_current; //the target current      
00500     thermal_multiplier = mc_get_thermal_limiter(current); //thermal multiplier, est. temp and limit current
00501     mechanical_multiplier = mc_smart_check_current(current);
00502   
00503     if (mc_is_running()){
00504       // ******************************* calc prop and integral **************************
00505       //Calculate the error:
00506       mc_error = (target_current - current);
00507       //Calculate integral if no direction control active
00508       if (mc_negative && mc_positive){
00509         mc_data.integral += mc_error;
00510       }
00511       
00512       // ****************** check for saturation of integral constant ********************
00513       if (mc_data.integral > mc_data.max_integral){
00514         mc_data.integral = mc_data.max_integral; 
00515         error_occurred(ERROR_MC_INTEG_SATUR);
00516       } else if (mc_data.integral < -1*mc_data.max_integral){
00517         mc_data.integral = -1*mc_data.max_integral;
00518         error_occurred(ERROR_MC_INTEG_SATUR);
00519       }
00520       
00521       // Sum the terms of the PID algorithm to give the new PWM duty cycle value
00522       //Note: Need to bound each component such that their sum can't overflow
00523       p_component = fixed_mult_to_long((mc_error),(mc_data.kp));
00524       i_component = fixed_mult_to_long((mc_data.integral),(mc_data.ki));
00525       pid_sum = p_component + i_component;
00526     
00527       // check for fixed point overflow
00528       if(pid_sum > FIXED_MAX) {
00529         pid_sum = FIXED_MAX;
00530         error_occurred(ERROR_MC_FIXED_LIMIT);
00531       } else if (pid_sum < -1*FIXED_MAX) {
00532         pid_sum = -1*FIXED_MAX;
00533         error_occurred(ERROR_MC_FIXED_LIMIT);
00534       }
00535       
00536       if(thermal_multiplier < mechanical_multiplier){ //use whichever multiplier is smaller
00537         pid_sum = fixed_mult(pid_sum, thermal_multiplier);
00538       } else {
00539         pid_sum = fixed_mult(pid_sum, mechanical_multiplier);
00540       }
00541       
00542       curr_pwm = fixed_to_int((pid_sum));
00543           
00544       // ********************** check if PWM is within limits ****************************
00545       if (curr_pwm > mc_data.max_pwm){
00546         curr_pwm = mc_data.max_pwm;
00547         error_occurred(ERROR_MC_PWM_LIMIT);
00548       }
00549       else if (curr_pwm < -1*mc_data.max_pwm){
00550         curr_pwm = -1*mc_data.max_pwm;
00551         error_occurred(ERROR_MC_PWM_LIMIT);
00552       }
00553           
00554       mc_set_pwm(curr_pwm); 
00555       
00556     } else {
00557       mc_set_pwm(0);
00558     }
00559   } else {
00560     start_count--;
00561   } 
00562 }
00563 
00570 void mc_run_no_control(void)
00571 {
00572   if (mc_is_running()){
00573     mc_update_watchdog();
00574 //    mc_check_current(mc_data.get_current());
00575   }
00576 }
00577 
00588 static fixed mc_smart_check_current(fixed current){
00589   //Check max current for mechanical shutoff
00590   //Must change error_limit to around 20.
00591   static fixed current_error = 0;
00592   fixed motor_current = current;
00593   static fixed multiplier = FIXED_ONE;
00594   
00595   // we'll set the limit to 4.5 amps.. if it is below these limits then the things are fine.
00596   // as soon as we try to go above this limit a multiplier less than one will be generated.  
00597   // ****** Integrate Error ****** //
00598   if (motor_current > 0){
00599     current_error = current_error + (motor_current - mc_data.current_max);
00600   } else if (motor_current < 0){
00601     current_error = current_error - (motor_current - mc_data.current_min);
00602   }
00603   if (current_error <= 0){  
00604     current_error = 0;
00605     if (mc_mech_off){ //previously turned off due to mechanical limit
00606       mc_turnon(MC_MECH); //integrator <= 0 so safe to turn back on
00607     }
00608   }
00609 
00610   // ****** Calculate multiplier ****** //
00611   multiplier = FIXED_ONE - (fixed_mult(current_error , mc_data.smart_error_limit_inverse));
00612   if (multiplier < 0){multiplier = 0;}
00613   mc_mult = fixed_to_float(multiplier);
00614   // smart_erroe_limit_inverse is not exactly 1/smart_error_limit, which would mean multiplier goes from one to zero when error goes from 0 to error limit
00615   // instead I wanted the multiplier to decay faster becasue its taking time to respond/ or the error is growing very fast.
00616   // so I am settign it to be like 1/.1/smart_error_limit so that it goes to zero quicker and then remains zero
00617   
00618   // ****** Check absolute limit ****** //
00619   if (current_error >= mc_data.smart_error_limit){ //set limit to ~10, can be changed.
00620     mc_turnoff(MC_MECH);
00621   }
00622   //this is so that when curren go to dangerous level of 8 
00623   //then the error 8-4.5 = 3.5 will have about three tic tocs to reach to 10 and stops.
00624   //3.3 ms is the mechanichal time constant.  
00625         
00626   return multiplier;
00627                      
00628 }
00629 
00641 static fixed mc_get_thermal_limiter(fixed current){
00642   //Check max current for thermal shutoff
00643   //Must create current_factor, thermal_safe, thermal_limit
00644   
00645   static fixed multiplier = FIXED_ONE;
00646   static fixed a = 0; // a is  C/R*(temperature - environmental temperature), C is heat capacity of motor, R is electric resitance which causes the heating I^2*R
00647   // DANGEREROUS TO INITIALIZE A to zero, eg after lettting the robo on for a while, then switching off and then turning on again
00648   
00649   fixed a_dot_dt;  //derivative of 'a' multiplied by time step dt
00650   fixed motor_current = current;
00651   a_dot_dt = fixed_mult((fixed_mult(motor_current, motor_current)-fixed_mult(mc_data.tau_inv, a)), mc_data.dt); // derivative is -(1/tau)*a + I^2 , this is newton's law of cooling
00652   a = a + a_dot_dt;   //  
00653  
00654   
00655   if (a < mc_data.thermal_safe){ //below limit, full current
00656     multiplier = FIXED_ONE;
00657     if (mc_therm_off){ //previously turned off by thermal limit, but safe again
00658       mc_turnon(MC_THERM); //turn on controller
00659     }
00660   } else if (a >= mc_data.thermal_safe && a  < mc_data.thermal_limit){ //above safety limit, use mult factor
00661     multiplier = FIXED_ONE - (fixed_mult(a - mc_data.thermal_safe, mc_data.thermal_diff));  //mc.thermal_diff = float_to_fixed(1.0/(thermal_limit - thermal_safe))
00662     error_occurred(ERROR_MC_TEMP_SAFE);   
00663   } else if (a >= mc_data.thermal_limit){ //above strict limit, shutdown
00664     mc_turnoff(MC_THERM); //turn off controller due to thermal limit
00665     multiplier = 0;
00666   } else {
00667     multiplier = FIXED_ONE; //should never reach here
00668   }
00669     
00670   return multiplier;
00671  
00672 } 
00673     
00674   
Generated on Tue Jun 29 16:36:14 2010 by  doxygen 1.6.3