x2d_adaptive.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2010-2015, LABUST, UNIZG-FER
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the LABUST nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 #include <labust/allocation/x2d_adaptive.h>
00035 #include <labust/math/NumberManipulation.hpp>
00036 
00037 #include <pluginlib/class_list_macros.h>
00038 #include <std_msgs/Float32.h>
00039 #include <ros/ros.h>
00040 
00041 #include <boost/array.hpp>
00042 #include <Eigen/Dense>
00043 
00044 #include <cmath>
00045 #include <vector>
00046 
00047 using namespace labust::allocation;
00048 
00049 X2dAdaptive::X2dAdaptive():
00050                 _windup(6,false),
00051                 minN(1),
00052                 daisy_chain(true),
00053                 multi_chain(true),
00054                 tau_ach(Eigen::VectorXd::Zero(6)){}
00055 
00056 bool X2dAdaptive::configure(ros::NodeHandle& nh, ros::NodeHandle& ph)
00057 {
00058         //Load the thruster configuration
00059         bool valid = thrusters.configure(nh, ph);
00060         if (!valid) return false;
00061         //Read the minimum torque
00062         ph.param("min_torque", minN, minN);
00063         ph.param("daisy_chain", daisy_chain, daisy_chain);
00064         ph.param("multi_chain", multi_chain, multi_chain);
00065         Eigen::Vector3d tn(0,0,minN);
00066         tnmax = (thrusters.Binv()*tn).cwiseAbs();
00067         tnmin = -tnmax;
00068 
00069         return valid;
00070 }
00071 
00072 const std::vector<double>& X2dAdaptive::allocate(const Eigen::VectorXd& tau)
00073 {
00074         //Get overall operational limits
00075         const Eigen::VectorXd& tmax(thrusters.maxF());
00076         const Eigen::VectorXd& tmin(thrusters.minF());
00077         //Get XY operational limits
00078         Eigen::Vector4d txymax(tmax - tnmax);
00079         Eigen::Vector4d txymin(tmin - tnmin);
00080         Eigen::Vector4d tnmax(this->tnmax);
00081         Eigen::Vector4d tnmin(this->tnmin);
00082 
00083         //Separate variables
00084         Eigen::Vector3d tauN, tauXY;
00085         tauN<<0.0,0.0,tau(N);
00086         tauXY<<tau(X),tau(Y),0.0;
00087         //Perform inverse allocation
00088         Eigen::Vector4d tN, tXY;
00089         tN = thrusters.Binv()*tauN;
00090         tXY = thrusters.Binv()*tauXY;
00091 
00092         //Test for saturation
00093         bool satN(false),satXY(false);
00094 
00095         for (int i=0; i<tN.size(); ++i)
00096         {
00097                 satN = satN || ((tN(i) > tnmax(i)) || (tN(i) < tnmin(i)));
00098           satXY = satXY || ((tXY(i) > txymax(i)) || (tXY(i) < txymin(i)));
00099         }
00100 
00101         if (!satN && !satXY)
00102         {
00103             //Everything is within limits
00104             ROS_DEBUG("Everything allocated without saturation.");
00105         }
00106         else if (satN && !satXY)
00107         {
00108             ROS_DEBUG("Yaw DoF is saturated.");
00109             this->recalcOpLimits(tXY,txymax,txymin,&tnmax,&tnmin);
00110             satN = saturate(tN, tnmin, tnmax);
00111         }
00112         else if (satXY && !satN)
00113         {
00114             ROS_DEBUG("XY DoF are saturated.");
00115             this->recalcOpLimits(tN,tnmax,tnmin,&txymax,&txymin);
00116             satXY = saturate(tXY, txymin, txymax);
00117         }
00118         else if (satXY && satN)
00119         {
00120             ROS_DEBUG("All DoFs are saturated.");
00121             satN = saturate(tN, tnmin, tnmax);
00122             satXY = saturate(tXY, txymin, txymax);
00123         }
00124         Eigen::VectorXd tT = tXY+tN;
00125         Eigen::VectorXd tauA = thrusters.B()*tT;
00126 
00127         //Make a second run if nothing was achieved
00128         if (daisy_chain && (satXY || satN))
00129         {
00130                 Eigen::VectorXd tauS(3);
00131                 tauS<<tau(X),tau(Y),tau(N);
00132                 bool ssat = this->secondRun(tauS, tmax, tmin, &tauA, &tT);
00133                 Eigen::VectorXd arem = (tauS - tauA).cwiseAbs();
00134                 const double sm_th(0.001);
00135                 satXY = (arem(X) > sm_th) || (arem(Y) > sm_th);
00136                 satN = (arem(Z) > sm_th);
00137         }
00138 
00139         _windup[X] = _windup[Y] = satXY;
00140         _windup[N] = satN;
00141 
00142         //Copy to external vector
00143         enum {Xi=0,Yi=1,Ni=2};
00144         tau_ach(X) = tauA(Xi);
00145         tau_ach(Y) = tauA(Yi);
00146         tau_ach(N) = tauA(Ni);
00147 
00148         return thrusters.pwm(tT);
00149 }
00150 
00151 void X2dAdaptive::recalcOpLimits(Eigen::Vector4d& used, Eigen::Vector4d& pmax,
00152                 Eigen::Vector4d& pmin, Eigen::Vector4d* cmax, Eigen::Vector4d* cmin)
00153 {
00154   for (int i=0; i<used.size(); ++i)
00155   {
00156       if (used(i) >= 0)
00157       {
00158           double delta = pmax(i) - used(i);
00159           (*cmax)(i) += delta;
00160           (*cmin)(i) += pmin(i);
00161       }
00162       else
00163       {
00164           double delta = pmin(i) - used(i);
00165           (*cmin)(i) += delta;
00166           (*cmax)(i) += pmax(i);
00167       }
00168   }
00169 }
00170 
00171 bool X2dAdaptive::saturateN(Eigen::Vector4d& t,
00172                 const Eigen::Vector4d& pmin, const Eigen::Vector4d& pmax)
00173 {
00174         bool retVal(false);
00175   for (int i=0; i<t.size(); ++i)
00176   {
00177         double tn = labust::math::coerce(t(i), pmin(i), pmax(i));
00178         if (tn != t(i)) retVal |= true;
00179         t(i) = tn;
00180   }
00181   return retVal;
00182 }
00183 
00184 bool X2dAdaptive::saturate(Eigen::Vector4d& t,
00185                 const Eigen::Vector4d& pmin, const Eigen::Vector4d& pmax)
00186 {
00187         double scalef(1.0), scale(0.0);
00188   for (int i=0;i<t.size(); ++i)
00189   {
00190      if (t(i) >= 0)
00191         scale = t(i)/pmax(i);
00192      else
00193         scale = t(i)/pmin(i);
00194 
00195      if (scale > scalef) scalef=scale;
00196   }
00197   t = t/scalef;
00198 
00199   return (scalef > 1.0);
00200 }
00201 
00202 bool X2dAdaptive::secondRun(const Eigen::VectorXd& tau,
00203                 const Eigen::VectorXd& tmax, const Eigen::VectorXd& tmin,
00204                 Eigen::VectorXd* tauA, Eigen::VectorXd* tT)
00205 {
00206         bool retVal;
00207         bool do_loop(true);
00208 
00209         //Small floating point
00210         const double sm_th(0.001);
00211 
00212         while (do_loop)
00213         {
00214                 Eigen::VectorXd tauR(tau - (*tauA));
00215                 //If the request is almost satisfied
00216                 if (tauR.norm() < sm_th)
00217                 {
00218                         ROS_DEBUG("Norm is smaller that threshold: %f", tauR.norm());
00219                         retVal = false;
00220                         break;
00221                 }
00222 
00223                 std::vector<double> tdmax,tdmin;
00224                 std::vector<int> notsat;
00225 
00226                 for(int i=0; i<tT->size(); ++i)
00227                 {
00228                         if ((*tT)(i) >= 0)
00229                         {
00230                                 double cmax = tmax(i) - (*tT)(i);
00231                                 //If not saturated
00232                                 if (cmax > sm_th)
00233                                 {
00234                                         notsat.push_back(i);
00235                                         tdmax.push_back(cmax);
00236                                         tdmin.push_back(tmin(i));
00237                                 }
00238                         }
00239                         else
00240                         {
00241                                 double cmin = tmin(i) - (*tT)(i);
00242                                 if (cmin < -sm_th)
00243                                 {
00244                                         notsat.push_back(i);
00245                                         tdmin.push_back(cmin);
00246                                         tdmax.push_back(tmax(i));
00247                                 }
00248                         }
00249                 }
00250 
00251                 if (!multi_chain && (notsat.size() < (tT->size()-1)))
00252                 {
00253                         ROS_DEBUG("Less than %d are saturated. Daisy chain allocation complete.", int(tT->size()-1));
00254                         retVal = true;
00255                         break;
00256                 }
00257                 //Assemble the reduced allocation matrix
00258                 Eigen::MatrixXd Bd(tau.size(), notsat.size());
00259                 for (int i=0; i<notsat.size(); ++i)
00260                 {
00261                         Bd.col(i) = thrusters.B().col(notsat[i]);
00262                 }
00263 
00264                 Eigen::MatrixXd Bdinv;
00265                 if (notsat.size() <= (tT->size()-2))
00266                 {
00267                         Bdinv = (Bd.transpose()*Bd).inverse()*Bd.transpose();
00268                 }
00269                 else
00270                 {
00271                         Bdinv = Bd.transpose()*(Bd*Bd.transpose()).inverse();
00272                 }
00273 
00274                 //Sanity check
00275                 if (isnan(Bdinv.norm()))
00276                 {
00277                         ROS_ERROR("Singular inverse allocation matrix. Aborting daisy chain.");
00278                         retVal = true;
00279                         break;
00280                 }
00281 
00282                 Eigen::VectorXd tTd = Bdinv*tauR;
00283 
00284                 //std::stringstream out;
00285                 //out<<tTd;
00286                 //ROS_ERROR("Td (%d): %s",tTd.size(),out.str().c_str());
00287 
00288                 double scalef(1.0),scale(0.0);
00289     for (int i=0; i< tTd.size(); ++i)
00290     {
00291         if (tTd(i) >= 0)
00292             scale = tTd(i)/tdmax[i];
00293         else
00294             scale = tTd(i)/tdmin[i];
00295 
00296         if (scale > scalef) scalef=scale;
00297     }
00298     tTd = tTd/scalef;
00299 
00300     Eigen::VectorXd tTn(*tT);
00301     for (int i=0; i< notsat.size(); ++i)
00302     {
00303         tTn(notsat[i]) += tTd(i);
00304     }
00305     Eigen::VectorXd tauf = thrusters.B()*(tTn);
00306     Eigen::VectorXd tauRf= tau - tauf;
00307 
00308     enum {Xi=0,Yi=1,Ni=2};
00309     //Test if we gained a monotonous increase (scaling will be perserved then)
00310     bool limit = fabs(tauR(Xi)) - fabs(tauRf(Xi)) < -sm_th;
00311     limit = limit || (fabs(tauR(Yi)) - fabs(tauRf(Yi)) < -sm_th);
00312                 limit = limit || (fabs(tauR(Ni)) - fabs(tauRf(Ni)) < -sm_th);
00313 
00314     if (limit)
00315     {
00316         ROS_DEBUG("Scaling or yaw contract broken. Skipping this contribution.");
00317         retVal = true;
00318         break;
00319     }
00320 
00321     if ((tTd.norm() < sm_th))
00322     {
00323         ROS_DEBUG("The daisy chain allocation is not contributing any change.");
00324         retVal = true;
00325         break;
00326     }
00327 
00328     (*tT) = tTn;
00329     (*tauA) = tauf;
00330         }
00331 
00332         return retVal;
00333 }
00334 
00335 PLUGINLIB_EXPORT_CLASS(labust::allocation::X2dAdaptive, labust::allocation::AllocationInterface)


labust_allocation
Author(s): Gyula Nagy
autogenerated on Fri Aug 28 2015 11:23:28