Tetrapod Project
single_leg_controller.cpp
Go to the documentation of this file.
2 
3 SingleLegController::SingleLegController(double _publish_frequency)
4 {
5  // Store the publish frequency for the controller
6  this->publish_frequency = _publish_frequency;
7 
8  // Update the number of iterations per phase based on the publish frequency
10 
11  // Set all the states to uninitialized
12  for(int i = 0; i < NUMBER_OF_MOTORS; i++)
13  {
14  this->joint_pos(i) = UNINITIALIZED_STATE;
15  }
16 
17  // Set the initial goal position
18  this->foot_pos_goal[0] = this->x_nominal;
19  this->foot_pos_goal[1] = this->y_nominal;
20  this->foot_pos_goal[2] = - this->hip_height;
21 
22  // Set the actuator gains
23  double k_pos = 5.0;
24 
25  this->k_p_pos_hy = k_pos;
26  this->k_i_pos_hy = k_pos;
27 
28  this->k_p_pos_hp = k_pos;
29  this->k_i_pos_hp = k_pos;
30 
31  this->k_p_pos_kp = k_pos;
32  this->k_i_pos_kp = k_pos;
33 
34  double k_vel = 10.0;
35 
36  this->k_p_vel_hy = k_vel;
37  this->k_i_vel_hy = k_vel;
38 
39  this->k_p_vel_hp = k_vel;
40  this->k_i_vel_hp = k_vel;
41 
42  this->k_p_vel_kp = k_vel;
43  this->k_p_vel_kp = k_vel;
44 
45  double k_torque = 50.0;
46 
47  this->k_p_torque_hy = k_torque;
48  this->k_i_torque_hy = k_torque;
49 
50  this->k_p_torque_hp = k_torque;
51  this->k_i_torque_hp = k_torque;
52 
53  this->k_p_torque_kp = k_torque;
54  this->k_i_torque_kp = k_torque;
55 
56  // Set the closed loop torque control gains
57  double k_p_hy = 100.0;
58  double k_p_hp = 100.0;
59  double k_p_kp = 100.0;
60 
61  double k_d_hy = 5.0;
62  double k_d_hp = 5.0;
63  double k_d_kp = 5.0;
64 
65  // Store the values in matricies that will be used by the torque controller
66  this->K_p(0, 0) = k_p_hy;
67  this->K_p(1, 1) = k_p_hp;
68  this->K_p(2, 2) = k_p_kp;
69 
70  this->K_d(0, 0) = k_d_hy;
71  this->K_d(1, 1) = k_d_hp;
72  this->K_d(2, 2) = k_d_kp;
73 
74  // Set the velocity control position error gains
75  double k_pos_error_vel_control_hy = 25.0;
76  double k_pos_error_vel_control_hp = 25.0;
77  double k_pos_error_vel_control_kp = 25.0;
78 
79  K_pos_error_vel_control(0, 0) = k_pos_error_vel_control_hy;
80  K_pos_error_vel_control(1, 1) = k_pos_error_vel_control_hp;
81  K_pos_error_vel_control(2, 2) = k_pos_error_vel_control_kp;
82 
83  // Initializing the size of the logging messages
84  for(int i = 0; i < NUMBER_OF_MOTORS; i++)
85  {
86  this->joint_state_log_msg.position.push_back(UNINITIALIZED_STATE);
87  this->joint_state_log_msg.velocity.push_back(UNINITIALIZED_STATE);
88  this->joint_state_log_msg.effort.push_back(UNINITIALIZED_STATE);
89 
90  this->joint_reference_log_msg.position.push_back(UNINITIALIZED_STATE);
91  this->joint_reference_log_msg.velocity.push_back(UNINITIALIZED_STATE);
92  this->joint_reference_log_msg.effort.push_back(UNINITIALIZED_STATE);
93  }
94 }
95 
96 
97 
98 /*** ROS FUNCTIONS ***/
99 
101 {
102  // Check if ROS has been initialized. If false, initialize ROS.
103  if(!ros::isInitialized())
104  {
105  int argc = 0;
106  char **argv = NULL;
107  ros::init
108  (
109  argc,
110  argv,
111  "single_leg_controller_node",
112  ros::init_options::NoSigintHandler
113  );
114  }
115 
116  // Do this to prevent multiple nodes from using the same name
117  this->node_handle.reset(new ros::NodeHandle("single_leg_controller_node"));
118 
119  // Initialize the generalized coordinates subscriber
121  (
122  "/my_robot/gen_coord",
123  1,
125  this
126  );
127 
128  // Initialize the generalized velocities subscriber
130  (
131  "/my_robot/gen_vel",
132  1,
134  this
135  );
136 
137  this->generalized_forces_subscriber = node_handle->subscribe
138  (
139  "/my_robot/joint_forces",
140  1,
142  this
143  );
144 
145  // Initialize the subscriber listening to real world motor states
146  this->joint_state_subscriber = node_handle->subscribe
147  (
148  "/motor/states",
149  1,
151  this
152  );
153 
154  // Initialize the ready to proceed subscriber
155  this->ready_to_proceed_subscriber = node_handle->subscribe
156  (
157  "/ready_to_proceed",
158  1,
160  this
161  );
162 
163  // Initialize the joint setpoint subscriber
164  this->joint_setpoint_subscriber = node_handle->subscribe
165  (
166  "/controller/joint_setpoints",
167  1,
169  this
170  );
171 
172  // Initialize the motor confirmation subscriber
173  this->motor_confirmation_subscriber = node_handle->subscribe
174  (
175  "/motor/confirmation",
176  1,
178  this
179  );
180 
181  // Initialize the joint state publisher
182  this->joint_state_publisher = this->node_handle->advertise<sensor_msgs::JointState>("/my_robot/joint_state_cmd", 1);
183 
184  // Initialize the motor set motor gain publisher
185  this->motor_gain_publisher = this->node_handle->advertise<std_msgs::Float64MultiArray>("/motor/gains", 1);
186 
187  // Initialize the state logging publisher
188  this->log_joint_states_publisher = this->node_handle->advertise<sensor_msgs::JointState>("/logging/joint_states", 10);
189 
190  // Initialize the reference logging publisher
191  this->log_joint_references_publisher = this->node_handle->advertise<sensor_msgs::JointState>("/logging/joint_references", 10);
192 }
193 
194 void SingleLegController::generalizedCoordinatesCallback(const std_msgs::Float64MultiArrayConstPtr &_msg)
195 {
196  for(int i = 0; i < NUMBER_OF_MOTORS; i++)
197  {
198  // + 6 is added because the first six states are (x, y, z, roll, pitch, yaw)
199  // The remaining three states are theta_hy, theta_hp, theta_kp
200  this->joint_pos(i) = _msg->data[i + 6];
201  }
202 }
203 
204 void SingleLegController::generalizedVelocitiesCallback(const std_msgs::Float64MultiArrayConstPtr &_msg)
205 {
206  for(int i = 0; i < NUMBER_OF_MOTORS; i++)
207  {
208  // + 6 is added because the first six states are (x_dot, y_dot, z_dot, roll_dot, pitch_dot, yaw_dot)
209  // The remaining three states are theta_hy_dot, theta_hp_dot, theta_kp_dot
210  this->joint_vel(i) = _msg->data[i + 6];
211  }
212 }
213 
214 void SingleLegController::generalizedForcesCallback(const std_msgs::Float64MultiArrayConstPtr &_msg)
215 {
216  for(int i = 0; i < NUMBER_OF_MOTORS; i++)
217  {
218  // + 6 is added because the first six states are (x_dot, y_dot, z_dot, roll_dot, pitch_dot, yaw_dot)
219  // The remaining three states are theta_hy_dot, theta_hp_dot, theta_kp_dot
220  this->joint_torque(i) = _msg->data[i];
221  }
222 }
223 
224 void SingleLegController::jointStateCallback(const sensor_msgs::JointStatePtr &_msg)
225 {
226  for(int i = 0; i < NUMBER_OF_MOTORS; i++)
227  {
228  // Store the jont angles
229  this->joint_pos(i) = _msg->position[i];
230 
231  // Store the joint velocities
232  this->joint_vel(i) = _msg->velocity[i];
233 
234  // Store the joint torques
235  this->joint_torque(i) = _msg->effort[i];
236  }
237 }
238 
239 void SingleLegController::readyToProceedCallback(const std_msgs::Bool &_msg)
240 {
241  // Store the message
242  this->ready_to_proceed = _msg.data;
243 }
244 
245 void SingleLegController::jointSetpointCallback(const std_msgs::Float64MultiArrayConstPtr &_msg)
246 {
247  for(int i = 0; i < 3; i++)
248  {
249  // Set the desired joint state goal
250  this->foot_pos_goal(i) = _msg->data[i];
251  }
252 }
253 
254 void SingleLegController::motorConfirmationCallback(const std_msgs::Bool &_msg)
255 {
256  // Inform that the gains have been set
257  this->gains_set = _msg.data;
258 }
259 
260 
261 /*** CONTROL FUNCTIONS ***/
262 
263 Eigen::Matrix<double, 3, 1> SingleLegController::calculateSwingLegHeightTrajectory(double _percentage, double _period, double _max_swing_height, double _hip_height)
264 {
265  // Defining parameters for the polynomial. Please see the report for an explanation
266  double c = _max_swing_height;
267  double a = c/pow(0.5, 4.0);
268  double b = -2.0*a*pow(0.5, 2.0);
269 
270  // Define a vector to store the foot height trajectory
271  Eigen::Matrix<double, 3, 1> trajectory;
272 
273  // Shift the gait cycle location to be centered around 0, [-0.5, 0.5]
274  double x = _percentage - 0.5;
275 
276  // Calculate the swing foot height (See report)
277  trajectory(0) = a*pow(x, 4.0) + b*pow(x, 2.0) + c - _hip_height;
278 
279  // Calculate the swing foot height velocity (see report)
280  trajectory(1) = (4.0*a*pow(x, 3.0) + 2.0*b*x)/_period;
281 
282  // Calculate the swing foot height acceleration (see report)
283  trajectory(2) = (12.0*a*pow(x, 2.0) + 2.0*b)/pow(_period, 2.0);
284 
285  // Return the trajectory
286  return trajectory;
287 }
288 
290 (
291  const double &_percentage,
292  const double &_period,
293  const double &_max_travel,
294  double &_axis_pos,
295  double &_axis_vel,
296  double &_axis_acc
297 )
298 {
299  // Define paramaters for the polynomial. Please see the report for an explanation
300  double a = 30.0*_max_travel;
301  double b = -15.0*_max_travel;
302  double c = 1.875*_max_travel;
303  double d = -_max_travel*7.0/16.0;
304 
305  // Calculate the position from the polynomial
306  _axis_pos = 0.2*a*pow((_percentage - 0.5), 5.0) + b*pow((_percentage - 0.5), 3.0)/3.0 + c*_percentage + d;
307 
308  // Calculate the velocity from the derivative of the polynomial
309  _axis_vel = (a*pow((_percentage - 0.5), 4.0) + b*pow((_percentage - 0.5), 2.0) + c)/_period;
310 
311  // Calculate the acceleration from the two times derivative of the polynomial
312  _axis_acc = (4.0*a*pow((_percentage - 0.5), 3.0) + 2.0*b*(_percentage - 0.5))/(_period*_period);
313 }
314 
316 {
317  // Calculate the location in the gait cycle
318  double progress = current_iteration/final_iteration;
319 
320  // Foot position [m] in x direction relative to the nominal foot position
321  double foot_dx;
322 
323  // Foot position [m] in the y direction relative to the nominal foot position
324  double foot_dy;
325 
326  // Foot velocity [m/s] in the x direction relative to the hip
327  double foot_vel_x;
328 
329  // Foot velocity [m/s] in the y direction relative to the hip
330  double foot_vel_y;
331 
332  // Foot acceleration [m/s^2] in the x direction relative to the hip
333  double foot_acc_x;
334 
335  // Foot accaleration [m/s^2] in the y direction relative to the hip
336  double foot_acc_y;
337 
338  // Update the foot trajectory in the hip x direction
339  this->calculateSingleAxisTrajectory(progress, this->phase_period, this->x_step_distance, foot_dx, foot_vel_x, foot_acc_x);
340 
341  // Update the foot trajectory in the hip y direction
342  this->calculateSingleAxisTrajectory(progress, this->phase_period, this->y_step_distance, foot_dy, foot_vel_y, foot_acc_y);
343 
344  // Update the foot trajectory in the hip z direction
345  Eigen::Matrix<double, 3, 1> z = this->calculateSwingLegHeightTrajectory(progress, this->phase_period, this->max_swing_height, this->hip_height);
346  //z(0) = -hip_height;
347  //z(1) = 0.0;
348  //z(2) = 0.0;
349 
350  // Update the foot positions in the hip frame
351  this->foot_pos_ref(0) = this->x_nominal - this->x_step_distance + foot_dx;
352  this->foot_pos_ref(1) = this->y_nominal - this->y_step_distance + foot_dy;
353  this->foot_pos_ref(2) = z(0);
354  //this->foot_pos_ref(2) = -this->hip_height; // For testing
355 
356  // Update the foot velocities in the hip frame
357  this->foot_vel_ref(0) = foot_vel_x;
358  this->foot_vel_ref(1) = foot_vel_y;
359  this->foot_vel_ref(2) = z(1);
360 
361  // Update the foot accelerations in the hip frame
362  this->foot_acc_ref(0) = foot_acc_x;
363  this->foot_acc_ref(1) = foot_acc_y;
364  this->foot_acc_ref(2) = z(2);
365 }
366 
368 {
369  // Calculate the location in the gait cycle
370  double progress = current_iteration/final_iteration;
371 
372  // Foot position [m] in x direction relative to the nominal foot position
373  double foot_dx;
374 
375  // Foot position [m] in the y direction relative to the nominal foot position
376  double foot_dy;
377 
378  // Foot velocity [m/s] in the x direction relative to the hip
379  double foot_vel_x;
380 
381  // Foot velocity [m/s] in the y direction relative to the hip
382  double foot_vel_y;
383 
384  // Foot acceleration [m/s^2] in the x direction relative to the hip
385  double foot_acc_x;
386 
387  // Foot accaleration [m/s^2] in the y direction relative to the hip
388  double foot_acc_y;
389 
390  // Update the foot trajectory in the hip x direction
391  this->calculateSingleAxisTrajectory(progress, phase_period, x_step_distance, foot_dx, foot_vel_x, foot_acc_x);
392 
393  // Update the foot trajectory in the hip y direction
394  this->calculateSingleAxisTrajectory(progress, phase_period, y_step_distance, foot_dy, foot_vel_y, foot_acc_y);
395 
396  // Update the foot positions in the hip frame
397  // To push the body of the robot forward we have to move the stance foot backwards
398  this->foot_pos_ref(0) = x_nominal - foot_dx;
399  this->foot_pos_ref(1) = y_nominal - foot_dy;
400 
401  // We assume a constant height of the ground. !!! Here a lot can be done to handle obstacles
402  this->foot_pos_ref(2) = - hip_height;
403 
404  // If we have not reached the final iteration, simply assign the foot trajectory velocity and acceleration
406  {
407  this->foot_vel_ref(0) = - foot_vel_x;
408  this->foot_vel_ref(1) = - foot_vel_y;
409  this->foot_vel_ref(2) = 0.0;
410 
411  this->foot_acc_ref(0) = - foot_acc_x;
412  this->foot_acc_ref(1) = - foot_acc_y;
413  this->foot_acc_ref(2) = 0.0;
414  }
415  // If we have reached the final iteration and not changed state, set the velocities to zero to keep the foot still.
416  else
417  {
418  this->foot_vel_ref(0) = 0.0;
419  this->foot_vel_ref(1) = 0.0;
420  this->foot_vel_ref(2) = 0.0;
421 
422  this->foot_acc_ref(0) = 0.0;
423  this->foot_acc_ref(1) = 0.0;
424  this->foot_acc_ref(2) = 0.0;
425  }
426 }
427 
428 void SingleLegController::setPoseGoal(Eigen::Matrix<double, 3, 1> _foot_pos)
429 {
430  // Set the trajectory goal joint positions given by the foot pos
431  if(!this->kinematics.SolveSingleLegInverseKinematics(false, _foot_pos, this->joint_pos_goal))
432  {
433  ROS_WARN("[setFootPose] Failed to solve inverse kinematics");
434 
435  return;
436  }
437 
438  // Set the trajectory initial joint position
439  this->joint_pos_initial = this->joint_pos;
440 
441  double max_joint_error = 0.0;
442 
443  for(int i = 0; i < this->NUMBER_OF_MOTORS; i++)
444  {
445  if(abs(joint_pos_goal(i) - joint_pos_initial(i)) > max_joint_error)
446  {
447  max_joint_error = abs(joint_pos_goal(i) - joint_pos_initial(i));
448  }
449  }
450 
451  // Calculate the desired period based on the maximum allowable velocity
452  double a = 30.0 * max_joint_error;
453  double b = -15.0 * max_joint_error;
454  double c = 1.875 * max_joint_error;
455  double d = - max_joint_error * 7.0/16.0;
456  double _percentage = 0.5;
457 
458  this->pose_period = (a*pow((_percentage - 0.5), 4.0) + b*pow((_percentage - 0.5), 2.0) + c)/this->max_pose_vel;
459 
460  // Update the number of iterations
461  this->current_pose_iteration = 0;
462 
463  this->final_pose_iteration = this->pose_period * this->publish_frequency;
464 }
465 
467 {
468  double progress = this->current_pose_iteration/this->final_pose_iteration;
469 
470  double hy_pos;
471  double hy_vel;
472  double hy_acc;
473 
474  double hp_pos;
475  double hp_vel;
476  double hp_acc;
477 
478  double kp_pos;
479  double kp_vel;
480  double kp_acc;
481 
482  //hy_pos = (joint_pos_goal(0) - joint_pos_initial(0))*progress;
483  //hp_pos = (joint_pos_goal(1) - joint_pos_initial(1))*progress;
484  //kp_pos = (joint_pos_goal(2) - joint_pos_initial(2))*progress;
485 
486 
487  this->calculateSingleAxisTrajectory(progress, this->pose_period, joint_pos_goal(0) - joint_pos_initial(0), hy_pos, hy_vel, hy_acc);
488 
489  this->calculateSingleAxisTrajectory(progress, this->pose_period, joint_pos_goal(1) - joint_pos_initial(1), hp_pos, hp_vel, hp_acc);
490 
491  this->calculateSingleAxisTrajectory(progress, this->pose_period, joint_pos_goal(2) - joint_pos_initial(2), kp_pos, kp_vel, kp_acc);
492 
493  this->joint_pos_ref(0) = this->joint_pos_initial(0) + hy_pos;
494  this->joint_vel_ref(0) = hy_vel;
495  this->joint_acc_ref(0) = hy_acc;
496 
497  this->joint_pos_ref(1) = this->joint_pos_initial(1) + hp_pos;
498  this->joint_vel_ref(1) = hp_vel;
499  this->joint_acc_ref(1) = hp_acc;
500 
501  this->joint_pos_ref(2) = this->joint_pos_initial(2) + kp_pos;
502  this->joint_vel_ref(2) = kp_vel;
503  this->joint_acc_ref(2) = kp_acc;
504 
505  ROS_INFO("P: %f\thy_pos: %f\thp_pos: %f\tkp_pos: %f", progress, hy_pos, hp_pos, kp_pos);
506 
507  if(this->current_pose_iteration < this->final_pose_iteration)
508  {
509  this->current_pose_iteration++;
510 
511  // If the goal position is not reached we return false
512  return false;
513  }
514  else
515  {
517 
518  // When the goal position is reached we return true
519  return true;
520  }
521 
522 
523 }
524 
526 {
527  // Try to solve the inverse kinematics for the single leg to obtain the joint angle references from the foot position reference
528  if(!this->kinematics.SolveSingleLegInverseKinematics(false, foot_pos_ref, this->joint_pos_ref))
529  {
530  ROS_WARN("[updateJointReferences] Failed to solve inverse kinematics");
531  }
532  else // If we successfully found a solution to the inverse kinematics problem
533  {
534  // Calculate the translation Jacobian for the foot
535  Eigen::Matrix<double, 3, 3> J_s = this->kinematics.GetTranslationJacobianInB(Kinematics::LegType::frontLeft, Kinematics::BodyType::foot, joint_pos_ref(0), joint_pos_ref(1), joint_pos_ref(2));
536 
537  // Calculate the derivative of the translation Jacobian for the foot
538  Eigen::Matrix<double, 3, 3> J_s_d = this->kinematics.GetTranslationJacobianInBDiff(Kinematics::LegType::frontLeft, Kinematics::BodyType::foot, joint_pos_ref(0), joint_pos_ref(1), joint_pos_ref(2), joint_vel_ref(0), joint_vel_ref(1), joint_vel_ref(2));
539 
540  // Update the joint angle velocity references
541  this->joint_vel_ref = J_s.inverse()*this->foot_vel_ref;
542 
543  // Update the joint angle acceleration references
544  this->joint_acc_ref = J_s.inverse()*(this->foot_acc_ref - J_s_d*this->foot_vel_ref);
545  }
546 }
547 
549 {
551 }
552 
554 {
555  // Updates the joint torque references based on the joint reference trajectories and estimated joint states
557 }
558 
560 (
561  const Eigen::Matrix<double, 3, 1> &_joint_pos_ref,
562  const Eigen::Matrix<double, 3, 1> &_joint_vel_ref,
563  const Eigen::Matrix<double, 3, 1> &_joint_acc_ref,
564  const Eigen::Matrix<double, 3, 1> &_joint_pos,
565  const Eigen::Matrix<double, 3, 1> &_joint_vel
566 )
567 {
568  // Calculate the closed loop joint torque
569  Eigen::Matrix<double, 3, 1> normalized_joint_torques = _joint_acc_ref - K_p*(_joint_pos - _joint_pos_ref) - K_d*(_joint_vel - _joint_vel_ref);
570 
571  // Calculate the mass inertia matrix
572  Eigen::Matrix<double, 3, 3> M = this->kinematics.GetSingleLegMassMatrix(_joint_pos);
573 
574  // Calculate the Coriolis/Centrifugal vector
575  Eigen::Matrix<double, 3, 1> b = this->kinematics.GetSingleLegCoriolisAndCentrifugalTerms(_joint_pos, _joint_vel);
576 
577  // Calculate the gravity vector
578  Eigen::Matrix<double, 3, 1> g = this->kinematics.GetSingleLegGravitationalTerms(_joint_pos);
579 
580  /*
581  ROS_INFO("y_ref: %f\t y_d_ref: %f\t y_dd_ref: %f\t y: %f\t y_d: %f", _q_ref(1), _joint_vel_ref(1), _joint_acc_ref(1), _q(1), _joint_vel(1));
582 
583  ROS_INFO(
584  "Mass matrix:\n%f\t%f\t%f\t\n%f\t%f\t%f\t\n%f\t%f\t%f\t\n",
585  M(0, 0), M(0, 1), M(0, 2), M(1, 0), M(1, 1), M(1, 2), M(2, 0), M(2, 1), M(2, 2));
586 
587 
588  ROS_INFO("z1: %f\tz2: %f\tz3: %f", normalized_joint_torques(0), normalized_joint_torques(1), normalized_joint_torques(2));
589  ROS_INFO("b1: %f\tb2: %f\tb3: %f", b(0), b(1), b(2));
590  ROS_INFO("g1: %f\tg2: %f\tg3: %f", g(0), g(1), g(2));
591  */
592 
593  this->joint_torque_commands = b + g + M*normalized_joint_torques;
594 }
595 
597 {
598  this->joint_torque_commands = 0.0*this->joint_acc_ref - this->K_p*(this->joint_pos - this->joint_pos_ref) - this->K_d*(this->joint_vel - this->joint_vel_ref);
599 }
600 
602 {
603  // Check if the position commands are within the joint angle limit constraints
604  if (this->kinematics.ValidateSolution(this->joint_pos_ref) == false)
605  {
606  // If they are violated report the problem and don't send a command to the motors
607  ROS_WARN("Position setpoint violates joint limits. Command canceled.");
608  }
609  else // If the joint angles are valid
610  {
611  // Create a joint state message
612  sensor_msgs::JointState joint_state_msg;
613 
614  // Set the time of the joint state message
615  joint_state_msg.header.stamp = ros::Time::now();
616 
617  // Indicate that position control should be used
618  joint_state_msg.name.push_back("position");
619 
620  // Create a float array for joint angle commands
621  std_msgs::Float64MultiArray position_commands;
622 
623  // Put the joint angle reference vector into the position command message
624  tf::matrixEigenToMsg(this->joint_pos_ref, position_commands);
625 
626  // Add the position commands to the joint state message
627  joint_state_msg.position = position_commands.data;
628 
629  // Publish the message
630  joint_state_publisher.publish(joint_state_msg);
631  }
632 }
633 
635 {
636  // Create a joint state message
637  sensor_msgs::JointState joint_state_msg;
638 
639  // Set the time of the joint state message
640  joint_state_msg.header.stamp = ros::Time::now();
641 
642  // Indicate that velocity control should be used
643  joint_state_msg.name.push_back("velocity");
644 
645  // Create a float array for joint torque commands
646  std_msgs::Float64MultiArray joint_vel_command_array;
647 
648  // Put the joint velocity command array into the joint velocity command message
649  tf::matrixEigenToMsg(this->joint_vel_commands, joint_vel_command_array);
650 
651  // Add the joint velocity commands to the joint state message
652  joint_state_msg.effort = joint_vel_command_array.data;
653 
654  // Publish the message
655  joint_state_publisher.publish(joint_state_msg);
656 }
657 
659 {
660  // Create a joint state message
661  sensor_msgs::JointState joint_state_msg;
662 
663  // Set the time of the joint state message
664  joint_state_msg.header.stamp = ros::Time::now();
665 
666  // Indicate that torque control should be used
667  joint_state_msg.name.push_back("torque");
668 
669  // Create a float array for joint torque commands
670  std_msgs::Float64MultiArray joint_torque_command_array;
671 
672  // Put the joint torque reference vector into the torque command message
673  tf::matrixEigenToMsg(this->joint_torque_commands, joint_torque_command_array);
674 
675  // Add the torque commands to the joint state message
676  joint_state_msg.effort = joint_torque_command_array.data;
677 
678  // Publish the message
679  joint_state_publisher.publish(joint_state_msg);
680 }
681 
682 bool SingleLegController::moveFootToPosition(Eigen::Matrix<double, 3, 1> _foot_goal_pos)
683 {
684  // Starting angle of the trajectory
685  Eigen::Matrix<double, 3, 1> trajectory_initial_joint_pos = joint_pos;
686 
687  // Target angles of the trajectory
688  Eigen::Matrix<double, 3, 1> trajectory_goal_joint_pos;
689 
690  // Check if the desired goal position has a valid inverse kinematics solution
691  if(this->kinematics.SolveSingleLegInverseKinematics(this->kinematics.GetflOffset(), _foot_goal_pos, trajectory_goal_joint_pos) == false)
692  {
693  ROS_WARN("In the function SingleLegController::moveFootToPosition the inverse kinematics solver failed to find a valid solution.");
694  return false;
695  }
696 
697  // Check if the inverse kinematics solution violates any of theangle constraints
698  if(this->kinematics.ValidateSolution(trajectory_goal_joint_pos) == false)
699  {
700  ROS_WARN("In the function SingleLegController::moveFootToPosition the goal joint angles violated the angle constaints of the leg.");
701  return false;
702  }
703 
704  // Set the control command send rate
705  ros::Rate command_send_rate(publish_frequency);
706 
707  // Find the maximum joint angle error
708  double max_joint_error = 0.0;
709 
710  for(int i = 0; i < NUMBER_OF_MOTORS; i++)
711  {
712  if(abs(trajectory_goal_joint_pos(i) - trajectory_initial_joint_pos(i)) > max_joint_error)
713  {
714  max_joint_error = abs(trajectory_goal_joint_pos(i) - trajectory_initial_joint_pos(i));
715  }
716  }
717 
718  // Set the fastest joint_velocity
719  double max_joint_velocity = math_utils::degToRad(20);
720 
721  // The joint with largest error decides the trajectory duration
722  double trajectory_duration = max_joint_error/max_joint_velocity;
723 
724  // Calculate the number of iterations to reach the goal
725  double _final_iteration = trajectory_duration*this->publish_frequency;
726 
727  // Set initial iteration to zero
728  double _iteration = 0.0;
729 
730  while((_iteration <= _final_iteration) || !isTargetPositionReached() || !isJointVelocitySmall() || !ready_to_proceed)
731  {
732  ros::spinOnce();
733 
734  this->joint_pos_ref = trajectory_initial_joint_pos + (trajectory_goal_joint_pos - trajectory_initial_joint_pos)*_iteration/_final_iteration;
735 
737 
738  command_send_rate.sleep();
739 
740  if(_iteration <= _final_iteration)
741  {
742  _iteration ++;
743  }
744 
745  ROS_INFO("Trying to get to final position. I: %f\tI_f: %f", _iteration, _final_iteration);
746  }
747 
748  return true;
749 }
750 
752 {
753  return this->moveFootToPosition(foot_pos_goal);
754 }
755 
757 {
758  Eigen::Matrix<double, 3, 1> nominal_foot_position(this->x_nominal, this->y_nominal, -this->hip_height);
759 
760  return this->moveFootToPosition(nominal_foot_position);
761 }
762 
764 {
765  // Increment the gait cycle iterator
766  this->current_iteration++;
767 
768  // If we reach the final iteration stop incrementing
769  if(this->current_iteration >= this->final_iteration)
770  {
771  this->current_iteration = this->final_iteration;
772  }
773 }
774 
776 {
777  // Increase the iterator
778  this->current_iteration++;
779 
780  // If the the final iteration is reached
781  if(this->current_iteration >= this->final_iteration)
782  {
783  // Reset the iterator for the new phase
784  this->current_iteration = 0.0;
785 
786  // If the previous phase was not the stance phase, change to stance phase
787  if(this->gait_phase != GaitPhase::stance)
788  {
789  this->gait_phase = GaitPhase::stance;
790  }
791  else // If not change to swing phase
792  {
793  this->gait_phase = GaitPhase::swing;
794  }
795 
796  // We have reached the end of gait phase
797  return true;
798  }
799 
800  // We have not reached the end of gait phase
801  return false;
802 }
803 
805 {
806  if(this->gait_phase == GaitPhase::swing)
807  {
808  ROS_INFO("SWING");
809 
810  // Update the foot trajectory as a swing foot trajectory
812  }
813  else
814  {
815  ROS_INFO("STANCE");
816 
817  // Update the foot tractory as a stance foot trajectory
819  }
820 }
821 
822 
823 
824 /*** HELPER FUNCTIONS ***/
825 
827 {
828  ros::spinOnce();
829 }
830 
832 {
833  Eigen::Matrix<double, 3, 1> joint_error = joint_pos - this->joint_pos_ref;
834  double joint_pos_speed = joint_vel.transpose()*joint_vel;
835  printAllStates();
836  if((joint_error.transpose()*joint_error < POSITION_CONVERGENCE_CRITERIA) && (joint_pos_speed < 0.050))
837  {
838  ROS_INFO("Target Reached");
839  return true;
840  }
841  else
842  {
843  ROS_INFO("Error too large");
844  return false;
845  }
846 }
847 
849 {
850  if(joint_vel.transpose()*joint_vel < 3.0*math_utils::degToRad(3.0)*math_utils::degToRad(3.0))
851  {
852  ROS_INFO("Velocity is small");
853  return true;
854  }
855  else
856  {
857  ROS_INFO("Velocity is too large");
858  return false;
859  }
860 }
861 
863 {
864  // Check if the current joint angle is equal to the uninitialized state
865  if(this->joint_pos(0) == this->UNINITIALIZED_STATE)
866  {
867  return false;
868  }
869  else
870  {
871  return true;
872  }
873 }
874 
876 {
877  ROS_INFO("T1: %f\tT2: %f\tT3: %f", this->joint_torque_ref(0), this->joint_torque_ref(1), this->joint_torque_ref(2));
878 }
879 
881 {
882  ROS_INFO("Pos: %f, %f, %f\tVel: %f, %f, %f\tAcc: %f, %f, %f", this->foot_pos_ref(0), this->foot_pos_ref(1), this->foot_pos_ref(2), this->foot_vel_ref(0), this->foot_vel_ref(1), this->foot_vel_ref(2), this->foot_acc_ref(0), this->foot_acc_ref(1), this->foot_acc_ref(2));
883 }
884 
886 {
887  ROS_INFO("joint_pos_ref: %f, %f, %f\tq_d_ref: %f, %f, %f\tq_dd_ref: %f, %f, %f", this->joint_pos_ref(0), this->joint_pos_ref(1), this->joint_pos_ref(2), this->joint_vel_ref(0), this->joint_vel_ref(1), this->joint_vel_ref(2), this->joint_acc_ref(0), this->joint_acc_ref(1), this->joint_acc_ref(2));
888 }
889 
891 {
892  ROS_INFO("I: %.0f\tP: %.3f, %.3f, %.3f\tV: %.3f, %.3f, %.3f\tA: %.3f, %.3f, %.3f\tq_r: %.3f, %.3f, %.3f\tq_d_r: %.3f, %.3f, %.3f\tq_dd_r: %.3f, %.3f, %.3f\tq: %.3f, %.3f, %.3f\tq_d: %.3f, %.3f, %.3f\tT: %.3f, %.3f, %.3f",
894  this->foot_pos_ref(0), this->foot_pos_ref(1), this->foot_pos_ref(2), this->foot_vel_ref(0), this->foot_vel_ref(1), this->foot_vel_ref(2), this->foot_acc_ref(0), this->foot_acc_ref(1), this->foot_acc_ref(2),
898  );
899 }
900 
902 {
903  // Fill the log messages with the latest data
904  for(int i = 0; i < NUMBER_OF_MOTORS; i++)
905  {
906  this->joint_state_log_msg.position[i] = this->joint_pos(i);
907  this->joint_state_log_msg.velocity[i] = this->joint_vel(i);
908  this->joint_state_log_msg.effort[i] = this->joint_torque(i);
909 
910  this->joint_reference_log_msg.position[i] = this->joint_pos_ref(i);
911  this->joint_reference_log_msg.velocity[i] = this->joint_vel_ref(i);
912  this->joint_reference_log_msg.effort[i] = this->joint_torque_commands(i);
913  }
914 
915  // Update the time stamps
916  this->joint_state_log_msg.header.stamp = ros::Time::now();
917  this->joint_reference_log_msg.header.stamp = ros::Time::now();
918 
919  // Publish the messages
922 }
923 
925 {
926  // Rate of which we try to set the motor gains
927  ros::Rate set_gains_rate(0.4);
928 
929  // Inform that the gains have not been set to their desired value
930  this->gains_set = false;
931 
932  // Create a multi array message for the motor gains
933  std_msgs::Float64MultiArray motor_gain_msg;
934 
935  // Add the motor gains for the hip yaw motor
936  motor_gain_msg.data.push_back(k_p_pos_hy);
937  motor_gain_msg.data.push_back(k_i_pos_hy);
938  motor_gain_msg.data.push_back(k_p_vel_hy);
939  motor_gain_msg.data.push_back(k_i_vel_hy);
940  motor_gain_msg.data.push_back(k_p_torque_hy);
941  motor_gain_msg.data.push_back(k_i_torque_hy);
942 
943  // Add the motor gains for the hip pitch motor
944  motor_gain_msg.data.push_back(k_p_pos_hp);
945  motor_gain_msg.data.push_back(k_i_pos_hp);
946  motor_gain_msg.data.push_back(k_p_vel_hp);
947  motor_gain_msg.data.push_back(k_i_vel_hp);
948  motor_gain_msg.data.push_back(k_p_torque_hp);
949  motor_gain_msg.data.push_back(k_i_torque_hp);
950 
951  // Add the motor gains for the knee pitch motor
952  motor_gain_msg.data.push_back(k_p_pos_kp);
953  motor_gain_msg.data.push_back(k_i_pos_kp);
954  motor_gain_msg.data.push_back(k_p_vel_kp);
955  motor_gain_msg.data.push_back(k_p_vel_kp);
956  motor_gain_msg.data.push_back(k_p_torque_kp);
957  motor_gain_msg.data.push_back(k_i_torque_kp);
958 
959  // Keep looping until a a confirmation message from the motors have been received
960  while(!gains_set)
961  {
962  // Publish the motor gain message to the motors
963  this->motor_gain_publisher.publish(motor_gain_msg);
964 
965  // Print to the screen that we are trying to set the motor gains
966  ROS_INFO("Waiting for gains set confirmation message");
967 
968  // Wait for relies for a short while
969  set_gains_rate.sleep();
970 
971  // Check for incomming messages to see if the confirmation message has been received
972  ros::spinOnce();
973  }
974 
975  // Report that we set the motor gains successfully
976  ROS_INFO("Gains set sucessfully");
977 }
bool ValidateSolution(const JointSpaceVector &_q_r)
The ValidateSolution function evaluates whether a set of joint angles is within joint limits.
Eigen::Matrix< double, 3, 3 > GetSingleLegMassMatrix(const Eigen::Matrix< double, 3, 1 > &_q)
Eigen::Matrix< double, 3, 3 > GetTranslationJacobianInB(const LegType &_leg, const BodyType &_body, const double &_theta_hy, const double &_theta_hp, const double &_theta_kp)
The GetTranslationJacobianInB function returns the 3x3 Jacobian matrix for body linear velocities in ...
Definition: kinematics.cpp:723
Eigen::Matrix< double, 3, 1 > GetSingleLegCoriolisAndCentrifugalTerms(const Eigen::Matrix< double, 3, 1 > &_q, const Eigen::Matrix< double, 3, 1 > &_u)
Eigen::Matrix< double, 3, 3 > GetTranslationJacobianInBDiff(const LegType &_leg, const BodyType &_body, const double &_theta_hy, const double &_theta_hp, const double &_theta_kp, const double &_dot_theta_hy, const double &_dot_theta_hp, const double &_dot_theta_kp)
The GetTranslationJacobianDerivativeInB function returns the time derivative of the 3x3 translation J...
bool SolveSingleLegInverseKinematics(const bool &_offset, const Vector3d &_h_pos, const Vector3d &_f_pos, Vector3d &_joint_angles)
The SolveSingeLegInverseKinematics function calculates the inverse kinematics for a single leg.
Definition: kinematics.cpp:296
Eigen::Matrix< double, 3, 1 > GetSingleLegGravitationalTerms(const Eigen::Matrix< double, 3, 1 > &_q)
const int NUMBER_OF_MOTORS
The number of motors used in single leg control tests.
Eigen::Matrix< double, 3, 1 > foot_pos_goal
When using setpointTrajectory control these are the joint angles we want to reach eventually.
double k_p_torque_kp
Knee pitch motor torque control proportional gain.
ros::Subscriber motor_confirmation_subscriber
Subscribes to confirmation messages from the motor.
double max_swing_height
The maximum height above the ground to lift the foot.
void writeToLog()
Write the joint references and estimated states to the log.
ros::Subscriber generalized_forces_subscriber
Subscribes to generalized forces messages.
std::unique_ptr< ros::NodeHandle > node_handle
ROS node handle.
double k_i_torque_hp
Hip pitch motor torque control integral gain.
void jointSetpointCallback(const std_msgs::Float64MultiArrayConstPtr &_msg)
Listens to joint setpoints messages.
double max_pose_vel
Max pose control joint angle velocity [rad/s].
Eigen::Matrix< double, 3, 1 > joint_torque_ref
The reference joint torques for the motors.
void updateJointVelocityCommands()
Updates the joint velocity control commands based on the joint velocity reference and the joint posit...
Eigen::Matrix< double, 3, 1 > joint_vel_ref
The reference joint velocities for the motors.
void setMotorGains()
Tries to set the control gains in the motors. The function loops until it succeeds.
double k_p_torque_hp
Hip pitch motor torque control proportional gain.
void printJointTrajectories()
Print the joint trajectory references.
double k_p_torque_hy
Hip yaw motor torque control proportional gain.
ros::Publisher log_joint_references_publisher
Publishes joint position references for logging.
void printAllStates()
Print various information about all joints.
double publish_frequency
The expected publish frequency of the robot. This is used to calculate the maximum number of iteratio...
void updateFootTrajectoryReference()
Update the foot trajectory based on the leg state.
const double POSITION_CONVERGENCE_CRITERIA
Convergence crieria for position control test.
Eigen::Matrix< double, 3, 1 > foot_acc_ref
The foot acceleration reference relative to the hip.
double k_p_vel_hy
Hip yaw joint motor velocity control proportional gain.
void updateClosedLoopTorqueCommands()
A non model based torque controller.
void printSpatialTrajectories()
Print the foot trajectory reference.
Eigen::Matrix< double, 3, 1 > joint_torque
The estimated joint torques of the motors.
Eigen::Matrix< double, 3, 1 > joint_pos_goal
The joint angles we want to eventually reach when using pose trajectory control.
Eigen::Matrix< double, 3, 1 > joint_torque_commands
The joint torque command being sent to the motors.
double k_i_vel_hp
Hip pitch joint motor velocity control integral gain.
double k_i_vel_hy
Hip yaw joint motor velocity control integral gain.
double k_i_torque_kp
Knee pitch motor torque control integral gain.
double final_pose_iteration
The final iteration used for pose control.
bool moveFootToNominalPosition()
The function tries to move the foot to the nominal trajectory position.
Eigen::Matrix< double, 3, 1 > joint_pos
The estimated joint angles of the motors.
Eigen::Matrix< double, 3, 1 > joint_acc_ref
The reference joint accelerations for the motors.
ros::Subscriber ready_to_proceed_subscriber
Subscribes to messages deciding whether or not we should proceed in the script.
void updateStanceFootPositionTrajectory()
Update the stance foot tractory for the leg.
ros::Publisher log_joint_states_publisher
Publishes joint positions for logging.
Eigen::Matrix< double, 3, 1 > joint_pos_ref
The reference joint angles for the motors.
double k_p_vel_hp
Hip pitch joint motor velocity control proportional gain.
ros::Subscriber generalized_velocities_subscriber
Subscribes to generalized velocities messages.
ros::Publisher joint_state_publisher
Publishes velocity commands to the teensy to control the motors.
Kinematics kinematics
Kinematics object.
void printTorqueReferences()
Print the joint torque references.
ros::Subscriber generalized_coordinates_subscriber
Subscribes to generalized coordinates messages.
double k_p_pos_kp
Knee pitch joint motor position control proportional gain.
void generalizedForcesCallback(const std_msgs::Float64MultiArrayConstPtr &_msg)
Listens to joint force messages from the simulated leg.
double phase_period
The duration of a phase period in seconds.
bool gains_set
Keeps track of whether or not the motor gains have been set.
double k_p_vel_kp
Knee pitch joint motor velocity control proportional gain.
ros::Subscriber joint_setpoint_subscriber
Subscribes to joint setpoint messages.
Eigen::Matrix< double, 3, 3 > K_d
A diagonal matrix of derivative gains for the closed loop torque controller.
Eigen::Matrix< double, 3, 1 > joint_vel
The estimated joint velocities of the motors.
void jointStateCallback(const sensor_msgs::JointStatePtr &_msg)
Listens to joint state messages from the motors The messages contains joint angles,...
Eigen::Matrix< double, 3, 1 > foot_pos_ref
The foot position reference relative to the hip.
void initROS()
This function initilizes ROS.
SingleLegController(double _publish_frequency)
Constructor.
void sendJointVelocityCommands()
Updates the joint velocity commands and send them to the motors.
void sendJointPositionCommands()
Sends a joint position commands to the motors.
double x_step_distance
The step distance in the x direction.
bool updatePoseControlJointTrajectoryReference()
Update the pose control trajectory reference for the single leg.
bool isInitialStateReceived()
Check if the uninitalized joint state of the robot has been overridden by measurements.
double current_pose_iteration
The current iteration used for pose control.
double k_p_pos_hp
Hip pitch joint motor position control proportional gain.
double hip_height
The standard height of the hips above the ground.
Eigen::Matrix< double, 3, 1 > calculateSwingLegHeightTrajectory(double _percentage, double _period, double _max_swing_height, double _hip_height)
This function calculates the swing leg height trajectory based on various gait cycle parameters.
void motorConfirmationCallback(const std_msgs::Bool &_msg)
Listens to a confirmation message from the motors which is used to indicate if the motor gains have b...
double final_iteration
The final iteration of a gait phase.
bool isJointVelocitySmall()
Checks if the sum of squared joint velocities are smaller than a certain threshold.
void updateSwingFootPositionTrajectory()
Update the swing foot trajectory for the leg.
void setPoseGoal(Eigen::Matrix< double, 3, 1 > _foot_pos)
Set the final and initial states for pose control trajectories.
double y_step_distance
The step distance in the y direction.
sensor_msgs::JointState joint_state_log_msg
The joint state message that is used to log states.
double k_i_pos_hy
Hip yaw joint motor position control integral gain.
double k_i_torque_hy
Hip yaw motor torque control integral gain.
Eigen::Matrix< double, 3, 1 > foot_vel_ref
The foot velocity reference relative to the hip.
void updateJointReferences()
Update The joint trajectory reference based on the spatial foot trajectory.
ros::Subscriber joint_state_subscriber
Subscribes to joint state messages from the motors.
double current_iteration
The current iteration in a gait phase. This should be incremented for every control loop.
void generalizedVelocitiesCallback(const std_msgs::Float64MultiArrayConstPtr &_msg)
Listens to joint velocity messages from the simulated leg.
Eigen::Matrix< double, 3, 3 > K_p
A diagonal matrix of proportional gains for the closed loop torque controller.
ros::Publisher motor_gain_publisher
Publishes desired motor gains to the motors.
bool moveJointsToSetpoints()
The function tries to move the foot to the foot position given by the input parameters.
Eigen::Matrix< double, 3, 1 > joint_pos_initial
The joint angles at the beginning of a trajectory.
bool ready_to_proceed
A variable used to control the flow of the program. It can be set throuh a boolean message received b...
sensor_msgs::JointState joint_reference_log_msg
The joint state message that is used to log references.
bool updateGaitPhase()
Increments the gait cycle iterator and updates the leg state.
bool moveFootToPosition(Eigen::Matrix< double, 3, 1 > _foot_goal_pos)
Creates a trajectory from the current foot position to the goal foot position and moves the foot to t...
void increaseIterator()
Increment the gait cycle iterator. It stops iterating when the final iteration is reached.
double k_p_pos_hy
Hip yaw joint motor position control proportional gain.
void readyToProceedCallback(const std_msgs::Bool &_msg)
The ready_to_proceed parameter is changed based on the incomming message.
void sendJointTorqueCommands()
Updates the joint torque control commands based on the desired foot position.
void checkForNewMessages()
Function used to check if any new ROS messages has been received.
void updateJointTorqueCommands()
Overloaded function that uses the current joint references and joint states.
double y_nominal
The nominal y position of the foot relative to the hip.
void generalizedCoordinatesCallback(const std_msgs::Float64MultiArrayConstPtr &_msg)
Listens to joint angle messages from the simulated leg.
double pose_period
The time used to change from one pose to the next.
bool isTargetPositionReached()
Check if the squared error between the current joint angles and target joint angles is smaller than s...
double k_i_pos_kp
Knee pitch joint motor position control integral gain.
const double UNINITIALIZED_STATE
The value of an uninitialized state.
Eigen::Matrix< double, 3, 1 > joint_vel_commands
The velocity commands sent to the motors.
Eigen::Matrix< double, 3, 3 > K_pos_error_vel_control
A digonal matrix for of gains used for the position error in the closed loop velocity controller.
double k_i_pos_hp
Hip pitch joint motor position control integral gain.
GaitPhase gait_phase
The current gait phase of the leg.
void calculateSingleAxisTrajectory(const double &_percentage, const double &_period, const double &_max_travel, double &_axis_pos, double &_axis_vel, double &_axis_acc)
This functions calculates a smooth trajectory along a single linear axis. Please see the report for d...
double x_nominal
The nominal x position of the foot relative to the hip.
double degToRad(const double &_deg)
The degToRad function converts degrees to radians.
Definition: angle_utils.cpp:33
double radToDeg(const double &_rad)
The radToDeg function converts radians to degrees.
Definition: angle_utils.cpp:39