gazebo_gimbal_controller_plugin.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012-2015 Open Source Robotics Foundation
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  *
00016 */
00017 
00018 #include <gazebo/physics/physics.hh>
00019 #include <gazebo/transport/transport.hh>
00020 #include "rotors_gazebo_plugins/external/gazebo_gimbal_controller_plugin.h"
00021 
00022 using namespace gazebo;
00023 using namespace std;
00024 
00025 GZ_REGISTER_MODEL_PLUGIN(GimbalControllerPlugin)
00026 
00027 
00028 GimbalControllerPlugin::GimbalControllerPlugin()
00029   :status("closed")
00030 {
00032   this->pitchPid.Init(5, 0, 0, 0, 0, 0.3, -0.3);
00033   this->rollPid.Init(5, 0, 0, 0, 0, 0.3, -0.3);
00034   this->yawPid.Init(1.0, 0, 0, 0, 0, 1.0, -1.0);
00035   this->pitchCommand = 0.5* M_PI;
00036   this->rollCommand = 0;
00037   this->yawCommand = 0;
00038 }
00039 
00041 void GimbalControllerPlugin::Load(
00042     physics::ModelPtr _model,
00043     sdf::ElementPtr _sdf)
00044 {
00045 
00046   if(kPrintOnPluginLoad) {
00047     gzdbg << __FUNCTION__ << "() called." << std::endl;
00048   }
00049 
00050   this->model = _model;
00051 
00052   this->sdf = _sdf;
00053 
00054   std::string yawJointName = "cgo3_vertical_arm_joint";
00055   this->yawJoint = this->model->GetJoint(yawJointName);
00056   if (this->sdf->HasElement("joint_yaw"))
00057   {
00058     // Add names to map
00059     yawJointName = sdf->Get<std::string>("joint_yaw");
00060     if (this->model->GetJoint(yawJointName))
00061     {
00062       this->yawJoint = this->model->GetJoint(yawJointName);
00063     }
00064     else
00065     {
00066       gzwarn << "joint_yaw [" << yawJointName << "] does not exist?\n";
00067     }
00068   }
00069   if (!this->yawJoint)
00070   {
00071     gzerr << "GimbalControllerPlugin::Load ERROR! Can't get yaw joint '"
00072           << yawJointName << "' " << endl;
00073   }
00074 
00075   std::string rollJointName = "cgo3_horizontal_arm_joint";
00076   this->rollJoint = this->model->GetJoint(rollJointName);
00077   if (this->sdf->HasElement("joint_roll"))
00078   {
00079     // Add names to map
00080     rollJointName = sdf->Get<std::string>("joint_roll");
00081     if (this->model->GetJoint(rollJointName))
00082     {
00083       this->rollJoint = this->model->GetJoint(rollJointName);
00084     }
00085     else
00086     {
00087       gzwarn << "joint_roll [" << rollJointName << "] does not exist?\n";
00088     }
00089   }
00090   if (!this->rollJoint)
00091   {
00092     gzerr << "GimbalControllerPlugin::Load ERROR! Can't get roll joint '"
00093           << rollJointName << "' " << endl;
00094   }
00095 
00096 
00097   std::string pitchJointName = "cgo3_camera_joint";
00098   this->pitchJoint = this->model->GetJoint(pitchJointName);
00099   if (this->sdf->HasElement("joint_pitch"))
00100   {
00101     // Add names to map
00102     pitchJointName = sdf->Get<std::string>("joint_pitch");
00103     if (this->model->GetJoint(pitchJointName))
00104     {
00105       this->pitchJoint = this->model->GetJoint(pitchJointName);
00106     }
00107     else
00108     {
00109       gzwarn << "joint_pitch [" << pitchJointName << "] does not exist?\n";
00110     }
00111   }
00112   if (!this->pitchJoint)
00113   {
00114     gzerr << "GimbalControllerPlugin::Load ERROR! Can't get pitch joint '"
00115           << pitchJointName << "' " << endl;
00116   }
00117 
00118 
00119   // get imu sensor
00120   std::string imuSensorName = "camera_imu";
00121   if (this->sdf->HasElement("imu"))
00122   {
00123     // Add names to map
00124     imuSensorName = sdf->Get<std::string>("imu");
00125   }
00126 
00127   this->imuSensor = std::static_pointer_cast<sensors::ImuSensor>(
00128     sensors::SensorManager::Instance()->GetSensor(imuSensorName));
00129 
00130   if (!this->imuSensor)
00131   {
00132     gzerr << "GimbalControllerPlugin::Load ERROR! Can't get imu sensor '"
00133           << imuSensorName << "' " << endl;
00134   }
00135 }
00136 
00138 void GimbalControllerPlugin::Init()
00139 {
00140   this->node = transport::NodePtr(new transport::Node());
00141   this->node->Init(this->model->GetWorld()->GetName());
00142 
00143   this->lastUpdateTime = this->model->GetWorld()->GetSimTime();
00144 
00145   // receive pitch command via gz transport
00146   std::string pitchTopic = std::string("~/") +  this->model->GetName() +
00147     "/gimbal_pitch_cmd";
00148   this->pitchSub = this->node->Subscribe(pitchTopic,
00149      &GimbalControllerPlugin::OnPitchStringMsg, this);
00150   // receive roll command via gz transport
00151   std::string rollTopic = std::string("~/") +  this->model->GetName() +
00152     "/gimbal_roll_cmd";
00153   this->rollSub = this->node->Subscribe(rollTopic,
00154      &GimbalControllerPlugin::OnRollStringMsg, this);
00155   // receive yaw command via gz transport
00156   std::string yawTopic = std::string("~/") +  this->model->GetName() +
00157     "/gimbal_yaw_cmd";
00158   this->yawSub = this->node->Subscribe(yawTopic,
00159      &GimbalControllerPlugin::OnYawStringMsg, this);
00160 
00161   // plugin update
00162   this->connections.push_back(event::Events::ConnectWorldUpdateBegin(
00163           boost::bind(&GimbalControllerPlugin::OnUpdate, this)));
00164 
00165   // publish pitch status via gz transport
00166   pitchTopic = std::string("~/") +  this->model->GetName()
00167     + "/gimbal_pitch_status";
00168   this->pitchPub = node->Advertise<gazebo::msgs::Any>(pitchTopic);
00169 
00170   // publish roll status via gz transport
00171   rollTopic = std::string("~/") +  this->model->GetName()
00172     + "/gimbal_roll_status";
00173   this->rollPub = node->Advertise<gazebo::msgs::Any>(rollTopic);
00174 
00175 
00176   // publish yaw status via gz transport
00177   yawTopic = std::string("~/") +  this->model->GetName()
00178     + "/gimbal_yaw_status";
00179   this->yawPub = node->Advertise<gazebo::msgs::Any>(yawTopic);
00180 
00181   gzmsg << "GimbalControllerPlugin::Init" << std::endl;
00182 }
00183 
00185 void GimbalControllerPlugin::OnPitchStringMsg(ConstAnyPtr &_msg)
00186 {
00187 //  gzdbg << "pitch command received " << _msg->double_value() << std::endl;
00188   this->pitchCommand = _msg->double_value();
00189 }
00190 
00192 void GimbalControllerPlugin::OnRollStringMsg(ConstAnyPtr &_msg)
00193 {
00194 //  gzdbg << "roll command received " << _msg->double_value() << std::endl;
00195   this->rollCommand = _msg->double_value();
00196 }
00197 
00199 void GimbalControllerPlugin::OnYawStringMsg(ConstAnyPtr &_msg)
00200 {
00201 //  gzdbg << "yaw command received " << _msg->double_value() << std::endl;
00202   this->yawCommand = _msg->double_value();
00203 }
00204 
00206 ignition::ignition::math::Vector3d GimbalControllerPlugin::ThreeAxisRot(
00207   double r11, double r12, double r21, double r31, double r32)
00208 {
00209   return ignition::ignition::math::Vector3d d(
00210     atan2( r31, r32 ),
00211     asin ( r21 ),
00212     atan2( r11, r12 ));
00213 }
00214 
00216 ignition::ignition::math::Vector3d GimbalControllerPlugin::QtoZXY(
00217   const ignition::math::Quaterniond &_q)
00218 {
00219   // taken from
00220   // http://bediyap.com/programming/convert-quaternion-to-euler-rotations/
00221   // case zxy:
00222   ignition::ignition::math::Vector3d result = this->ThreeAxisRot(
00223     -2*(_q.X()*_q.Y() - _q.W()*_q.Z()),
00224     _q.W()*_q.W() - _q.X()*_q.X() + _q.Y()*_q.Y() - _q.Z()*_q.Z(),
00225     2*(_q.Y()*_q.Z() + _q.W()*_q.X()),
00226     -2*(_q.X()*_q.Z() - _q.W()*_q.Y()),
00227     _q.W()*_q.W() - _q.X()*_q.X() - _q.Y()*_q.Y() + _q.Z()*_q.Z());
00228   return result;
00229 }
00230 
00232 void GimbalControllerPlugin::OnUpdate()
00233 {
00234   if (!this->pitchJoint || !this->rollJoint || !this->yawJoint)
00235     return;
00236 
00237   common::Time time = this->model->GetWorld()->GetSimTime();
00238   if (time < this->lastUpdateTime)
00239   {
00240     gzerr << "time reset event\n";
00241     this->lastUpdateTime = time;
00242     return;
00243   }
00244   else if (time > this->lastUpdateTime)
00245   {
00246     double dt = (this->lastUpdateTime - time).Double();
00247 
00248     // anything to do with gazebo joint has
00249     // hardcoded negative joint axis for pitch and roll
00250     // TODO: make joint direction a parameter
00251     const double rDir = -1;
00252     const double pDir = -1;
00253     const double yDir = 1;
00254 
00255     // truncate command inside joint angle limits
00256     double rollLimited = ignition::math::clamp(this->rollCommand,
00257       rDir*this->rollJoint->GetUpperLimit(0).Radian(),
00258           rDir*this->rollJoint->GetLowerLimit(0).Radian());
00259     double pitchLimited = ignition::math::clamp(this->pitchCommand,
00260       pDir*this->pitchJoint->GetUpperLimit(0).Radian(),
00261       pDir*this->pitchJoint->GetLowerLimit(0).Radian());
00262     double yawLimited = ignition::math::clamp(this->yawCommand,
00263       yDir*this->yawJoint->GetLowerLimit(0).Radian(),
00264           yDir*this->yawJoint->GetUpperLimit(0).Radian());
00265 
00266     ignition::math::Quaterniond commandRPY(
00267       rollLimited, pitchLimited, yawLimited);
00268 
00269 
00271 
00274     ignition::ignition::math::Vector3d currentAngleYPRVariable(
00275       this->imuSensor->Orientation().Euler());
00276     ignition::ignition::math::Vector3d currentAnglePRYVariable(
00277       this->QtoZXY(currentAngleYPRVariable));
00278 
00281     ignition::ignition::math::Vector3d lowerLimitsPRY
00282       (pDir*this->pitchJoint->GetLowerLimit(0).Radian(),
00283        rDir*this->rollJoint->GetLowerLimit(0).Radian(),
00284        yDir*this->yawJoint->GetLowerLimit(0).Radian());
00285     ignition::ignition::math::Vector3d upperLimitsPRY
00286       (pDir*this->pitchJoint->GetUpperLimit(0).Radian(),
00287        rDir*this->rollJoint->GetUpperLimit(0).Radian(),
00288        yDir*this->yawJoint->GetUpperLimit(0).Radian());
00289 
00290     // normalize errors
00291     double pitchError = this->ShortestAngularDistance(
00292       pitchLimited, currentAnglePRYVariable.X());
00293     double rollError = this->ShortestAngularDistance(
00294       rollLimited, currentAnglePRYVariable.Y());
00295     double yawError = this->ShortestAngularDistance(
00296       yawLimited, currentAnglePRYVariable.Z());
00297 
00298     // Clamp errors based on current angle and estimated errors from rotations:
00299     // given error = current - target, then
00300     // if target (current angle - error) is outside joint limit, truncate error
00301     // so that current angle - error is within joint limit, i.e.:
00302     // lower limit < current angle - error < upper limit
00303     // or
00304     // current angle - lower limit > error > current angle - upper limit
00305     // re-expressed as clamps:
00306     // hardcoded negative joint axis for pitch and roll
00307     if (lowerLimitsPRY.X() < upperLimitsPRY.X())
00308     {
00309       pitchError = ignition::math::clamp(pitchError,
00310         currentAnglePRYVariable.X() - upperLimitsPRY.X(),
00311         currentAnglePRYVariable.X() - lowerLimitsPRY.X());
00312     }
00313     else
00314     {
00315       pitchError = ignition::math::clamp(pitchError,
00316         currentAnglePRYVariable.X() - lowerLimitsPRY.X(),
00317         currentAnglePRYVariable.X() - upperLimitsPRY.X());
00318     }
00319     if (lowerLimitsPRY.Y() < upperLimitsPRY.Y())
00320     {
00321       rollError = ignition::math::clamp(rollError,
00322         currentAnglePRYVariable.Y() - upperLimitsPRY.Y(),
00323         currentAnglePRYVariable.Y() - lowerLimitsPRY.Y());
00324     }
00325     else
00326     {
00327       rollError = ignition::math::clamp(rollError,
00328         currentAnglePRYVariable.Y() - lowerLimitsPRY.Y(),
00329         currentAnglePRYVariable.Y() - upperLimitsPRY.Y());
00330     }
00331     if (lowerLimitsPRY.Z() < upperLimitsPRY.Z())
00332     {
00333       yawError = ignition::math::clamp(yawError,
00334         currentAnglePRYVariable.Z() - upperLimitsPRY.Z(),
00335         currentAnglePRYVariable.Z() - lowerLimitsPRY.Z());
00336     }
00337     else
00338     {
00339       yawError = ignition::math::clamp(yawError,
00340         currentAnglePRYVariable.Z() - lowerLimitsPRY.Z(),
00341         currentAnglePRYVariable.Z() - upperLimitsPRY.Z());
00342     }
00343 
00344     // apply forces to move gimbal
00345     double pitchForce = this->pitchPid.Update(pitchError, dt);
00346     this->pitchJoint->SetForce(0, pDir*pitchForce);
00347 
00348     double rollForce = this->rollPid.Update(rollError, dt);
00349     this->rollJoint->SetForce(0, rDir*rollForce);
00350 
00351     double yawForce = this->yawPid.Update(yawError, dt);
00352     this->yawJoint->SetForce(0, yDir*yawForce);
00353 
00354     // ignition::ignition::math::Vector3d angles = this->imuSensor->Orientation().Euler();
00355     // gzerr << "ang[" << angles.X() << ", " << angles.Y() << ", " << angles.Z()
00356     //       << "] cmd[ " << this->rollCommand
00357     //       << ", " << this->pitchCommand << ", " << this->yawCommand
00358     //       << "] err[ " << rollError
00359     //       << ", " << pitchError << ", " << yawError
00360     //       << "] frc[ " << rollForce
00361     //       << ", " << pitchForce << ", " << yawForce << "]\n";
00362 
00363 
00364     this->lastUpdateTime = time;
00365   }
00366 
00367   static int i =1000;
00368   if (++i>100)
00369   {
00370     i = 0;
00371 
00372     gazebo::msgs::Any m;
00373     m.set_type(gazebo::msgs::Any_ValueType_DOUBLE);
00374 
00375     m.set_double_value(this->pitchJoint->GetAngle(0).Radian());
00376     this->pitchPub->Publish(m);
00377 
00378     m.set_double_value(this->rollJoint->GetAngle(0).Radian());
00379     this->rollPub->Publish(m);
00380 
00381     m.set_double_value(this->yawJoint->GetAngle(0).Radian());
00382     this->yawPub->Publish(m);
00383   }
00384 }
00385 
00387 double GimbalControllerPlugin::NormalizeAbout(double _angle, double reference)
00388 {
00389   double diff = _angle - reference;
00390   // normalize diff about (-pi, pi], then add reference
00391   while (diff <= -M_PI)
00392   {
00393     diff += 2.0*M_PI;
00394   }
00395   while (diff > M_PI)
00396   {
00397     diff -= 2.0*M_PI;
00398   }
00399   return diff + reference;
00400 }
00401 
00403 double GimbalControllerPlugin::ShortestAngularDistance(double _from, double _to)
00404 {
00405   return this->NormalizeAbout(_to, _from) - _from;
00406 }


rotors_gazebo_plugins
Author(s): Fadri Furrer, Michael Burri, Mina Kamel, Janosch Nikolic, Markus Achtelik
autogenerated on Thu Apr 18 2019 02:43:43