/** * @file PoseRTV.cpp * @author Alex Cunningham */ #include #include #include #include #include namespace gtsam { using namespace std; static const Vector g = delta(3, 2, 9.81); /* ************************************************************************* */ double bound(double a, double min, double max) { if (a < min) return min; else if (a > max) return max; else return a; } /* ************************************************************************* */ PoseRTV::PoseRTV(double roll, double pitch, double yaw, double x, double y, double z, double vx, double vy, double vz) : Rt_(Rot3::RzRyRx(roll, pitch, yaw), Point3(x, y, z)), v_(vx, vy, vz) {} /* ************************************************************************* */ PoseRTV::PoseRTV(const Vector& rtv) : Rt_(Rot3::RzRyRx(rtv.head(3)), Point3(rtv.segment(3, 3))), v_(rtv.tail(3)) { } /* ************************************************************************* */ Vector PoseRTV::vector() const { Vector rtv(9); rtv.head(3) = Rt_.rotation().xyz(); rtv.segment(3,3) = Rt_.translation().vector(); rtv.tail(3) = v_.vector(); return rtv; } /* ************************************************************************* */ bool PoseRTV::equals(const PoseRTV& other, double tol) const { return Rt_.equals(other.Rt_, tol) && v_.equals(other.v_, tol); } /* ************************************************************************* */ void PoseRTV::print(const string& s) const { cout << s << ":" << endl; gtsam::print((Vector)R().xyz(), " R:rpy"); t().print(" T"); v_.print(" V"); } /* ************************************************************************* */ PoseRTV PoseRTV::Expmap(const Vector9& v) { Pose3 newPose = Pose3::Expmap(v.head<6>()); Velocity3 newVel = Velocity3::Expmap(v.tail<3>()); return PoseRTV(newPose, newVel); } /* ************************************************************************* */ Vector9 PoseRTV::Logmap(const PoseRTV& p) { Vector6 Lx = Pose3::Logmap(p.Rt_); Vector3 Lv = Velocity3::Logmap(p.v_); return (Vector9() << Lx, Lv); } /* ************************************************************************* */ PoseRTV PoseRTV::retract(const Vector& v) const { assert(v.size() == 9); // First order approximation Pose3 newPose = Rt_.retract(sub(v, 0, 6)); Velocity3 newVel = v_ + Rt_.rotation() * Point3(sub(v, 6, 9)); return PoseRTV(newPose, newVel); } /* ************************************************************************* */ Vector PoseRTV::localCoordinates(const PoseRTV& p1) const { const Pose3& x0 = pose(), &x1 = p1.pose(); // First order approximation Vector6 poseLogmap = x0.localCoordinates(x1); Vector3 lv = rotation().unrotate(p1.velocity() - v_).vector(); return (Vector(9) << poseLogmap, lv); } /* ************************************************************************* */ PoseRTV inverse_(const PoseRTV& p) { return p.inverse(); } PoseRTV PoseRTV::inverse(boost::optional H1) const { if (H1) *H1 = numericalDerivative11(inverse_, *this, 1e-5); return PoseRTV(Rt_.inverse(), v_.inverse()); } /* ************************************************************************* */ PoseRTV compose_(const PoseRTV& p1, const PoseRTV& p2) { return p1.compose(p2); } PoseRTV PoseRTV::compose(const PoseRTV& p, boost::optional H1, boost::optional H2) const { if (H1) *H1 = numericalDerivative21(compose_, *this, p, 1e-5); if (H2) *H2 = numericalDerivative22(compose_, *this, p, 1e-5); return PoseRTV(Rt_.compose(p.Rt_), v_.compose(p.v_)); } /* ************************************************************************* */ PoseRTV between_(const PoseRTV& p1, const PoseRTV& p2) { return p1.between(p2); } PoseRTV PoseRTV::between(const PoseRTV& p, boost::optional H1, boost::optional H2) const { if (H1) *H1 = numericalDerivative21(between_, *this, p, 1e-5); if (H2) *H2 = numericalDerivative22(between_, *this, p, 1e-5); return inverse().compose(p); } /* ************************************************************************* */ PoseRTV PoseRTV::planarDynamics(double vel_rate, double heading_rate, double max_accel, double dt) const { // split out initial state const Rot3& r1 = R(); const Velocity3& v1 = v(); // Update vehicle heading Rot3 r2 = r1.retract((Vector(3) << 0.0, 0.0, heading_rate * dt)); const double yaw2 = r2.ypr()(0); // Update vehicle position const double mag_v1 = v1.norm(); // FIXME: this doesn't account for direction in velocity bounds double dv = bound(vel_rate - mag_v1, - (max_accel * dt), max_accel * dt); double mag_v2 = mag_v1 + dv; Velocity3 v2 = mag_v2 * Velocity3(cos(yaw2), sin(yaw2), 0.0); Point3 t2 = translationIntegration(r2, v2, dt); return PoseRTV(r2, t2, v2); } /* ************************************************************************* */ PoseRTV PoseRTV::flyingDynamics( double pitch_rate, double heading_rate, double lift_control, double dt) const { // split out initial state const Rot3& r1 = R(); const Velocity3& v1 = v(); // Update vehicle heading (and normalise yaw) Vector rot_rates = (Vector(3) << 0.0, pitch_rate, heading_rate); Rot3 r2 = r1.retract(rot_rates*dt); // Work out dynamics on platform const double thrust = 50.0; const double lift = 50.0; const double drag = 0.1; double yaw2 = r2.yaw(); double pitch2 = r2.pitch(); double forward_accel = -thrust * sin(pitch2); // r2, pitch (in global frame?) controls forward force double loss_lift = lift*fabs(sin(pitch2)); Rot3 yaw_correction_bn = Rot3::yaw(yaw2); Point3 forward(forward_accel, 0.0, 0.0); Vector Acc_n = yaw_correction_bn.rotate(forward).vector() // applies locally forward force in the global frame - drag * (Vector(3) << v1.x(), v1.y(), 0.0) // drag term dependent on v1 + delta(3, 2, loss_lift - lift_control); // falling due to lift lost from pitch // Update Vehicle Position and Velocity Velocity3 v2 = v1 + Velocity3(Acc_n * dt); Point3 t2 = translationIntegration(r2, v2, dt); return PoseRTV(r2, t2, v2); } /* ************************************************************************* */ PoseRTV PoseRTV::generalDynamics( const Vector& accel, const Vector& gyro, double dt) const { // Integrate Attitude Equations Rot3 r2 = rotation().retract(gyro * dt); // Integrate Velocity Equations Velocity3 v2 = v_.compose(Velocity3(dt * (r2.matrix() * accel + g))); // Integrate Position Equations Point3 t2 = translationIntegration(r2, v2, dt); return PoseRTV(t2, r2, v2); } /* ************************************************************************* */ Vector6 PoseRTV::imuPrediction(const PoseRTV& x2, double dt) const { // split out states const Rot3 &r1 = R(), &r2 = x2.R(); const Velocity3 &v1 = v(), &v2 = x2.v(); Vector6 imu; // acceleration Vector accel = v1.localCoordinates(v2) / dt; imu.head<3>() = r2.transpose() * (accel - g); // rotation rates // just using euler angles based on matlab code // FIXME: this is silly - we shouldn't use differences in Euler angles Matrix Enb = RRTMnb(r1); Vector euler1 = r1.xyz(), euler2 = r2.xyz(); Vector dR = euler2 - euler1; // normalize yaw in difference (as per Mitch's code) dR(2) = Rot2::fromAngle(dR(2)).theta(); dR /= dt; imu.tail<3>() = Enb * dR; // imu.tail(3) = r1.transpose() * dR; return imu; } /* ************************************************************************* */ Point3 PoseRTV::translationIntegration(const Rot3& r2, const Velocity3& v2, double dt) const { // predict point for constraint // NOTE: uses simple Euler approach for prediction Point3 pred_t2 = t() + v2 * dt; return pred_t2; } /* ************************************************************************* */ double range_(const PoseRTV& A, const PoseRTV& B) { return A.range(B); } double PoseRTV::range(const PoseRTV& other, boost::optional H1, boost::optional H2) const { if (H1) *H1 = numericalDerivative21(range_, *this, other, 1e-5); if (H2) *H2 = numericalDerivative22(range_, *this, other, 1e-5); return t().distance(other.t()); } /* ************************************************************************* */ PoseRTV transformed_from_(const PoseRTV& global, const Pose3& transform) { return global.transformed_from(transform); } PoseRTV PoseRTV::transformed_from(const Pose3& trans, boost::optional Dglobal, boost::optional Dtrans) const { // Note that we rotate the velocity Matrix DVr, DTt; Velocity3 newvel = trans.rotation().rotate(v_, DVr, DTt); if (!Dglobal && !Dtrans) return PoseRTV(trans.compose(pose()), newvel); // Pose3 transform is just compose Matrix DTc, DGc; Pose3 newpose = trans.compose(pose(), DTc, DGc); if (Dglobal) { *Dglobal = zeros(9,9); insertSub(*Dglobal, DGc, 0, 0); // Rotate velocity insertSub(*Dglobal, eye(3,3), 6, 6); // FIXME: should this actually be an identity matrix? } if (Dtrans) { *Dtrans = numericalDerivative22(transformed_from_, *this, trans, 1e-8); // // *Dtrans = zeros(9,6); // // directly affecting the pose // insertSub(*Dtrans, DTc, 0, 0); // correct in tests // // // rotating the velocity // Matrix vRhat = skewSymmetric(-v_.x(), -v_.y(), -v_.z()); // trans.rotation().print("Transform rotation"); // gtsam::print(vRhat, "vRhat"); // gtsam::print(DVr, "DVr"); // // FIXME: find analytic derivative //// insertSub(*Dtrans, vRhat, 6, 0); // works if PoseRTV.rotation() = I //// insertSub(*Dtrans, trans.rotation().matrix() * vRhat, 6, 0); // FAIL: both tests fail } return PoseRTV(newpose, newvel); } /* ************************************************************************* */ Matrix PoseRTV::RRTMbn(const Vector& euler) { assert(euler.size() == 3); const double s1 = sin(euler(1-1)), c1 = cos(euler(1-1)); const double t2 = tan(euler(2-1)), c2 = cos(euler(2-1)); Matrix Ebn(3,3); Ebn << 1.0, s1 * t2, c1 * t2, 0.0, c1, -s1, 0.0, s1 / c2, c1 / c2; return Ebn; } /* ************************************************************************* */ Matrix PoseRTV::RRTMbn(const Rot3& att) { return PoseRTV::RRTMbn(att.rpy()); } /* ************************************************************************* */ Matrix PoseRTV::RRTMnb(const Vector& euler) { assert(euler.size() == 3); Matrix Enb(3,3); const double s1 = sin(euler(1-1)), c1 = cos(euler(1-1)); const double s2 = sin(euler(2-1)), c2 = cos(euler(2-1)); Enb << 1.0, 0.0, -s2, 0.0, c1, s1*c2, 0.0, -s1, c1*c2; return Enb; } /* ************************************************************************* */ Matrix PoseRTV::RRTMnb(const Rot3& att) { return PoseRTV::RRTMnb(att.rpy()); } /* ************************************************************************* */ } // \namespace gtsam