offboard_control.h
Go to the documentation of this file.
1 
10 /*
11  * Copyright 2015 Nuno Marques, Andre Nguyen.
12  *
13  * This file is part of the mavros package and subject to the license terms
14  * in the top-level LICENSE file of the mavros repository.
15  * https://github.com/mavlink/mavros/tree/master/LICENSE.md
16  */
17 
18 #include <array>
19 #include <random>
20 #include <angles/angles.h>
23 
24 #include <geometry_msgs/Point.h>
25 #include <geometry_msgs/PoseStamped.h>
26 #include <geometry_msgs/TwistStamped.h>
27 
28 namespace testsetup {
36 typedef enum {
40 } control_mode;
41 
42 typedef enum {
47 } path_shape;
48 
50 public:
52  nh_sp(test.nh),
53  local_pos_sp_pub(nh_sp.advertise<geometry_msgs::PoseStamped>("/mavros/setpoint_position/local", 10)),
54  vel_sp_pub(nh_sp.advertise<geometry_msgs::TwistStamped>("/mavros/setpoint_velocity/cmd_vel", 10)),
55  local_pos_sub(nh_sp.subscribe("/mavros/local_position/local", 10, &OffboardControl::local_pos_cb, this)),
57  { };
58 
59  void init() {
63  test.setup(nh_sp);
64  rate = test.rate;
65  use_pid = test.use_pid;
66  num_of_tests = test.num_of_tests;
67 
68  if (use_pid) {
74  // Linear velocity PID gains and bound of integral windup
75  nh_sp.param("linvel_p_gain", linvel_p_gain, 0.4);
76  nh_sp.param("linvel_i_gain", linvel_i_gain, 0.05);
77  nh_sp.param("linvel_d_gain", linvel_d_gain, 0.12);
78  nh_sp.param("linvel_i_max", linvel_i_max, 0.1);
79  nh_sp.param("linvel_i_min", linvel_i_min, -0.1);
80 
81  // Yaw rate PID gains and bound of integral windup
82  nh_sp.param("yawrate_p_gain", yawrate_p_gain, 0.011);
83  nh_sp.param("yawrate_i_gain", yawrate_i_gain, 0.00058);
84  nh_sp.param("yawrate_d_gain", yawrate_d_gain, 0.12);
85  nh_sp.param("yawrate_i_max", yawrate_i_max, 0.005);
86  nh_sp.param("yawrate_i_min", yawrate_i_min, -0.005);
87 
88  // Setup of the PID controllers
91  }
92 
101  std::string mode_;
102  nh_sp.param<std::string>("mode", mode_, "position");
103 
113  std::string shape_;
114  nh_sp.param<std::string>("shape", shape_, "square");
115 
116  if (mode_ == "position")
117  mode = POSITION;
118  else if (mode_ == "velocity")
119  mode = VELOCITY;
120  else if (mode_ == "acceleration")
121  mode = ACCELERATION;
122  else {
123  ROS_ERROR_NAMED("sitl_test", "Control mode: wrong/unexistant control mode name %s", mode_.c_str());
124  return;
125  }
126 
127  if (shape_ == "square")
128  shape = SQUARE;
129  else if (shape_ == "circle")
130  shape = CIRCLE;
131  else if (shape_ == "eight")
132  shape = EIGHT;
133  else if (shape_ == "ellipse")
134  shape = ELLIPSE;
135  else {
136  ROS_ERROR_NAMED("sitl_test", "Path shape: wrong/unexistant path shape name %s", shape_.c_str());
137  return;
138  }
139  }
140 
141  /* -*- main routine -*- */
142 
143  void spin(int argc, char *argv[]) {
144  init();
145  ros::Rate loop_rate(rate);
146 
147  ROS_INFO("SITL Test: Offboard control test running!");
148 
149  if (mode == POSITION) {
150  ROS_INFO("Position control mode selected.");
151  }
152  else if (mode == VELOCITY) {
153  ROS_INFO("Velocity control mode selected.");
154  }
155  else if (mode == ACCELERATION) {
156  ROS_INFO("Acceleration control mode selected.");
157  ROS_ERROR_NAMED("sitl_test", "Control mode: acceleration control mode not supported in PX4 current Firmware.");
161  return;
162  }
163 
164  if (shape == SQUARE) {
165  ROS_INFO("Test option: square-shaped path...");
166  square_path_motion(loop_rate, mode);
167  }
168  else if (shape == CIRCLE) {
169  ROS_INFO("Test option: circle-shaped path...");
170  circle_path_motion(loop_rate, mode);
171  }
172  else if (shape == EIGHT) {
173  ROS_INFO("Test option: eight-shaped path...");
174  eight_path_motion(loop_rate, mode);
175  }
176  else if (shape == ELLIPSE) {
177  ROS_INFO("Test option: ellipse-shaped path...");
178  ellipse_path_motion(loop_rate, mode);
179  }
180  }
181 
182 private:
185 
186  double rate;
187  bool use_pid;
188  int num_of_tests; //TODO: find a way to use this...
189 
193  double linvel_i_max;
194  double linvel_i_min;
195 
201 
202  control_mode mode;
203  path_shape shape;
204 
209 
210  geometry_msgs::PoseStamped localpos, ps;
211  geometry_msgs::TwistStamped vs;
212 
213  Eigen::Vector3d current;
214 
215  std::array<double, 100> threshold;
216 
217  /* -*- helper functions -*- */
218 
222  Eigen::Vector3d pos_setpoint(int tr_x, int tr_y, int tr_z){
224  return Eigen::Vector3d(tr_x * 2.0f, tr_y * 2.0f, tr_z * 1.0f); // meters
225  }
226 
230  Eigen::Vector3d circle_shape(int angle){
232  double r = 5.0f; // 5 meters radius
233 
234  return Eigen::Vector3d(r * cos(angles::from_degrees(angle)),
235  r * sin(angles::from_degrees(angle)),
236  1.0f);
237  }
238 
242  Eigen::Vector3d eight_shape(int angle){
244  double a = 5.0f; // vertical tangent with 5 meters size
245 
246  return Eigen::Vector3d(a * cos(angles::from_degrees(angle)),
247  a * sin(angles::from_degrees(angle)) * cos(angles::from_degrees(angle)),
248  1.0f);
249  }
250 
254  Eigen::Vector3d ellipse_shape(int angle){
256  double a = 5.0f; // major axis
257  double b = 2.0f; // minor axis
258 
259  // rotation around y-axis
260  return Eigen::Vector3d(a * cos(angles::from_degrees(angle)),
261  0.0f,
262  2.5f + b * sin(angles::from_degrees(angle)));
263  }
264 
268  void square_path_motion(ros::Rate loop_rate, control_mode mode){
269  uint8_t pos_target = 1;
270 
271  ROS_INFO("Testing...");
272 
273  while (ros::ok()) {
274  wait_and_move(ps);
275 
276  // motion routine
277  switch (pos_target) {
278  case 1:
279  tf::pointEigenToMsg(pos_setpoint(1, 1, 1), ps.pose.position);
280  break;
281  case 2:
282  tf::pointEigenToMsg(pos_setpoint(-1, 1, 1), ps.pose.position);
283  break;
284  case 3:
285  tf::pointEigenToMsg(pos_setpoint(-1, -1, 1), ps.pose.position);
286  break;
287  case 4:
288  tf::pointEigenToMsg(pos_setpoint(1, -1, 1), ps.pose.position);
289  break;
290  case 5:
291  tf::pointEigenToMsg(pos_setpoint(1, 1, 1), ps.pose.position);
292  break;
293  default:
294  break;
295  }
296 
297  if (pos_target == 6) {
298  ROS_INFO("Test complete!");
299  ros::shutdown();
300  }
301  else
302  ++pos_target;
303 
304  loop_rate.sleep();
305  ros::spinOnce();
306  }
307  }
308 
312  void circle_path_motion(ros::Rate loop_rate, control_mode mode){
313  ROS_INFO("Testing...");
314  ros::Time last_time = ros::Time::now();
315 
316  while (ros::ok()) {
317  tf::pointMsgToEigen(localpos.pose.position, current);
318 
319  // starting point
320  if (mode == POSITION) {
321  tf::pointEigenToMsg(Eigen::Vector3d(5.0f, 0.0f, 1.0f), ps.pose.position);
322  local_pos_sp_pub.publish(ps);
323  }
324  else if (mode == VELOCITY) {
325  if (use_pid)
327  Eigen::Vector3d(5.0f, 0.0f, 1.0f), current, last_time), vs.twist.linear);
328  else
329  tf::vectorEigenToMsg(Eigen::Vector3d(5.0f - current.x(), -current.y(), 1.0f - current.z()), vs.twist.linear);
330  vel_sp_pub.publish(vs);
331  }
332  else if (mode == ACCELERATION) {
333  // TODO
334  return;
335  }
336 
337  wait_and_move(ps);
338 
339  // motion routine
340  for (int theta = 0; theta <= 360; theta++) {
341  tf::pointMsgToEigen(localpos.pose.position, current);
342 
343  if (mode == POSITION) {
344  tf::pointEigenToMsg(circle_shape(theta), ps.pose.position);
345  local_pos_sp_pub.publish(ps);
346  }
347  else if (mode == VELOCITY) {
348  if (use_pid)
349  tf::vectorEigenToMsg(pid.compute_linvel_effort(circle_shape(theta), current, last_time), vs.twist.linear);
350  else
351  tf::vectorEigenToMsg(circle_shape(theta) - current, vs.twist.linear);
352  vel_sp_pub.publish(vs);
353  }
354  else if (mode == ACCELERATION) {
355  // TODO
356  return;
357  }
358  if (theta == 360) {
359  ROS_INFO("Test complete!");
360  ros::shutdown();
361  }
362  last_time = ros::Time::now();
363  loop_rate.sleep();
364  ros::spinOnce();
365  }
366  }
367  }
368 
372  void eight_path_motion(ros::Rate loop_rate, control_mode mode){
373  ROS_INFO("Testing...");
374  ros::Time last_time = ros::Time::now();
375 
376  while (ros::ok()) {
377  tf::pointMsgToEigen(localpos.pose.position, current);
378 
379  // starting point
380  if (mode == POSITION) {
381  tf::pointEigenToMsg(Eigen::Vector3d(0.0f, 0.0f, 1.0f), ps.pose.position);
382  local_pos_sp_pub.publish(ps);
383  }
384  else if (mode == VELOCITY) {
385  if (use_pid)
387  Eigen::Vector3d(0.0f, 0.0f, 1.0f), current, last_time), vs.twist.linear);
388  else
389  tf::vectorEigenToMsg(Eigen::Vector3d(-current.x(), -current.y(), 1.0f - current.z()), vs.twist.linear);
390  vel_sp_pub.publish(vs);
391  }
392  else if (mode == ACCELERATION) {
393  // TODO
394  return;
395  }
396 
397  wait_and_move(ps);
398 
399  // motion routine
400  for (int theta = -180; theta <= 180; theta++) {
401  tf::pointMsgToEigen(localpos.pose.position, current);
402 
403  if (mode == POSITION) {
404  tf::pointEigenToMsg(eight_shape(theta), ps.pose.position);
405  local_pos_sp_pub.publish(ps);
406  }
407  else if (mode == VELOCITY) {
408  if (use_pid)
409  tf::vectorEigenToMsg(pid.compute_linvel_effort(eight_shape(theta), current, last_time), vs.twist.linear);
410  else
411  tf::vectorEigenToMsg(eight_shape(theta) - current, vs.twist.linear);
412  vel_sp_pub.publish(vs);
413  }
414  else if (mode == ACCELERATION) {
415  // TODO
416  return;
417  }
418  if (theta == 180) {
419  ROS_INFO("Test complete!");
420  ros::shutdown();
421  }
422  last_time = ros::Time::now();
423  loop_rate.sleep();
424  ros::spinOnce();
425  }
426  }
427  }
428 
432  void ellipse_path_motion(ros::Rate loop_rate, control_mode mode){
433  ROS_INFO("Testing...");
434  ros::Time last_time = ros::Time::now();
435 
436  while (ros::ok()) {
437  tf::pointMsgToEigen(localpos.pose.position, current);
438 
439  // starting point
440  if (mode == POSITION) {
441  tf::pointEigenToMsg(Eigen::Vector3d(0.0f, 0.0f, 2.5f), ps.pose.position);
442  local_pos_sp_pub.publish(ps);
443  }
444  else if (mode == VELOCITY) {
445  if (use_pid)
447  Eigen::Vector3d(0.0f, 0.0f, 2.5f), current, last_time), vs.twist.linear);
448  else
449  tf::vectorEigenToMsg(Eigen::Vector3d(-current.x(), -current.y(), 2.5f - current.z()), vs.twist.linear);
450  vel_sp_pub.publish(vs);
451  }
452  else if (mode == ACCELERATION) {
453  // TODO
454  return;
455  }
456 
457  wait_and_move(ps);
458 
459  // motion routine
460  for (int theta = 0; theta <= 360; theta++) {
461  tf::pointMsgToEigen(localpos.pose.position, current);
462 
463  if (mode == POSITION) {
464  tf::pointEigenToMsg(ellipse_shape(theta), ps.pose.position);
465  local_pos_sp_pub.publish(ps);
466  }
467  else if (mode == VELOCITY) {
468  if (use_pid)
469  tf::vectorEigenToMsg(pid.compute_linvel_effort(ellipse_shape(theta), current, last_time), vs.twist.linear);
470  else
471  tf::vectorEigenToMsg(ellipse_shape(theta) - current, vs.twist.linear);
472  vel_sp_pub.publish(vs);
473  }
474  else if (mode == ACCELERATION) {
475  // TODO
476  return;
477  }
478  if (theta == 360) {
479  ROS_INFO("Test complete!");
480  ros::shutdown();
481  }
482  last_time = ros::Time::now();
483  loop_rate.sleep();
484  ros::spinOnce();
485  }
486  }
487  }
488 
493  void wait_and_move(geometry_msgs::PoseStamped target){
494  ros::Rate loop_rate(rate);
495  ros::Time last_time = ros::Time::now();
496  bool stop = false;
497 
498  Eigen::Vector3d dest;
499 
500  double distance;
501  double err_th = threshold[rand() % threshold.size()];
502 
503  ROS_DEBUG("Next setpoint: accepted error threshold: %1.3f", err_th);
504 
505  while (ros::ok() && !stop) {
506  tf::pointMsgToEigen(target.pose.position, dest);
507  tf::pointMsgToEigen(localpos.pose.position, current);
508 
509  distance = sqrt((dest - current).x() * (dest - current).x() +
510  (dest - current).y() * (dest - current).y() +
511  (dest - current).z() * (dest - current).z());
512 
513  if (distance <= err_th)
514  stop = true;
515 
516  if (mode == POSITION) {
517  local_pos_sp_pub.publish(target);
518  }
519  else if (mode == VELOCITY) {
520  if (use_pid)
521  tf::vectorEigenToMsg(pid.compute_linvel_effort(dest, current, last_time), vs.twist.linear);
522  else
523  tf::vectorEigenToMsg(dest - current, vs.twist.linear);
524  vel_sp_pub.publish(vs);
525  }
526  else if (mode == ACCELERATION) {
527  // TODO
528  return;
529  }
530  last_time = ros::Time::now();
531  loop_rate.sleep();
532  ros::spinOnce();
533  }
534  }
535 
539  std::array<double, 100> threshold_definition(){
540  std::random_device rd;
541  std::mt19937 gen(rd());
542  std::array<double, 100> th_values;
543 
544  std::normal_distribution<double> th(0.1f,0.05f);
545 
546  for (auto &value : th_values) {
547  value = th(gen);
548  }
549  return th_values;
550  }
551 
552  /* -*- callbacks -*- */
553 
554  void local_pos_cb(const geometry_msgs::PoseStampedConstPtr& msg){
555  localpos = *msg;
556  }
557 };
558 }; // namespace testsetup
SitlTest node implementation class.
ros::NodeHandle nh
void publish(const boost::shared_ptr< M > &message) const
void setup_yawrate_pid(double p_gain, double i_gain, double d_gain, double i_max, double i_min, const ros::NodeHandle &node)
Sets up the PID values for computation of the yaw rate effort.
pidcontroller::PIDController pid
std::array< double, 100 > threshold
geometry_msgs::TwistStamped vs
TFSIMD_FORCE_INLINE tfScalar distance(const Vector3 &v) const
geometry_msgs::PoseStamped localpos
Eigen::Vector3d pos_setpoint(int tr_x, int tr_y, int tr_z)
Defines single position setpoint.
control_mode
Offboard controller tester.
void subscribe()
void setup_linvel_pid(double p_gain, double i_gain, double d_gain, double i_max, double i_min, const ros::NodeHandle &node)
Sets up the PID values for computation of the linear velocities effort.
static double from_degrees(double degrees)
#define ROS_INFO(...)
void vectorEigenToMsg(const Eigen::Vector3d &e, geometry_msgs::Vector3 &m)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
ROSCPP_DECL bool ok()
std::array< double, 100 > threshold_definition()
Gaussian noise generator for accepted position threshold.
void pointMsgToEigen(const geometry_msgs::Point &m, Eigen::Vector3d &e)
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
TFSIMD_FORCE_INLINE const tfScalar & z() const
void pointEigenToMsg(const Eigen::Vector3d &e, geometry_msgs::Point &m)
bool sleep()
void spin(int argc, char *argv[])
geometry_msgs::PoseStamped ps
void local_pos_cb(const geometry_msgs::PoseStampedConstPtr &msg)
void wait_and_move(geometry_msgs::PoseStamped target)
Defines the accepted threshold to the destination/target position before moving to the next setpoint...
Eigen::Vector3d ellipse_shape(int angle)
Defines ellipse path.
#define ROS_ERROR_NAMED(name,...)
static Time now()
ROSCPP_DECL void shutdown()
Eigen::Vector3d eight_shape(int angle)
Defines Gerono lemniscate path.
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
void ellipse_path_motion(ros::Rate loop_rate, control_mode mode)
Ellipse path motion routine.
void eight_path_motion(ros::Rate loop_rate, control_mode mode)
Eight path motion routine.
ROSCPP_DECL void spinOnce()
Eigen::Vector3d compute_linvel_effort(Eigen::Vector3d goal, Eigen::Vector3d current, ros::Time last_time)
Computes the linear velocity effort to apply to each axis.
Eigen::Vector3d circle_shape(int angle)
Defines circle path.
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
void square_path_motion(ros::Rate loop_rate, control_mode mode)
Square path motion routine.
void circle_path_motion(ros::Rate loop_rate, control_mode mode)
Circle path motion routine.
#define ROS_DEBUG(...)


test_mavros
Author(s): Nuno Marques , Vladimir Ermakov
autogenerated on Mon Jul 8 2019 03:20:22