00001
00002
00003
00004
00005
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
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
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
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
00125 refTrajectory.setAcceleration( Acceleration3D(0.0, 0.0, GRAVITY) );
00126 refTrajectory.setYawRate(0.0);
00127
00128
00129
00130
00131 try {
00132 massEstimator = meLoader.createClassInstance(options.tMassPluginLookupName->getValue());
00133
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
00139 ros::shutdown();
00140 }
00141
00142 try {
00143 inertiaEstimator = imeLoader.createClassInstance(options.tInertiaPluginLookupName->getValue());
00144
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
00150 ros::shutdown();
00151 }
00152
00153
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
00166 currentMass = massEstimator->getInitialMass();
00167
00168
00169
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
00187 Vector3D rpyOrientation = state.getEulerRPY();
00188 Eigen::Vector4d ctrlInputs(0.0,0.0,0.0,0.0);
00189
00190 boost::mutex::scoped_lock refTrajectoryLock(refTrajectoryMutex);
00191 run(refTrajectory, state, currentMass, currentInertiaMatrix, ctrlInputs);
00192
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
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);
00212
00213 MassEstimOutput meOutput;
00214 massEstimator->run(meInput, meOutput);
00215
00216 currentMass = meOutput.estMass;
00217 }
00218
00219
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
00227 currentInertiaMatrix = imeOutput.estInertiaMatrix;
00228 }
00229
00230 telekyb_msgs::TKMotorCommands commandsMsg;
00231
00232
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);
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
00260
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
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
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
00325 const Eigen::Vector3d gravity(0.0, 0.0, -GRAVITY);
00326 const Eigen::Vector3d zB = R.col(2);
00327
00328
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
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
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
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
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);
00428
00429
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 }