Skip to content

Commit 14053a7

Browse files
Updated diff_drive odometry class to consistantly
use velocity where it claims to, and pass timestep in to every method that makes use of it. This was requested in ros-controls#271. - The result is that the class is very slightly less efficient (but still quite trivial) due to passing around a extra double. - The other result is that it is no longer possible for velocity based odometry updates calculated incorrectly when the timestep fails to keep up. To prevent position based updates from now suffering the inverse of the velocity timestep/dt mismatch, the measured delta is used exclusively for position based updates (retaining the existing behavior), while the configured timestep is used for open and closed loop velocity updates. I also added a 1s throttled warning if there is a major mismatch between measured and configured timestep (+/-20%) since this would cause a slight accumulation of integration errors on curved paths (I believe), depending on velocity, curvature and mismatch size. Similar requested period/measured period mismatches may be a nuisance for open loop (which is velocity based) and closed loop velocity based updates, but the impact is likely negligible (in this class).
1 parent 9b344c7 commit 14053a7

File tree

3 files changed

+45
-33
lines changed

3 files changed

+45
-33
lines changed

diff_drive_controller/include/diff_drive_controller/odometry.hpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ class Odometry
4040
explicit Odometry(size_t velocity_rolling_window_size = 10);
4141

4242
void init(const rclcpp::Time & time);
43-
bool update(double left_pos, double right_pos, const rclcpp::Time & time);
44-
bool updateFromVelocity(double left_vel, double right_vel, const rclcpp::Time & time);
45-
void updateOpenLoop(double linear, double angular, const rclcpp::Time & time);
43+
bool updateFromPosition(double left_pos, double right_pos, const rclcpp::Time & time, const double dt);
44+
bool updateFromVelocity(double left_vel, double right_vel, const rclcpp::Time & time, const double dt);
45+
void updateFromVelocityOpenLoop(double linear, double angular, const rclcpp::Time & time, const double dt);
4646
void resetOdometry();
4747

4848
double getX() const { return x_; }
@@ -62,8 +62,8 @@ class Odometry
6262
using RollingMeanAccumulator = rcppmath::RollingMeanAccumulator<double>;
6363
#endif
6464

65-
void integrateRungeKutta2(double linear, double angular);
66-
void integrateExact(double linear, double angular);
65+
void integrateRungeKutta2(double linear, double angular, const double dt);
66+
void integrateExact(double linear, double angular, const double dt);
6767
void resetAccumulators();
6868

6969
// Current timestamp:

diff_drive_controller/src/diff_drive_controller.cpp

+20-4
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,10 @@ controller_interface::return_type DiffDriveController::update(
141141
const double left_wheel_radius = params_.left_wheel_radius_multiplier * params_.wheel_radius;
142142
const double right_wheel_radius = params_.right_wheel_radius_multiplier * params_.wheel_radius;
143143

144+
144145
if (params_.open_loop)
145146
{
146-
odometry_.updateOpenLoop(linear_command, angular_command, time);
147+
odometry_.updateFromVelocityOpenLoop(linear_command, angular_command, time, period.seconds());
147148
}
148149
else
149150
{
@@ -171,13 +172,28 @@ controller_interface::return_type DiffDriveController::update(
171172

172173
if (params_.position_feedback)
173174
{
174-
odometry_.update(left_feedback_mean, right_feedback_mean, time);
175+
// Warn if actual timestep doesn't match period,
176+
// since
177+
const double measured_period = (
178+
get_node()->get_clock()->now() - previous_update_timestamp_).seconds();
179+
180+
// +/- 20% of nominal loop time as a starting point to warn wheel encoder
181+
// users that the real update rate doesn't match the requested update period
182+
if ( std::fabs(period.seconds()-measured_period) > period.seconds()/5.0 ) {
183+
RCLCPP_WARN_THROTTLE(
184+
get_node()->get_logger(), *(get_node()->get_clock()), 1000,
185+
"Integrating odometry at measured delta of %.3fs, but desired update period is %.3fs",
186+
measured_period, period.seconds());
187+
}
188+
189+
// update odometry
190+
odometry_.updateFromPosition(left_feedback_mean, right_feedback_mean, time, measured_period);
175191
}
176192
else
177193
{
178194
odometry_.updateFromVelocity(
179-
left_feedback_mean * left_wheel_radius * period.seconds(),
180-
right_feedback_mean * right_wheel_radius * period.seconds(), time);
195+
left_feedback_mean * left_wheel_radius,
196+
right_feedback_mean * right_wheel_radius, time, period.seconds());
181197
}
182198
}
183199

diff_drive_controller/src/odometry.cpp

+20-24
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,9 @@ void Odometry::init(const rclcpp::Time & time)
4545
timestamp_ = time;
4646
}
4747

48-
bool Odometry::update(double left_pos, double right_pos, const rclcpp::Time & time)
48+
bool Odometry::updateFromPosition(double left_pos, double right_pos, const rclcpp::Time & time, const double dt)
4949
{
5050
// We cannot estimate the speed with very small time intervals:
51-
const double dt = time.seconds() - timestamp_.seconds();
5251
if (dt < 0.0001)
5352
{
5453
return false; // Interval too small to integrate with
@@ -59,52 +58,49 @@ bool Odometry::update(double left_pos, double right_pos, const rclcpp::Time & ti
5958
const double right_wheel_cur_pos = right_pos * right_wheel_radius_;
6059

6160
// Estimate velocity of wheels using old and current position:
62-
const double left_wheel_est_vel = left_wheel_cur_pos - left_wheel_old_pos_;
63-
const double right_wheel_est_vel = right_wheel_cur_pos - right_wheel_old_pos_;
61+
const double left_wheel_est_vel = (left_wheel_cur_pos - left_wheel_old_pos_)/dt;
62+
const double right_wheel_est_vel = (right_wheel_cur_pos - right_wheel_old_pos_)/dt;
6463

6564
// Update old position with current:
6665
left_wheel_old_pos_ = left_wheel_cur_pos;
6766
right_wheel_old_pos_ = right_wheel_cur_pos;
6867

69-
updateFromVelocity(left_wheel_est_vel, right_wheel_est_vel, time);
68+
updateFromVelocity(left_wheel_est_vel, right_wheel_est_vel, time, dt);
7069

7170
return true;
7271
}
7372

74-
bool Odometry::updateFromVelocity(double left_vel, double right_vel, const rclcpp::Time & time)
73+
bool Odometry::updateFromVelocity(double left_vel, double right_vel, const rclcpp::Time & time, const double dt)
7574
{
76-
const double dt = time.seconds() - timestamp_.seconds();
77-
7875
// Compute linear and angular diff:
7976
const double linear = (left_vel + right_vel) * 0.5;
8077
// Now there is a bug about scout angular velocity
8178
const double angular = (right_vel - left_vel) / wheel_separation_;
8279

8380
// Integrate odometry:
84-
integrateExact(linear, angular);
81+
integrateExact(linear, angular, dt);
8582

8683
timestamp_ = time;
8784

8885
// Estimate speeds using a rolling mean to filter them out:
89-
linear_accumulator_.accumulate(linear / dt);
90-
angular_accumulator_.accumulate(angular / dt);
86+
linear_accumulator_.accumulate(linear);
87+
angular_accumulator_.accumulate(angular);
9188

9289
linear_ = linear_accumulator_.getRollingMean();
9390
angular_ = angular_accumulator_.getRollingMean();
9491

9592
return true;
9693
}
9794

98-
void Odometry::updateOpenLoop(double linear, double angular, const rclcpp::Time & time)
95+
void Odometry::updateFromVelocityOpenLoop(double linear, double angular, const rclcpp::Time & time, const double dt)
9996
{
10097
/// Save last linear and angular velocity:
10198
linear_ = linear;
10299
angular_ = angular;
103100

104101
/// Integrate odometry:
105-
const double dt = time.seconds() - timestamp_.seconds();
106102
timestamp_ = time;
107-
integrateExact(linear * dt, angular * dt);
103+
integrateExact(linear, angular, dt);
108104
}
109105

110106
void Odometry::resetOdometry()
@@ -129,28 +125,28 @@ void Odometry::setVelocityRollingWindowSize(size_t velocity_rolling_window_size)
129125
resetAccumulators();
130126
}
131127

132-
void Odometry::integrateRungeKutta2(double linear, double angular)
128+
void Odometry::integrateRungeKutta2(double linear, double angular, const double dt)
133129
{
134-
const double direction = heading_ + angular * 0.5;
130+
const double direction = heading_ + angular * 0.5 * dt;
135131

136132
/// Runge-Kutta 2nd order integration:
137-
x_ += linear * cos(direction);
138-
y_ += linear * sin(direction);
139-
heading_ += angular;
133+
x_ += linear * cos(direction) * dt;
134+
y_ += linear * sin(direction) * dt;
135+
heading_ += angular * dt;
140136
}
141137

142-
void Odometry::integrateExact(double linear, double angular)
138+
void Odometry::integrateExact(double linear, double angular, const double dt)
143139
{
144-
if (fabs(angular) < 1e-6)
140+
if (fabs(angular*dt) < 1e-6)
145141
{
146-
integrateRungeKutta2(linear, angular);
142+
integrateRungeKutta2(linear, angular, dt);
147143
}
148144
else
149145
{
150146
/// Exact integration (should solve problems when angular is zero):
151147
const double heading_old = heading_;
152-
const double r = linear / angular;
153-
heading_ += angular;
148+
const double r = linear / angular * dt;
149+
heading_ += angular * dt;
154150
x_ += r * (sin(heading_) - sin(heading_old));
155151
y_ += -r * (cos(heading_) - cos(heading_old));
156152
}

0 commit comments

Comments
 (0)