controller.cpp
Go to the documentation of this file.
1 
2 // Subscribe to a topic about the state of a dynamic system and calculate feedback to
3 // stabilize it.
4 // Should run at a faster loop rate than the plant.
5 
6 
8 
9 
10 int main(int argc, char **argv)
11 {
12  ROS_INFO("Check the num. of states and num. of inputs.");
13  ROS_INFO("These can be adjusted in 'controller_msg.msg' and 'plant_msg.msg' files.");
14  ROS_INFO("num_states: %i num_inputs: %i",num_states,num_inputs);
15  ROS_INFO(" ");
16  ROS_INFO("Also check the parameters of the controller at the top of lyap_control/include/controller.h");
17 
18  // ROS stuff
19  ros::init(argc, argv, "controller");
20  ros::NodeHandle controller_node;
21  ros::Publisher chatter_pub = controller_node.advertise<lyap_control::controller_msg>("control_effort", 1);
22  ros::Subscriber sub = controller_node.subscribe("state", 1, chatterCallback );
23 
24  // Main loop
25  while (ros::ok())
26  {
27  ros::spinOnce();
28 
29  // Publish the stabilizing control effort
30  chatter_pub.publish(u_msg);
31  }
32 
33  return 0;
34 }
35 
36 
38 // The main callback where a stabilizing u is calculated.
39 // This is where the magic happens.
41 void chatterCallback(const lyap_control::plant_msg& msg)
42 {
43  //ROS_INFO("I heard x: %f %f", msg.x[0]);
44 
45  if ( first_callback )
46  {
48  }
49 
50  //Convert the message into the boost type we need
51  for (int i=0; i<num_states; i++)
52  {
53  x[i]=msg.x[i];
54  setpoint[i] = msg.setpoint[i];
55  }
56 
57  // dx_dot_du is calculated
58  boost::numeric::ublas::matrix<double> dx_dot_du (num_inputs, num_states);
59  ublas_vector open_loop_dx_dt(x);
60  calculate_dx_dot_du( dx_dot_du, open_loop_dx_dt );
61  //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) );
62 
63  //Calc D's: the sum of x[i]*dx_dot[i]/du for all u's
65 
66  D.clear();
67 
68  for (int i=0; i<num_inputs; i++)
69  for (int j=0; j<num_states; j++)
70  // D[i] = sum( x[j]*df[j]/du[i])
71  D[i] = D[i]+( msg.x[j]-setpoint[j] )*dx_dot_du(i,j);
72  //ROS_INFO("D[0]: %f D[1]: %f", D[0], D[1]);
73 
74  // Calculate V_initial, V, and V_dot_target
75  double V_dot_target;
76  calculate_V_and_damping(V_dot_target);
77 
78  //Calc u to force V_dot<0
79  calculate_u(D, open_loop_dx_dt, V_dot_target, dx_dot_du);
80 
81  //Check control effort saturation
82  for (int i=0; i<num_inputs; i++)
83  {
84  if ( u[i] < low_saturation_limit[i] )
85  {
86  u[i] = low_saturation_limit[i];
87  }
88  if ( u[i] > high_saturation_limit[i] )
89  {
90  u[i] = high_saturation_limit[i];
91  }
92  }
93 
94  // Stuff the message to be published
95  for (int i=0; i<num_inputs; i++)
96  u_msg.u[i] = u[i];
97 
98  //ROS_INFO("Calculated u: %f", u_msg.u[0]);
99 }
100 
102 // Initial error check
104 void initial_error_check(const lyap_control::plant_msg& msg)
105 {
106  // Convert the user-specified aggressiveness from log to linear value
107  V_dot_target_initial = pow(-10, V_dot_target_initial_dB/(-20.0));
108 
109  if ( V_dot_target_initial >= 0 )
110  {
111  ROS_ERROR("V_dot_target_initial must be negative. Change it in controller_parameters.h.");
112  ros::shutdown();
113  }
114 
115  if ( (sizeof(high_saturation_limit)+sizeof(low_saturation_limit)) / sizeof(*high_saturation_limit) != 2*num_inputs )
116  {
117  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.");
118  ros::shutdown();
119  }
120 
121  for (int i=0; i<num_inputs; i++)
123  {
124  ROS_ERROR("The 'low saturation limit' is higher than 'high saturation limit.' Change them in controller_parameters.h.");
125  ros::shutdown();
126  };
127 
128  if ( msg.setpoint.size() != num_states )
129  {
130  ROS_ERROR("The published setpoint's length does not equal # of states. Check the data which is published by the plant.");
131  ros::shutdown();
132  }
133 }
134 
136 // Calculate dx_dot_du and open_loop_dx_dt
138 void calculate_dx_dot_du( boost::numeric::ublas::matrix<double> &dx_dot_du, ublas_vector &open_loop_dx_dt )
139 {
141  // Base case, open loop. Compare against this.
143 
144  // Clear u since this is open loop
145  u.clear();
146 
147  model_definition(x,open_loop_dx_dt,t);
148 
150  // Now apply a u and see the result
152 
153  ublas_vector closed_loop_dx_dt(num_states);
154 
155  for ( int i=0; i<num_inputs; i++ )
156  {
157  u.clear();
158  u[i] = 1.0;
159  model_definition(x,closed_loop_dx_dt,t); //See how this u affects the ode's. Closed_loop_dx_dt is returned
160  for ( int j=0; j<num_states; j++ )
161  {
162  dx_dot_du(i,j) = closed_loop_dx_dt[j]-open_loop_dx_dt[j]; // divide by u[i]=1.0 ==> neglected for speed
163  }
164  }
165 }
166 
168 // Calculate V_initial, V, and V_dot_target
170 void calculate_V_and_damping(double &V_dot_target)
171 {
172  //Calculate V given the incoming msg.x
173  V = 0.5*inner_prod(x-setpoint,x-setpoint);
174  ROS_INFO("V: %f", V);
175  // Store this V as V_initial if this is the first time through.
176  if ( first_callback )
177  {
178  first_callback=0;
179  V_initial = V;
180  }
181 
182  //Adjust the Lyapunov damping
183  V_dot_target = pow((V/V_initial),2.0)*V_dot_target_initial;
184 }
185 
187 // Calculate a stabilizing control effort
189 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)
190 {
191  u.clear();
192 
193  // Find the largest |D_i|
194  // It will be the base for the u_i calculations.
195  // If all D_i's are ~0, use V2.
196  int largest_D_index = index_norm_inf(D);
197 
198  if ( fabs(D[largest_D_index]) > switching_threshold ) // Use V1
199  {
200  ublas_vector P = element_prod(x-setpoint,open_loop_dx_dt); // x_i*x_i_dot
201 
202  // Start with the u that has the largest effect
203  if ( fabs( D[largest_D_index] + ( sum( element_prod(D,D) ) - pow(D[largest_D_index],2.0) )/D[largest_D_index]) > 0.0001 )
204  u(largest_D_index) = D[largest_D_index]*(V_dot_target-sum(P)) /
205  sum( element_prod(D,D) );
206 
207  // Now scale the other u's (if any) according to u_max*Di/D_max
208  for ( int i=0; i<num_inputs; i++ )
209  if ( i != largest_D_index ) // Skip this entry, we already did it
210  {
211  //if ( fabs(D[i]) > 0.1*fabs(D[largest_D_index]) ) // If this D has a significant effect. Otherwise this u will remain zero
212  //{
213  u[i] = u[largest_D_index]*D[i]/D[largest_D_index];
214  //}
215  }
216  } // End of V1 calcs
217 
218  else // Use V2
219  {
220 
221  //ublas_vector dV2_dx = (x-setpoint)*(0.9+0.1*sum( element_prod(x-setpoint,x-setpoint) ));
222 
223  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));
224 
225  // The first entry in dV2_dx is unique because the step is specified in x1, so replace the previous
226  dV2_dx[0] = (x[0]-setpoint[0])*(0.9+0.1*tanh(g))+
227  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));
228 
229 
230  //MATLAB equivalent:
231  //P_star = dV2_dx.*f_at_u_0(1:num_states);
232 
233  ublas_vector P_star = element_prod(dV2_dx, open_loop_dx_dt);
234 
235  //MATLAB equivalent:
236  //D_star = zeros(num_inputs,1);
237  //for i=1:num_inputs
238  // for j=1:num_states
239  // D_star(i) = D_star[i]+dV2_dx(j)*dx_dot_du(i,j);
240  // end
241  //end
242 
243  ublas_vector D_star(num_inputs);
244 
245  for (int i=0; i<num_inputs-1; i++)
246  for (int j=0; j<num_inputs-1; j++)
247  D_star[i] = D_star[i]+dV2_dx[j]*dx_dot_du(i,j);
248 
249  // The first input is unique.
250  // MATLAB equivalent:
251  //u(epoch,1) = (V_dot_target-sum(P_star)) / ...
252  //( D_star(1) + (sum( D_star.*D_star )- D_star(1)^2) /...
253  //D_star(1) );
254 
255  u[0] = (V_dot_target-sum(P_star)) /
256  ( D_star[0] + (sum( element_prod(D_star,D_star) )-pow(D_star[0],2)) / D_star[0] );
257 
258  // For the other inputs
259  // MATLAB equivalent:
260  //for i=2:num_inputs
261  // u(epoch,i) = u(epoch,1)*D_star(i)/D_star(1);
262  //end
263 
264  for (int i=1; i<num_inputs; i++)
265  {
266  u[i] = u[0]*D_star[i]/D_star(0);
267  }
268 
269  // Check for NaN (caused by D_star(1)==0).
270  // It means the system is likely uncontrollable.
271  // MATLAB equivalent:
272  //if ~isfinite( u(epoch,:) )
273  // u(epoch,:)= zeros(num_inputs,1);
274  //end
275 
276  for (int i=0; i<num_inputs; i++)
277  {
278  if ( (boost::math::isnan)(u[i]) )
279  {
280  u.clear(); // Reset u to zeros
281  //ROS_INFO("isnan");
282  break; // Short circuit, could save time for long u vectors
283  }
284  }
285  } // End of V2 calcs
286 }
static const int num_inputs
static double t
void publish(const boost::shared_ptr< M > &message) const
static double V_dot_target_initial
void initial_error_check(const lyap_control::plant_msg &msg)
Definition: controller.cpp:104
static const double low_saturation_limit[]
Definition: controller.h:25
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void chatterCallback(const lyap_control::plant_msg &msg)
Definition: controller.cpp:41
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
boost::numeric::ublas::vector< double > ublas_vector
Definition: controller.h:5
lyap_control::controller_msg u_msg
static const double high_saturation_limit[]
Definition: controller.h:24
ublas_vector x(num_states)
ublas_vector u(num_inputs)
static double V_initial
void calculate_V_and_damping(double &V_dot_target)
Definition: controller.cpp:170
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)
Definition: controller.cpp:189
#define ROS_INFO(...)
static double V
ROSCPP_DECL bool ok()
static const double g
Definition: controller.h:28
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
void model_definition(const ublas_vector &x, ublas_vector &dxdt, const double t)
Definition: controller.h:36
static int first_callback
ublas_vector setpoint(num_states)
static const double switching_threshold
Definition: controller.h:31
ROSCPP_DECL void shutdown()
int main(int argc, char **argv)
Definition: controller.cpp:10
static const double V_dot_target_initial_dB
Definition: controller.h:22
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)
void calculate_dx_dot_du(boost::numeric::ublas::matrix< double > &dx_dot_du, ublas_vector &open_loop_dx_dt)
Definition: controller.cpp:138
static const int num_states


lyap_control
Author(s): Andy Zelenak
autogenerated on Mon Jun 10 2019 13:50:32