3#include <gtsam/base/Vector.h>
4#include <gtsam/geometry/Point2.h>
5#include <gtsam/geometry/Pose2.h>
6#include <gtsam/geometry/Rot2.h>
7#include <gtsam/inference/Symbol.h>
8#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
9#include <gtsam/nonlinear/Marginals.h>
10#include <gtsam/nonlinear/Values.h>
11#include <gtsam/sam/BearingFactor.h>
12#include <gtsam/sam/BearingRangeFactor.h>
13#include <gtsam/sam/RangeFactor.h>
14#include <gtsam/slam/BetweenFactor.h>
15#include <gtsam/slam/PriorFactor.h>
16#include <gtsam/slam/ProjectionFactor.h>
36 const gtsam::Pose2 current_pose =
38 state(0) = current_pose.x();
39 state(1) = current_pose.y();
40 state(2) = current_pose.theta();
41 unsigned int landmark_id = 3;
44 if (gtsam::Symbol(it->key).chr() ==
'l') {
45 gtsam::Point2 landmark = it->value.cast<gtsam::Point2>();
46 state(landmark_id++) = landmark.x();
47 state(landmark_id++) = landmark.y();
79 std::shared_ptr<BaseOptimizer> optimizer)
80 : _params_(params), _optimizer_(optimizer), _pose_timestamps_(params.max_pose_history_graph) {
83 const gtsam::Pose2 prior_pose(0.0, 0.0,
87 const gtsam::noiseModel::Diagonal::shared_ptr prior_noise =
88 gtsam::noiseModel::Diagonal::Sigmas(gtsam::Vector3(0.0, 0.0, 0.0));
89 _factor_graph_.add(gtsam::PriorFactor<gtsam::Pose2>(pose_symbol, prior_pose, prior_noise));
90 if (
const auto optimizer_ptr = std::dynamic_pointer_cast<ISAM2Optimizer>(this->
_optimizer_)) {
91 optimizer_ptr->_new_factors_.add(
92 gtsam::PriorFactor<gtsam::Pose2>(pose_symbol, prior_pose, prior_noise));
93 optimizer_ptr->_new_values_.insert(pose_symbol, prior_pose);
117 if (
this == &other)
return *
this;
135 const Eigen::Vector3d& noise_vector,
136 const Eigen::Vector3d& new_pose,
137 const rclcpp::Time& pose_timestamp) {
138 RCLCPP_DEBUG(rclcpp::get_logger(
"slam"),
"GraphSLAMInstance - Processing new pose %lf %lf %lf",
139 new_pose(0), new_pose(1), new_pose(2));
152 if (
const auto optimizer_ptr = std::dynamic_pointer_cast<ISAM2Optimizer>(this->
_optimizer_)) {
153 optimizer_ptr->_new_values_.insert(new_pose_symbol, new_pose_gtsam);
156 RCLCPP_DEBUG(rclcpp::get_logger(
"slam"),
157 "GraphSLAMInstance - Adding prior pose %lf %lf %lf with noise %lf %lf %lf and "
158 "pose difference %lf %lf %lf",
159 new_pose_gtsam.x(), new_pose_gtsam.y(), new_pose_gtsam.theta(), noise_vector(0),
160 noise_vector(1), noise_vector(2), pose_difference(0), pose_difference(1),
167 const Eigen::Vector3d& noise_vector,
168 unsigned int before_pose_id,
169 unsigned int after_pose_id) {
170 gtsam::Symbol before_pose_symbol(
'x', before_pose_id);
171 gtsam::Symbol after_pose_symbol(
'x', after_pose_id);
172 gtsam::noiseModel::Diagonal::shared_ptr motion_noise = gtsam::noiseModel::Diagonal::Sigmas(
173 gtsam::Vector3(noise_vector(0), noise_vector(1), noise_vector(2)));
175 this->
_factor_graph_.add(gtsam::BetweenFactor<gtsam::Pose2>(before_pose_symbol, after_pose_symbol,
176 pose_difference_gtsam, motion_noise));
177 if (
const auto optimizer_ptr = std::dynamic_pointer_cast<ISAM2Optimizer>(this->
_optimizer_)) {
178 optimizer_ptr->_new_factors_.add(gtsam::BetweenFactor<gtsam::Pose2>(
179 before_pose_symbol, after_pose_symbol, pose_difference_gtsam, motion_noise));
184 const Eigen::Vector3d& noise_vector,
185 unsigned int before_pose_id) {
191 const Eigen::Vector3d& noise_vector) {
197 Eigen::VectorXd& observations = *(observation_data.
observations_);
198 Eigen::VectorXi& associations = *(observation_data.
associations_);
201 unsigned int pose_id_at_observation_time =
203 RCLCPP_INFO(rclcpp::get_logger(
"slam"),
"locked_landmarks= %s",
205 for (
unsigned int i = 0; i < observations.size() / 2; i++) {
206 gtsam::Point2 landmark;
207 gtsam::Symbol landmark_symbol;
208 bool landmark_lost_in_optimization =
210 if (associations(i) == -2) {
213 }
else if ((associations(i) == -1 || landmark_lost_in_optimization) &&
217 landmark = gtsam::Point2(observations_global(i * 2), observations_global(i * 2 + 1));
219 if (
const auto optimizer_ptr = std::dynamic_pointer_cast<ISAM2Optimizer>(this->
_optimizer_)) {
220 optimizer_ptr->_new_values_.insert(
221 landmark_symbol, landmark);
223 }
else if (associations(i) >= 0) {
228 landmark_symbol = gtsam::Symbol(
'l', (associations(i)) / 2);
233 const Eigen::Vector2d observation_cartesian(observations[i * 2], observations[i * 2 + 1]);
235 observation_cartesian);
236 const gtsam::Rot2 observation_rotation(observation_cylindrical(1));
238 const gtsam::noiseModel::Diagonal::shared_ptr observation_noise =
240 this->_params_.observation_x_noise_));
242 if (
const auto optimizer_ptr = std::dynamic_pointer_cast<ISAM2Optimizer>(this->
_optimizer_)) {
243 optimizer_ptr->_new_factors_.add(gtsam::BearingRangeFactor<gtsam::Pose2, gtsam::Point2>(
244 gtsam::Symbol(
'x', pose_id_at_observation_time), landmark_symbol, observation_rotation,
245 observation_cylindrical(0),
249 this->
_factor_graph_.add(gtsam::BearingRangeFactor<gtsam::Pose2, gtsam::Point2>(
250 gtsam::Symbol(
'x', pose_id_at_observation_time), landmark_symbol, observation_rotation,
251 observation_cylindrical(0), observation_noise));
258 double preloaded_map_noise) {
259 RCLCPP_INFO(rclcpp::get_logger(
"slam"),
"GraphSLAMInstance - Loading map ");
263 const gtsam::Pose2 prior_pose(pose(0), pose(1), pose(2));
265 const gtsam::noiseModel::Diagonal::shared_ptr prior_noise =
266 gtsam::noiseModel::Diagonal::Sigmas(gtsam::Vector3(0.0, 0.0, 0.0));
267 _factor_graph_.add(gtsam::PriorFactor<gtsam::Pose2>(pose_symbol, prior_pose, prior_noise));
274 unsigned int num_landmarks = map.size() / 2;
275 for (
unsigned int i = 0; i < num_landmarks; i++) {
276 gtsam::Point2 landmark(map(i * 2), map(i * 2 + 1));
279 const gtsam::noiseModel::Diagonal::shared_ptr landmark_noise =
280 gtsam::noiseModel::Diagonal::Sigmas(
281 gtsam::Vector2(preloaded_map_noise, preloaded_map_noise));
283 gtsam::PriorFactor<gtsam::Point2>(landmark_symbol, landmark, landmark_noise));
288 RCLCPP_DEBUG(rclcpp::get_logger(
"slam"),
289 "GraphSLAMInstance - Optimizing graph with %ld factors and %ld values",
300 gtsam::Symbol l(
'l', i);
303 RCLCPP_INFO(rclcpp::get_logger(
"slam"),
304 "GraphSLAMInstance - Landmark %d does not exist, skipping lock", i);
308 auto noise = gtsam::noiseModel::Constrained::All(2);
311 _factor_graph_.add(gtsam::PriorFactor<gtsam::Point2>(l, value, noise));
318 throw std::runtime_error(
"No poses in buffer");
324 if (curr.
timestamp.seconds() <= timestamp.seconds()) {
325 if (i == 0)
return curr.
pose_id;
328 auto diff_curr = timestamp.seconds() - curr.
timestamp.seconds();
329 auto diff_next = next.
timestamp.seconds() - timestamp.seconds();
Graph SLAM instance class - class to hold the factor graph and the values.
unsigned int get_landmark_id_at_time(const rclcpp::Time ×tamp) const
const gtsam::Values & get_graph_values_reference() const
Get the graph values reference.
gtsam::Values _graph_values_
unsigned int get_landmark_counter() const
Get the number of landmarks.
GraphSLAMInstance & operator=(const GraphSLAMInstance &other)
bool _new_observation_factors_
Eigen::VectorXd get_state_vector() const
Get the state vector, EKF style.
unsigned int get_pose_counter() const
Get the number of poses.
void lock_landmarks()
Soft locks the positions of the landmarks, can be used after the first lap.
bool new_pose_factors() const
Checks if new observation factors should be added.
unsigned int _landmark_counter_
void load_initial_state(const Eigen::VectorXd &map, const Eigen::Vector3d &pose, double preloaded_map_noise)
Loads a map and an initial pose into the graph.
gtsam::NonlinearFactorGraph _factor_graph_
void process_observations(const ObservationData &observation_data)
Add observation factors to the graph.
void process_pose_difference(const Eigen::Vector3d &pose_difference, const Eigen::Vector3d &noise_vector, unsigned int before_pose_id, unsigned int after_pose_id)
Process a pose difference and add the respective factor to the graph.
void process_new_pose(const Eigen::Vector3d &pose_difference, const Eigen::Vector3d &noise_vector, const Eigen::Vector3d &new_pose, const rclcpp::Time &pose_timestamp)
Process a new pose and respective pose difference.
std::shared_ptr< BaseOptimizer > _optimizer_
void optimize()
Runs optimization on the graph.
const gtsam::Pose2 get_pose() const
Get the current pose estimate.
unsigned int _pose_counter_
CircularBuffer< TimedPose > _pose_timestamps_
GraphSLAMInstance()=default
bool new_observation_factors() const
Checks if it is worth running optimization.
Eigen::MatrixXd get_covariance_matrix() const
Get the covariance matrix of the graph.
Eigen::Vector2d cartesian_to_cylindrical(const Eigen::Vector2d &cartesian)
Function to convert cartesian coordinates to cylindrical coordinates.
gtsam::Pose2 eigen_to_gtsam_pose(const Eigen::Vector3d &pose)
Struct containing observation data.
std::shared_ptr< Eigen::VectorXi > associations_
std::shared_ptr< Eigen::VectorXd > observations_
std::shared_ptr< Eigen::VectorXd > observations_global_
Parameters for the SLAM node.
float observation_y_noise_