/* * ===================================================================================== * * Filename: state.cpp * * Description: A Class for managing body and features. * * Version: 1.0 * Created: 03/17/2017 07:55:56 PM * Revision: none * Compiler: gcc * * Author: Martin Miller (MHM), miller7@illinois.edu * Organization: Aerospace Robotics and Controls Lab (ARC) * * ===================================================================================== */ #include "state.h" State::State ( ) { body = new Body; P = Matrix::Zero(9,9); P.block<3,3>(6,6) = 1e-6*Matrix3d::Identity(); return ; } /* ----- end of method State::State ----- */ void State::vel ( const Matrix &v ) { body->vel(v); return ; } /* ----- end of method State::vel ----- */ void State::pos ( const UTM &utm ) { utm_c = utm.zone_c; utm_i = utm.zone_i; body->pos(utm); return ; } /* ----- end of method State::pos ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: H * Description: Returns the matrix H for the measurements z *-------------------------------------------------------------------------------------- */ MatrixXd State::H ( const Quaterniond &q, const std::vector &z ) { Vector3d pos; pos = body->ned(); MatrixXd h; // Determine the size of H int cols = 9 + 3*features.size(); int rows = Hrows(z); h = MatrixXd::Zero(rows,cols); for (int i=0,row=0; i(row,0) = fi->Hx(pos,q); h.block<6,3>(row,col) = fi->Hy(pos,q); row += 6; break; case MONO: col = rowById(z[i].id); if (col==-1) { fprintf(stderr, "Feature %d not found, quitting.\n", z[i].id); exit(1); } fi = featureById( z[i].id ); h.block<4,9>(row,0) = fi->Hx(pos,q); break; case HEIGHT: h.block<1,9>(row,0) = body->H(); row += 1; break; default: break; } /* ----- end switch ----- */ } return h; } /* ----- end of method State::H ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: Hrows * Description: Returns the number of rows in the measurement vector based on * the number and types of measurements. *-------------------------------------------------------------------------------------- */ int State::Hrows ( const std::vector &z) { int rows = 0; for (auto i=z.begin(); i!=z.end(); ++i) { switch ( i->z_type ) { case REFLECTION: rows += 6; break; case MONO: rows += 4; break; case HEIGHT: rows += 1; break; default: break; } /* ----- end switch ----- */ } return rows ; } /* ----- end of method State::Hrows ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: R * Description: Returns the measurement noise matrix. *-------------------------------------------------------------------------------------- */ MatrixXd State::R ( const std::vector &z ) { int rows = Hrows(z); MatrixXd r; r = MatrixXd::Zero(rows,rows); for (int i=0,row=0; i(row,row) = fi->R(); row += 6; break; case MONO: fprintf(stderr, "mono points not supported.\n"); exit(1); break; case HEIGHT: r.block<1,1>(row,row) = body->R(); row += 1; break; default: break; } /* ----- end switch ----- */ } return r ; } /* ----- end of method State::R ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: kalmanUpdate * Description: Performs the kalman update. *-------------------------------------------------------------------------------------- */ void State::kalmanUpdate( const MatrixXd &h, const MatrixXd &S, const std::vector &z, const Quaterniond &q) { MatrixXd K; // P^T is implied here since P is symmetric K = S.fullPivLu().solve(h*P).transpose(); // Compute P_k|k P = (MatrixXd::Identity(P.rows(),P.rows())-K*h)*P; P = 0.5*(P+P.transpose()); // Compute the innovation or error Matrix y; y = innovation(z,q); // Get the update Matrix dx; dx = K*y; body->dx(dx.segment<9>(0)); { int row=9; for (auto i=features.begin(); i!=features.end(); ++i, row+=3) { (*i)->dx(dx.segment<3>(row)); } } return ; } /* ----- end of method State::kalmanUpdate ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: innovation * Description: Returns the innovation vector z-hhat *-------------------------------------------------------------------------------------- */ Matrix State::innovation( const std::vector &z, const Quaterniond &q) { Matrix y; int rows = Hrows(z); y = Matrix::Zero(rows,1); for (int i=0,row=0; i hi, zi; hi = fi->h(body->ned(), q); zi = fi->measurement_vector(z[i].source, z[i].reflection); y.segment<6>(row) = zi-hi; row += 6; } else if (mt==MONO) { fprintf(stderr, "mono points not supported.\n"); exit(1); } else if (mt==HEIGHT) { Matrix zi, hi; zi[0] = z[i].height; Vector3d xbw; xbw = body->ned(); hi[0] = xbw[2]; y.segment<1>(row) = zi-hi; row += 1; } else { fprintf(stderr, "Unknown measurement type\n"); } } return y; } /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: handle_measurements * Description: Update EKF, and adds new features to state. *-------------------------------------------------------------------------------------- */ void State::handle_measurements ( const std::vector &z, const Quaterniond &q) { std::vector featuresToAdd; std::vector featuresToUpdate; double zmeas = body->ned()[2]; for (auto i=z.begin(); i!=z.end(); ++i) { if (i->z_type==HEIGHT) { featuresToUpdate.push_back(*i); zmeas = i->height; } else if (exists(i->id)) { featuresToUpdate.push_back(*i); } else { featuresToAdd.push_back(*i); } } if (featuresToUpdate.size()>1) { #ifdef FULLS MatrixXd h; h = H(q,featuresToUpdate); MatrixXd S; S = h*P*h.transpose() + R(featuresToUpdate); kalmanUpdate(h,S,featuresToUpdate,q); #else /* ----- not FULLS ----- */ #endif /* ----- not FULLS ----- */ } addFeatures( featuresToAdd, q, zmeas); return ; } /* ----- end of method State::feature_update ----- */ void State::addFeatures ( std::vector &F, const Quaterniond &q, double z) { // Add new features Vector3d pos = body->ned(); for (auto i=F.begin(); i!=F.end(); ++i) { if (features.size()>MAXFEATURES) break; // Create feature Feature *f = new Feature(i->id, i->source, i->reflection, pos, q, z); if (!f->sane()) { delete f; continue; } features.push_back(f); // Expand P expandP(f->P0()); } return ; } /* ----- end of method State::addFeatures ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: initializePi * Description: Zeros out the off-diagonal blocks and sets the Pii block to Pi. *-------------------------------------------------------------------------------------- */ void State::initializePi ( int i, const Matrix &Pi ) { int pt=9+3*(i-1); P.block(pt,0,3,P.cols()) = MatrixXd::Zero(3,P.cols()); P.block(0,pt,P.rows(),3) = MatrixXd::Zero(P.rows(),3); P.block<3,3>(pt,pt) = Pi; return ; } /* ----- end of method State::initializePi ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: Q * Description: Compose full Q from body and features. *-------------------------------------------------------------------------------------- */ MatrixXd State::Q ( double dt ) { MatrixXd q; int rows = 9 + 3*features.size(); q = MatrixXd::Zero(rows,rows); q.topLeftCorner<9,9>() = body->Q(dt); { // limit i's scope auto i = features.begin(); for (int row=9; row(row,row) = (*i)->Q(dt); } } return q; } /* ----- end of method State::q ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: F * Description: Compose full F from body and features *-------------------------------------------------------------------------------------- */ MatrixXd State::F ( const Quaterniond &q, const Vector3d &w, double dt ) { Vector3d v; v = body->vel(); // Allocate matrix F MatrixXd f; int rows = 9 + 3*features.size(); f = MatrixXd::Zero(rows,rows); // Set body F f.topLeftCorner<9,9>() = body->F(w,q,dt); // Set Fxi Fyi { // limit i's scope auto i = features.begin(); for (int row=9; row(row,0) = (*i)->Fx(dt); f.block<3,3>(row,row) = (*i)->Fy(v,w,dt); } } return f ; } /* ----- end of method State::F ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: Pkk1 * Description: Updates P_k|k-1 *-------------------------------------------------------------------------------------- */ void State::Pkk1 ( const Vector3d &ang, const Quaterniond &q, const double dt) { MatrixXd f,fullQ; fullQ = Q(dt); f = F ( q, ang, dt ); P = f*P*f.transpose()+fullQ; // Enforce symmetry P = 0.5*(P+P.transpose()); return ; } /* ----- end of method State::Pkk1 ----- */ void State::motionModel ( const Vector3d &acc, const Vector3d &ang, const Quaterniond &q, const double dt) { Pkk1(ang,q,dt); body->motionModel(acc,ang,q,dt); Vector3d vel; vel = body->vel(); #ifdef FASTMOTIONMODEL Matrix fullL; fullL = L(); Matrix V; V << vel[1], vel[2], vel[0], ang[1], ang[2], ang[0]; Matrix f; f = fullL*V*dt; // Update features' states { int rows = 3*features.size(); auto i = features.begin(); for (int row=0; row(row); (*i)->dx(dx); } } #else /* ----- not FASTMOTIONMODEL ----- */ for (auto i=features.begin(); i!=features.end(); ++i) { (*i)->motionModel(ang,vel,dt); } #endif /* ----- not FASTMOTIONMODEL ----- */ // Remove features that leave FOV { auto i = features.begin(); int j=0; while (i!=features.end()) { if ((*i)->inFOV()) { ++i; ++j; } else { i=removeFeature(i,j); } } } return ; } /* ----- end of method State::motionModel ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: removeFeature * Description: Remove the Feature i and Pj in a clean fashion. *-------------------------------------------------------------------------------------- */ std::list::iterator State::removeFeature ( std::list::iterator &i, int j ) { // It is important to shrink P first, because it depends on knowing the // current size of the feature vector. shrinkP(j); delete *i; i = features.erase(i); return i; } /* ----- end of method State::removeFeature ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: L * Description: Return the composition of all Li's of the features *-------------------------------------------------------------------------------------- */ Matrix State::L ( ) { Matrix l; int rows = 3*features.size(); l = Matrix::Zero(rows,6); { auto i=features.begin(); for (int row=0; row(row,0) = (*i)->L(); } } return l; } /* ----- end of method State::L ----- */ void State::position_covariance ( const Matrix &p ) { P.block(0,0,3,3) = p; return ; } /* ----- end of method State::position_covariance ----- */ void State::velocity_covariance ( const Matrix &p ) { P.block(3,3,3,3) = p; return ; } /* ----- end of method State::velocity_covariance ----- */ void State::unicsv ( ) { body->unicsv(); return ; } /* ----- end of method State::unicsv ----- */ void State::accelerometer_bias ( const Vector3d &b) { body->accelerometer_bias(b); return ; } /* ----- end of method State::accelerometer_bias ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: exists * Description: Tests if the id is a current feature. *-------------------------------------------------------------------------------------- */ bool State::exists ( int id ) { for (auto i=features.begin(); i!=features.end(); ++i) { if ((*i)->id()==id) return true; } return false; } /* ----- end of method State::exists ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: rowById * Description: Returns the first row of the feature with the given ID. Returns * -1 if not found. *-------------------------------------------------------------------------------------- */ int State::rowById ( int id ) { int j=9; for (auto i=features.begin(); i!=features.end(); ++i, j+=3) { if ((*i)->id()==id) return j; } return -1; } /* ----- end of method State::rowById ----- */ Feature * State::featureById ( int id ) { for (auto i=features.begin(); i!=features.end(); ++i) { if ((*i)->id()==id) return *i; } return NULL; } /* ----- end of method State::featureById ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: expandP * Description: Expand the P matrix to fit one more feature. *-------------------------------------------------------------------------------------- */ void State::expandP ( const Matrix3d &Pi) { P.conservativeResizeLike(MatrixXd::Zero(3+P.cols(),3+P.cols())); P.bottomRightCorner<3,3>() = Pi; return ; } /* ----- end of method State::expandP ----- */ Matrix State::Pxx ( ) { return P.topLeftCorner<9,9>() ; } /* ----- end of method State::Pxx ----- */ /* *-------------------------------------------------------------------------------------- * Class: State * Method: State :: shrinkP * Description: Delete the ith feature from P *-------------------------------------------------------------------------------------- */ void State::shrinkP ( int i ) { int N = 9 + 3*features.size(); int I = 9 + 3*i; P.block(I,0,N-I-3,I) = P.bottomLeftCorner(N-I-3,I); P.block(0,I,I,N-I-3) = P.topRightCorner(I,N-I-3); P.block(I,I,N-I-3,N-I-3) = P.bottomRightCorner(N-I-3,N-I-3); P.conservativeResize(N-3,N-3); return ; } /* ----- end of method State::shrinkP ----- */