controller.cpp
Go to the documentation of this file.
00001 /***************************************************************************/
00036 // Subscribe to a topic about the state of a dynamic system and calculate feedback to
00037 // stabilize it.
00038 
00039 #include <pid/controller.h>
00040 
00041 using namespace pid;
00042 
00043 void setpoint_callback(const std_msgs::Float64& setpoint_msg)
00044 {
00045   setpoint = setpoint_msg.data;
00046 }
00047 
00048 void plant_state_callback(const std_msgs::Float64& state_msg)
00049 {
00050 
00051   if ( !((Kp<=0. && Ki<=0. && Kd<=0.) || (Kp>=0. && Ki>=0. && Kd>=0.)) ) // All 3 gains should have the same sign
00052   {
00053     ROS_WARN("All three gains (Kp, Ki, Kd) should have the same sign for stability.");
00054   }
00055 
00056   plant_state = state_msg.data;
00057 
00058   error.at(2) = error.at(1);
00059   error.at(1) = error.at(0);
00060   error.at(0) = setpoint - plant_state; // Current error goes to slot 0
00061 
00062   // If the angle_error param is true, then address discontinuity in error calc.
00063   // For example, this maintains an angular error between -180:180.
00064   if (angle_error)
00065     {
00066       while (error.at(0) < -1.0*angle_wrap/2.0)
00067         {
00068           error.at(0) += angle_wrap;
00069         }
00070       while (error.at(0) > angle_wrap/2.0)
00071         {
00072           error.at(0) -= angle_wrap;
00073         }
00074       // The proportional error will flip sign, but the integral error
00075       // won't and the derivative error will be poorly defined. So,
00076       // reset them.
00077       error.at(2) = 0.;
00078       error.at(1) = 0.;
00079       error_integral = 0.;
00080     }
00081 
00082   // calculate delta_t
00083   if (!prev_time.isZero()) // Not first time through the program  
00084   {
00085     delta_t = ros::Time::now() - prev_time;
00086     prev_time = ros::Time::now();
00087     if (0 == delta_t.toSec())
00088     {
00089       ROS_ERROR("delta_t is 0, skipping this loop. Possible overloaded cpu at time: %f", ros::Time::now().toSec());
00090       return;
00091     }
00092   }
00093   else
00094   {
00095     ROS_INFO("prev_time is 0, doing nothing");
00096     prev_time = ros::Time::now();
00097     return;
00098   }
00099 
00100   // integrate the error
00101   error_integral += error.at(0) * delta_t.toSec();
00102 
00103   // Apply windup limit to limit the size of the integral term
00104   if ( error_integral > fabsf(windup_limit))
00105     error_integral = fabsf(windup_limit);
00106 
00107   if ( error_integral < -fabsf(windup_limit))
00108     error_integral = -fabsf(windup_limit);
00109 
00110   // My filter reference was Julius O. Smith III, Intro. to Digital Filters With Audio Applications.
00111   if (cutoff_frequency != -1)
00112   {
00113     // Check if tan(_) is really small, could cause c = NaN
00114     tan_filt = tan( (cutoff_frequency*6.2832)*delta_t.toSec()/2 );
00115 
00116     // Avoid tan(0) ==> NaN
00117     if ( (tan_filt<=0.) && (tan_filt>-0.01) )
00118       tan_filt = -0.01;
00119     if ( (tan_filt>=0.) && (tan_filt<0.01) )
00120       tan_filt = 0.01;
00121 
00122     c = 1/tan_filt;
00123   }
00124  
00125   filtered_error.at(2) = filtered_error.at(1);
00126   filtered_error.at(1) = filtered_error.at(0); 
00127   filtered_error.at(0) = (1/(1+c*c+1.414*c))*(error.at(2)+2*error.at(1)+error.at(0)-(c*c-1.414*c+1)*filtered_error.at(2)-(-2*c*c+2)*filtered_error.at(1));
00128 
00129   // Take derivative of error
00130   // First the raw, unfiltered data:
00131   error_deriv.at(2) = error_deriv.at(1);
00132   error_deriv.at(1) = error_deriv.at(0);
00133   error_deriv.at(0) = (error.at(0)-error.at(1))/delta_t.toSec();
00134 
00135   filtered_error_deriv.at(2) = filtered_error_deriv.at(1);
00136   filtered_error_deriv.at(1) = filtered_error_deriv.at(0);
00137 
00138   filtered_error_deriv.at(0) = (1/(1+c*c+1.414*c))*(error_deriv.at(2)+2*error_deriv.at(1)+error_deriv.at(0)-(c*c-1.414*c+1)*filtered_error_deriv.at(2)-(-2*c*c+2)*filtered_error_deriv.at(1));
00139 
00140   // calculate the control effort
00141   proportional = Kp * filtered_error.at(0);
00142   integral = Ki * error_integral;
00143   derivative = Kd * filtered_error_deriv.at(0);
00144   control_effort = proportional + integral + derivative;
00145 
00146   // Apply saturation limits
00147   if (control_effort > upper_limit)
00148   {
00149     control_effort = upper_limit;
00150   }
00151   else if (control_effort < lower_limit)
00152   {
00153     control_effort = lower_limit;
00154   }
00155 
00156   // Publish the stabilizing control effort if the controller is enabled
00157   if (pid_enabled)
00158   {
00159     control_msg.data = control_effort;
00160     control_effort_pub.publish(control_msg);
00161   }
00162   else
00163   {
00164     error_integral = 0.0;
00165   }
00166 
00167   return;
00168 }
00169 
00170 void pid_enable_callback(const std_msgs::Bool& pid_enable_msg)
00171 {
00172   pid_enabled = pid_enable_msg.data;
00173 }
00174 
00175 void get_params(double in, double &value, double &scale)
00176 {
00177   int digits = 0;
00178   value = in;
00179   while((fabs(value) > 1.0 || fabs(value) < 0.1) && (digits < 2 && digits > -1))
00180   {
00181     if(fabs(value) > 1.0)
00182     {
00183       value /= 10.0;
00184       digits++;
00185     }
00186     else
00187     {
00188       value *= 10.0;
00189       digits--;
00190     }
00191   }
00192   if(value > 1.0)
00193     value = 1.0;
00194   if(value < -1.0)
00195     value = -1.0;
00196 
00197   scale = pow(10.0,digits);
00198 }
00199 
00200 void reconfigure_callback(pid::PidConfig &config, uint32_t level)
00201 {
00202   if (first_reconfig)
00203   {
00204     get_params(Kp, config.Kp, config.Kp_scale);
00205     get_params(Ki, config.Ki, config.Ki_scale);
00206     get_params(Kd, config.Kd, config.Kd_scale);
00207     first_reconfig = false;
00208     return;     // Ignore the first call to reconfigure which happens at startup
00209   }
00210 
00211   Kp = config.Kp * config.Kp_scale;
00212   Ki = config.Ki * config.Ki_scale;
00213   Kd = config.Kd * config.Kd_scale;
00214   ROS_INFO("Pid reconfigure request: Kp: %f, Ki: %f, Kd: %f", Kp, Ki, Kd);
00215 }
00216 
00218   // Error checking
00220 
00221 bool validate_parameters()
00222 {
00223   if ( lower_limit > upper_limit )
00224   {
00225     ROS_ERROR("The lower saturation limit cannot be greater than the upper saturation limit.");
00226     return(false);
00227   }
00228 
00229   return true;;
00230 }
00231 
00233   // Display parameters
00235 void print_parameters()
00236 {
00237   std::cout<< std::endl<<"PID PARAMETERS"<<std::endl<<"-----------------------------------------"<<std::endl;
00238   std::cout << "Kp: " << Kp << ",  Ki: " << Ki << ",  Kd: " << Kd << std::endl;
00239   if ( cutoff_frequency== -1) // If the cutoff frequency was not specified by the user
00240     std::cout<<"LPF cutoff frequency: 1/4 of sampling rate"<<std::endl;
00241   else
00242     std::cout<<"LPF cutoff frequency: "<< cutoff_frequency << std::endl;
00243   std::cout << "pid node name: " << ros::this_node::getName() << std::endl;
00244   std::cout << "Name of topic from controller: " << topic_from_controller << std::endl;
00245   std::cout << "Name of topic from the plant: " << topic_from_plant << std::endl;
00246   std::cout << "Name of setpoint topic: " << setpoint_topic << std::endl;
00247   std::cout << "Integral-windup limit: " << windup_limit << std::endl;
00248   std::cout << "Saturation limits: " << upper_limit << "/" << lower_limit << std::endl;
00249   std::cout << "-----------------------------------------" << std::endl;
00250 
00251   return;
00252 }
00253 
00255 // Main
00257 
00258 int main(int argc, char **argv)
00259 {
00260   ROS_INFO("Starting pid with node name %s", node_name.c_str());
00261 
00262   // Initialize ROS stuff
00263   ros::init(argc, argv, node_name);     // Note node_name can be overidden by launch file
00264   ros::NodeHandle node;
00265   ros::NodeHandle node_priv("~");
00266 
00267   while (ros::Time(0) == ros::Time::now())
00268   {
00269     ROS_INFO("controller spinning waiting for time to become non-zero");
00270     sleep(1);
00271   }
00272 
00273   // Get params if specified in launch file or as params on command-line, set defaults
00274   node_priv.param<double>("Kp", Kp, 1.0);
00275   node_priv.param<double>("Ki", Ki, 0.0);
00276   node_priv.param<double>("Kd", Kd, 0.0);
00277   node_priv.param<double>("upper_limit", upper_limit, 1000.0);
00278   node_priv.param<double>("lower_limit", lower_limit, -1000.0);
00279   node_priv.param<double>("windup_limit", windup_limit, 1000.0);
00280   node_priv.param<double>("cutoff_frequency", cutoff_frequency, -1.0);
00281   node_priv.param<std::string>("topic_from_controller", topic_from_controller, "control_effort");
00282   node_priv.param<std::string>("topic_from_plant", topic_from_plant, "state");
00283   node_priv.param<std::string>("setpoint_topic", setpoint_topic, "setpoint");
00284   node_priv.param<std::string>("pid_enable_topic", pid_enable_topic, "pid_enable");
00285   node_priv.param<double>("max_loop_frequency", max_loop_frequency, 1.0);
00286   node_priv.param<double>("min_loop_frequency", min_loop_frequency, 1000.0);
00287 
00288   // Two parameters to allow for error calculation with discontinous value
00289   node_priv.param<bool>("angle_error", angle_error, false);
00290   node_priv.param<double>("angle_wrap", angle_wrap, 2.0*3.14159);
00291 
00292   // Update params if specified as command-line options, & print settings
00293   print_parameters();
00294   if (not validate_parameters())
00295   {
00296     std::cout << "Error: invalid parameter\n";
00297   }
00298 
00299   // instantiate publishers & subscribers
00300   control_effort_pub = node.advertise<std_msgs::Float64>(topic_from_controller, 1);
00301 
00302   ros::Subscriber sub = node.subscribe(topic_from_plant, 1, plant_state_callback );
00303   ros::Subscriber setpoint_sub = node.subscribe(setpoint_topic, 1, setpoint_callback );
00304   ros::Subscriber pid_enabled_sub = node.subscribe(pid_enable_topic, 1, pid_enable_callback );
00305 
00306   // configure dynamic reconfiguration
00307   dynamic_reconfigure::Server<pid::PidConfig> config_server;
00308   dynamic_reconfigure::Server<pid::PidConfig>::CallbackType f;
00309   f = boost::bind(&reconfigure_callback, _1, _2);
00310   config_server.setCallback(f);
00311 
00312   // Respond to inputs until shut down
00313   ros::spin();
00314 
00315   return 0;
00316 }
00317 


pid
Author(s): Andy Zelenak , Paul Bouchier
autogenerated on Tue May 2 2017 02:49:51