gazebo_quadrotor_simple_controller.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_gazebo_plugins/gazebo_quadrotor_simple_controller.h>
00030 #include <gazebo/common/Events.hh>
00031 #include <gazebo/physics/physics.hh>
00032 
00033 #include <cmath>
00034 
00035 #include <geometry_msgs/Wrench.h>
00036 
00037 namespace gazebo {
00038 
00039 GazeboQuadrotorSimpleController::GazeboQuadrotorSimpleController()
00040 {
00041 }
00042 
00044 // Destructor
00045 GazeboQuadrotorSimpleController::~GazeboQuadrotorSimpleController()
00046 {
00047   event::Events::DisconnectWorldUpdateBegin(updateConnection);
00048 
00049   node_handle_->shutdown();
00050   delete node_handle_;
00051 }
00052 
00054 // Load the controller
00055 void GazeboQuadrotorSimpleController::Load(physics::ModelPtr _model, sdf::ElementPtr _sdf)
00056 {
00057   world = _model->GetWorld();
00058   link = _model->GetLink();
00059   link_name_ = link->GetName();
00060 
00061   // default parameters
00062   namespace_.clear();
00063   velocity_topic_ = "cmd_vel";
00064   imu_topic_.clear();
00065   state_topic_.clear();
00066   wrench_topic_ = "wrench_out";
00067   max_force_ = -1;
00068 
00069   // load parameters
00070   if (_sdf->HasElement("robotNamespace")) namespace_ = _sdf->GetElement("robotNamespace")->GetValueString();
00071   if (_sdf->HasElement("topicName"))      velocity_topic_ = _sdf->GetElement("topicName")->GetValueString();
00072   if (_sdf->HasElement("imuTopic"))       imu_topic_ = _sdf->GetElement("imuTopic")->GetValueString();
00073   if (_sdf->HasElement("stateTopic"))     state_topic_ = _sdf->GetElement("stateTopic")->GetValueString();
00074   if (_sdf->HasElement("wrenchTopic"))    wrench_topic_ = _sdf->GetElement("wrenchTopic")->GetValueString();
00075   if (_sdf->HasElement("maxForce"))       max_force_ = _sdf->GetElement("maxForce")->GetValueDouble();
00076 
00077   if (_sdf->HasElement("bodyName") && _sdf->GetElement("bodyName")->GetValue()) {
00078     link_name_ = _sdf->GetElement("bodyName")->GetValueString();
00079     link = _model->GetLink(link_name_);
00080   }
00081 
00082   if (!link)
00083   {
00084     ROS_FATAL("gazebo_ros_baro plugin error: bodyName: %s does not exist\n", link_name_.c_str());
00085     return;
00086   }
00087 
00088   // configure controllers
00089   controllers_.roll.Load(_sdf, "rollpitch");
00090   controllers_.pitch.Load(_sdf, "rollpitch");
00091   controllers_.yaw.Load(_sdf, "yaw");
00092   controllers_.velocity_x.Load(_sdf, "velocityXY");
00093   controllers_.velocity_y.Load(_sdf, "velocityXY");
00094   controllers_.velocity_z.Load(_sdf, "velocityZ");
00095 
00096   // Get inertia and mass of quadrotor body
00097   inertia = link->GetInertial()->GetPrincipalMoments();
00098   mass = link->GetInertial()->GetMass();
00099 
00100   // start ros node
00101   if (!ros::isInitialized())
00102   {
00103     int argc = 0;
00104     char **argv = NULL;
00105     ros::init(argc,argv,"gazebo",ros::init_options::NoSigintHandler|ros::init_options::AnonymousName);
00106   }
00107   node_handle_ = new ros::NodeHandle(namespace_);
00108 
00109   // subscribe command
00110   if (!velocity_topic_.empty())
00111   {
00112     ros::SubscribeOptions ops = ros::SubscribeOptions::create<geometry_msgs::Twist>(
00113       velocity_topic_, 1,
00114       boost::bind(&GazeboQuadrotorSimpleController::VelocityCallback, this, _1),
00115       ros::VoidPtr(), &callback_queue_);
00116     velocity_subscriber_ = node_handle_->subscribe(ops);
00117   }
00118 
00119   // subscribe imu
00120   if (!imu_topic_.empty())
00121   {
00122     ros::SubscribeOptions ops = ros::SubscribeOptions::create<sensor_msgs::Imu>(
00123       imu_topic_, 1,
00124       boost::bind(&GazeboQuadrotorSimpleController::ImuCallback, this, _1),
00125       ros::VoidPtr(), &callback_queue_);
00126     imu_subscriber_ = node_handle_->subscribe(ops);
00127 
00128     ROS_INFO_NAMED("quadrotor_simple_controller", "Using imu information on topic %s as source of orientation and angular velocity.", imu_topic_.c_str());
00129   }
00130 
00131   // subscribe state
00132   if (!state_topic_.empty())
00133   {
00134     ros::SubscribeOptions ops = ros::SubscribeOptions::create<nav_msgs::Odometry>(
00135       state_topic_, 1,
00136       boost::bind(&GazeboQuadrotorSimpleController::StateCallback, this, _1),
00137       ros::VoidPtr(), &callback_queue_);
00138     state_subscriber_ = node_handle_->subscribe(ops);
00139 
00140     ROS_INFO_NAMED("quadrotor_simple_controller", "Using state information on topic %s as source of state information.", state_topic_.c_str());
00141   }
00142 
00143   // publish wrench
00144   if (!wrench_topic_.empty())
00145   {
00146     ros::AdvertiseOptions ops = ros::AdvertiseOptions::create<geometry_msgs::Wrench>(
00147       wrench_topic_, 10,
00148       ros::SubscriberStatusCallback(), ros::SubscriberStatusCallback(), ros::VoidConstPtr(), &callback_queue_
00149     );
00150     wrench_publisher_ = node_handle_->advertise(ops);
00151   }
00152 
00153   // callback_queue_thread_ = boost::thread( boost::bind( &GazeboQuadrotorSimpleController::CallbackQueueThread,this ) );
00154 
00155 
00156   Reset();
00157 
00158   // New Mechanism for Updating every World Cycle
00159   // Listen to the update event. This event is broadcast every
00160   // simulation iteration.
00161   controlTimer.Load(world, _sdf);
00162   updateConnection = event::Events::ConnectWorldUpdateBegin(
00163       boost::bind(&GazeboQuadrotorSimpleController::Update, this));
00164 }
00165 
00167 // Callbacks
00168 void GazeboQuadrotorSimpleController::VelocityCallback(const geometry_msgs::TwistConstPtr& velocity)
00169 {
00170   velocity_command_ = *velocity;
00171 }
00172 
00173 void GazeboQuadrotorSimpleController::ImuCallback(const sensor_msgs::ImuConstPtr& imu)
00174 {
00175   pose.rot.Set(imu->orientation.w, imu->orientation.x, imu->orientation.y, imu->orientation.z);
00176   euler = pose.rot.GetAsEuler();
00177   angular_velocity = pose.rot.RotateVector(math::Vector3(imu->angular_velocity.x, imu->angular_velocity.y, imu->angular_velocity.z));
00178 }
00179 
00180 void GazeboQuadrotorSimpleController::StateCallback(const nav_msgs::OdometryConstPtr& state)
00181 {
00182   math::Vector3 velocity1(velocity);
00183 
00184   if (imu_topic_.empty()) {
00185     pose.pos.Set(state->pose.pose.position.x, state->pose.pose.position.y, state->pose.pose.position.z);
00186     pose.rot.Set(state->pose.pose.orientation.w, state->pose.pose.orientation.x, state->pose.pose.orientation.y, state->pose.pose.orientation.z);
00187     euler = pose.rot.GetAsEuler();
00188     angular_velocity.Set(state->twist.twist.angular.x, state->twist.twist.angular.y, state->twist.twist.angular.z);
00189   }
00190 
00191   velocity.Set(state->twist.twist.linear.x, state->twist.twist.linear.y, state->twist.twist.linear.z);
00192 
00193   // calculate acceleration
00194   double dt = !state_stamp.isZero() ? (state->header.stamp - state_stamp).toSec() : 0.0;
00195   state_stamp = state->header.stamp;
00196   if (dt > 0.0) {
00197     acceleration = (velocity - velocity1) / dt;
00198   } else {
00199     acceleration.Set();
00200   }
00201 }
00202 
00204 // Update the controller
00205 void GazeboQuadrotorSimpleController::Update()
00206 {
00207   // Get new commands/state
00208   callback_queue_.callAvailable();
00209 
00210   double dt;
00211   if (controlTimer.update(dt) && dt > 0.0) {
00212     // Get Pose/Orientation from Gazebo (if no state subscriber is active)
00213     if (imu_topic_.empty()) {
00214       pose = link->GetWorldPose();
00215       angular_velocity = link->GetWorldAngularVel();
00216       euler = pose.rot.GetAsEuler();
00217     }
00218     if (state_topic_.empty()) {
00219       acceleration = (link->GetWorldLinearVel() - velocity) / dt;
00220       velocity = link->GetWorldLinearVel();
00221     }
00222 
00223   //  static Time lastDebug;
00224   //  if ((world->GetSimTime() - lastDebug).Double() > 0.5) {
00225   //    ROS_DEBUG_STREAM_NAMED("quadrotor_simple_controller", "Velocity:         gazebo = [" << link->GetWorldLinearVel()   << "], state = [" << velocity << "]");
00226   //    ROS_DEBUG_STREAM_NAMED("quadrotor_simple_controller", "Acceleration:     gazebo = [" << link->GetWorldLinearAccel() << "], state = [" << acceleration << "]");
00227   //    ROS_DEBUG_STREAM_NAMED("quadrotor_simple_controller", "Angular Velocity: gazebo = [" << link->GetWorldAngularVel() << "], state = [" << angular_velocity << "]");
00228   //    lastDebug = world->GetSimTime();
00229   //  }
00230 
00231     // Get gravity
00232     math::Vector3 gravity_body = pose.rot.RotateVector(world->GetPhysicsEngine()->GetGravity());
00233     double gravity = gravity_body.GetLength();
00234     double load_factor = gravity * gravity / world->GetPhysicsEngine()->GetGravity().Dot(gravity_body);  // Get gravity
00235 
00236     // Rotate vectors to coordinate frames relevant for control
00237     math::Quaternion heading_quaternion(cos(euler.z/2),0,0,sin(euler.z/2));
00238     math::Vector3 velocity_xy = heading_quaternion.RotateVectorReverse(velocity);
00239     math::Vector3 acceleration_xy = heading_quaternion.RotateVectorReverse(acceleration);
00240     math::Vector3 angular_velocity_body = pose.rot.RotateVectorReverse(angular_velocity);
00241 
00242     // update controllers
00243     force.Set(0.0, 0.0, 0.0);
00244     torque.Set(0.0, 0.0, 0.0);
00245     double pitch_command =  controllers_.velocity_x.update(velocity_command_.linear.x, velocity_xy.x, acceleration_xy.x, dt) / gravity;
00246     double roll_command  = -controllers_.velocity_y.update(velocity_command_.linear.y, velocity_xy.y, acceleration_xy.y, dt) / gravity;
00247     torque.x = inertia.x *  controllers_.roll.update(roll_command, euler.x, angular_velocity_body.x, dt);
00248     torque.y = inertia.y *  controllers_.pitch.update(pitch_command, euler.y, angular_velocity_body.y, dt);
00249     // torque.x = inertia.x *  controllers_.roll.update(-velocity_command_.linear.y/gravity, euler.x, angular_velocity_body.x, dt);
00250     // torque.y = inertia.y *  controllers_.pitch.update(velocity_command_.linear.x/gravity, euler.y, angular_velocity_body.y, dt);
00251     torque.z = inertia.z *  controllers_.yaw.update(velocity_command_.angular.z, angular_velocity.z, 0, dt);
00252     force.z  = mass      * (controllers_.velocity_z.update(velocity_command_.linear.z,  velocity.z, acceleration.z, dt) + load_factor * gravity);
00253     if (max_force_ > 0.0 && force.z > max_force_) force.z = max_force_;
00254     if (force.z < 0.0) force.z = 0.0;
00255 
00256   //  static double lastDebugOutput = 0.0;
00257   //  if (last_time.Double() - lastDebugOutput > 0.1) {
00258   //    ROS_DEBUG_NAMED("quadrotor_simple_controller", "Velocity = [%g %g %g], Acceleration = [%g %g %g]", velocity.x, velocity.y, velocity.z, acceleration.x, acceleration.y, acceleration.z);
00259   //    ROS_DEBUG_NAMED("quadrotor_simple_controller", "Command: linear = [%g %g %g], angular = [%g %g %g], roll/pitch = [%g %g]", velocity_command_.linear.x, velocity_command_.linear.y, velocity_command_.linear.z, velocity_command_.angular.x*180/M_PI, velocity_command_.angular.y*180/M_PI, velocity_command_.angular.z*180/M_PI, roll_command*180/M_PI, pitch_command*180/M_PI);
00260   //    ROS_DEBUG_NAMED("quadrotor_simple_controller", "Mass: %g kg, Inertia: [%g %g %g], Load: %g g", mass, inertia.x, inertia.y, inertia.z, load_factor);
00261   //    ROS_DEBUG_NAMED("quadrotor_simple_controller", "Force: [%g %g %g], Torque: [%g %g %g]", force.x, force.y, force.z, torque.x, torque.y, torque.z);
00262   //    lastDebugOutput = last_time.Double();
00263   //  }
00264 
00265     // Publish wrench
00266     if (wrench_publisher_) {
00267       geometry_msgs::Wrench wrench;
00268       wrench.force.x = force.x;
00269       wrench.force.y = force.y;
00270       wrench.force.z = force.z;
00271       wrench.torque.x = torque.x;
00272       wrench.torque.y = torque.y;
00273       wrench.torque.z = torque.z;
00274       wrench_publisher_.publish(wrench);
00275     }
00276   }
00277 
00278   // set force and torque in gazebo
00279   link->AddRelativeForce(force);
00280   link->AddRelativeTorque(torque - link->GetInertial()->GetCoG().Cross(force));
00281 }
00282 
00284 // Reset the controller
00285 void GazeboQuadrotorSimpleController::Reset()
00286 {
00287   controllers_.roll.reset();
00288   controllers_.pitch.reset();
00289   controllers_.yaw.reset();
00290   controllers_.velocity_x.reset();
00291   controllers_.velocity_y.reset();
00292   controllers_.velocity_z.reset();
00293 
00294   force.Set();
00295   torque.Set();
00296 
00297   // reset state
00298   pose.Reset();
00299   velocity.Set();
00300   angular_velocity.Set();
00301   acceleration.Set();
00302   euler.Set();
00303   state_stamp = ros::Time();
00304 }
00305 
00307 // PID controller implementation
00308 GazeboQuadrotorSimpleController::PIDController::PIDController()
00309 {
00310 }
00311 
00312 GazeboQuadrotorSimpleController::PIDController::~PIDController()
00313 {
00314 }
00315 
00316 void GazeboQuadrotorSimpleController::PIDController::Load(sdf::ElementPtr _sdf, const std::string& prefix)
00317 {
00318   gain_p = 0.0;
00319   gain_d = 0.0;
00320   gain_i = 0.0;
00321   time_constant = 0.0;
00322   limit = -1.0;
00323 
00324   if (!_sdf) return;
00325   // _sdf->PrintDescription(_sdf->GetName());
00326   if (_sdf->HasElement(prefix + "ProportionalGain")) gain_p = _sdf->GetElement(prefix + "ProportionalGain")->GetValueDouble();
00327   if (_sdf->HasElement(prefix + "DifferentialGain")) gain_d = _sdf->GetElement(prefix + "DifferentialGain")->GetValueDouble();
00328   if (_sdf->HasElement(prefix + "IntegralGain"))     gain_i = _sdf->GetElement(prefix + "IntegralGain")->GetValueDouble();
00329   if (_sdf->HasElement(prefix + "TimeConstant"))     time_constant = _sdf->GetElement(prefix + "TimeConstant")->GetValueDouble();
00330   if (_sdf->HasElement(prefix + "Limit"))            limit = _sdf->GetElement(prefix + "Limit")->GetValueDouble();
00331 }
00332 
00333 double GazeboQuadrotorSimpleController::PIDController::update(double new_input, double x, double dx, double dt)
00334 {
00335   // limit command
00336   if (limit > 0.0 && fabs(new_input) > limit) new_input = (new_input < 0 ? -1.0 : 1.0) * limit;
00337 
00338   // filter command
00339   if (dt + time_constant > 0.0) {
00340     dinput = (new_input - input) / (dt + time_constant);
00341     input  = (dt * new_input + time_constant * input) / (dt + time_constant);
00342   }
00343 
00344   // update proportional, differential and integral errors
00345   p = input - x;
00346   d = dinput - dx;
00347   i = i + dt * p;
00348 
00349   // update control output
00350   output = gain_p * p + gain_d * d + gain_i * i;
00351   return output;
00352 }
00353 
00354 void GazeboQuadrotorSimpleController::PIDController::reset()
00355 {
00356   input = dinput = 0;
00357   p = i = d = output = 0;
00358 }
00359 
00360 // Register this plugin with the simulator
00361 GZ_REGISTER_MODEL_PLUGIN(GazeboQuadrotorSimpleController)
00362 
00363 } // namespace gazebo


hector_quadrotor_gazebo_plugins
Author(s): Johannes Meyer
autogenerated on Mon Oct 6 2014 00:30:24