Skip to content

Commit f79de33

Browse files
Fixing #4661: MPPI ackermann reversing taking incorrect sign sometimes (#4664) (#4668)
* Fixing #4661: MPPI ackermann reversing taking incorrect sign sometimes Signed-off-by: Steve Macenski <stevenmacenski@gmail.com> * fixing unit test for type implicit cast Signed-off-by: Steve Macenski <stevenmacenski@gmail.com> --------- Signed-off-by: Steve Macenski <stevenmacenski@gmail.com> (cherry picked from commit 7eb47d8) Co-authored-by: Steve Macenski <stevenmacenski@gmail.com>
1 parent ab8fa82 commit f79de33

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

nav2_mppi_controller/include/nav2_mppi_controller/motion_models.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ class AckermannMotionModel : public MotionModel
112112
auto & vx = control_sequence.vx;
113113
auto & wz = control_sequence.wz;
114114

115-
auto view = xt::masked_view(wz, xt::fabs(vx) / xt::fabs(wz) < min_turning_r_);
116-
view = xt::sign(wz) * vx / min_turning_r_;
115+
auto view = xt::masked_view(wz, (xt::fabs(vx) / xt::fabs(wz)) < min_turning_r_);
116+
view = xt::sign(wz) * xt::fabs(vx) / min_turning_r_;
117117
}
118118

119119
/**

nav2_mppi_controller/test/motion_model_tests.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ TEST(MotionModelTests, AckermannTest)
164164
// VX equal since this doesn't change, the WZ is reduced if breaking the constraint
165165
EXPECT_EQ(initial_control_sequence.vx, control_sequence.vx);
166166
EXPECT_NE(initial_control_sequence.wz, control_sequence.wz);
167+
for (unsigned int i = 1; i != control_sequence.wz.shape(0); i++) {
168+
EXPECT_GT(control_sequence.wz(i), 0.0);
169+
}
167170

168171
// Now, check the specifics of the minimum curvature constraint
169172
EXPECT_NEAR(model->getMinTurningRadius(), 0.2, 1e-6);
@@ -177,3 +180,78 @@ TEST(MotionModelTests, AckermannTest)
177180
// Check it cleanly destructs
178181
model.reset();
179182
}
183+
184+
TEST(MotionModelTests, AckermannReversingTest)
185+
{
186+
models::ControlSequence control_sequence;
187+
models::ControlSequence control_sequence2;
188+
models::State state;
189+
int batches = 1000;
190+
int timesteps = 50;
191+
control_sequence.reset(timesteps); // populates with zeros
192+
control_sequence2.reset(timesteps); // populates with zeros
193+
state.reset(batches, timesteps); // populates with zeros
194+
auto node = std::make_shared<rclcpp_lifecycle::LifecycleNode>("my_node");
195+
ParametersHandler param_handler(node);
196+
std::unique_ptr<AckermannMotionModel> model =
197+
std::make_unique<AckermannMotionModel>(&param_handler, node->get_name());
198+
199+
// Check that predict properly populates the trajectory velocities with the control velocities
200+
state.cvx = 10 * xt::ones<float>({batches, timesteps});
201+
state.cvy = 5 * xt::ones<float>({batches, timesteps});
202+
state.cwz = 1 * xt::ones<float>({batches, timesteps});
203+
204+
// Manually set state index 0 from initial conditions which would be the speed of the robot
205+
xt::view(state.vx, xt::all(), 0) = 10;
206+
xt::view(state.wz, xt::all(), 0) = 1;
207+
208+
model->predict(state);
209+
210+
EXPECT_EQ(state.vx, state.cvx);
211+
EXPECT_EQ(state.vy, xt::zeros<float>({batches, timesteps})); // non-holonomic
212+
EXPECT_EQ(state.wz, state.cwz);
213+
214+
// Check that application of constraints are non-empty for Ackermann Drive
215+
for (unsigned int i = 0; i != control_sequence.vx.shape(0); i++) {
216+
float idx = static_cast<float>(i);
217+
control_sequence.vx(i) = -idx * idx * idx; // now reversing
218+
control_sequence.wz(i) = idx * idx * idx * idx;
219+
}
220+
221+
models::ControlSequence initial_control_sequence = control_sequence;
222+
model->applyConstraints(control_sequence);
223+
// VX equal since this doesn't change, the WZ is reduced if breaking the constraint
224+
EXPECT_EQ(initial_control_sequence.vx, control_sequence.vx);
225+
EXPECT_NE(initial_control_sequence.wz, control_sequence.wz);
226+
for (unsigned int i = 1; i != control_sequence.wz.shape(0); i++) {
227+
EXPECT_GT(control_sequence.wz(i), 0.0);
228+
}
229+
230+
// Repeat with negative rotation direction
231+
for (unsigned int i = 0; i != control_sequence2.vx.shape(0); i++) {
232+
float idx = static_cast<float>(i);
233+
control_sequence2.vx(i) = -idx * idx * idx; // now reversing
234+
control_sequence2.wz(i) = -idx * idx * idx * idx;
235+
}
236+
237+
models::ControlSequence initial_control_sequence2 = control_sequence2;
238+
model->applyConstraints(control_sequence2);
239+
// VX equal since this doesn't change, the WZ is reduced if breaking the constraint
240+
EXPECT_EQ(initial_control_sequence2.vx, control_sequence2.vx);
241+
EXPECT_NE(initial_control_sequence2.wz, control_sequence2.wz);
242+
for (unsigned int i = 1; i != control_sequence2.wz.shape(0); i++) {
243+
EXPECT_LT(control_sequence2.wz(i), 0.0);
244+
}
245+
246+
// Now, check the specifics of the minimum curvature constraint
247+
EXPECT_NEAR(model->getMinTurningRadius(), 0.2, 1e-6);
248+
for (unsigned int i = 1; i != control_sequence2.vx.shape(0); i++) {
249+
EXPECT_TRUE(fabs(control_sequence2.vx(i)) / fabs(control_sequence2.wz(i)) >= 0.2);
250+
}
251+
252+
// Check that Ackermann Drive is properly non-holonomic and parameterized
253+
EXPECT_EQ(model->isHolonomic(), false);
254+
255+
// Check it cleanly destructs
256+
model.reset();
257+
}

0 commit comments

Comments
 (0)