00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <hector_quadrotor_model/quadrotor_propulsion.h>
00030 #include <hector_quadrotor_model/helpers.h>
00031
00032 #include <ros/node_handle.h>
00033 #include <ros/callback_queue.h>
00034
00035 #include <boost/array.hpp>
00036
00037 extern "C" {
00038 #include "quadrotorPropulsion/quadrotorPropulsion.h"
00039 #include "quadrotorPropulsion/quadrotorPropulsion_initialize.h"
00040 }
00041
00042 namespace hector_quadrotor_model {
00043
00044
00045 struct QuadrotorPropulsion::PropulsionModel {
00046 PropulsionParameters parameters_;
00047 boost::array<real_T,4> x;
00048 boost::array<real_T,4> x_pred;
00049 boost::array<real_T,10> u;
00050 boost::array<real_T,14> y;
00051 };
00052
00053 QuadrotorPropulsion::QuadrotorPropulsion()
00054 {
00055
00056 quadrotorPropulsion_initialize();
00057 propulsion_model_ = new PropulsionModel;
00058 }
00059
00060 QuadrotorPropulsion::~QuadrotorPropulsion()
00061 {
00062 delete propulsion_model_;
00063 }
00064
00065 inline void QuadrotorPropulsion::f(const real_T xin[4], const real_T uin[10], real_T dt, real_T y[14], real_T xpred[4]) const
00066 {
00067 ::quadrotorPropulsion(xin, uin, propulsion_model_->parameters_, dt, y, xpred);
00068 }
00069
00070 void QuadrotorPropulsion::configure(const std::string &ns)
00071 {
00072 ros::NodeHandle param(ns);
00073
00074
00075 param.getParam("k_m", propulsion_model_->parameters_.k_m);
00076 param.getParam("k_t", propulsion_model_->parameters_.k_t);
00077 param.getParam("CT0s", propulsion_model_->parameters_.CT0s);
00078 param.getParam("CT1s", propulsion_model_->parameters_.CT1s);
00079 param.getParam("CT2s", propulsion_model_->parameters_.CT2s);
00080 param.getParam("J_M", propulsion_model_->parameters_.J_M);
00081 param.getParam("l_m", propulsion_model_->parameters_.l_m);
00082 param.getParam("Psi", propulsion_model_->parameters_.Psi);
00083 param.getParam("R_A", propulsion_model_->parameters_.R_A);
00084 param.getParam("alpha_m", propulsion_model_->parameters_.alpha_m);
00085 param.getParam("beta_m", propulsion_model_->parameters_.beta_m);
00086
00087 #ifndef NDEBUG
00088 std::cout << "Loaded the following quadrotor propulsion model parameters from namespace " << param.getNamespace() << ":" << std::endl;
00089 std::cout << "k_m = " << propulsion_model_->parameters_.k_m << std::endl;
00090 std::cout << "k_t = " << propulsion_model_->parameters_.k_t << std::endl;
00091 std::cout << "CT2s = " << propulsion_model_->parameters_.CT2s << std::endl;
00092 std::cout << "CT1s = " << propulsion_model_->parameters_.CT1s << std::endl;
00093 std::cout << "CT0s = " << propulsion_model_->parameters_.CT0s << std::endl;
00094 std::cout << "Psi = " << propulsion_model_->parameters_.Psi << std::endl;
00095 std::cout << "J_M = " << propulsion_model_->parameters_.J_M << std::endl;
00096 std::cout << "R_A = " << propulsion_model_->parameters_.R_A << std::endl;
00097 std::cout << "l_m = " << propulsion_model_->parameters_.l_m << std::endl;
00098 std::cout << "alpha_m = " << propulsion_model_->parameters_.alpha_m << std::endl;
00099 std::cout << "beta_m = " << propulsion_model_->parameters_.beta_m << std::endl;
00100 #endif
00101
00102 initial_voltage_ = 14.8;
00103 param.getParam("supply_voltage", initial_voltage_);
00104 reset();
00105 }
00106
00107 void QuadrotorPropulsion::reset()
00108 {
00109 boost::mutex::scoped_lock lock(mutex_);
00110
00111 propulsion_model_->x.assign(0.0);
00112 propulsion_model_->x_pred.assign(0.0);
00113 propulsion_model_->u.assign(0.0);
00114 propulsion_model_->y.assign(0.0);
00115
00116 wrench_ = geometry_msgs::Wrench();
00117
00118 motor_status_ = hector_uav_msgs::MotorStatus();
00119 motor_status_.voltage.resize(4);
00120 motor_status_.frequency.resize(4);
00121 motor_status_.current.resize(4);
00122
00123 supply_ = hector_uav_msgs::Supply();
00124 supply_.voltage.resize(1);
00125 supply_.voltage[0] = initial_voltage_;
00126
00127 last_command_time_ = ros::Time();
00128
00129 command_queue_ = std::queue<hector_uav_msgs::MotorPWMConstPtr>();
00130 }
00131
00132 void QuadrotorPropulsion::setVoltage(const hector_uav_msgs::MotorPWM& voltage)
00133 {
00134 boost::mutex::scoped_lock lock(mutex_);
00135 last_command_time_ = voltage.header.stamp;
00136
00137 if (motor_status_.on && voltage.pwm.size() >= 4) {
00138 propulsion_model_->u[6] = voltage.pwm[0] * supply_.voltage[0] / 255.0;
00139 propulsion_model_->u[7] = voltage.pwm[1] * supply_.voltage[0] / 255.0;
00140 propulsion_model_->u[8] = voltage.pwm[2] * supply_.voltage[0] / 255.0;
00141 propulsion_model_->u[9] = voltage.pwm[3] * supply_.voltage[0] / 255.0;
00142 } else {
00143 propulsion_model_->u[6] = 0.0;
00144 propulsion_model_->u[7] = 0.0;
00145 propulsion_model_->u[8] = 0.0;
00146 propulsion_model_->u[9] = 0.0;
00147 }
00148 }
00149
00150 void QuadrotorPropulsion::setTwist(const geometry_msgs::Twist &twist)
00151 {
00152 boost::mutex::scoped_lock lock(mutex_);
00153 propulsion_model_->u[0] = twist.linear.x;
00154 propulsion_model_->u[1] = -twist.linear.y;
00155 propulsion_model_->u[2] = -twist.linear.z;
00156 propulsion_model_->u[3] = twist.angular.x;
00157 propulsion_model_->u[4] = -twist.angular.y;
00158 propulsion_model_->u[5] = -twist.angular.z;
00159 }
00160
00161 void QuadrotorPropulsion::addVoltageToQueue(const hector_uav_msgs::MotorPWMConstPtr& command)
00162 {
00163 boost::mutex::scoped_lock lock(command_queue_mutex_);
00164
00165 if (!motor_status_.on) {
00166 ROS_WARN_NAMED("quadrotor_propulsion", "Received new motor command. Enabled motors.");
00167 engage();
00168 }
00169
00170 ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "Received motor command valid at " << command->header.stamp);
00171 command_queue_.push(command);
00172 command_condition_.notify_all();
00173 }
00174
00175 bool QuadrotorPropulsion::processQueue(const ros::Time ×tamp, const ros::Duration &tolerance, const ros::Duration &delay, const ros::WallDuration &wait, ros::CallbackQueue *callback_queue) {
00176 boost::mutex::scoped_lock lock(command_queue_mutex_);
00177 bool new_command = false;
00178
00179 ros::Time min(timestamp), max(timestamp);
00180 try {
00181 min = timestamp - delay - tolerance ;
00182 } catch (std::runtime_error &e) {}
00183
00184 try {
00185 max = timestamp - delay + tolerance;
00186 } catch (std::runtime_error &e) {}
00187
00188 do {
00189
00190 if (!command_queue_.empty()) {
00191 hector_uav_msgs::MotorPWMConstPtr new_motor_voltage = command_queue_.front();
00192 ros::Time new_time = new_motor_voltage->header.stamp;
00193
00194 if (new_time.isZero() || (new_time >= min && new_time <= max)) {
00195 setVoltage(*new_motor_voltage);
00196 command_queue_.pop();
00197 new_command = true;
00198
00199 ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "Using motor command valid at t = " << new_time.toSec() << "s for simulation step at t = " << timestamp.toSec() << "s (dt = " << (timestamp - new_time).toSec() << "s)");
00200
00201
00202 } else if (new_time < min) {
00203 ROS_DEBUG_NAMED("quadrotor_propulsion", "Command received was %fs too old. Discarding.", (new_time - timestamp).toSec());
00204 command_queue_.pop();
00205 continue;
00206
00207
00208 } else {
00209
00210 }
00211
00212 } else {
00213 if (!motor_status_.on || wait.isZero()) break;
00214
00215 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());
00216 if (!callback_queue) {
00217 if (command_condition_.timed_wait(lock, wait.toBoost())) continue;
00218 } else {
00219 lock.unlock();
00220 callback_queue->callAvailable(wait);
00221 lock.lock();
00222 if (!command_queue_.empty()) continue;
00223 }
00224
00225 ROS_ERROR_NAMED("quadrotor_propulsion", "Command timed out. Disabled motors.");
00226 shutdown();
00227 }
00228
00229 } while(false);
00230
00231 return new_command;
00232 }
00233
00234 void QuadrotorPropulsion::update(double dt)
00235 {
00236 if (dt <= 0.0) return;
00237
00238
00239
00240
00241
00242
00243 checknan(propulsion_model_->u, "propulsion model input");
00244 checknan(propulsion_model_->x, "propulsion model state");
00245
00246
00247 f(propulsion_model_->x.data(), propulsion_model_->u.data(), dt, propulsion_model_->y.data(), propulsion_model_->x_pred.data());
00248 propulsion_model_->x = propulsion_model_->x_pred;
00249
00250 checknan(propulsion_model_->y, "propulsion model output");
00251
00252
00253
00254
00255
00256
00257 wrench_.force.x = propulsion_model_->y[0];
00258 wrench_.force.y = -propulsion_model_->y[1];
00259 wrench_.force.z = propulsion_model_->y[2];
00260 wrench_.torque.x = propulsion_model_->y[3];
00261 wrench_.torque.y = -propulsion_model_->y[4];
00262 wrench_.torque.z = -propulsion_model_->y[5];
00263
00264 motor_status_.voltage[0] = propulsion_model_->u[6];
00265 motor_status_.voltage[1] = propulsion_model_->u[7];
00266 motor_status_.voltage[2] = propulsion_model_->u[8];
00267 motor_status_.voltage[3] = propulsion_model_->u[9];
00268
00269 motor_status_.frequency[0] = propulsion_model_->y[6];
00270 motor_status_.frequency[1] = propulsion_model_->y[7];
00271 motor_status_.frequency[2] = propulsion_model_->y[8];
00272 motor_status_.frequency[3] = propulsion_model_->y[9];
00273 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;
00274
00275 motor_status_.current[0] = propulsion_model_->y[10];
00276 motor_status_.current[1] = propulsion_model_->y[11];
00277 motor_status_.current[2] = propulsion_model_->y[12];
00278 motor_status_.current[3] = propulsion_model_->y[13];
00279 }
00280
00281 void QuadrotorPropulsion::engage()
00282 {
00283 motor_status_.on = true;
00284 }
00285
00286 void QuadrotorPropulsion::shutdown()
00287 {
00288 motor_status_.on = false;
00289 }
00290
00291 }