35 #include <boost/array.hpp> 67 , J_M(
std::numeric_limits<
real_T>::infinity())
68 , R_A(
std::numeric_limits<
real_T>::infinity())
77 boost::array<real_T,4>
x;
79 boost::array<real_T,10>
u;
80 boost::array<real_T,14>
y;
92 delete propulsion_model_;
136 for (i = 0; i < 4; i++) {
143 memset(&y[0], 0, 14U *
sizeof(
real_T));
144 for (i = 0; i < 4; i++) {
151 for (i = 0; i < 3; i++) {
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];
168 for (i = 0; i < 4; i++) {
183 temp = (U[i] * parameter.
beta_m - parameter.
Psi * xin[i]) / (2.0 *
187 d0 = parameter.
Psi * 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])));
218 y[3] = (F_m[3] - F_m[1]) * parameter.
l_m;
221 y[4] = (F_m[0] - F_m[2]) * parameter.
l_m;
224 y[5] = ((-M_e[0] - M_e[2]) + M_e[1]) + M_e[3];
227 for (i = 0; i < 4; i++) {
232 for (i = 0; i < 4; i++) {
241 inline void QuadrotorPropulsion::f(
const double xin[4],
const double uin[10],
double dt,
double y[14],
double xpred[4])
const 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;
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;
276 initial_voltage_ = 14.8;
277 param.
getParam(
"supply_voltage", initial_voltage_);
285 boost::mutex::scoped_lock lock(mutex_);
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);
292 wrench_ = geometry_msgs::Wrench();
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);
299 supply_ = hector_uav_msgs::Supply();
300 supply_.voltage.resize(1);
301 supply_.voltage[0] = initial_voltage_;
305 command_queue_ = std::queue<hector_uav_msgs::MotorPWMConstPtr>();
310 boost::mutex::scoped_lock lock(mutex_);
311 last_command_time_ = voltage.header.stamp;
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;
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;
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;
338 limit(boost::iterator_range<boost::array<real_T,10>::iterator>(&(propulsion_model_->u[0]), &(propulsion_model_->u[6])), -100.0, 100.0);
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);
360 boost::mutex::scoped_lock lock(command_queue_mutex_);
362 if (!motor_status_.on) {
363 ROS_WARN_NAMED(
"quadrotor_propulsion",
"Received new motor command. Enabled motors.");
368 command_queue_.push(pwm);
369 command_condition_.notify_all();
373 boost::mutex::scoped_lock lock(command_queue_mutex_);
374 bool new_command =
false;
376 ros::Time min(timestamp), max(timestamp);
378 min = timestamp - delay - tolerance ;
379 }
catch (std::runtime_error &e) {}
382 max = timestamp - delay + tolerance;
383 }
catch (std::runtime_error &e) {}
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;
391 if (new_time.
isZero() || (new_time >= min && new_time <= max)) {
392 setVoltage(*new_motor_voltage);
393 command_queue_.pop();
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();
407 if (command_queue_.empty() && !new_command) {
408 if (!motor_status_.on || wait.
isZero())
break;
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;
417 if (!command_queue_.empty())
continue;
420 ROS_ERROR_NAMED(
"quadrotor_propulsion",
"Command timed out. Disabled motors.");
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)");
435 if (dt <= 0.0)
return;
441 checknan(propulsion_model_->u,
"propulsion model input");
442 checknan(propulsion_model_->x,
"propulsion model state");
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;
450 "propulsion.torque: " <<
PrintVector<double>(propulsion_model_->y.begin() + 3, propulsion_model_->y.begin() + 6));
452 checknan(propulsion_model_->y,
"propulsion model output");
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];
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];
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;
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];
480 motor_status_.on =
true;
485 motor_status_.on =
false;
bool configure(const ros::NodeHandle ¶m=ros::NodeHandle("~"))
boost::array< real_T, 4 > x
#define ROS_DEBUG_STREAM_NAMED(name, args)
#define ROS_WARN_NAMED(name,...)
bool processQueue(const ros::Time ×tamp, const ros::Duration &tolerance=ros::Duration(), const ros::Duration &delay=ros::Duration(), const ros::WallDuration &wait=ros::WallDuration(), ros::CallbackQueue *callback_queue=0)
PropulsionParameters parameters_
boost::array< real_T, 4 > x_pred
static real_T rt_powd_snf(real_T u0, real_T u1)
#define ROS_DEBUG_NAMED(name,...)
boost::array< real_T, 10 > u
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)
static void checknan(T &value, const std::string &text="")
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,...)
boost::array< real_T, 14 > y
void setTwist(const geometry_msgs::Twist &twist)
void addCommandToQueue(const hector_uav_msgs::MotorCommandConstPtr &command)