controller.cpp
Go to the documentation of this file.
00001 
00002 // Subscribe to a topic about the state of a dynamic system and calculate feedback to
00003 // stabilize it.
00004 // Should run at a faster loop rate than the plant.
00005 
00006 
00007 #include "lyap_control/controller.h"
00008 
00009 
00010 int main(int argc, char **argv)
00011 {
00012     ROS_INFO("Check the num. of states and num. of inputs.");
00013     ROS_INFO("These can be adjusted in 'controller_msg.msg' and 'plant_msg.msg' files.");
00014     ROS_INFO("num_states: %i num_inputs: %i",num_states,num_inputs);
00015     ROS_INFO(" ");
00016     ROS_INFO("Also check the parameters of the controller at the top of lyap_control/include/controller.h");
00017 
00018     // ROS stuff
00019     ros::init(argc, argv, "controller");
00020     ros::NodeHandle controller_node;
00021     ros::Publisher chatter_pub = controller_node.advertise<lyap_control::controller_msg>("control_effort", 1);
00022     ros::Subscriber sub = controller_node.subscribe("state", 1, chatterCallback );
00023 
00024     // Main loop
00025     while (ros::ok())
00026     {
00027         ros::spinOnce();
00028 
00029         // Publish the stabilizing control effort
00030         chatter_pub.publish(u_msg);
00031     }
00032 
00033     return 0;
00034 }
00035 
00036 
00038 // The main callback where a stabilizing u is calculated.
00039 // This is where the magic happens.
00041 void chatterCallback(const lyap_control::plant_msg& msg)
00042 {
00043     //ROS_INFO("I heard x: %f %f", msg.x[0]);
00044 
00045     if ( first_callback )
00046     {
00047         initial_error_check(msg);
00048     }
00049 
00050     //Convert the message into the boost type we need
00051     for (int i=0; i<num_states; i++)
00052     {
00053         x[i]=msg.x[i];
00054         setpoint[i] = msg.setpoint[i];
00055     }
00056 
00057     // dx_dot_du is calculated
00058     boost::numeric::ublas::matrix<double> dx_dot_du (num_inputs, num_states);
00059     ublas_vector open_loop_dx_dt(x);
00060     calculate_dx_dot_du( dx_dot_du, open_loop_dx_dt );
00061     //ROS_INFO( "dxdotdu: %f %f %f %f", dx_dot_du(0,0), dx_dot_du(0,1), dx_dot_du(1,0), dx_dot_du(1,1) );
00062 
00063     //Calc D's: the sum of x[i]*dx_dot[i]/du for all u's
00064     ublas_vector D(num_inputs);
00065 
00066     D.clear();
00067 
00068     for (int i=0; i<num_inputs; i++)
00069         for (int j=0; j<num_states; j++)
00070             // D[i] = sum( x[j]*df[j]/du[i])
00071             D[i] = D[i]+( msg.x[j]-setpoint[j] )*dx_dot_du(i,j);
00072     //ROS_INFO("D[0]: %f D[1]: %f", D[0], D[1]);
00073 
00074     // Calculate V_initial, V, and V_dot_target
00075     double V_dot_target;
00076     calculate_V_and_damping(V_dot_target);
00077 
00078     //Calc u to force V_dot<0
00079     calculate_u(D, open_loop_dx_dt, V_dot_target, dx_dot_du);
00080 
00081     //Check control effort saturation
00082     for (int i=0; i<num_inputs; i++)
00083     {
00084         if ( u[i] < low_saturation_limit[i] )
00085         {
00086             u[i] = low_saturation_limit[i];
00087         }
00088         if ( u[i] > high_saturation_limit[i] )
00089         {
00090             u[i] = high_saturation_limit[i];
00091         }
00092     }
00093 
00094     // Stuff the message to be published
00095     for (int i=0; i<num_inputs; i++)
00096         u_msg.u[i] = u[i];
00097 
00098     //ROS_INFO("Calculated u: %f", u_msg.u[0]);
00099 }
00100 
00102 // Initial error check
00104 void initial_error_check(const lyap_control::plant_msg& msg)
00105 {
00106     // Convert the user-specified aggressiveness from log to linear value
00107     V_dot_target_initial = pow(-10, V_dot_target_initial_dB/(-20.0));
00108 
00109     if ( V_dot_target_initial >= 0 )
00110     {
00111         ROS_ERROR("V_dot_target_initial must be negative. Change it in controller_parameters.h.");
00112         ros::shutdown();
00113     }
00114 
00115     if ( (sizeof(high_saturation_limit)+sizeof(low_saturation_limit)) / sizeof(*high_saturation_limit) != 2*num_inputs )
00116     {
00117         ROS_ERROR("Check your saturation limit definitions. There are too many or too few values in one of the arrays. They must match the # of inputs to the system.");
00118         ros::shutdown();
00119     }
00120 
00121     for (int i=0; i<num_inputs; i++)
00122         if ( low_saturation_limit[i] >= high_saturation_limit[i] )
00123         {
00124             ROS_ERROR("The 'low saturation limit' is higher than 'high saturation limit.' Change them in controller_parameters.h.");
00125             ros::shutdown();
00126         };
00127 
00128     if ( msg.setpoint.size() != num_states )
00129     {
00130         ROS_ERROR("The published setpoint's length does not equal # of states. Check the data which is published by the plant.");
00131         ros::shutdown();
00132     }
00133 }
00134 
00136 // Calculate dx_dot_du and open_loop_dx_dt
00138 void calculate_dx_dot_du( boost::numeric::ublas::matrix<double> &dx_dot_du, ublas_vector &open_loop_dx_dt )
00139 {
00141     // Base case, open loop. Compare against this.
00143 
00144     // Clear u since this is open loop
00145     u.clear();
00146 
00147     model_definition(x,open_loop_dx_dt,t);
00148 
00150     // Now apply a u and see the result
00152 
00153     ublas_vector closed_loop_dx_dt(num_states);
00154 
00155     for ( int i=0; i<num_inputs; i++ )
00156     {
00157         u.clear();
00158         u[i] = 1.0;
00159         model_definition(x,closed_loop_dx_dt,t); //See how this u affects the ode's. Closed_loop_dx_dt is returned
00160         for ( int j=0; j<num_states; j++ )
00161         {
00162             dx_dot_du(i,j) = closed_loop_dx_dt[j]-open_loop_dx_dt[j]; // divide by u[i]=1.0 ==> neglected for speed
00163         }
00164     }
00165 }
00166 
00168 // Calculate V_initial, V, and V_dot_target
00170 void calculate_V_and_damping(double &V_dot_target)
00171 {
00172     //Calculate V given the incoming msg.x
00173     V = 0.5*inner_prod(x-setpoint,x-setpoint);
00174     ROS_INFO("V: %f", V);
00175     // Store this V as V_initial if this is the first time through.
00176     if ( first_callback )
00177     {
00178         first_callback=0;
00179         V_initial = V;
00180     }
00181 
00182     //Adjust the Lyapunov damping
00183     V_dot_target = pow((V/V_initial),2.0)*V_dot_target_initial;
00184 }
00185 
00187 // Calculate a stabilizing control effort
00189 void calculate_u(ublas_vector &D, ublas_vector &open_loop_dx_dt, const double &V_dot_target, boost::numeric::ublas::matrix<double> &dx_dot_du)
00190 {
00191     u.clear();
00192 
00193     // Find the largest |D_i|
00194     // It will be the base for the u_i calculations.
00195     // If all D_i's are ~0, use V2.
00196     int largest_D_index = index_norm_inf(D);
00197 
00198     if ( fabs(D[largest_D_index]) > switching_threshold ) // Use V1
00199     {
00200         ublas_vector P = element_prod(x-setpoint,open_loop_dx_dt); // x_i*x_i_dot
00201 
00202         // Start with the u that has the largest effect
00203         if ( fabs( D[largest_D_index] + ( sum( element_prod(D,D) ) - pow(D[largest_D_index],2.0) )/D[largest_D_index]) > 0.0001 )
00204             u(largest_D_index) = D[largest_D_index]*(V_dot_target-sum(P)) /
00205                     sum( element_prod(D,D) );
00206 
00207         // Now scale the other u's (if any) according to u_max*Di/D_max
00208         for ( int i=0; i<num_inputs; i++ )
00209             if ( i != largest_D_index ) // Skip this entry, we already did it
00210             {
00211                 //if ( fabs(D[i]) > 0.1*fabs(D[largest_D_index]) ) // If this D has a significant effect. Otherwise this u will remain zero
00212                 //{
00213                 u[i] = u[largest_D_index]*D[i]/D[largest_D_index];
00214                 //}
00215             }
00216     } // End of V1 calcs
00217 
00218     else // Use V2
00219     {
00220 
00221         //ublas_vector dV2_dx = (x-setpoint)*(0.9+0.1*sum( element_prod(x-setpoint,x-setpoint) ));
00222 
00223         ublas_vector dV2_dx = (x-setpoint)*(0.9+0.1*tanh(g)+0.05*sum( element_prod(x-setpoint,x-setpoint) )*pow(cosh(g),-2));
00224 
00225         // The first entry in dV2_dx is unique because the step is specified in x1, so replace the previous
00226         dV2_dx[0] = (x[0]-setpoint[0])*(0.9+0.1*tanh(g))+
00227                 0.05*sum(element_prod(x-setpoint,x-setpoint))*(pow((x[0]-setpoint[0]),-1)*tanh(g)+(x[0]-setpoint[0])*pow(cosh(g),-2));
00228 
00229 
00230         //MATLAB equivalent:
00231         //P_star = dV2_dx.*f_at_u_0(1:num_states);
00232 
00233         ublas_vector P_star = element_prod(dV2_dx, open_loop_dx_dt);
00234 
00235         //MATLAB equivalent:
00236         //D_star = zeros(num_inputs,1);
00237         //for i=1:num_inputs
00238         //  for j=1:num_states
00239         //    D_star(i) = D_star[i]+dV2_dx(j)*dx_dot_du(i,j);
00240         //  end
00241         //end
00242 
00243         ublas_vector D_star(num_inputs);
00244 
00245         for (int i=0; i<num_inputs-1; i++)
00246             for (int j=0; j<num_inputs-1; j++)
00247                 D_star[i] = D_star[i]+dV2_dx[j]*dx_dot_du(i,j);
00248 
00249         // The first input is unique.
00250         // MATLAB equivalent:
00251         //u(epoch,1) = (V_dot_target-sum(P_star)) / ...
00252         //( D_star(1) + (sum( D_star.*D_star )- D_star(1)^2) /...
00253         //D_star(1) );
00254 
00255         u[0] = (V_dot_target-sum(P_star)) /
00256                 ( D_star[0] + (sum( element_prod(D_star,D_star) )-pow(D_star[0],2)) / D_star[0] );
00257 
00258         // For the other inputs
00259         // MATLAB equivalent:
00260         //for i=2:num_inputs
00261         //  u(epoch,i) = u(epoch,1)*D_star(i)/D_star(1);
00262         //end
00263 
00264         for (int i=1; i<num_inputs; i++)
00265         {
00266             u[i] = u[0]*D_star[i]/D_star(0);
00267         }
00268 
00269         // Check for NaN (caused by D_star(1)==0).
00270         // It means the system is likely uncontrollable.
00271         // MATLAB equivalent:
00272         //if ~isfinite( u(epoch,:) )
00273         //  u(epoch,:)= zeros(num_inputs,1);
00274         //end
00275 
00276         for (int i=0; i<num_inputs; i++)
00277         {
00278             if ( (boost::math::isnan)(u[i]) )
00279             {
00280                 u.clear(); // Reset u to zeros
00281                 //ROS_INFO("isnan");
00282                 break; // Short circuit, could save time for long u vectors
00283             }
00284         }
00285     } // End of V2 calcs
00286 }


lyap_control
Author(s): Andy Zelenak
autogenerated on Wed Mar 2 2016 12:14:43