00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
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
00059 bool valid = thrusters.configure(nh, ph);
00060 if (!valid) return false;
00061
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
00075 const Eigen::VectorXd& tmax(thrusters.maxF());
00076 const Eigen::VectorXd& tmin(thrusters.minF());
00077
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
00084 Eigen::Vector3d tauN, tauXY;
00085 tauN<<0.0,0.0,tau(N);
00086 tauXY<<tau(X),tau(Y),0.0;
00087
00088 Eigen::Vector4d tN, tXY;
00089 tN = thrusters.Binv()*tauN;
00090 tXY = thrusters.Binv()*tauXY;
00091
00092
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
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
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
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
00210 const double sm_th(0.001);
00211
00212 while (do_loop)
00213 {
00214 Eigen::VectorXd tauR(tau - (*tauA));
00215
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
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
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
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
00285
00286
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
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)