Jafar
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
sensorAbsloc.hpp
Go to the documentation of this file.
00001 
00012 #ifndef SENSORABSLOC_HPP_
00013 #define SENSORABSLOC_HPP_
00014 
00015 #include "jmath/jblas.hpp"
00016 #include "jmath/ublasExtra.hpp"
00017 #include "jmath/misc.hpp"
00018 
00019 #include "rtslam/rtSlam.hpp"
00020 #include "rtslam/quatTools.hpp"
00021 #include "rtslam/sensorAbstract.hpp"
00022 #include "rtslam/innovation.hpp"
00023 
00024 namespace jafar {
00025   namespace rtslam {
00026 
00027     class SensorAbsloc;
00028     typedef boost::shared_ptr<SensorAbsloc> absloc_ptr_t;
00029     
00037     class SensorAbsloc: public SensorProprioAbstract {
00038       protected:
00039         jblas::ind_array ia_rs;
00040         Innovation *innovation;
00041         Measurement *measurement;
00042         Expectation *expectation;
00043         jblas::mat EXP_rs;
00044         jblas::mat INN_rs;
00045         jblas::mat EXP_q;
00046         double mahaDist2;
00047         double sigMax;
00048         int inns; 
00049         int dats; 
00050         jblas::vec origin; 
00051 
00052       private:
00053         size_t indexE_Pos, indexE_OriEuler, indexE_Bundleobs, indexE_AbsVel, indexE_NormVel, indexE_Vel; 
00054         int indexD_Pos, indexD_OriQuat, indexD_OriEuler, indexD_Bundleobs, indexD_AbsVel, indexD_NormVel, indexD_Vel; 
00055         ublas::range indexR_Pos, indexR_OriQuat, indexR_AbsVel, indexR_AngVel; 
00056         ublas::range indexJ_Pos, indexJ_OriQuat, indexJ_AbsVel, indexJ_AngVel; 
00057         hardware::HardwareSensorProprioAbstract::CovType covType;
00058 
00059       public:
00060 
00061         std::vector<RobotAbstract::Quantity> returnQuants(bool needPose, bool needVel, bool needAngVel)
00062         {
00063           std::vector<RobotAbstract::Quantity> res;
00064           if (needPose) { res.push_back(RobotAbstract::qPos); res.push_back(RobotAbstract::qOriQuat); }
00065           if (needVel) res.push_back(RobotAbstract::qAbsVel);
00066           if (needAngVel) res.push_back(RobotAbstract::qAngVel);
00067           return res;
00068         }
00069 
00070 
00071 
00072         SensorAbsloc(const robot_ptr_t & robPtr, const filtered_obj_t inFilter = UNFILTERED, double mahaDist = -1.0, double sigMax = 1e9,
00073           bool needPose = true, bool needVel = false, bool needAngVel = false):
00074           SensorProprioAbstract(robPtr, inFilter, 0, returnQuants(needPose, needVel, needAngVel)),
00075           ia_rs(ia_globalPose), innovation(NULL), measurement(NULL), expectation(NULL),
00076           mahaDist2(mahaDist*fabs(mahaDist)), sigMax(sigMax), inns(0), dats(0)
00077         {}
00078 
00079         ~SensorAbsloc() { delete innovation; delete measurement; }
00080         virtual void setHardwareSensor(hardware::hardware_sensorprop_ptr_t hardwareSensorPtr_)
00081         {
00082           hardwareSensorPtr = hardwareSensorPtr_;
00083           // initialize jacobians and innovation sizes
00084           inns = hardwareSensorPtr->obsSize();
00085           dats = hardwareSensorPtr->dataSize();
00086           innovation = new Innovation(inns);
00087           measurement = new Measurement(inns);
00088           expectation = new Expectation(inns);
00089           EXP_rs.resize(inns, ia_rs.size(), false);
00090           INN_rs.resize(inns, ia_rs.size(), false);
00091           EXP_q.resize(3, 4, false);
00092 
00093           // initialize type of data the hardware sensor is providing
00094           size_t indexE = 0; // current index when building expectation
00095 
00096           // pos for GPS or Motion Capture
00097           indexD_Pos = hardwareSensorPtr->getQuantity(hardware::HardwareSensorProprioAbstract::qPos);
00098           if (indexD_Pos >= 0) { indexE_Pos = indexE; indexE += hardwareSensorPtr->QuantityObsSizes[hardware::HardwareSensorProprioAbstract::qPos]; }
00099           // quaternion orientation (actually converting quaternion observations to euler observations)
00100           indexD_OriQuat = hardwareSensorPtr->getQuantity(hardware::HardwareSensorProprioAbstract::qOriQuat);
00101           if (indexD_OriQuat >= 0) { indexE_OriEuler = indexE; indexE += hardwareSensorPtr->QuantityObsSizes[hardware::HardwareSensorProprioAbstract::qOriEuler]; }
00102           // euler orientation
00103           indexD_OriEuler = hardwareSensorPtr->getQuantity(hardware::HardwareSensorProprioAbstract::qOriEuler);
00104           if (indexD_OriEuler >= 0) { indexE_OriEuler = indexE; indexE += hardwareSensorPtr->QuantityObsSizes[hardware::HardwareSensorProprioAbstract::qOriEuler]; }
00105           // bundle observation for known landmark observation
00106           indexD_Bundleobs = hardwareSensorPtr->getQuantity(hardware::HardwareSensorProprioAbstract::qBundleobs);
00107           if (indexD_Bundleobs >= 0) { indexE_Bundleobs = indexE; indexE += hardwareSensorPtr->QuantityObsSizes[hardware::HardwareSensorProprioAbstract::qBundleobs]; }
00108           // absolute velocity for GPS
00109           indexD_AbsVel = hardwareSensorPtr->getQuantity(hardware::HardwareSensorProprioAbstract::qAbsVel);
00110           if (indexD_AbsVel >= 0) { indexE_AbsVel = indexE; indexE += hardwareSensorPtr->QuantityObsSizes[hardware::HardwareSensorProprioAbstract::qAbsVel]; }
00111           // norm of velocity for Odometry (mode 1)
00112           indexD_NormVel = hardwareSensorPtr->getQuantity(hardware::HardwareSensorProprioAbstract::qNormVel);
00113           if (indexD_NormVel >= 0) { indexE_NormVel = indexE; indexE += hardwareSensorPtr->QuantityObsSizes[hardware::HardwareSensorProprioAbstract::qNormVel]; }
00114           // velocity for Odometry (mode 2)
00115           indexD_Vel = hardwareSensorPtr->getQuantity(hardware::HardwareSensorProprioAbstract::qVel);
00116           if (indexD_Vel >= 0) { indexE_Vel = indexE; indexE += hardwareSensorPtr->QuantityObsSizes[hardware::HardwareSensorProprioAbstract::qVel]; }
00117 
00118           indexR_Pos = quantsState.getQuantity(RobotQuantity::qPos);
00119           indexR_OriQuat = quantsState.getQuantity(RobotQuantity::qOriQuat);
00120           indexR_AbsVel = quantsState.getQuantity(RobotQuantity::qAbsVel);
00121           indexR_AngVel = quantsState.getQuantity(RobotQuantity::qAngVel);
00122 
00123           indexJ_Pos = quantsJacob.getQuantity(RobotQuantity::qPos);
00124           indexJ_OriQuat = quantsJacob.getQuantity(RobotQuantity::qOriQuat);
00125           indexJ_AbsVel = quantsJacob.getQuantity(RobotQuantity::qAbsVel);
00126           indexJ_AngVel = quantsJacob.getQuantity(RobotQuantity::qAngVel);
00127 
00128           covType = hardwareSensorPtr->covType();
00129         }
00130         
00131         
00132         virtual void init(unsigned id)
00133         {
00134           RawInfos infos;
00135           queryAvailableRaws(infos);
00136 
00137           // find minimum variance
00138           jblas::vec3 min_var; min_var(0) = min_var(1) = min_var(2) = 1e3;
00139           for(std::vector<RawInfo>::iterator it = infos.available.begin(); it != infos.available.end(); ++it)
00140           {
00141             hardwareSensorPtr->observeRaw((*it).id, reading);
00142             for(int i = 0; i < 3; ++i) if (reading.data(i+1+dats) < min_var(i)) min_var(i) = reading.data(i+1+dats);
00143             if ((*it).id == id) break;
00144           }
00145 
00146           // do the weighted average using only readings with variance between min and 2*min
00147           jblas::vec3 average; average.clear();
00148           jblas::vec3 sum_coeffs; sum_coeffs.clear();
00149 //          jblas::veci3 sum; sum.clear();
00150           for(std::vector<RawInfo>::iterator it = infos.available.begin(); it != infos.available.end(); ++it)
00151           {
00152             hardwareSensorPtr->observeRaw((*it).id, reading);
00153             for(int i = 0; i < 3; ++i)
00154               if (reading.data(i+1+dats) < 2*min_var(i))
00155                 { average(i) += reading.data(i+1)*reading.data(i+1+dats); sum_coeffs(i) += reading.data(i+1+dats); }
00156 //                { average(i) += reading.data(i+1); sum(i) += 1; sum_coeffs(i) += jmath::sqr(reading.data(i+1+dats)); }
00157             if ((*it).id == id) break;
00158           }
00159           for(int i = 0; i < 3; ++i) average(i) /= sum_coeffs(i);
00160 //          for(int i = 0; i < 3; ++i) { average(i) /= sum(i); sum_coeffs(i) = sqrt(sum_coeffs(i))/sum(i); }
00161 
00162           // now initialize the robot state with this variance
00163           for(int i = 0; i < 3; ++i) { reading.data(i+1) = average(i); reading.data(i+1+dats) = min_var(i); }
00164 //          for(int i = 0; i < 3; ++i) { reading.data(i+1) = average(i); reading.data(i+1+dats) = sum_coeffs(i); }
00165         }
00166         
00167 
00168         virtual void process(unsigned id, double date_limit)
00169         {
00170           // FIXME should use globalPose() function so that extrinsic estimation works
00171           if (robotPtr()->is_start_pos_abs_init) use_for_init = false;
00172 
00173           if (use_for_init)
00174             init(id);
00175           else
00176             hardwareSensorPtr->getRaw(id, reading);
00177 
00178           EXP_rs.clear();
00179           expectation->P() = jblas::zero_mat(inns);
00180           jblas::vec T = ublas::subrange(pose.x(), 0, 3);
00181           jblas::vec r = ublas::subrange(pose.x(), 3, 7);
00182           jblas::vec p = ublas::subrange(robotPtr()->pose.x(), 0, 3);
00183           jblas::vec q = ublas::subrange(robotPtr()->pose.x(), 3, 7);
00184           jblas::vec Tr = quaternion::rotate(q,T);
00185 
00186           bool detect_faults = false; // will the data uncertainty be consistent when sensor is faulty (bias): see gating
00187 
00188           // POSITION
00189           if (indexD_Pos >= 0)
00190           {
00191             for(size_t i = 0; i < 3; ++i) if (reading.data(indexD_Pos+i+dats) > sigMax) if (!use_for_init) return;
00192 
00193             // compute expectation->x and EXP_rs
00194             quaternion::rotate_by_dq(q, T, EXP_q);
00195             ublas::subrange(EXP_rs, indexE_Pos,indexE_Pos+3, indexJ_Pos.start(), indexJ_Pos.start()+indexJ_Pos.size()) = jblas::identity_mat(3);
00196             ublas::subrange(EXP_rs, indexE_Pos,indexE_Pos+3, indexJ_OriQuat.start(), indexJ_OriQuat.start()+indexJ_OriQuat.size()) = EXP_q;
00197             ublas::subrange(expectation->x(), indexE_Pos,indexE_Pos+3) = p + Tr;
00198 
00199             // fill measurement
00200             ublas::subrange(measurement->x(), indexE_Pos,indexE_Pos+3) = ublas::subrange(reading.data, indexD_Pos,indexD_Pos+3) - robotPtr()->start_pos_abs;
00201             for(int i = 0; i < 3; ++i) measurement->P()(indexE_Pos+i,indexE_Pos+i) = jmath::sqr(reading.data(indexD_Pos+i+dats));
00202 //std::cout << "sensorAbsLoc pos measurement " << *measurement << std::endl;
00203           }
00204 
00205           // ORIENTATION QUATERNION AND EULER
00206           /*
00207            * We cannot directly observe quaternion because there is an unconstrained degree of liberty
00208            * and the cov matrix has rank 3 but size 4, so the filter update does not work.
00209            * So we only observe euler angles, taking care of the modulo for innovation computation.
00210            * TODO When pitch is close to +-PI/2 we should not use euler but rotation vector for instance
00211            */
00212           if (indexD_OriQuat >= 0 || indexD_OriEuler >= 0)
00213           {
00214             bool isEuler = (indexD_OriEuler >= 0);
00215             size_t indexE = indexE_OriEuler;
00216 
00217             size_t indexD = isEuler ? indexD_OriEuler : indexD_OriQuat;
00218             size_t sizeD = isEuler ? 3 : 4;
00219             for(size_t i = 0; i < sizeD; ++i) if (reading.data(indexD+i+dats) > sigMax) if (!use_for_init) return;
00220 
00221 
00222             // compute expectation->x and EXP_rs
00223             jblas::mat QR_q(4,4);
00224             jblas::vec4 qr = quaternion::qProd(q, r);
00225             quaternion::qProd_by_dq1(r, QR_q);
00226             jblas::mat ER_qr(3,4);
00227             jblas::vec3 er; quaternion::q2e(qr, er, ER_qr);
00228             jblas::mat ER_q = ublas::prod(ER_qr, QR_q);
00229             ublas::subrange(expectation->x(), indexE,indexE+3) = er;
00230             ublas::subrange(EXP_rs, indexE,indexE+3, indexJ_OriQuat.start(), indexJ_OriQuat.start()+indexJ_OriQuat.size()) = ER_q;
00231 
00232             // fill measurement
00233             if (isEuler)
00234             {
00235               ublas::subrange(measurement->x(), indexE,indexE+3) = ublas::subrange(reading.data, indexD_OriEuler,indexD_OriEuler+3);
00236               ublas::subrange(measurement->P(), indexE,indexE+3, indexE,indexE+3) =
00237                 hardwareSensorPtr->extractDataCovBlock(indexD_OriEuler, 3);
00238             } else
00239             {
00240               // convert from quaternion to euler
00241               jblas::mat E_q(3,4); jblas::vec3 euler;
00242               quaternion::q2e(ublas::subrange(reading.data, indexD_OriQuat,indexD_OriQuat+4), euler, E_q);
00243               ublas::subrange(measurement->x(), indexE,indexE+3) = euler;
00244               jblas::sym_mat quat_P = hardwareSensorPtr->extractDataCovBlock(indexD_OriQuat, 4);
00245               ublas::subrange(measurement->P(), indexE,indexE+3, indexE,indexE+3) = prod_JPJt(quat_P, E_q);
00246             }
00247 
00248             // correct measurement to take into account modulo
00249 //std::cout << "observe euler ; before modulo : " << measurement->x() << " ; " << measurement->P() << std::endl;
00250             for(size_t i = indexE; i < indexE+3; ++i)
00251             {
00252               double inn = measurement->x()(i) - expectation->x()(i);
00253               if (inn >  M_PI) measurement->x()(i) -= 2*M_PI; else
00254               if (inn < -M_PI) measurement->x()(i) += 2*M_PI;
00255             }
00256 //std::cout << "after modulo : " << measurement->x() << " ; " << measurement->P() << std::endl;
00257 
00258 //std::cout << "sensorAbsLoc ori measurement " << *measurement << std::endl;
00259           }
00260 
00261           // BUNDLE OBSERVATION
00262           if (indexD_Bundleobs >= 0)
00263           {
00264             // get data
00265             jblas::vec3 pos = ublas::subrange(reading.data, indexD_Bundleobs+0,indexD_Bundleobs+3);
00266             jblas::vec3 dir = ublas::subrange(reading.data, indexD_Bundleobs+3,indexD_Bundleobs+6);
00267 
00268             // compute expectation->x and EXP_rs and expectation->P
00269             jblas::mat33 U_p;
00270             jblas::vec3 u = pos-p;
00271             jmath::ublasExtra::normalizeJac(u, U_p);
00272             jmath::ublasExtra::normalize(u);
00273             ublas::subrange(expectation->x(), indexE_Bundleobs,indexE_Bundleobs+3) = u;
00274             ublas::subrange(EXP_rs, indexE_Bundleobs,indexE_Bundleobs+3, indexJ_OriQuat.start(), indexJ_OriQuat.start()+indexJ_OriQuat.size()) = -U_p;
00275 
00276             jblas::sym_mat Pos = hardwareSensorPtr->extractDataCovBlock(indexD_Bundleobs, 3);
00277             ublas::subrange(expectation->P(), indexE_Bundleobs,indexE_Bundleobs+3, indexE_Bundleobs,indexE_Bundleobs+3) = prod_JPJt(Pos, U_p);
00278 
00279             // fill measurement
00280             ublas::subrange(measurement->x(), indexE_Bundleobs,indexE_Bundleobs+3) = dir;
00281             ublas::subrange(measurement->P(), indexE_Bundleobs,indexE_Bundleobs+3, indexE_Bundleobs,indexE_Bundleobs+3) =
00282               ublasExtra::createSymMat(reading.data, dats+1, dats, 3, 3);
00283 
00284             detect_faults = true;
00285           }
00286 
00287           // NORM_VEL
00288           if (indexD_NormVel >= 0)
00289           {
00290             if (reading.data(indexD_NormVel+0+dats) > sigMax) if (!use_for_init) return; // don't bother to make useless obs, it means that we want to ignore it
00291 
00292             jblas::vec v = ublas::project(robotPtr()->state.x(), indexR_AbsVel);
00293             // TODO need to use angular velocity when T != 0
00294             JFR_ASSERT(ublas::norm_2(ublas::subrange(T,0,2)) < 1e-6, "Currently odometry is only supported with SlamOrigin vertically aligned with RobotOrigin");
00295 
00296             // compute expectation->x and EXP_rs
00297 //            quaternion::rotate_by_dq(q, T, EXP_q);
00298             jblas::mat NV_v(1, 3); // norm jacobian
00299             expectation->x(indexE_NormVel) = ublasExtra::norm_2(v, NV_v);
00300             ublas::subrange(EXP_rs, indexE_NormVel,indexE_NormVel+1, indexJ_AbsVel.start(), indexJ_AbsVel.start()+indexJ_AbsVel.size()) = NV_v;
00301             // fill measurement
00302             ublas::subrange(measurement->x(), indexE_NormVel,indexE_NormVel+1) = ublas::subrange(reading.data, indexD_NormVel,indexD_NormVel+1);
00303             measurement->P()(indexE_NormVel,indexE_NormVel) = jmath::sqr(reading.data(indexD_NormVel+1));
00304 
00305             detect_faults = false;//true; // FIXME should be true but should nevertheless include the noise part
00306             mahaDist2 = -1; // FIXME TEMP FIX see above
00307           }
00308 
00309 
00310           if (use_for_init)
00311           {
00312             // for first reading we force initialization
00313             // FIXME do it correctly if the robot has already started... not sure it is useful though
00314 
00315             if (indexD_OriQuat >= 0 || indexD_OriEuler >= 0) // need to set ori uncertainty before pos uncertainty because of sensor lever arm
00316             {
00317               size_t indexE = indexE_OriEuler;
00318               jblas::mat Q_exp(4,3);
00319               quaternion::e2q(ublas::subrange(measurement->x(), indexE,indexE+3), q, Q_exp);
00320 
00321               jblas::vec ri = quaternion::q2qc(r);
00322               q = quaternion::qProd(q, ri);
00323               jblas::mat Q_r(4,4);
00324               quaternion::qProd_by_dq1(ri, Q_r);
00325               Q_exp = ublas::prod(Q_r, Q_exp);
00326 
00327               ublas::subrange(robotPtr()->pose.x(), 3, 7) = q;
00328               ublas::subrange(robotPtr()->pose.P(), 3,7, 3,7) = ublasExtra::prod_JPJt(
00329                 ublas::subrange(measurement->P(), indexE,indexE+3, indexE,indexE+3), Q_exp);
00330 
00331               // update tmp variables depending on q
00332               Tr = quaternion::rotate(q,T);
00333 
00334               std::cout << "AbsLoc sets initial orientation q = " << ublas::subrange(robotPtr()->pose.x(), 3, 7) <<
00335                 " e = " <<  quaternion::q2e(ublas::subrange(robotPtr()->pose.x(), 3, 7)) << std::endl;
00336             }
00337 
00338             if (indexD_Pos >= 0)
00339             {
00340               robotPtr()->start_pos_abs = ublas::subrange(measurement->x(), indexE_Pos,indexE_Pos+3) - Tr - ublas::subrange(robotPtr()->state.x(),0,3);
00341               ublas::subrange(robotPtr()->pose.x(), 0, 3) = jblas::zero_vec(3);
00342               ublas::subrange(robotPtr()->pose.P(), 0,3, 0,3) = ublas::subrange(measurement->P(), indexE_Pos,indexE_Pos+3, indexE_Pos,indexE_Pos+3) +
00343                 ublasExtra::prod_JPJt(ublas::subrange(robotPtr()->pose.P(), 3,7, 3,7), EXP_q);
00344 
00345               std::cout << "AbsLoc has origin " << robotPtr()->start_pos_abs <<
00346                 " ; initial position " << ublas::subrange(robotPtr()->pose.x(), 0,3) <<
00347                 " ; initial position var " << ublas::subrange(robotPtr()->pose.P(), 0,3, 0,3) << std::endl;
00348             }
00349           } else
00350           {
00351             // compute expectation->P and innovation
00352             expectation->P() += ublasExtra::prod_JPJt(ublas::project(robotPtr()->mapPtr()->filterPtr->P(), ia_rs, ia_rs), EXP_rs);
00353             innovation->x() = measurement->x() - expectation->x();
00354             innovation->P() = measurement->P() + expectation->P();
00355             INN_rs = -EXP_rs;
00356 
00357             // gating
00358             bool integrate = true;
00359             if (mahaDist2 > 0.0)
00360             {
00361               double maha;
00362               if (!detect_faults)
00363                 maha = innovation->mahalanobis();
00364               else
00365               {
00366                 // if the sensor detects faults and maintains the uncertainty consistent,
00367                 // we should not include the measurement uncertainty in the mahalanobis dist
00368                 // because large meas uncert will make the obs always consistent,
00369                 // but as the fault is a bias it will impact the filter even with large uncert
00370                 jafar::jmath::ublasExtra::lu_inv(expectation->P(), innovation->iP_);
00371                 maha = ublasExtra::prod_xt_iP_x(innovation->iP_, innovation->x());
00372               }
00373               if (maha > mahaDist2) integrate = false;
00374             }
00375 
00376             // correct
00377             if (integrate)
00378             {
00379               map_ptr_t mapPtr = robotPtr()->mapPtr();
00380               ind_array ia_x = mapPtr->ia_used_states();
00381               mapPtr->filterPtr->correct(ia_x,*innovation,INN_rs,ia_rs);
00382             }
00383           }
00384 
00385           if (use_for_init)
00386           {
00387             use_for_init = false;
00388             robotPtr()->is_start_pos_abs_init = true;
00389             hardwareSensorPtr->getRaw(id, reading); // just to release data
00390           }
00391           
00392           //hardwareSensorPtr->release();
00393         }
00394 
00395         
00396     };
00397 }}
00398 
00399 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated on Wed Oct 15 2014 00:37:27 for Jafar by doxygen 1.7.6.1
LAAS-CNRS