SMURFTrajectoryTracker.cpp
Go to the documentation of this file.
00001 /*
00002  * SMURFTrajectoryTracker.cpp
00003  *
00004  *  Created on: Jun 8, 2012
00005  *      Author: rspica
00006  */
00007 
00008 #include "SMURFTrajectoryTracker.hpp"
00009 
00010 #include <telekyb_base/ROS.hpp>
00011 #include <telekyb_msgs/TKTTCommands.h>
00012 #include <telekyb_msgs/TKMotorCommands.h>
00013 #include <tk_draft_msgs/SMURFIntegTerms.h>
00014 
00015 //
00016 #include <tk_state/StateEstimatorController.hpp>
00017 
00018 #include <telekyb_defines/physic_defines.hpp>
00019 
00020 #include <pluginlib/class_list_macros.h>
00021 #include <cmath>
00022 
00023 PLUGINLIB_DECLARE_CLASS(tk_trajctrl, SMURFTrajectoryTracker, TELEKYB_NAMESPACE::SMURFTrajectoryTracker, TELEKYB_NAMESPACE::TrajectoryTracker);
00024 
00025 namespace TELEKYB_NAMESPACE {
00026 
00027 // Options
00028 SMURFTrajectoryTrackerOptions::SMURFTrajectoryTrackerOptions()
00029         : OptionContainer("SMURFTrajectoryTracker")
00030 {
00031         tPositionGain = addOption<Eigen::Vector3d>("tPositionGain", "Linear proportional gain", Eigen::Vector3d::Zero(), false, false);
00032         tVelocityGain = addOption<Eigen::Vector3d>("tVelocityGain", "Linear derivative  gain", Eigen::Vector3d::Zero(), false, false);
00033         tOrientationGain = addOption<Eigen::Vector3d>("tOrientationGain", "Angular proportional gain", Eigen::Vector3d::Zero(), false, false);
00034         tAngVelGain = addOption<Eigen::Vector3d>("tAngVelGain", "Angular derivative gain", Eigen::Vector3d::Zero(), false, false);
00035         tRotIntGain = addOption<Eigen::Vector3d>("tRotIntGain", "Angular integral gain", Eigen::Vector3d::Zero(), false, false);
00036         tSatRotInt = addOption<Eigen::Vector3d>("tSatRotInt", "Angular integral saturation", Eigen::Vector3d::Zero(), false, false);
00037         tPosIntGain = addOption<Eigen::Vector3d>("tPosIntGain", "Position integral gain", Eigen::Vector3d::Zero(), false, false);
00038         tSatPosInt = addOption<Eigen::Vector3d>("tSatPosInt", "Position integral saturation", Eigen::Vector3d::Zero(), false, false);
00039 
00040 
00041         tArmLength = addOption<double>("tArmLength", "Quadrotor arm length", 0.25, false, false);
00042         tCParam = addOption<double>("tCParam", "c parameter in the lookup table", 0.0131, false, false);
00043 
00044         tCommandTopic = addOption<std::string>("tCommandTopic","Topic for publishing commands","TTcommands",false,false);
00045 
00046         tMassPluginLookupName = addOption<std::string>("tMassPluginLookupName",
00047                                 "Specifies the Mass Estimation Plugin for the " + getOptionContainerNamespace(),
00048                                 "tk_param_estimator/StandardMassEstimator", false, true);
00049 
00050         tInertiaPluginLookupName = addOption<std::string>("tInertiaPluginLookupName",
00051                                         "Specifies the Inertia Matrix Estimation Plugin for the " + getOptionContainerNamespace(),
00052                                         "tk_param_estimator/StandardInertiaMatrixEstimator", false, true);
00053 
00054         tMinThrust = addOption<double>("tMinThrust", "Minimum thrust", 3.0, false, false);
00055 
00056         tMinForce = addOption<double>("tMinForce", "Minimum force", -INFINITY, false, false);
00057         tMaxForce = addOption<double>("tMaxForce", "Maximum force", +INFINITY, false, false);
00058 
00059         tSaturationType = addOption<SaturationTypeBaseEnum<const char*>::Type>("tSaturationType",
00060                         "Specifies the type of command saturation that must be applyed (none/uniform/qp)", SaturationType::none, false, false);
00061 }
00062 
00063 
00064 SMURFTrajectoryTracker::SMURFTrajectoryTracker()
00065         : tDoMassEstimation( NULL ),
00066           tDoInertiaMatrixEstimation( NULL ),
00067           meLoader( "tk_param_estimator", std::string( TELEKYB_NAMESPACE_STRING ) + "::MassEstimator" ),
00068           imeLoader( "tk_param_estimator", std::string( TELEKYB_NAMESPACE_STRING ) + "::InertiaMatrixEstimator" ),
00069           massEstimator( NULL ),
00070           inertiaEstimator( NULL ),
00071           nodeHandle( ROSModule::Instance().getMainNodeHandle() ),
00072           commandNodeHandle( nodeHandle, TELEKYB_COMMAND_NODESUFFIX ),
00073           rotIntState(Eigen::Vector3d::Zero()),
00074           posIntState(Eigen::Vector3d::Zero()),
00075           firstExecution(true)
00076 {
00077 
00078 }
00079 
00080 SMURFTrajectoryTracker::~SMURFTrajectoryTracker()
00081 {
00082         ROS_INFO("Using SMURF!");
00083         if (massEstimator) {
00084                 massEstimator->destroy();
00085                 delete massEstimator;
00086         }
00087         if (inertiaEstimator) {
00088                 inertiaEstimator->destroy();
00089                 delete inertiaEstimator;
00090         }
00091 }
00092 
00093 void SMURFTrajectoryTracker::initialize()
00094 {
00095 
00096         tTcCommandsPub = nodeHandle.advertise<telekyb_msgs::TKMotorCommands>(options.tCommandTopic->getValue(), 10);
00097 
00098         double alpha = 1/(2*options.tArmLength->getValue());
00099         double  beta = 1/(4*options.tCParam->getValue());
00100 
00101         /*double l = options.tArmLength->getValue();
00102         double c = options.tCParam->getValue();
00103         Eigen::Matrix4d A;
00104 
00105         A << 1.0, 1.0, 1.0, 1.0,
00106                  0.0, 0.0,   l,  -l,
00107                   -l,   l, 0.0, 0.0,
00108                    c,   c,  -c,  -c;
00109 
00110         invA = A.inverse();
00111 
00112         ROS_INFO_STREAM("A: " << A << std::endl << "invA: " << invA);
00113         */
00114 
00115         invA << .25,    0.0, -alpha,  beta,
00116                         .25,    0.0,  alpha,  beta,
00117                         .25,  alpha,    0.0, -beta,
00118                         .25, -alpha,    0.0, -beta;
00119 
00120         ROS_INFO_STREAM("invA: " << invA);
00121 
00122         tIntPub = nodeHandle.advertise<tk_draft_msgs::SMURFIntegTerms>("IntegralTerms", 10);
00123 
00124         // CurrentState
00125         refTrajectory.setAcceleration( Acceleration3D(0.0, 0.0, GRAVITY) );
00126         refTrajectory.setYawRate(0.0);
00127 
00128         //std::string tkStateTopicName = StateEstimatorController::Instance().getSePublisherTopic();
00129         //tTcStateSub = nodeHandle.subscribe(tkStateTopicName,1,&StandardTrajectoryTracker::tkStateCB, this);
00130 
00131         try {
00132                 massEstimator = meLoader.createClassInstance(options.tMassPluginLookupName->getValue());
00133                 // Currently RunTime Switch is not supported. This has to be changed then.
00134                 massEstimator->initialize();
00135 
00136         } catch (pluginlib::PluginlibException& e) {
00137                 ROS_FATAL("Trajectory Tracker %s failed to load: %s", options.tMassPluginLookupName->getValue().c_str(), e.what());
00138                 //ROS_BREAK();
00139                 ros::shutdown();
00140         }
00141 
00142         try {
00143                 inertiaEstimator = imeLoader.createClassInstance(options.tInertiaPluginLookupName->getValue());
00144                 // Currently RunTime Switch is not supported. This has to be changed then.
00145                 inertiaEstimator->initialize();
00146 
00147         } catch (pluginlib::PluginlibException& e) {
00148                 ROS_FATAL("Trajectory Tracker %s failed to load: %s", options.tInertiaPluginLookupName->getValue().c_str(), e.what());
00149                 //ROS_BREAK();
00150                 ros::shutdown();
00151         }
00152 
00153         // Get Option
00154         tDoMassEstimation = OptionContainer::getGlobalOptionByName<bool>("TrajectoryController","tDoMassEstimation");
00155         if (!tDoMassEstimation) {
00156                 ROS_ERROR("Unable to get Option TrajectoryController/tDoMassEstimation. Quitting...");
00157                 ros::shutdown();
00158         }
00159         tDoInertiaMatrixEstimation = OptionContainer::getGlobalOptionByName<bool>("TrajectoryController","tDoInertiaMatrixEstimation");
00160         if (!tDoInertiaMatrixEstimation) {
00161                 ROS_ERROR("Unable to get Option TrajectoryController/tDoInertiaMatrixEstimation. Quitting...");
00162                 ros::shutdown();
00163         }
00164 
00165         // fill currentMass with Initial Value!
00166         currentMass = massEstimator->getInitialMass();
00167 
00168         // fill currentInertiaMatrix with Initial Value!
00169         //TODO
00170         currentInertiaMatrix = inertiaEstimator->getInitialInertiaMatrix();
00171 
00172 }
00173 
00174 void SMURFTrajectoryTracker::destroy(){}
00175 
00176 std::string SMURFTrajectoryTracker::getName() const{
00177         return "SMURFTrajectoryTracker";
00178 }
00179 
00180 void SMURFTrajectoryTracker::trajectoryCB(const TKTrajectory& trajectory){
00181         boost::mutex::scoped_lock refTrajectoryLock(refTrajectoryMutex);
00182         refTrajectory = trajectory;
00183 }
00184 
00185 void SMURFTrajectoryTracker::stateCB(const TKState& state){
00186         // new State Message. Triggers control step!
00187         Vector3D rpyOrientation = state.getEulerRPY();
00188         Eigen::Vector4d ctrlInputs(0.0,0.0,0.0,0.0);
00189         // lock
00190         boost::mutex::scoped_lock refTrajectoryLock(refTrajectoryMutex);
00191         run(refTrajectory, state, currentMass, currentInertiaMatrix, ctrlInputs);
00192         // unlock trajectory input
00193         refTrajectoryLock.unlock();
00194 
00195         if (options.tSaturationType->getValue()==SaturationType::none){
00196         } else if (options.tSaturationType->getValue()==SaturationType::uniform){
00197                 saturateUniform(ctrlInputs, currentMass);
00198         } else if (options.tSaturationType->getValue()==SaturationType::qp){
00199                 saturateQP(ctrlInputs, currentMass);
00200         }
00201 
00202         if (ctrlInputs(0)>0.0){
00203                 ROS_WARN("Positive thrust");
00204         }
00205         // Mass Estimation only when enabled.
00206         if (tDoMassEstimation->getValue()) {
00207                 MassEstimInput meInput;
00208                 meInput.roll = rpyOrientation(0);
00209                 meInput.pitch = rpyOrientation(1);
00210                 meInput.thrust = ctrlInputs(0);
00211                 meInput.vertVel = state.linVelocity(2); // z
00212 
00213                 MassEstimOutput meOutput;
00214                 massEstimator->run(meInput, meOutput);
00215                 // update Mass.
00216                 currentMass = meOutput.estMass;
00217         }
00218 
00219         // Inertia Matrix Estimation only when enabled.
00220         if (tDoInertiaMatrixEstimation->getValue()) {
00221                 InertiaMatrixEstimInput imeInput;
00222                 imeInput.torque = ctrlInputs.block<3, 1>(1,0);
00223                 imeInput.angVel = state.angVelocity;
00224                 InertiaMatrixEstimOutput imeOutput;
00225                 inertiaEstimator->run(imeInput, imeOutput);
00226                 // update Inertia Matrix.
00227                 currentInertiaMatrix = imeOutput.estInertiaMatrix;
00228         }
00229 
00230         telekyb_msgs::TKMotorCommands commandsMsg;
00231 
00232         /* Solve with precomputed inverse */
00233         Eigen::Vector4d forces = invA*ctrlInputs;
00234 
00235         commandsMsg.force.resize(4);
00236         for (int i = 0; i < 4; i++)
00237         {
00238                 commandsMsg.force[i] = -forces(i); //forces are negative!!
00239         }
00240         commandsMsg.header.stamp = ros::Time::now();
00241         tTcCommandsPub.publish(commandsMsg);
00242 
00243         tk_draft_msgs::SMURFIntegTerms intMsg;
00244         intMsg.header.stamp = ros::Time::now();
00245         intMsg.position.x = posIntState(0);
00246         intMsg.position.y = posIntState(1);
00247         intMsg.position.z = posIntState(2);
00248 
00249         intMsg.orientation.x = rotIntState(0);
00250         intMsg.orientation.y = rotIntState(1);
00251         intMsg.orientation.z = rotIntState(2);
00252         tIntPub.publish(intMsg);
00253 
00254 }
00255 
00256 void SMURFTrajectoryTracker::run(const TKTrajectory& input, const TKState& currentState, const double mass, const Eigen::Matrix3d& inertia, Eigen::Vector4d& ctrlInputs)
00257 {
00258 
00259         //ROS_INFO("Mass: %f", mass);
00260         // Current State
00261         const Eigen::Quaterniond transformW(0.0,1.0,0.0,0.0);
00262         const Eigen::Quaterniond transformB(0.0,1.0,0.0,0.0);
00263 
00264         const Eigen::Vector3d r = transformW*currentState.position;
00265         const Eigen::Vector3d dr = transformW*currentState.linVelocity;
00266         const Eigen::Matrix3d R = (transformW*currentState.orientation*transformB).toRotationMatrix();
00267         const Eigen::Vector3d B_w = transformB*currentState.angVelocity;
00268 
00269         // Reference trajectory
00270 
00271         const Eigen::Vector3d rt = transformW*input.position;
00272         const Eigen::Vector3d drt = 1.0*(transformW*input.velocity);
00273         const Eigen::Vector3d ddrt = 1.0*(transformW*input.acceleration);
00274         const Eigen::Vector3d dat = 1.0*(transformW*input.jerk);
00275         const Eigen::Vector3d ddat = 1.0*(transformW*input.snap);
00276 
00277         double psit = -1.0*(input.yawAngle);
00278         double dpsit = -1.0*(input.yawRate);
00279         double ddpsit = -1.0*(input.yawAcceleration);
00280 
00281         // Control gains
00282         Eigen::Vector3d gainKp = options.tPositionGain->getValue();
00283         Eigen::Vector3d gainKv = options.tVelocityGain->getValue();
00284         Eigen::Vector3d gainKr = options.tOrientationGain->getValue();
00285         Eigen::Vector3d gainKw = options.tAngVelGain->getValue();
00286         Eigen::Vector3d gainKir = options.tRotIntGain->getValue();
00287         Eigen::Vector3d gainKip = options.tPosIntGain->getValue();
00288 
00289         if (input.xAxisCtrl == PosControlType::Velocity) {
00290                 gainKp(0) = 0.0;
00291                 gainKip(0) = 0.0;
00292         } else if (input.xAxisCtrl == PosControlType::Acceleration) {
00293                 gainKp(0) = 0.0;
00294                 gainKv(0) = 0.0;
00295                 gainKip(0) = 0.0;
00296         }
00297 
00298         if (input.yAxisCtrl == PosControlType::Velocity) {
00299                 gainKp(1) = 0.0;
00300                 gainKip(1) = 0.0;
00301         } else if (input.yAxisCtrl == PosControlType::Acceleration) {
00302                 gainKp(1) = 0.0;
00303                 gainKv(1) = 0.0;
00304                 gainKip(1) = 0.0;
00305         }
00306 
00307         if (input.zAxisCtrl == PosControlType::Velocity) {
00308                 gainKp(2) = 0.0;
00309                 gainKip(2) = 0.0;
00310         } else if (input.zAxisCtrl == PosControlType::Acceleration) {
00311                 gainKp(2) = 0.0;
00312                 gainKv(2) = 0.0;
00313                 gainKip(2) = 0.0;
00314         }
00315 
00316         if (input.yawCtrl == YawControlType::RateOffBoard ||
00317                         input.yawCtrl == YawControlType::RateOnBoard) {
00318                 psit = atan2((double)R(1,0),(double)R(0,0));
00319         } else if (input.yawCtrl == YawControlType::AccelerationOffBoard ||
00320                         input.yawCtrl == YawControlType::AccelerationOnBoard) {
00321                 ROS_ERROR("YawControlType::AccelerationOnBoard and YawControlType::AccelerationOffBoard not implemented!");
00322         }
00323 
00324         // Temporary variables
00325         const Eigen::Vector3d gravity(0.0, 0.0, -GRAVITY);
00326         const Eigen::Vector3d zB = R.col(2);
00327 
00328         // Compute thrust input
00329         const Eigen::Vector3d Fd =  gainKip.asDiagonal()*posIntState + gainKp.asDiagonal()*(rt-r) + gainKv.asDiagonal()*(drt-dr) + mass*(ddrt - gravity);
00330 
00331         double u1 =  Fd.transpose() * zB;
00332 
00333         // Compute desired attitude
00334         const Eigen::Vector3d ad = Fd/mass;
00335         const double nad = ad.norm();
00336 
00337         Eigen::Vector3d zBd;
00338         if (nad > 1e-6){
00339                 zBd = ad/nad;
00340         } else {
00341                 zBd = zB;
00342         }
00343 
00344         const Eigen::Vector3d yCd(-sin(psit), cos(psit), 0.0);
00345 
00346         const Eigen::Vector3d xCd(yCd(1), -yCd(0), 0.0);
00347 
00348         Eigen::Vector3d xBd = yCd.cross(zBd);
00349         double nxBd = xBd.norm();
00350         xBd = xBd/nxBd;
00351         const Eigen::Vector3d yBd = zBd.cross(xBd);
00352 
00353         Eigen::Matrix3d Rd;
00354         Rd << xBd, yBd, zBd;
00355 
00356         // Compute desired angular velocity
00357         const Eigen::Vector3d ddr = u1/mass*zB + gravity;
00358         const Eigen::Vector3d ea = ddrt - ddr;
00359         const Eigen::Vector3d dad = (gainKp.asDiagonal()*(drt-dr) + gainKv.asDiagonal()*ea)/mass + dat;
00360         double du1nd = zBd.transpose()*dad;
00361 
00362         Eigen::Vector3d hd;
00363         if (nad > 1e-6){
00364                 hd = (dad-du1nd*zBd)/nad;
00365         } else {
00366                 hd = Eigen::Vector3d::Zero();
00367         }
00368 
00369         Eigen::Vector3d Bd_wd;
00370         Bd_wd(0) = -hd.transpose()*yBd;
00371         Bd_wd(1) = hd.transpose()*xBd;
00372 
00373         double zBdTyCd = zBd.transpose()*yCd;
00374         double xBdTxCd = xBd.transpose()*xCd;
00375 
00376         Bd_wd(2) = (zBdTyCd*Bd_wd(1)+xBdTxCd*dpsit)/nxBd;
00377 
00378         // Compute desired angular acceleration
00379         const Eigen::Vector3d w = R*B_w;
00380         const Eigen::Vector3d dzB = w.cross(zB);
00381         const Eigen::Vector3d ej = dat - zB*(dad.transpose()*zB+ad.transpose()*dzB) - dzB*(ad.transpose()*zB);
00382 
00383         const Eigen::Vector3d ddad = (gainKp.asDiagonal()*ea + gainKv.asDiagonal()*ej)/mass + ddat;
00384 
00385         Eigen::Vector3d deltad = ddad - nad*(Rd*Bd_wd).cross(hd);
00386         const double ddu1nd = zBd.transpose()*deltad;
00387 
00388         Eigen::Vector3d ld;
00389         if (nad > 1e-6){
00390                 ld = (deltad-ddu1nd*zBd-2*du1nd*hd)/nad;
00391         } else {
00392                 ld = Eigen::Vector3d::Zero();
00393         }
00394 
00395         Eigen::Vector3d Bd_dwd;
00396         Bd_dwd(0) = -ld.transpose()*yBd;
00397         Bd_dwd(1) =  ld.transpose()*xBd;
00398 
00399         double yBdTxCd = yBd.transpose()*xCd;
00400         double zBdTxCd = zBd.transpose()*xCd;
00401 
00402         Bd_dwd(2) = (xBdTxCd*ddpsit + zBdTyCd*(Bd_dwd(1)-Bd_wd(0)*Bd_wd(2)) + 2*dpsit*(yBdTxCd*Bd_wd(2)-zBdTxCd*Bd_wd(1)))/nxBd - Bd_wd(0)*Bd_wd(1);
00403 
00404         // Compute torque ctrlInputs
00405         const Eigen::Vector3d er = .5*vee(R.transpose()*Rd-(R.transpose()*Rd).transpose());
00406         const Eigen::Vector3d ew = R.transpose()*Rd*Bd_wd - B_w;
00407 
00408 
00409         if (firstExecution){
00410                 firstExecution = false;
00411         } else {
00412                 double deltaT = integralTimer.getElapsed().toDSec();
00413                 rotIntState += er*deltaT;
00414                 Eigen::Vector3d satRotInt = gainKir.asDiagonal()*options.tSatRotInt->getValue();
00415                 rotIntState(0) = std::max(std::min(rotIntState(0),satRotInt(0)),-satRotInt(0));
00416                 rotIntState(1) = std::max(std::min(rotIntState(1),satRotInt(1)),-satRotInt(1));
00417                 rotIntState(2) = std::max(std::min(rotIntState(2),satRotInt(2)),-satRotInt(2));
00418                 posIntState += (rt-r)*deltaT;
00419                 Eigen::Vector3d satPosInt = gainKip.asDiagonal()*options.tSatPosInt->getValue();
00420                 posIntState(0) = std::max(std::min(posIntState(0),satPosInt(0)),-satPosInt(0));
00421                 posIntState(1) = std::max(std::min(posIntState(1),satPosInt(1)),-satPosInt(1));
00422                 posIntState(2) = std::max(std::min(posIntState(2),satPosInt(2)),-satPosInt(2));
00423         }
00424         integralTimer.reset();
00425 
00426         const Eigen::Vector3d comTorque = gainKir.asDiagonal()*rotIntState + gainKr.asDiagonal()*er
00427                         + gainKw.asDiagonal()*ew + inertia*((R.transpose()*Rd)*Bd_dwd - hat(B_w)*(R.transpose()*Rd)*Bd_wd); //+B_w.cross(inertia*B_w)
00428 
00429         // Output result
00430         ctrlInputs <<                   -u1,
00431                                  transformB*comTorque;
00432 
00433 }
00434 
00435 
00436 void SMURFTrajectoryTracker::saturateUniform(Eigen::Vector4d& ctrlInputs, double mass){
00437 
00438         Eigen::Vector4d ctrlInputs0(-mass*GRAVITY, 0.0, 0.0, 0.0);
00439         Eigen::Vector4d ctrlInputs1 = ctrlInputs-ctrlInputs0;
00440 
00441         Eigen::Vector4d force0 = invA*ctrlInputs0;
00442         Eigen::Vector4d force1 = invA*ctrlInputs1;
00443 
00444         double scaling = 1.0;
00445 
00446         for (unsigned short int i=0; i<4; i++){
00447             if (force1(i) < options.tMinForce->getValue() - force0(i)){
00448                 scaling = std::min(scaling, std::abs((options.tMinForce->getValue() - force0(i))/force1(i)));
00449             }
00450             if (force1(i) > options.tMaxForce->getValue() - force0(i)){
00451                 scaling = std::min(scaling, std::abs((options.tMaxForce->getValue() - force0(i))/force1(i)));
00452             }
00453         }
00454 
00455         ctrlInputs = ctrlInputs1*scaling + ctrlInputs0;
00456 }
00457 
00458 
00459 } /* TELEKYB_NAMESPACE */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends


tk_trajctrl
Author(s): Dr. Antonio Franchi and Martin Riedel
autogenerated on Mon Nov 11 2013 11:13:14