quadrotor_propulsion.cpp
Go to the documentation of this file.
1 //=================================================================================================
2 // Copyright (c) 2012-2016, Institute of Flight Systems and Automatic Control,
3 // Technische Universität Darmstadt.
4 // All rights reserved.
5 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of hector_quadrotor nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //=================================================================================================
28 
31 
32 #include <ros/node_handle.h>
33 #include <ros/callback_queue.h>
34 
35 #include <boost/array.hpp>
36 
37 #include "matlab_helpers.h"
38 
39 //extern "C" {
40 // #include "quadrotorPropulsion/quadrotorPropulsion.h"
41 // #include "quadrotorPropulsion/quadrotorPropulsion_initialize.h"
42 //}
43 
44 namespace hector_quadrotor_model {
45 
47 {
59 
61  : k_m(0.0)
62  , k_t(0.0)
63  , CT2s(0.0)
64  , CT1s(0.0)
65  , CT0s(0.0)
66  , Psi(0.0)
67  , J_M(std::numeric_limits<real_T>::infinity())
68  , R_A(std::numeric_limits<real_T>::infinity())
69  , alpha_m(0.0)
70  , beta_m(0.0)
71  , l_m(0.0)
72  {}
73 };
74 
77  boost::array<real_T,4> x;
78  boost::array<real_T,4> x_pred;
79  boost::array<real_T,10> u;
80  boost::array<real_T,14> y;
81 };
82 
84 {
85  // initialize propulsion model
86  // quadrotorPropulsion_initialize();
87  propulsion_model_ = new PropulsionModel;
88 }
89 
91 {
92  delete propulsion_model_;
93 }
94 
95 /*
96  * quadrotorPropulsion.c
97  *
98  * Code generation for function 'quadrotorPropulsion'
99  *
100  * C source code generated on: Sun Nov 3 13:34:35 2013
101  *
102  */
103 
104 /* Include files */
105 //#include "rt_nonfinite.h"
106 //#include "motorspeed.h"
107 //#include "quadrotorPropulsion.h"
108 //#include "quadrotorPropulsion_rtwutil.h"
109 
110 /* Type Definitions */
111 
112 /* Named Constants */
113 
114 /* Variable Declarations */
115 
116 /* Variable Definitions */
117 
118 /* Function Declarations */
119 
120 /* Function Definitions */
121 void quadrotorPropulsion(const real_T xin[4], const real_T uin[10], const
122  PropulsionParameters parameter, real_T dt, real_T y[14], real_T xpred[4])
123 {
124  real_T v_1[4];
125  int32_T i;
126  real_T F_m[4];
127  real_T U[4];
128  real_T M_e[4];
129  real_T I[4];
130  real_T F[3];
131  real_T b_F_m;
132  real_T temp;
133  real_T d0;
134 
135  /* initialize vectors */
136  for (i = 0; i < 4; i++) {
137  xpred[i] = xin[i];
138 
139  /* motorspeed */
140  v_1[i] = 0.0;
141  }
142 
143  memset(&y[0], 0, 14U * sizeof(real_T));
144  for (i = 0; i < 4; i++) {
145  F_m[i] = 0.0;
146  U[i] = 0.0;
147  M_e[i] = 0.0;
148  I[i] = 0.0;
149  }
150 
151  for (i = 0; i < 3; i++) {
152  F[i] = 0.0;
153  }
154 
155  /* Input variables */
156  U[0] = uin[6];
157  U[1] = uin[7];
158  U[2] = uin[8];
159  U[3] = uin[9];
160 
161  /* Constants */
162  v_1[0] = -uin[2] + parameter.l_m * uin[4];
163  v_1[1] = -uin[2] - parameter.l_m * uin[3];
164  v_1[2] = -uin[2] - parameter.l_m * uin[4];
165  v_1[3] = -uin[2] + parameter.l_m * uin[3];
166 
167  /* calculate thrust for all 4 rotors */
168  for (i = 0; i < 4; i++) {
169  /* if the flow speed at infinity is negative */
170  if (v_1[i] < 0.0) {
171  b_F_m = (parameter.CT2s * rt_powd_snf(v_1[i], 2.0) + parameter.CT1s *
172  v_1[i] * xin[i]) + parameter.CT0s * rt_powd_snf(xin[i], 2.0);
173 
174  /* if the flow speed at infinity is positive */
175  } else {
176  b_F_m = (-parameter.CT2s * rt_powd_snf(v_1[i], 2.0) + parameter.CT1s *
177  v_1[i] * xin[i]) + parameter.CT0s * rt_powd_snf(xin[i], 2.0);
178  }
179 
180  /* sum up all rotor forces */
181  /* Identification of Roxxy2827-34 motor with 10x4.5 propeller */
182  /* temporarily used Expressions */
183  temp = (U[i] * parameter.beta_m - parameter.Psi * xin[i]) / (2.0 *
184  parameter.R_A);
185  temp += sqrt(rt_powd_snf(temp, 2.0) + U[i] * parameter.alpha_m /
186  parameter.R_A);
187  d0 = parameter.Psi * temp;
188 
189  /* electrical torque motor 1-4 */
190  /* new version */
191  /* old version */
192  /* fx = (Psi/R_A*(U-Psi*omega_m) - M_m)/J_M; */
193  /* A = -(Psi^2/R_A)/J_M; */
194  /* B(1) = Psi/(J_M*R_A); */
195  /* B(2) = -1/J_M; */
196  /* system outputs. Use euler solver to predict next time step */
197  /* predicted motor speed */
198  /* electric torque */
199  /* y = [M_e I]; */
200  /* system jacobian */
201  /* A = 1 + dt*A; */
202  /* input jacobian */
203  /* B = A*B*dt; */
204  M_e[i] = d0;
205  I[i] = temp;
206  xpred[i] = xin[i] + dt * (1.0 / parameter.J_M * (d0 - (parameter.k_t * b_F_m
207  + parameter.k_m * xin[i])));
208  F_m[i] = b_F_m;
209  F[2] += b_F_m;
210  }
211 
212  /* System output, i.e. force and torque of quadrotor */
213  y[0] = 0.0;
214  y[1] = 0.0;
215  y[2] = F[2];
216 
217  /* torque for rotating quadrocopter around x-axis is the mechanical torque */
218  y[3] = (F_m[3] - F_m[1]) * parameter.l_m;
219 
220  /* torque for rotating quadrocopter around y-axis is the mechanical torque */
221  y[4] = (F_m[0] - F_m[2]) * parameter.l_m;
222 
223  /* torque for rotating quadrocopter around z-axis is the electrical torque */
224  y[5] = ((-M_e[0] - M_e[2]) + M_e[1]) + M_e[3];
225 
226  /* motor speeds (rad/s) */
227  for (i = 0; i < 4; i++) {
228  y[i + 6] = xpred[i];
229  }
230 
231  /* motor current (A) */
232  for (i = 0; i < 4; i++) {
233  y[i + 10] = I[i];
234  }
235 
236  /* M_e(1:4) / Psi; */
237 }
238 
239 /* End of code generation (quadrotorPropulsion.c) */
240 
241 inline void QuadrotorPropulsion::f(const double xin[4], const double uin[10], double dt, double y[14], double xpred[4]) const
242 {
243  quadrotorPropulsion(xin, uin, propulsion_model_->parameters_, dt, y, xpred);
244 }
245 
247 {
248  // get model parameters
249  if (!param.getParam("k_m", propulsion_model_->parameters_.k_m)) return false;
250  if (!param.getParam("k_t", propulsion_model_->parameters_.k_t)) return false;
251  if (!param.getParam("CT0s", propulsion_model_->parameters_.CT0s)) return false;
252  if (!param.getParam("CT1s", propulsion_model_->parameters_.CT1s)) return false;
253  if (!param.getParam("CT2s", propulsion_model_->parameters_.CT2s)) return false;
254  if (!param.getParam("J_M", propulsion_model_->parameters_.J_M)) return false;
255  if (!param.getParam("l_m", propulsion_model_->parameters_.l_m)) return false;
256  if (!param.getParam("Psi", propulsion_model_->parameters_.Psi)) return false;
257  if (!param.getParam("R_A", propulsion_model_->parameters_.R_A)) return false;
258  if (!param.getParam("alpha_m", propulsion_model_->parameters_.alpha_m)) return false;
259  if (!param.getParam("beta_m", propulsion_model_->parameters_.beta_m)) return false;
260 
261 #ifndef NDEBUG
262  std::cout << "Loaded the following quadrotor propulsion model parameters from namespace " << param.getNamespace() << ":" << std::endl;
263  std::cout << "k_m = " << propulsion_model_->parameters_.k_m << std::endl;
264  std::cout << "k_t = " << propulsion_model_->parameters_.k_t << std::endl;
265  std::cout << "CT2s = " << propulsion_model_->parameters_.CT2s << std::endl;
266  std::cout << "CT1s = " << propulsion_model_->parameters_.CT1s << std::endl;
267  std::cout << "CT0s = " << propulsion_model_->parameters_.CT0s << std::endl;
268  std::cout << "Psi = " << propulsion_model_->parameters_.Psi << std::endl;
269  std::cout << "J_M = " << propulsion_model_->parameters_.J_M << std::endl;
270  std::cout << "R_A = " << propulsion_model_->parameters_.R_A << std::endl;
271  std::cout << "l_m = " << propulsion_model_->parameters_.l_m << std::endl;
272  std::cout << "alpha_m = " << propulsion_model_->parameters_.alpha_m << std::endl;
273  std::cout << "beta_m = " << propulsion_model_->parameters_.beta_m << std::endl;
274 #endif
275 
276  initial_voltage_ = 14.8;
277  param.getParam("supply_voltage", initial_voltage_);
278  reset();
279 
280  return true;
281 }
282 
284 {
285  boost::mutex::scoped_lock lock(mutex_);
286 
287  propulsion_model_->x.assign(0.0);
288  propulsion_model_->x_pred.assign(0.0);
289  propulsion_model_->u.assign(0.0);
290  propulsion_model_->y.assign(0.0);
291 
292  wrench_ = geometry_msgs::Wrench();
293 
294  motor_status_ = hector_uav_msgs::MotorStatus();
295  motor_status_.voltage.resize(4);
296  motor_status_.frequency.resize(4);
297  motor_status_.current.resize(4);
298 
299  supply_ = hector_uav_msgs::Supply();
300  supply_.voltage.resize(1);
301  supply_.voltage[0] = initial_voltage_;
302 
303  last_command_time_ = ros::Time();
304 
305  command_queue_ = std::queue<hector_uav_msgs::MotorPWMConstPtr>(); // .clear();
306 }
307 
308 void QuadrotorPropulsion::setVoltage(const hector_uav_msgs::MotorPWM& voltage)
309 {
310  boost::mutex::scoped_lock lock(mutex_);
311  last_command_time_ = voltage.header.stamp;
312 
313  if (motor_status_.on && voltage.pwm.size() >= 4) {
314  propulsion_model_->u[6] = voltage.pwm[0] * supply_.voltage[0] / 255.0;
315  propulsion_model_->u[7] = voltage.pwm[1] * supply_.voltage[0] / 255.0;
316  propulsion_model_->u[8] = voltage.pwm[2] * supply_.voltage[0] / 255.0;
317  propulsion_model_->u[9] = voltage.pwm[3] * supply_.voltage[0] / 255.0;
318  } else {
319  propulsion_model_->u[6] = 0.0;
320  propulsion_model_->u[7] = 0.0;
321  propulsion_model_->u[8] = 0.0;
322  propulsion_model_->u[9] = 0.0;
323  }
324 }
325 
326 void QuadrotorPropulsion::setTwist(const geometry_msgs::Twist &twist)
327 {
328  boost::mutex::scoped_lock lock(mutex_);
329  propulsion_model_->u[0] = twist.linear.x;
330  propulsion_model_->u[1] = -twist.linear.y;
331  propulsion_model_->u[2] = -twist.linear.z;
332  propulsion_model_->u[3] = twist.angular.x;
333  propulsion_model_->u[4] = -twist.angular.y;
334  propulsion_model_->u[5] = -twist.angular.z;
335 
336  // We limit the input velocities to +-100. Required for numeric stability in case of collisions,
337  // where velocities returned by Gazebo can be very high.
338  limit(boost::iterator_range<boost::array<real_T,10>::iterator>(&(propulsion_model_->u[0]), &(propulsion_model_->u[6])), -100.0, 100.0);
339 }
340 
341 void QuadrotorPropulsion::addCommandToQueue(const hector_uav_msgs::MotorCommandConstPtr& command)
342 {
343  hector_uav_msgs::MotorPWMPtr pwm(new hector_uav_msgs::MotorPWM);
344  pwm->header = command->header;
345  pwm->pwm.resize(command->voltage.size());
346  for(std::size_t i = 0; i < command->voltage.size(); ++i) {
347  int temp = round(command->voltage[i] / supply_.voltage[0] * 255.0);
348  if (temp < 0)
349  pwm->pwm[i] = 0;
350  else if (temp > 255)
351  pwm->pwm[i] = 255;
352  else
353  pwm->pwm[i] = temp;
354  }
355  addPWMToQueue(pwm);
356 }
357 
358 void QuadrotorPropulsion::addPWMToQueue(const hector_uav_msgs::MotorPWMConstPtr& pwm)
359 {
360  boost::mutex::scoped_lock lock(command_queue_mutex_);
361 
362  if (!motor_status_.on) {
363  ROS_WARN_NAMED("quadrotor_propulsion", "Received new motor command. Enabled motors.");
364  engage();
365  }
366 
367  ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "Received motor command valid at " << pwm->header.stamp);
368  command_queue_.push(pwm);
369  command_condition_.notify_all();
370 }
371 
372 bool QuadrotorPropulsion::processQueue(const ros::Time &timestamp, const ros::Duration &tolerance, const ros::Duration &delay, const ros::WallDuration &wait, ros::CallbackQueue *callback_queue) {
373  boost::mutex::scoped_lock lock(command_queue_mutex_);
374  bool new_command = false;
375 
376  ros::Time min(timestamp), max(timestamp);
377  try {
378  min = timestamp - delay - tolerance /* - ros::Duration(dt) */;
379  } catch (std::runtime_error &e) {}
380 
381  try {
382  max = timestamp - delay + tolerance;
383  } catch (std::runtime_error &e) {}
384 
385  do {
386 
387  while(!command_queue_.empty()) {
388  hector_uav_msgs::MotorPWMConstPtr new_motor_voltage = command_queue_.front();
389  ros::Time new_time = new_motor_voltage->header.stamp;
390 
391  if (new_time.isZero() || (new_time >= min && new_time <= max)) {
392  setVoltage(*new_motor_voltage);
393  command_queue_.pop();
394  new_command = true;
395 
396  // new motor command is too old:
397  } else if (new_time < min) {
398  ROS_DEBUG_NAMED("quadrotor_propulsion", "Command received was %fs too old. Discarding.", (new_time - timestamp).toSec());
399  command_queue_.pop();
400 
401  // new motor command is too new:
402  } else {
403  break;
404  }
405  }
406 
407  if (command_queue_.empty() && !new_command) {
408  if (!motor_status_.on || wait.isZero()) break;
409 
410  ROS_DEBUG_NAMED("quadrotor_propulsion", "Waiting for command at simulation step t = %fs... last update was %fs ago", timestamp.toSec(), (timestamp - last_command_time_).toSec());
411  if (!callback_queue) {
412  if (command_condition_.timed_wait(lock, wait.toBoost())) continue;
413  } else {
414  lock.unlock();
415  callback_queue->callAvailable(wait);
416  lock.lock();
417  if (!command_queue_.empty()) continue;
418  }
419 
420  ROS_ERROR_NAMED("quadrotor_propulsion", "Command timed out. Disabled motors.");
421  shutdown();
422  }
423 
424  } while(false);
425 
426  if (new_command) {
427  ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "Using motor command valid at t = " << last_command_time_.toSec() << "s for simulation step at t = " << timestamp.toSec() << "s (dt = " << (timestamp - last_command_time_).toSec() << "s)");
428  }
429 
430  return new_command;
431 }
432 
434 {
435  if (dt <= 0.0) return;
436 
437  ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "propulsion.twist: " << PrintVector<double>(propulsion_model_->u.begin(), propulsion_model_->u.begin() + 6));
438  ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "propulsion.voltage: " << PrintVector<double>(propulsion_model_->u.begin() + 6, propulsion_model_->u.begin() + 10));
439  ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "propulsion.x_prior: " << PrintVector<double>(propulsion_model_->x.begin(), propulsion_model_->x.end()));
440 
441  checknan(propulsion_model_->u, "propulsion model input");
442  checknan(propulsion_model_->x, "propulsion model state");
443 
444  // update propulsion model
445  f(propulsion_model_->x.data(), propulsion_model_->u.data(), dt, propulsion_model_->y.data(), propulsion_model_->x_pred.data());
446  propulsion_model_->x = propulsion_model_->x_pred;
447 
448  ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "propulsion.x_post: " << PrintVector<double>(propulsion_model_->x.begin(), propulsion_model_->x.end()));
449  ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "propulsion.force: " << PrintVector<double>(propulsion_model_->y.begin() + 0, propulsion_model_->y.begin() + 3) << " " <<
450  "propulsion.torque: " << PrintVector<double>(propulsion_model_->y.begin() + 3, propulsion_model_->y.begin() + 6));
451 
452  checknan(propulsion_model_->y, "propulsion model output");
453 
454  wrench_.force.x = propulsion_model_->y[0];
455  wrench_.force.y = -propulsion_model_->y[1];
456  wrench_.force.z = propulsion_model_->y[2];
457  wrench_.torque.x = propulsion_model_->y[3];
458  wrench_.torque.y = -propulsion_model_->y[4];
459  wrench_.torque.z = -propulsion_model_->y[5];
460 
461  motor_status_.voltage[0] = propulsion_model_->u[6];
462  motor_status_.voltage[1] = propulsion_model_->u[7];
463  motor_status_.voltage[2] = propulsion_model_->u[8];
464  motor_status_.voltage[3] = propulsion_model_->u[9];
465 
466  motor_status_.frequency[0] = propulsion_model_->y[6];
467  motor_status_.frequency[1] = propulsion_model_->y[7];
468  motor_status_.frequency[2] = propulsion_model_->y[8];
469  motor_status_.frequency[3] = propulsion_model_->y[9];
470  motor_status_.running = motor_status_.frequency[0] > 1.0 && motor_status_.frequency[1] > 1.0 && motor_status_.frequency[2] > 1.0 && motor_status_.frequency[3] > 1.0;
471 
472  motor_status_.current[0] = propulsion_model_->y[10];
473  motor_status_.current[1] = propulsion_model_->y[11];
474  motor_status_.current[2] = propulsion_model_->y[12];
475  motor_status_.current[3] = propulsion_model_->y[13];
476 }
477 
479 {
480  motor_status_.on = true;
481 }
482 
484 {
485  motor_status_.on = false;
486 }
487 
488 } // namespace hector_quadrotor_model
bool configure(const ros::NodeHandle &param=ros::NodeHandle("~"))
#define ROS_DEBUG_STREAM_NAMED(name, args)
#define ROS_WARN_NAMED(name,...)
f
bool processQueue(const ros::Time &timestamp, const ros::Duration &tolerance=ros::Duration(), const ros::Duration &delay=ros::Duration(), const ros::WallDuration &wait=ros::WallDuration(), ros::CallbackQueue *callback_queue=0)
uint32_t int32_T
double real_T
static real_T rt_powd_snf(real_T u0, real_T u1)
#define ROS_DEBUG_NAMED(name,...)
void quadrotorPropulsion(const real_T xin[4], const real_T uin[10], const PropulsionParameters parameter, real_T dt, real_T y[14], real_T xpred[4])
void addPWMToQueue(const hector_uav_msgs::MotorPWMConstPtr &pwm)
const std::string & getNamespace() const
boost::posix_time::time_duration toBoost() const
void limit(T &value, const T &min, const T &max)
Definition: helpers.h:70
static void checknan(T &value, const std::string &text="")
Definition: helpers.h:85
ROSCONSOLE_DECL void shutdown()
void f(const double xin[4], const double uin[10], double dt, double y[14], double xpred[4]) const
bool getParam(const std::string &key, std::string &s) const
void setVoltage(const hector_uav_msgs::MotorPWM &command)
#define ROS_ERROR_NAMED(name,...)
void setTwist(const geometry_msgs::Twist &twist)
void addCommandToQueue(const hector_uav_msgs::MotorCommandConstPtr &command)


hector_quadrotor_model
Author(s): Johannes Meyer , Alexander Sendobry
autogenerated on Mon Jun 10 2019 13:36:56