Skip to content

Commit e8f988e

Browse files
committed
fix odometry plugin
1 parent 172d02a commit e8f988e

2 files changed

Lines changed: 51 additions & 6 deletions

File tree

Industrial/gz_noisy_odometry/include/gz_noisy_odometry/NoisyOdometryPlugin.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ namespace custom_plugins
8787
// physically blocked (wall contact). Models wheel slip against the obstacle.
8888
// Typical range: 0.01 – 0.05.
8989
double slip_factor_{0.02};
90+
91+
// Ratio of actual/commanded motion below which a robot is considered blocked.
92+
// Avoids the fixed absolute threshold misfiring on slow robots or low-speed commands.
93+
double block_ratio_threshold_{0.15};
94+
95+
// Fraction of longitudinal slip applied as zero-mean lateral noise when blocked.
96+
// Models imperfect wheel-wall contact (small perpendicular micro-displacements).
97+
double lateral_slip_ratio_{0.2};
98+
99+
// Consecutive simulation ticks the robot must appear blocked before slip
100+
// activates, and the counter resets immediately on resumed motion.
101+
int blocked_hysteresis_ticks_{3};
102+
int blocked_ticks_{0};
90103
};
91104
}
92105

Industrial/gz_noisy_odometry/src/gz_noisy_odometry/NoisyOdometryPlugin.cpp

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ namespace custom_plugins
4545
alpha3_ = _sdf->Get<double>("alpha3", gaussian_noise_coeff_).first;
4646
alpha4_ = _sdf->Get<double>("alpha4", gaussian_noise_coeff_).first;
4747

48-
slip_factor_ = _sdf->Get<double>("slip_factor", 0.02).first;
48+
slip_factor_ = _sdf->Get<double>("slip_factor", 0.02).first;
49+
block_ratio_threshold_ = _sdf->Get<double>("block_ratio_threshold", 0.15).first;
50+
lateral_slip_ratio_ = _sdf->Get<double>("lateral_slip_ratio", 0.20).first;
51+
blocked_hysteresis_ticks_ = _sdf->Get<int> ("blocked_hysteresis_ticks", 3 ).first;
4952

5053
gz_node_.Subscribe(gz_cmd_vel_topic_, &NoisyOdometryPlugin::OnCmdVel, this);
5154

@@ -123,18 +126,34 @@ namespace custom_plugins
123126
// In that case a small fraction of the commanded motion leaks into the
124127
// odometry, simulating wheel slip against the obstacle — exactly what real
125128
// encoders would observe when the wheels spin without moving the chassis.
126-
const double trans_cmd = v_cmd * dt;
129+
const double trans_cmd = v_cmd * dt;
127130
const double delta_rot_cmd = w_cmd * dt;
128131

129-
const bool is_blocked =
130-
(std::abs(trans_cmd) > 1e-4 || std::abs(delta_rot_cmd) > 1e-4) &&
131-
(trans_true < 1e-4 && std::abs(delta_yaw_true) < 1e-4);
132+
// Ratio-based detection: blocked when a commanded axis achieves less than
133+
// block_ratio_threshold_ of the expected increment. More robust than a fixed
134+
// absolute threshold, which can misfire on slow robots or tiny commands.
135+
const bool is_commanded = std::abs(trans_cmd) > 1e-4 || std::abs(delta_rot_cmd) > 1e-4;
136+
const bool linear_stalled = std::abs(trans_cmd) > 1e-4 &&
137+
trans_true < block_ratio_threshold_ * std::abs(trans_cmd);
138+
const bool angular_stalled = std::abs(delta_rot_cmd) > 1e-4 &&
139+
std::abs(delta_yaw_true) < block_ratio_threshold_ * std::abs(delta_rot_cmd);
140+
const bool blocked_raw = is_commanded && (linear_stalled || angular_stalled);
141+
142+
// Hysteresis: require several consecutive blocked detections before
143+
// activating slip, and reset immediately when motion resumes.
144+
blocked_ticks_ = blocked_raw
145+
? std::min(blocked_ticks_ + 1, blocked_hysteresis_ticks_)
146+
: 0;
147+
const bool is_blocked = (blocked_ticks_ >= blocked_hysteresis_ticks_);
132148

133149
const double trans = is_blocked ? trans_cmd * slip_factor_ : trans_true;
134150
const double delta_yaw = is_blocked ? delta_rot_cmd * slip_factor_ : delta_yaw_true;
135151

152+
// When blocked, delta_body is near-zero simulator noise so atan2 returns a
153+
// random angle. Force rot1=0 so the slip integrates along the robot's
154+
// current heading, which is the only physically meaningful direction.
136155
const double rot1 =
137-
(trans > 1e-9) ? NormalizeAngle(std::atan2(delta_body.Y(), delta_body.X())) : 0.0;
156+
(!is_blocked && trans > 1e-9) ? NormalizeAngle(std::atan2(delta_body.Y(), delta_body.X())) : 0.0;
138157
const double rot2 = NormalizeAngle(delta_yaw - rot1);
139158

140159
// Odometry motion model:
@@ -169,6 +188,19 @@ namespace custom_plugins
169188
noisy_pose_internal_.Pos().Y() + trans_noisy * std::sin(heading_after_rot1));
170189
noisy_pose_internal_.Pos().Z(current_true_pose.Pos().Z());
171190

191+
// When blocked, wheels spinning against a wall generate small random
192+
// perpendicular micro-displacements (lateral slip). The direction is
193+
// always 90° to the heading, so it cannot reverse the longitudinal slip.
194+
if (is_blocked && lateral_slip_ratio_ > 0.0)
195+
{
196+
const double lateral = gz::math::Rand::DblNormal(
197+
0.0, std::abs(trans) * lateral_slip_ratio_);
198+
noisy_pose_internal_.Pos().X(
199+
noisy_pose_internal_.Pos().X() - lateral * std::sin(heading_after_rot1));
200+
noisy_pose_internal_.Pos().Y(
201+
noisy_pose_internal_.Pos().Y() + lateral * std::cos(heading_after_rot1));
202+
}
203+
172204
noisy_pose_internal_.Rot() = gz::math::Quaterniond(0.0, 0.0, new_noisy_yaw);
173205
noisy_pose_internal_.Rot().Normalize();
174206

0 commit comments

Comments
 (0)