quadrotor_propulsion.cpp
Go to the documentation of this file.
00001 //=================================================================================================
00002 // Copyright (c) 2012, Johannes Meyer, TU Darmstadt
00003 // All rights reserved.
00004 
00005 // Redistribution and use in source and binary forms, with or without
00006 // modification, are permitted provided that the following conditions are met:
00007 //     * Redistributions of source code must retain the above copyright
00008 //       notice, this list of conditions and the following disclaimer.
00009 //     * Redistributions in binary form must reproduce the above copyright
00010 //       notice, this list of conditions and the following disclaimer in the
00011 //       documentation and/or other materials provided with the distribution.
00012 //     * Neither the name of the Flight Systems and Automatic Control group,
00013 //       TU Darmstadt, nor the names of its contributors may be used to
00014 //       endorse or promote products derived from this software without
00015 //       specific prior written permission.
00016 
00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00018 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00019 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00020 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
00021 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00022 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00024 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00026 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027 //=================================================================================================
00028 
00029 #include <hector_quadrotor_controller/quadrotor_propulsion.h>
00030 #include <gazebo/common/Events.hh>
00031 #include <gazebo/physics/physics.hh>
00032 
00033 #include <rosgraph_msgs/Clock.h>
00034 
00035 extern "C" {
00036   #include "quadrotorPropulsion/quadrotorPropulsion.h"
00037   #include "quadrotorPropulsion/quadrotorPropulsion_initialize.h"
00038 }
00039 
00040 #include <boost/array.hpp>
00041 
00042 namespace gazebo {
00043 
00044 using namespace common;
00045 using namespace math;
00046 
00047 template <typename T> static inline T checknan(const T& value, const std::string& text = "") {
00048   if (!(value == value)) {
00049     if (!text.empty()) std::cerr << text << " contains **!?* Nan values!" << std::endl;
00050     return T();
00051   }
00052   return value;
00053 }
00054 
00055 // extern void quadrotorPropulsion(const real_T xin[4], const real_T uin[10], const PropulsionParameters parameter, real_T dt, real_T y[6], real_T xpred[4]);
00056 struct GazeboQuadrotorPropulsion::PropulsionModel {
00057   PropulsionParameters parameters_;
00058   boost::array<real_T,4>  x;
00059   boost::array<real_T,4>  x_pred;
00060   boost::array<real_T,10> u;
00061   boost::array<real_T,14> y;
00062 };
00063 
00064 GazeboQuadrotorPropulsion::GazeboQuadrotorPropulsion()
00065 {
00066   // initialize propulsion model
00067   quadrotorPropulsion_initialize();
00068   propulsion_model_ = new PropulsionModel;
00069 }
00070 
00072 // Destructor
00073 GazeboQuadrotorPropulsion::~GazeboQuadrotorPropulsion()
00074 {
00075   event::Events::DisconnectWorldUpdateStart(updateConnection);
00076   node_handle_->shutdown();
00077   callback_queue_thread_.join();
00078   delete node_handle_;
00079 
00080   delete propulsion_model_;
00081 }
00082 
00084 // Load the controller
00085 void GazeboQuadrotorPropulsion::Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
00086 {
00087   world = _model->GetWorld();
00088   link = _model->GetLink();
00089 
00090   // load parameters
00091   if (!_sdf->HasElement("robotNamespace"))
00092     namespace_.clear();
00093   else
00094     namespace_ = _sdf->GetElement("robotNamespace")->GetValueString() + "/";
00095 
00096   if (!_sdf->HasElement("paramNamespace"))
00097     param_namespace_ = "~/quadrotor_propulsion/";
00098   else
00099     param_namespace_ = _sdf->GetElement("paramNamespace")->GetValueString() + "/";
00100 
00101   if (!_sdf->HasElement("triggerTopic"))
00102     trigger_topic_ = "quadro/trigger";
00103   else
00104     trigger_topic_ = _sdf->GetElement("triggerTopic")->GetValueString();
00105 
00106   if (!_sdf->HasElement("voltageTopicName"))
00107     voltage_topic_ = "motor_pwm";
00108   else
00109     voltage_topic_ = _sdf->GetElement("voltageTopicName")->GetValueString();
00110 
00111   if (!_sdf->HasElement("wrenchTopic"))
00112     wrench_topic_ = "wrench_out";
00113   else
00114     wrench_topic_ = _sdf->GetElement("wrenchTopic")->GetValueString();
00115 
00116   if (!_sdf->HasElement("supplyTopic"))
00117     supply_topic_ = "supply";
00118   else
00119     supply_topic_ = _sdf->GetElement("supplyTopic")->GetValueString();
00120 
00121   if (!_sdf->HasElement("statusTopic"))
00122     status_topic_ = "motor_status";
00123   else
00124     status_topic_ = _sdf->GetElement("statusTopic")->GetValueString();
00125 
00126   // set control timing parameters
00127   controlTimer.Load(world, _sdf, "control");
00128   control_tolerance_ = Time();
00129   if (_sdf->HasElement("controlTolerance")) control_tolerance_ = _sdf->GetElement("controlTolerance")->GetValueDouble();
00130   control_delay_ = Time();
00131   if (_sdf->HasElement("controlDelay")) control_delay_ = _sdf->GetElement("controlDelay")->GetValueDouble();
00132 
00133   // set initial supply voltage
00134   supply_.voltage.resize(1);
00135   supply_.voltage[0] = 14.8;
00136   if (_sdf->HasElement("supplyVoltage")) supply_.voltage[0] = _sdf->GetElement("supplyVoltage")->GetValueDouble();
00137 
00138   // start ros node
00139   if (!ros::isInitialized())
00140   {
00141     int argc = 0;
00142     char **argv = NULL;
00143     ros::init(argc,argv,"gazebo",ros::init_options::NoSigintHandler|ros::init_options::AnonymousName);
00144   }
00145 
00146   node_handle_ = new ros::NodeHandle(namespace_);
00147 
00148 
00149   // publish trigger
00150   if (!trigger_topic_.empty())
00151   {
00152     ros::AdvertiseOptions ops = ros::AdvertiseOptions::create<rosgraph_msgs::Clock>(
00153       trigger_topic_, 10,
00154       ros::SubscriberStatusCallback(), ros::SubscriberStatusCallback(),
00155       ros::VoidConstPtr(), &callback_queue_);
00156     trigger_publisher_ = node_handle_->advertise(ops);
00157   }
00158 
00159   // subscribe command
00160   if (!voltage_topic_.empty())
00161   {
00162     ros::SubscribeOptions ops = ros::SubscribeOptions::create<hector_uav_msgs::MotorPWM>(
00163       voltage_topic_, 1,
00164       boost::bind(&GazeboQuadrotorPropulsion::CommandCallback, this, _1),
00165       ros::VoidPtr(), &callback_queue_);
00166     voltage_subscriber_ = node_handle_->subscribe(ops);
00167   }
00168 
00169   // publish wrench
00170   if (!wrench_topic_.empty())
00171   {
00172     ros::AdvertiseOptions ops = ros::AdvertiseOptions::create<geometry_msgs::Wrench>(
00173       wrench_topic_, 10,
00174       ros::SubscriberStatusCallback(), ros::SubscriberStatusCallback(),
00175       ros::VoidConstPtr(), &callback_queue_);
00176     wrench_publisher_ = node_handle_->advertise(ops);
00177   }
00178 
00179   // publish and latch supply
00180   if (!supply_topic_.empty())
00181   {
00182     ros::AdvertiseOptions ops = ros::AdvertiseOptions::create<hector_uav_msgs::Supply>(
00183       supply_topic_, 10,
00184       ros::SubscriberStatusCallback(), ros::SubscriberStatusCallback(),
00185       ros::VoidConstPtr(), &callback_queue_);
00186     ops.latch = true;
00187     supply_publisher_ = node_handle_->advertise(ops);
00188 
00189     supply_publisher_.publish(supply_);
00190   }
00191 
00192   // publish motor_status
00193   if (!status_topic_.empty())
00194   {
00195     ros::AdvertiseOptions ops = ros::AdvertiseOptions::create<hector_uav_msgs::MotorStatus>(
00196       status_topic_, 10,
00197       ros::SubscriberStatusCallback(), ros::SubscriberStatusCallback(),
00198       ros::VoidConstPtr(), &callback_queue_);
00199     motor_status_publisher_ = node_handle_->advertise(ops);
00200   }
00201 
00202   // callback_queue_thread_ = boost::thread( boost::bind( &GazeboQuadrotorPropulsion::QueueThread,this ) );
00203 
00204   Reset();
00205 
00206   // get model parameters
00207   ros::NodeHandle param(param_namespace_);
00208   param.getParam("k_m",     propulsion_model_->parameters_.k_m);
00209   param.getParam("k_t",     propulsion_model_->parameters_.k_t);
00210   param.getParam("CT0s",    propulsion_model_->parameters_.CT0s);
00211   param.getParam("CT1s",    propulsion_model_->parameters_.CT1s);
00212   param.getParam("CT2s",    propulsion_model_->parameters_.CT2s);
00213   param.getParam("J_M",     propulsion_model_->parameters_.J_M);
00214   param.getParam("l_m",     propulsion_model_->parameters_.l_m);
00215   param.getParam("Psi",     propulsion_model_->parameters_.Psi);
00216   param.getParam("R_A",     propulsion_model_->parameters_.R_A);
00217   param.getParam("alpha_m", propulsion_model_->parameters_.alpha_m);
00218   param.getParam("beta_m",  propulsion_model_->parameters_.beta_m);
00219 
00220   std::cout << "Loaded the following quadrotor propulsion model parameters from namespace " << param.getNamespace() << ":" << std::endl;
00221   std::cout << "k_m     = " << propulsion_model_->parameters_.k_m << std::endl;
00222   std::cout << "k_t     = " << propulsion_model_->parameters_.k_t << std::endl;
00223   std::cout << "CT2s    = " << propulsion_model_->parameters_.CT2s << std::endl;
00224   std::cout << "CT1s    = " << propulsion_model_->parameters_.CT1s << std::endl;
00225   std::cout << "CT0s    = " << propulsion_model_->parameters_.CT0s << std::endl;
00226   std::cout << "Psi     = " << propulsion_model_->parameters_.Psi << std::endl;
00227   std::cout << "J_M     = " << propulsion_model_->parameters_.J_M << std::endl;
00228   std::cout << "R_A     = " << propulsion_model_->parameters_.R_A << std::endl;
00229   std::cout << "l_m     = " << propulsion_model_->parameters_.l_m << std::endl;
00230   std::cout << "alpha_m = " << propulsion_model_->parameters_.alpha_m << std::endl;
00231   std::cout << "beta_m  = " << propulsion_model_->parameters_.beta_m << std::endl;
00232 
00233 
00234   // New Mechanism for Updating every World Cycle
00235   // Listen to the update event. This event is broadcast every
00236   // simulation iteration.
00237   updateConnection = event::Events::ConnectWorldUpdateStart(
00238       boost::bind(&GazeboQuadrotorPropulsion::Update, this));
00239 }
00240 
00242 // Callbacks
00243 void GazeboQuadrotorPropulsion::CommandCallback(const hector_uav_msgs::MotorPWMConstPtr& command)
00244 {
00245   //boost::mutex::scoped_lock lock(command_mutex_);
00246   if (!motor_status_.on)
00247     ROS_WARN_NAMED("quadrotor_propulsion", "Received new motor command. Enabled motors.");
00248 
00249   motor_status_.on = true;
00250   new_motor_voltages_.push(command);
00251   ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "Received motor command valid at " << command->header.stamp);
00252   command_condition_.notify_all();
00253 }
00254 
00256 // Update the controller
00257 void GazeboQuadrotorPropulsion::Update()
00258 {
00259   Vector3 force, torque;
00260   boost::mutex::scoped_lock lock(command_mutex_);
00261 
00262   // Get simulator time
00263   Time current_time = world->GetSimTime();
00264   Time dt = current_time - last_time_;
00265   last_time_ = current_time;
00266   if (dt <= 0.0) return;
00267 
00268   // Send trigger
00269   bool trigger = controlTimer.update();
00270   if (trigger && trigger_publisher_) {
00271     rosgraph_msgs::Clock clock;
00272     clock.clock = ros::Time(current_time.sec, current_time.nsec);
00273     trigger_publisher_.publish(clock);
00274 
00275     ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "Sent a trigger message at t = " << current_time.Double() << " (dt = " << (current_time - last_trigger_time_).Double() << ")");
00276     last_trigger_time_ = current_time;
00277   }
00278 
00279   // Get new commands/state
00280   callback_queue_.callAvailable();
00281 
00282   do {
00283     if (!new_motor_voltages_.empty()) {
00284       hector_uav_msgs::MotorPWMConstPtr new_motor_voltage = new_motor_voltages_.front();
00285       Time new_time = Time(new_motor_voltage->header.stamp.sec, new_motor_voltage->header.stamp.nsec);
00286 
00287       if (new_time == Time() || (new_time >= current_time - control_delay_ - dt - control_tolerance_ && new_time <= current_time - control_delay_ + control_tolerance_)) {
00288         motor_voltage_ = new_motor_voltage;
00289         new_motor_voltages_.pop();
00290         last_control_time_ = current_time;
00291         last_trigger_time_ = current_time;
00292         ROS_DEBUG_STREAM_NAMED("quadrotor_propulsion", "Using motor command valid at t = " << new_time.Double() << "s for simulation step at t = " << current_time.Double() << "s (dt = " << (current_time - new_time).Double() << "s)");
00293 
00294       // new motor command is too old:
00295       } else if (new_time < current_time - control_delay_ - control_tolerance_) {
00296         ROS_DEBUG_NAMED("quadrotor_propulsion", "command received was too old: %fs", (new_time - current_time).Double());
00297         new_motor_voltages_.pop();
00298         continue;
00299 
00300       // new motor command is too new:
00301       } else {
00302         // do nothing
00303       }
00304 
00305     } else {
00306       if (!motor_status_.on || !trigger) break;
00307 
00308       // if (new_motor_voltages_.empty() && motor_status_.on &&  control_period_ > 0 && current_time >= last_control_time_ + control_period_ + control_tolerance_) {
00309       ROS_DEBUG_NAMED("quadrotor_propulsion", "Waiting for command at simulation step t = %fs... last update was %fs ago", current_time.Double(), (current_time - last_control_time_).Double());
00310       // if (command_condition_.timed_wait(lock, (ros::Duration(control_period_.sec, control_period_.nsec) * 100.0).toBoost())) continue;
00311       callback_queue_.callAvailable(ros::WallDuration(1.0));
00312       if (!new_motor_voltages_.empty()) continue;
00313 
00314       ROS_ERROR_NAMED("quadrotor_propulsion", "Command timed out. Disabled motors.");
00315       motor_status_.on = false;
00316     }
00317 
00318   } while(false);
00319 
00320   // fill input vector u for propulsion model
00321   velocity = link->GetRelativeLinearVel();
00322   rate = link->GetRelativeAngularVel();
00323   propulsion_model_->u[0] = velocity.x;
00324   propulsion_model_->u[1] = -velocity.y;
00325   propulsion_model_->u[2] = -velocity.z;
00326   propulsion_model_->u[3] = rate.x;
00327   propulsion_model_->u[4] = -rate.y;
00328   propulsion_model_->u[5] = -rate.z;
00329   if (motor_status_.on && motor_voltage_ && motor_voltage_->pwm.size() >= 4) {
00330     propulsion_model_->u[6] = motor_voltage_->pwm[0] * supply_.voltage[0] / 255.0;
00331     propulsion_model_->u[7] = motor_voltage_->pwm[1] * supply_.voltage[0] / 255.0;
00332     propulsion_model_->u[8] = motor_voltage_->pwm[2] * supply_.voltage[0] / 255.0;
00333     propulsion_model_->u[9] = motor_voltage_->pwm[3] * supply_.voltage[0] / 255.0;
00334   } else {
00335     propulsion_model_->u[6] = 0.0;
00336     propulsion_model_->u[7] = 0.0;
00337     propulsion_model_->u[8] = 0.0;
00338     propulsion_model_->u[9] = 0.0;
00339   }
00340 
00341 //  std::cout << "u = [ ";
00342 //  for(std::size_t i = 0; i < propulsion_model_->u.size(); ++i)
00343 //    std::cout << propulsion_model_->u[i] << " ";
00344 //  std::cout << "]" << std::endl;
00345 
00346   checknan(propulsion_model_->u, "propulsion model input");
00347   checknan(propulsion_model_->x, "propulsion model state");
00348 
00349   // update propulsion model
00350   quadrotorPropulsion(propulsion_model_->x.data(), propulsion_model_->u.data(), propulsion_model_->parameters_, dt.Double(), propulsion_model_->y.data(), propulsion_model_->x_pred.data());
00351   propulsion_model_->x = propulsion_model_->x_pred;
00352   force  = force  + checknan(Vector3(propulsion_model_->y[0], -propulsion_model_->y[1], propulsion_model_->y[2]), "propulsion model force");
00353   torque = torque + checknan(Vector3(propulsion_model_->y[3], -propulsion_model_->y[4], -propulsion_model_->y[5]), "propulsion model torque");
00354 
00355 //  std::cout << "y = [ ";
00356 //  for(std::size_t i = 0; i < propulsion_model_->y.size(); ++i)
00357 //    std::cout << propulsion_model_->y[i] << " ";
00358 //  std::cout << "]" << std::endl;
00359 
00360   // publish wrench
00361   if (wrench_publisher_) {
00362     wrench_.force.x = force.x;
00363     wrench_.force.y = force.y;
00364     wrench_.force.z = force.z;
00365     wrench_.torque.x = torque.x;
00366     wrench_.torque.y = torque.y;
00367     wrench_.torque.z = torque.z;
00368     wrench_publisher_.publish(wrench_);
00369   }
00370 
00371   // set force and torque in gazebo
00372   link->AddRelativeForce(force);
00373   link->AddRelativeTorque(torque - link->GetInertial()->GetCoG().GetCrossProd(force));
00374 
00375   // publish motor status
00376   if (motor_status_publisher_ && trigger /* && current_time >= last_motor_status_time_ + control_period_ */) {
00377     motor_status_.header.stamp = ros::Time(current_time.sec, current_time.nsec);
00378 
00379     motor_status_.voltage.resize(4);
00380     motor_status_.voltage[0] = propulsion_model_->u[6];
00381     motor_status_.voltage[1] = propulsion_model_->u[7];
00382     motor_status_.voltage[2] = propulsion_model_->u[8];
00383     motor_status_.voltage[3] = propulsion_model_->u[9];
00384 
00385     motor_status_.frequency.resize(4);
00386     motor_status_.frequency[0] = propulsion_model_->y[6];
00387     motor_status_.frequency[1] = propulsion_model_->y[7];
00388     motor_status_.frequency[2] = propulsion_model_->y[8];
00389     motor_status_.frequency[3] = propulsion_model_->y[9];
00390     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;
00391 
00392     motor_status_.current.resize(4);
00393     motor_status_.current[0] = propulsion_model_->y[10];
00394     motor_status_.current[1] = propulsion_model_->y[11];
00395     motor_status_.current[2] = propulsion_model_->y[12];
00396     motor_status_.current[3] = propulsion_model_->y[13];
00397     motor_status_publisher_.publish(motor_status_);
00398     last_motor_status_time_ = current_time;
00399   }
00400 }
00401 
00403 // Reset the controller
00404 void GazeboQuadrotorPropulsion::Reset()
00405 {
00406   propulsion_model_->x.assign(0.0);
00407   propulsion_model_->x_pred.assign(0.0);
00408   last_control_time_ = Time();
00409   last_motor_status_time_ = Time();
00410   new_motor_voltages_ = std::queue<hector_uav_msgs::MotorPWMConstPtr>(); // .clear();
00411   motor_voltage_.reset();
00412 }
00413 
00415 // custom callback queue thread
00416 void GazeboQuadrotorPropulsion::QueueThread()
00417 {
00418   static const double timeout = 0.01;
00419 
00420   while (node_handle_->ok())
00421   {
00422     callback_queue_.callAvailable(ros::WallDuration(timeout));
00423   }
00424 }
00425 
00426 // Register this plugin with the simulator
00427 GZ_REGISTER_MODEL_PLUGIN(GazeboQuadrotorPropulsion)
00428 
00429 } // namespace gazebo
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Friends


hector_quadrotor_controller
Author(s): Johannes Meyer and Alexander Sendobry
autogenerated on Mon Jul 15 2013 16:57:22