Skip to content

Commit 3d702ca

Browse files
authored
UKF update (#751)
* Pulling UKF changes from Noetic * Adding a reset service to make tests more deterministic
1 parent 546eb70 commit 3d702ca

File tree

10 files changed

+319
-228
lines changed

10 files changed

+319
-228
lines changed

include/robot_localization/filter_utilities.hpp

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,24 +59,6 @@ namespace robot_localization
5959
namespace filter_utilities
6060
{
6161

62-
/**
63-
* @brief Utility method keeping RPY angles in the range [-pi, pi]
64-
* @param[in] rotation - The rotation to bind
65-
* @return the bounded value
66-
*/
67-
inline double clampRotation(double rotation)
68-
{
69-
while (rotation > PI) {
70-
rotation -= TAU;
71-
}
72-
73-
while (rotation < -PI) {
74-
rotation += TAU;
75-
}
76-
77-
return rotation;
78-
}
79-
8062
/**
8163
* @brief Utility method for appending tf2 prefixes cleanly
8264
* @param[in] tf_prefix - the tf2 prefix to append

include/robot_localization/ros_filter.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,13 @@ class RosFilter : public rclcpp::Node
279279
//!
280280
void initialize();
281281

282+
//! @brief Service callback for resetting the filter to its initial state. Parameters are unused.
283+
//!
284+
void resetSrvCallback(
285+
const std::shared_ptr<rmw_request_id_t>,
286+
const std::shared_ptr<std_srvs::srv::Empty::Request>,
287+
const std::shared_ptr<std_srvs::srv::Empty::Response>);
288+
282289
//! @brief Callback method for manually setting/resetting the internal pose
283290
//! estimate
284291
//! @param[in] msg - The ROS stamped pose with covariance message to take in
@@ -553,6 +560,10 @@ class RosFilter : public rclcpp::Node
553560
//!
554561
rclcpp::Duration history_length_;
555562

563+
//! @brief The sensor timeout value that gets passed to the core filter
564+
//!
565+
rclcpp::Duration sensor_timeout_;
566+
556567
//! @brief tf frame name for the robot's body frame
557568
//!
558569
std::string base_link_frame_id_;
@@ -586,6 +597,14 @@ class RosFilter : public rclcpp::Node
586597
//!
587598
Eigen::VectorXd latest_control_;
588599

600+
//! @brief The process noise covariance matrix that gets passed to the core filter
601+
//!
602+
Eigen::MatrixXd process_noise_covariance_;
603+
604+
//! @brief The initial estimate error covariance matrix that gets passed to the core filter
605+
//!
606+
Eigen::MatrixXd initial_estimate_error_covariance_;
607+
589608
//! @brief Message that contains our latest transform (i.e., state)
590609
//!
591610
//! We use the vehicle's latest state in a number of places, and often
@@ -745,6 +764,10 @@ class RosFilter : public rclcpp::Node
745764
//!
746765
rclcpp::Service<std_srvs::srv::Empty>::SharedPtr enable_filter_srv_;
747766

767+
//! @brief Service that resets the filter to its initial state
768+
//!
769+
rclcpp::Service<std_srvs::srv::Empty>::SharedPtr reset_srv_;
770+
748771
//! @brief Transform buffer for managing coordinate transforms
749772
//!
750773
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;

include/robot_localization/ukf.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,18 @@ class Ukf : public FilterBase
9393
const rclcpp::Duration & delta) override;
9494

9595
protected:
96+
/**
97+
* @brief Computes the weighted covariance and sigma points
98+
*/
99+
void generateSigmaPoints();
100+
101+
/**
102+
* @brief Carries out the predict step for the posteriori state of a sigma point
103+
* @param[in,out] sigma_point - The sigma point (state vector) to project
104+
* @param[in] delta - The time step over which to project
105+
*/
106+
void projectSigmaPoint(Eigen::VectorXd & sigma_point, const rclcpp::Duration & delta);
107+
96108
/**
97109
* @brief The UKF sigma points
98110
*

src/filter_base.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#include <robot_localization/filter_common.hpp>
3636
#include <robot_localization/filter_utilities.hpp>
3737

38+
#include <angles/angles.h>
39+
3840
#include <iomanip>
3941
#include <iostream>
4042
#include <limits>
@@ -420,12 +422,9 @@ inline double FilterBase::computeControlAcceleration(
420422

421423
void FilterBase::wrapStateAngles()
422424
{
423-
state_(StateMemberRoll) =
424-
filter_utilities::clampRotation(state_(StateMemberRoll));
425-
state_(StateMemberPitch) =
426-
filter_utilities::clampRotation(state_(StateMemberPitch));
427-
state_(StateMemberYaw) =
428-
filter_utilities::clampRotation(state_(StateMemberYaw));
425+
state_(StateMemberRoll) = angles::normalize_angle(state_(StateMemberRoll));
426+
state_(StateMemberPitch) = angles::normalize_angle(state_(StateMemberPitch));
427+
state_(StateMemberYaw) = angles::normalize_angle(state_(StateMemberYaw));
429428
}
430429

431430
bool FilterBase::checkMahalanobisThreshold(

src/filter_utilities.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ std::ostream & operator<<(std::ostream & os, const std::vector<int> & vec)
9292
os << "[";
9393
for (size_t dim = 0; dim < vec.size(); ++dim) {
9494
os << std::setiosflags(std::ios::left) << std::setw(3) <<
95-
(vec[dim] ? "t" : "f");
95+
(vec[dim] ? "t" : "f");
9696
}
9797
os << "]\n";
9898

src/navsat_transform.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <robot_localization/navsat_transform.hpp>
3737
#include <robot_localization/ros_filter_utilities.hpp>
3838

39+
#include <angles/angles.h>
3940
#include <GeographicLib/Geocentric.hpp>
4041
#include <GeographicLib/LocalCartesian.hpp>
4142
#include <nav_msgs/msg/odometry.hpp>
@@ -676,9 +677,9 @@ void NavSatTransform::imuCallback(const sensor_msgs::msg::Imu::SharedPtr msg)
676677
// Apply the offset (making sure to bound them), and throw them in a
677678
// vector
678679
tf2::Vector3 rpy_angles(
679-
filter_utilities::clampRotation(roll - roll_offset),
680-
filter_utilities::clampRotation(pitch - pitch_offset),
681-
filter_utilities::clampRotation(yaw - yaw_offset));
680+
angles::normalize_angle(roll - roll_offset),
681+
angles::normalize_angle(pitch - pitch_offset),
682+
angles::normalize_angle(yaw - yaw_offset));
682683

683684
// Now we need to rotate the roll and pitch by the yaw offset value.
684685
// Imagine a case where an IMU is mounted facing sideways. In that case

src/ros_filter.cpp

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <robot_localization/ros_filter_utilities.hpp>
3737
#include <robot_localization/ukf.hpp>
3838

39+
#include <angles/angles.h>
3940
#include <geometry_msgs/msg/transform_stamped.hpp>
4041
#include <rcl/time.h>
4142
#include <rclcpp/qos.hpp>
@@ -75,7 +76,10 @@ RosFilter<T>::RosFilter(const rclcpp::NodeOptions & options)
7576
frequency_(30.0),
7677
gravitational_acceleration_(9.80665),
7778
history_length_(0ns),
79+
sensor_timeout_(0ns),
7880
latest_control_(),
81+
process_noise_covariance_(STATE_SIZE, STATE_SIZE),
82+
initial_estimate_error_covariance_(STATE_SIZE, STATE_SIZE),
7983
last_diag_time_(0, 0, RCL_ROS_TIME),
8084
last_published_stamp_(0, 0, RCL_ROS_TIME),
8185
predict_to_current_time_(false),
@@ -149,6 +153,22 @@ void RosFilter<T>::reset()
149153

150154
// reset filter to uninitialized state
151155
filter_.reset();
156+
157+
// Restore filter parameters that we got from the ROS parameter server
158+
filter_.setSensorTimeout(sensor_timeout_);
159+
filter_.setProcessNoiseCovariance(process_noise_covariance_);
160+
filter_.setEstimateErrorCovariance(initial_estimate_error_covariance_);
161+
}
162+
163+
template<typename T>
164+
void RosFilter<T>::resetSrvCallback(
165+
const std::shared_ptr<rmw_request_id_t>,
166+
const std::shared_ptr<std_srvs::srv::Empty::Request>,
167+
const std::shared_ptr<std_srvs::srv::Empty::Response>)
168+
{
169+
RCLCPP_INFO(this->get_logger(), "Received a request to reset filter.");
170+
171+
reset();
152172
}
153173

154174
template<typename T>
@@ -852,8 +872,9 @@ void RosFilter<T>::loadParams()
852872

853873
predict_to_current_time_ = this->declare_parameter<bool>("predict_to_current_time", false);
854874

855-
double sensor_timeout = this->declare_parameter("sensor_timeout", 1.0 / frequency_);
856-
filter_.setSensorTimeout(rclcpp::Duration::from_seconds(sensor_timeout));
875+
sensor_timeout_ =
876+
rclcpp::Duration::from_seconds(this->declare_parameter("sensor_timeout", 1.0 / frequency_));
877+
filter_.setSensorTimeout(sensor_timeout_);
857878

858879
// Determine if we're in 2D mode
859880
two_d_mode_ = this->declare_parameter("two_d_mode", false);
@@ -879,7 +900,7 @@ void RosFilter<T>::loadParams()
879900
reset_on_time_jump_ = this->declare_parameter("reset_on_time_jump", false);
880901

881902
// Determine if we're using a control term
882-
double control_timeout = sensor_timeout;
903+
double control_timeout = sensor_timeout_.seconds();
883904
std::vector<bool> control_update_vector;
884905
std::vector<double> acceleration_limits;
885906
std::vector<double> acceleration_gains;
@@ -1049,6 +1070,13 @@ void RosFilter<T>::loadParams()
10491070
&RosFilter::enableFilterSrvCallback, this,
10501071
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
10511072

1073+
// Create a service for manually setting/resetting pose
1074+
reset_srv_ =
1075+
this->create_service<std_srvs::srv::Empty>(
1076+
"reset", std::bind(
1077+
&RosFilter<T>::resetSrvCallback, this,
1078+
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
1079+
10521080
// Create a service for toggling processing new measurements while still
10531081
// publishing
10541082
toggle_filter_processing_srv_ =
@@ -1205,7 +1233,7 @@ void RosFilter<T>::loadParams()
12051233
"Subscribed to " <<
12061234
odom_topic << " (" << odom_topic_name << ")\n\t" <<
12071235
odom_topic_name << "_differential is " <<
1208-
(differential ? "true" : "false") << "\n\t" << odom_topic_name <<
1236+
(differential ? "true" : "false") << "\n\t" << odom_topic_name <<
12091237
"_pose_rejection_threshold is " << pose_mahalanobis_thresh <<
12101238
"\n\t" << odom_topic_name << "_twist_rejection_threshold is " <<
12111239
twist_mahalanobis_thresh << "\n\t" << odom_topic_name <<
@@ -1325,7 +1353,7 @@ void RosFilter<T>::loadParams()
13251353
"Subscribed to " <<
13261354
pose_topic << " (" << pose_topic_name << ")\n\t" <<
13271355
pose_topic_name << "_differential is " <<
1328-
(differential ? "true" : "false") << "\n\t" << pose_topic_name <<
1356+
(differential ? "true" : "false") << "\n\t" << pose_topic_name <<
13291357
"_rejection_threshold is " << pose_mahalanobis_thresh <<
13301358
"\n\t" << pose_topic_name << " update vector is " <<
13311359
pose_update_vec);
@@ -1631,14 +1659,14 @@ void RosFilter<T>::loadParams()
16311659
"Subscribed to " <<
16321660
imu_topic << " (" << imu_topic_name << ")\n\t" <<
16331661
imu_topic_name << "_differential is " <<
1634-
(differential ? "true" : "false") << "\n\t" << imu_topic_name <<
1662+
(differential ? "true" : "false") << "\n\t" << imu_topic_name <<
16351663
"_pose_rejection_threshold is " << pose_mahalanobis_thresh <<
16361664
"\n\t" << imu_topic_name << "_twist_rejection_threshold is " <<
16371665
twist_mahalanobis_thresh << "\n\t" << imu_topic_name <<
16381666
"_linear_acceleration_rejection_threshold is " <<
16391667
accel_mahalanobis_thresh << "\n\t" << imu_topic_name <<
16401668
"_remove_gravitational_acceleration is " <<
1641-
(remove_grav_acc ? "true" : "false") << "\n\t" <<
1669+
(remove_grav_acc ? "true" : "false") << "\n\t" <<
16421670
imu_topic_name << " pose update vector is " << pose_update_vec <<
16431671
"\t" << imu_topic_name << " twist update vector is " <<
16441672
twist_update_vec << "\t" << imu_topic_name <<
@@ -1726,8 +1754,7 @@ void RosFilter<T>::loadParams()
17261754

17271755
// Load up the process noise covariance (from the launch file/parameter
17281756
// server)
1729-
Eigen::MatrixXd process_noise_covariance(STATE_SIZE, STATE_SIZE);
1730-
process_noise_covariance.setZero();
1757+
process_noise_covariance_.setZero();
17311758
std::vector<double> process_noise_covar_flat;
17321759

17331760
this->declare_parameter("process_noise_covariance", rclcpp::PARAMETER_DOUBLE_ARRAY);
@@ -1739,22 +1766,21 @@ void RosFilter<T>::loadParams()
17391766

17401767
for (int i = 0; i < STATE_SIZE; i++) {
17411768
for (int j = 0; j < STATE_SIZE; j++) {
1742-
process_noise_covariance(i, j) =
1769+
process_noise_covariance_(i, j) =
17431770
process_noise_covar_flat[i * STATE_SIZE + j];
17441771
}
17451772
}
17461773

17471774
RF_DEBUG(
17481775
"Process noise covariance is:\n" <<
1749-
process_noise_covariance << "\n");
1776+
process_noise_covariance_ << "\n");
17501777

1751-
filter_.setProcessNoiseCovariance(process_noise_covariance);
1778+
filter_.setProcessNoiseCovariance(process_noise_covariance_);
17521779
}
17531780

17541781
// Load up the process noise covariance (from the launch file/parameter
17551782
// server)
1756-
Eigen::MatrixXd initial_estimate_error_covariance(STATE_SIZE, STATE_SIZE);
1757-
initial_estimate_error_covariance.setZero();
1783+
initial_estimate_error_covariance_.setZero();
17581784
std::vector<double> estimate_error_covar_flat;
17591785

17601786
this->declare_parameter("initial_estimate_covariance", rclcpp::PARAMETER_DOUBLE_ARRAY);
@@ -1766,16 +1792,16 @@ void RosFilter<T>::loadParams()
17661792

17671793
for (int i = 0; i < STATE_SIZE; i++) {
17681794
for (int j = 0; j < STATE_SIZE; j++) {
1769-
initial_estimate_error_covariance(i, j) =
1795+
initial_estimate_error_covariance_(i, j) =
17701796
estimate_error_covar_flat[i * STATE_SIZE + j];
17711797
}
17721798
}
17731799

17741800
RF_DEBUG(
17751801
"Initial estimate error covariance is:\n" <<
1776-
estimate_error_covar_flat << "\n");
1802+
initial_estimate_error_covariance_ << "\n");
17771803

1778-
filter_.setEstimateErrorCovariance(initial_estimate_error_covariance);
1804+
filter_.setEstimateErrorCovariance(initial_estimate_error_covariance_);
17791805
}
17801806
}
17811807

@@ -2928,9 +2954,9 @@ bool RosFilter<T>::preparePose(
29282954
// 6b. Apply the offset (making sure to bound them), and throw them in a
29292955
// vector
29302956
tf2::Vector3 rpy_angles(
2931-
filter_utilities::clampRotation(roll - rollOffset),
2932-
filter_utilities::clampRotation(pitch - pitchOffset),
2933-
filter_utilities::clampRotation(yaw - yaw_offset));
2957+
angles::normalize_angle(roll - rollOffset),
2958+
angles::normalize_angle(pitch - pitchOffset),
2959+
angles::normalize_angle(yaw - yaw_offset));
29342960

29352961
// 6c. Now we need to rotate the roll and pitch by the yaw offset value.
29362962
// Imagine a case where an IMU is mounted facing sideways. In that case

0 commit comments

Comments
 (0)