Formula Student Autonomous Systems
The code for the main driverless system
Loading...
Searching...
No Matches
ekf_slam_solver.cpp
Go to the documentation of this file.
2
4 std::shared_ptr<DataAssociationModel> data_association,
5 std::shared_ptr<V2PMotionModel> motion_model,
6 std::shared_ptr<LandmarkFilter> landmark_filter,
7 std::shared_ptr<std::vector<double>> execution_times,
8 std::shared_ptr<LoopClosure> loop_closure)
9 : SLAMSolver(params, data_association, motion_model, landmark_filter, execution_times,
10 loop_closure),
11 slam_parameters_(params) {
12 this->covariance_ = Eigen::MatrixXd::Zero(3, 3);
13 this->covariance_(0, 0) = params.pose_x_initial_noise_;
14 this->covariance_(1, 1) = params.pose_y_initial_noise_;
15 this->covariance_(2, 2) = params.pose_theta_initial_noise_;
16 this->process_noise_matrix_ = Eigen::MatrixXd::Zero(3, 3);
17 this->process_noise_matrix_(0, 0) = params.velocity_x_noise_;
18 this->process_noise_matrix_(1, 1) = params.velocity_y_noise_;
20 this->observation_model_ = std::make_shared<SLAMObservationModel>();
21}
22
23Eigen::MatrixXd EKFSLAMSolver::get_observation_noise_matrix(int num_landmarks) const {
24 Eigen::MatrixXd observation_noise_matrix =
25 Eigen::MatrixXd::Zero(num_landmarks * 2, num_landmarks * 2);
26 for (int i = 0; i < num_landmarks; i++) {
27 observation_noise_matrix(2 * i, 2 * i) = this->slam_parameters_.observation_x_noise_;
28 observation_noise_matrix(2 * i + 1, 2 * i + 1) = this->slam_parameters_.observation_y_noise_;
29 }
30 return observation_noise_matrix;
31}
32
34 if (this->velocities_received_) {
35 {
36 std::unique_lock lock(this->mutex_);
38 velocities);
39 }
40 } else {
41 this->velocities_received_ = true;
42 }
43 std::unique_lock lock(this->mutex_);
44 this->last_update_ = velocities.timestamp_;
45}
46
47void EKFSLAMSolver::add_observations(const std::vector<common_lib::structures::Cone>& cones,
48 [[maybe_unused]] rclcpp::Time cones_time) {
49 int num_observations = static_cast<int>(cones.size());
50 std::vector<int> matched_landmarks_indices;
51 Eigen::VectorXd matched_observations(0);
52 Eigen::VectorXd observations(2 * num_observations);
53 Eigen::VectorXd observation_confidences(num_observations);
54 common_lib::conversions::cone_vector_to_eigen(cones, observations, observation_confidences);
55 Eigen::VectorXd state;
56 Eigen::MatrixXd covariance;
57 {
58 std::unique_lock lock(this->mutex_);
59 state = this->state_;
60 covariance = this->covariance_;
62 }
63
64 Eigen::VectorXd global_observations =
65 common_lib::maths::local_to_global_coordinates(state.segment(0, 3), observations);
66 Eigen::VectorXi associations = this->_data_association_->associate(
67 state.segment(3, state.size() - 3), global_observations,
68 covariance.block(3, 3, state.size() - 3, state.size() - 3), observation_confidences);
69
70 Eigen::VectorXd filtered_landmarks =
71 this->_landmark_filter_->filter(global_observations, observation_confidences, associations);
72 this->_landmark_filter_->delete_landmarks(filtered_landmarks);
73
74 for (int i = 0; i < num_observations; i++) {
75 if (associations(i) >= 0) {
76 // Existing landmark
77 matched_landmarks_indices.push_back(associations(i) + 3);
78 matched_observations.conservativeResize(matched_observations.size() + 2);
79 matched_observations(matched_observations.size() - 2) = observations(2 * i);
80 matched_observations(matched_observations.size() - 1) = observations(2 * i + 1);
81 }
82 }
83
84 if (!matched_landmarks_indices.empty()) {
85 this->correct(state, covariance, matched_landmarks_indices, matched_observations);
86 }
87 if (this->_mission_ != common_lib::competition_logic::Mission::NONE &&
89 (this->_mission_ == common_lib::competition_logic::Mission::SKIDPAD ||
90 this->_mission_ == common_lib::competition_logic::Mission::ACCELERATION)) &&
91 this->lap_counter_ == 0) {
92 this->state_augmentation(state, covariance, filtered_landmarks);
93 }
94 // Finally update the state
95 {
96 std::unique_lock lock(this->mutex_);
97 this->state_ = state;
98 this->state_.segment(0, 3) +=
99 this->pose_difference_; // Compensate for the difference accumulated during correction
100 this->pose_difference_ = Eigen::Vector3d::Zero();
101 this->correction_step_ongoing_ = false;
102 this->covariance_ = covariance;
103 }
104 LoopClosure::Result result = _loop_closure_->detect(
105 state.segment(0, 3), state.segment(3, state.size() - 3), associations, observations);
106 if (result.detected) {
107 lap_counter_++;
108 }
109}
110
111void EKFSLAMSolver::predict(Eigen::VectorXd& state, Eigen::MatrixXd& covariance,
112 const Eigen::MatrixXd& process_noise_matrix,
113 const rclcpp::Time last_update,
114 const common_lib::structures::Velocities& velocities) {
115 auto time_interval = velocities.timestamp_.seconds() - last_update.seconds();
116
117 Eigen::Vector3d previous_pose = state.segment(0, 3);
118 Eigen::Vector3d temp_velocities(velocities.velocity_x, velocities.velocity_y,
119 velocities.rotational_velocity);
120
121 Eigen::MatrixXd pose_jacobian =
122 this->_motion_model_->get_jacobian_pose(previous_pose, temp_velocities, time_interval);
123 Eigen::MatrixXd updated_covariance_block =
124 pose_jacobian * covariance.block(0, 0, 3, 3) * pose_jacobian.transpose() +
125 process_noise_matrix;
126
127 Eigen::Vector3d next_pose =
128 this->_motion_model_->get_next_pose(previous_pose, temp_velocities, time_interval);
129
130 // Already locked mutex
131 if (this->correction_step_ongoing_) {
132 this->pose_difference_ += (next_pose - previous_pose);
133 }
134 state.segment(0, 3) = next_pose;
135 covariance.block(0, 0, 3, 3) = updated_covariance_block;
136}
137
138void EKFSLAMSolver::correct(Eigen::VectorXd& state, Eigen::MatrixXd& covariance,
139 const std::vector<int>& matched_landmarks_indices,
140 const Eigen::VectorXd& matched_observations) {
141 Eigen::VectorXd predicted_observations =
142 this->observation_model_->observation_model(state, matched_landmarks_indices);
143 Eigen::MatrixXd jacobian =
144 this->observation_model_->observation_model_jacobian(state, matched_landmarks_indices);
145 Eigen::MatrixXd observation_noise_matrix =
146 get_observation_noise_matrix(matched_landmarks_indices.size());
147 Eigen::MatrixXd kalman_gain =
148 covariance * jacobian.transpose() *
149 (jacobian * covariance * jacobian.transpose() + observation_noise_matrix).inverse();
150 state += kalman_gain * (matched_observations - predicted_observations);
151 covariance =
152 (Eigen::MatrixXd::Identity(state.size(), state.size()) - kalman_gain * jacobian) * covariance;
153}
154
155void EKFSLAMSolver::state_augmentation(Eigen::VectorXd& state, Eigen::MatrixXd& covariance,
156 const Eigen::VectorXd& new_landmarks_coordinates) {
157 // Resize covariance matrix
158 int num_new_entries = static_cast<int>(new_landmarks_coordinates.size());
159 int covariance_size = static_cast<int>(covariance.rows());
160
161 covariance.conservativeResizeLike(
162 Eigen::MatrixXd::Zero(covariance_size + num_new_entries, covariance_size + num_new_entries));
163 for (int i = 0; i < num_new_entries / 2; i++) {
164 covariance(2 * i + covariance_size, 2 * i + covariance_size) =
166 covariance(2 * i + 1 + covariance_size, 2 * i + 1 + covariance_size) =
168 }
169 // Resize state vector
170 int original_state_size = static_cast<int>(state.size());
171 state.conservativeResizeLike(Eigen::VectorXd::Zero(original_state_size + num_new_entries));
172 state.segment(original_state_size, num_new_entries) = new_landmarks_coordinates;
173}
174
175void EKFSLAMSolver::load_initial_state(const Eigen::VectorXd& map, const Eigen::Vector3d& pose) {
176 if (map.size() % 2 != 0 || pose.size() != 3) {
177 throw std::runtime_error("Invalid map or pose size");
178 }
179 std::unique_lock lock(this->mutex_);
180 this->state_ = Eigen::VectorXd::Zero(3 + map.size());
181 this->state_.segment(0, 3) = pose;
182 this->state_.segment(3, map.size()) = map;
183 this->covariance_ = Eigen::MatrixXd::Identity(this->state_.size(), this->state_.size()) *
185 this->covariance_.block(0, 0, 3, 3) = Eigen::MatrixXd::Zero(3, 3);
189}
190
191std::vector<common_lib::structures::Cone> EKFSLAMSolver::get_map_estimate() {
192 std::vector<common_lib::structures::Cone> map;
193 Eigen::VectorXd state;
194 rclcpp::Time last_update;
195 {
196 std::unique_lock lock(this->mutex_);
197 state = this->state_;
198 last_update = this->last_update_;
199 }
200 for (int i = 3; i < state.size(); i += 2) {
201 map.push_back(
202 common_lib::structures::Cone(state(i), state(i + 1), "unknown", 1.0, last_update));
203 }
204 return map;
205}
206
208 std::unique_lock lock(this->mutex_);
209 return common_lib::structures::Pose(this->state_(0), this->state_(1), this->state_(2), 0, 0, 0,
210 this->last_update_);
211}
std::shared_mutex mutex_
Eigen::MatrixXd get_observation_noise_matrix(int num_landmarks) const
Get the observation noise matrix object used in the correction step of the EKF with the right dimensi...
EKFSLAMSolver(const SLAMParameters &params, std::shared_ptr< DataAssociationModel > data_association, std::shared_ptr< V2PMotionModel > motion_model, std::shared_ptr< LandmarkFilter > landmark_filter, std::shared_ptr< std::vector< double > > execution_times, std::shared_ptr< LoopClosure > loop_closure)
void add_velocities(const common_lib::structures::Velocities &velocities) override
Executed to deal with new velocity data.
void load_initial_state(const Eigen::VectorXd &map, const Eigen::Vector3d &pose) override
Initialize the EKF SLAM solver with a previously saved map and pose.
Eigen::MatrixXd covariance_
void correct(Eigen::VectorXd &state, Eigen::MatrixXd &covariance, const std::vector< int > &observed_landmarks_indices, const Eigen::VectorXd &matched_observations)
correction step of the EKF that updates the state and covariance based on the observed landmarks
common_lib::structures::Pose get_pose_estimate() override
Get the pose estimate object.
Eigen::Vector3d pose_difference_
std::shared_ptr< SLAMObservationModel > observation_model_
SLAMParameters slam_parameters_
rclcpp::Time last_update_
Eigen::VectorXd state_
void predict(Eigen::VectorXd &state, Eigen::MatrixXd &covariance, const Eigen::MatrixXd &process_noise_matrix, const rclcpp::Time last_update, const common_lib::structures::Velocities &velocities)
executed when velocity data is received.
void state_augmentation(Eigen::VectorXd &state, Eigen::MatrixXd &covariance, const Eigen::VectorXd &new_landmarks)
add new landmarks to the state vector and covariance matrix
std::vector< common_lib::structures::Cone > get_map_estimate() override
Get the map estimate object.
void add_observations(const std::vector< common_lib::structures::Cone > &positions, rclcpp::Time cones_timestamp) override
process obervations of landmarks
Eigen::MatrixXd process_noise_matrix_
Interface for SLAM solvers.
common_lib::competition_logic::Mission _mission_
std::shared_ptr< LoopClosure > _loop_closure_
SLAMParameters _params_
std::shared_ptr< V2PMotionModel > _motion_model_
std::shared_ptr< DataAssociationModel > _data_association_
std::shared_ptr< LandmarkFilter > _landmark_filter_
void cone_vector_to_eigen(const std::vector< common_lib::structures::Cone > &cones, Eigen::VectorXd &positions, Eigen::VectorXd &confidences)
Convert vector of the struct common_lib::structures::Cone to Eigen vectors.
Definition cones.cpp:3
Eigen::VectorXd local_to_global_coordinates(const Eigen::Vector3d &local_reference_frame, const Eigen::VectorXd &local_points)
Transform points from a local reference frame to a global reference frame.
Result of loop closure detection.
Parameters for the SLAM node.
double pose_x_initial_noise_
float angular_velocity_noise_
double pose_y_initial_noise_
double pose_theta_initial_noise_
double preloaded_map_noise_
Struct for pose representation.
Definition pose.hpp:13