/** * @file SmartRangeFactor.h * * @brief A smart factor for range-only SLAM that does initialization and marginalization * * @date Sep 10, 2012 * @author Alex Cunningham */ #pragma once #include #include #include #include #include #include namespace gtsam { /** * Smart factor for range SLAM * @addtogroup SLAM */ class GTSAM_UNSTABLE_EXPORT SmartRangeFactor: public NoiseModelFactor { protected: struct Circle2 { Circle2(const Point2& p, double r) : center(p), radius(r) { } Point2 center; double radius; }; /// Range measurements std::vector measurements_; public: /** Default constructor: don't use directly */ SmartRangeFactor() { } /** standard binary constructor */ SmartRangeFactor(const SharedNoiseModel& model) { } virtual ~SmartRangeFactor() { } /// Add a range measurement to a pose with given key. void addRange(Key key, double measuredRange) { keys_.push_back(key); measurements_.push_back(measuredRange); } // Testable /** print */ virtual void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const { } /** Check if two factors are equal */ virtual bool equals(const NonlinearFactor& f, double tol = 1e-9) const { return false; } // factor interface /** * Triangulate a point from at least three pose-range pairs * Checks for best pair that includes first point */ static Point2 triangulate(const std::list& circles) { Circle2 circle1 = circles.front(); boost::optional best_fh; boost::optional best_circle; BOOST_FOREACH(const Circle2& it, circles) { // distance between circle centers. double d = circle1.center.dist(it.center); if (d < 1e-9) continue; boost::optional fh = Point2::CircleCircleIntersection( circle1.radius / d, it.radius / d); if (fh && (!best_fh || fh->y() > best_fh->y())) { best_fh = fh; best_circle = it; } } std::list solutions = Point2::CircleCircleIntersection( circle1.center, best_circle->center, best_fh); // TODO, pick winner based on other measurement return solutions.front(); } /** * Error function *without* the NoiseModel, \f$ z-h(x) \f$. */ virtual Vector unwhitenedError(const Values& x, boost::optional&> H = boost::none) const { size_t K = size(); Vector errors = zero(K); if (K >= 3) { std::list circles; for (size_t i = 0; i < K; i++) { const Pose2& pose = x.at(keys_[i]); circles.push_back(Circle2(pose.translation(), measurements_[i])); } Point2 optimizedPoint = triangulate(circles); if (H) *H = std::vector(); for (size_t i = 0; i < K; i++) { const Pose2& pose = x.at(keys_[i]); if (H) { Matrix Hi; errors[i] = pose.range(optimizedPoint, Hi) - measurements_[i]; H->push_back(Hi); } else errors[i] = pose.range(optimizedPoint) - measurements_[i]; } } return errors; } }; } // \namespace gtsam