blob: f062234aef1d3050da636d60eebfc6ec95e1f070 [file] [log] [blame]
James Kuszmaul1057ce82019-02-09 17:58:24 -08001#include "y2019/control_loops/drivetrain/localizer.h"
2
3#include <random>
4#include <queue>
5
6#include "aos/testing/random_seed.h"
7#include "aos/testing/test_shm.h"
8#include "frc971/control_loops/drivetrain/trajectory.h"
9#include "frc971/control_loops/drivetrain/splinedrivetrain.h"
10#include "gflags/gflags.h"
11#if defined(SUPPORT_PLOT)
12#include "third_party/matplotlib-cpp/matplotlibcpp.h"
13#endif
14#include "gtest/gtest.h"
15#include "y2019/control_loops/drivetrain/drivetrain_base.h"
16#include "y2019/constants.h"
17
18DEFINE_bool(plot, false, "If true, plot");
19
20namespace y2019 {
21namespace control_loops {
22namespace testing {
23
24using ::y2019::constants::Field;
25
26constexpr size_t kNumCameras = 4;
27constexpr size_t kNumTargetsPerFrame = 3;
28
29typedef TypedLocalizer<kNumCameras, Field::kNumTargets, Field::kNumObstacles,
30 kNumTargetsPerFrame, double> TestLocalizer;
31typedef typename TestLocalizer::Camera TestCamera;
32typedef typename TestCamera::Pose Pose;
33typedef typename TestCamera::LineSegment Obstacle;
34
35typedef TestLocalizer::StateIdx StateIdx;
36
37using ::frc971::control_loops::drivetrain::DrivetrainConfig;
38
39// When placing the cameras on the robot, set them all kCameraOffset out from
40// the center, to test that we really can handle cameras that aren't at the
41// center-of-mass.
42constexpr double kCameraOffset = 0.1;
43
44#if defined(SUPPORT_PLOT)
45// Plots a line from a vector of Pose's.
46void PlotPlotPts(const ::std::vector<Pose> &poses,
47 const ::std::map<::std::string, ::std::string> &kwargs) {
48 ::std::vector<double> x;
49 ::std::vector<double> y;
50 for (const Pose & p : poses) {
51 x.push_back(p.abs_pos().x());
52 y.push_back(p.abs_pos().y());
53 }
54 matplotlibcpp::plot(x, y, kwargs);
55}
56#endif
57
58struct LocalizerTestParams {
59 // Control points for the spline to make the robot follow.
60 ::std::array<float, 6> control_pts_x;
61 ::std::array<float, 6> control_pts_y;
62 // The actual state to start the robot at. By setting voltage errors and the
63 // such you can introduce persistent disturbances.
64 TestLocalizer::State true_start_state;
65 // The initial state of the estimator.
66 TestLocalizer::State known_start_state;
67 // Whether or not to add Gaussian noise to the sensors and cameras.
68 bool noisify;
69 // Whether or not to add unmodelled disturbances.
70 bool disturb;
71 // The tolerances for the estimator and for the trajectory following at
72 // the end of the spline:
73 double estimate_tolerance;
74 double goal_tolerance;
75};
76
77class ParameterizedLocalizerTest
78 : public ::testing::TestWithParam<LocalizerTestParams> {
79 public:
80 ::aos::testing::TestSharedMemory shm_;
81
82 // Set up three targets in a row, at (-1, 0), (0, 0), and (1, 0).
83 // Make the right-most target (1, 0) be facing away from the camera, and give
84 // the middle target some skew.
85 // Place one camera facing forward, the other facing backward, and set the
86 // robot at (0, -5) with the cameras each 0.1m from the center.
87 // Place one obstacle in a place where it can block the left-most target (-1,
88 // 0).
89 ParameterizedLocalizerTest()
90 : field_(),
91 targets_(field_.targets()),
92 modeled_obstacles_(field_.obstacles()),
93 true_obstacles_(field_.obstacles()),
94 dt_config_(drivetrain::GetDrivetrainConfig()),
95 // Pull the noise for the encoders/gyros from the R matrix:
96 encoder_noise_(::std::sqrt(
97 dt_config_.make_kf_drivetrain_loop().observer().coefficients().R(
98 0, 0))),
99 gyro_noise_(::std::sqrt(
100 dt_config_.make_kf_drivetrain_loop().observer().coefficients().R(
101 2, 2))),
102 // As per the comments in localizer.h, we set the field of view and
103 // noise parameters on the robot_cameras_ so that they see a bit more
104 // than the true_cameras_. The robot_cameras_ are what is passed to the
105 // localizer and used to generate "expected" targets. The true_cameras_
106 // are what we actually use to generate targets to pass to the
107 // localizer.
108 robot_cameras_{
109 {TestCamera({&robot_pose_, {0.0, kCameraOffset, 0.0}, M_PI_2},
110 M_PI_2 * 1.1, robot_noise_parameters_, targets_,
111 modeled_obstacles_),
112 TestCamera({&robot_pose_, {kCameraOffset, 0.0, 0.0}, 0.0},
113 M_PI_2 * 1.1, robot_noise_parameters_, targets_,
114 modeled_obstacles_),
115 TestCamera({&robot_pose_, {-kCameraOffset, 0.0, 0.0}, M_PI},
116 M_PI_2 * 1.1, robot_noise_parameters_, targets_,
117 modeled_obstacles_),
118 TestCamera({&robot_pose_, {0.0, -kCameraOffset, 0.0}, -M_PI_2},
119 M_PI_2 * 1.1, robot_noise_parameters_, targets_,
120 modeled_obstacles_)}},
121 true_cameras_{
122 {TestCamera({&true_robot_pose_, {0.0, kCameraOffset, 0.0}, M_PI_2},
123 M_PI_2 * 0.9, true_noise_parameters_, targets_,
124 true_obstacles_),
125 TestCamera({&true_robot_pose_, {kCameraOffset, 0.0, 0.0}, 0.0},
126 M_PI_2 * 0.9, true_noise_parameters_, targets_,
127 true_obstacles_),
128 TestCamera({&true_robot_pose_, {-kCameraOffset, 0.0, 0.0}, M_PI},
129 M_PI_2 * 0.9, true_noise_parameters_, targets_,
130 true_obstacles_),
131 TestCamera(
132 {&true_robot_pose_, {-0.0, -kCameraOffset, 0.0}, -M_PI_2},
133 M_PI_2 * 0.9, true_noise_parameters_, targets_,
134 true_obstacles_)}},
135 localizer_(dt_config_, &robot_pose_),
136 spline_drivetrain_(dt_config_) {
137 // We use the default P() for initialization.
138 localizer_.ResetInitialState(t0_, GetParam().known_start_state,
139 localizer_.P());
140 }
141
142 void SetUp() {
143 ::frc971::control_loops::DrivetrainQueue::Goal goal;
144 goal.controller_type = 2;
145 goal.spline.spline_idx = 1;
146 goal.spline.spline_count = 1;
147 goal.spline_handle = 1;
148 ::std::copy(GetParam().control_pts_x.begin(),
149 GetParam().control_pts_x.end(), goal.spline.spline_x.begin());
150 ::std::copy(GetParam().control_pts_y.begin(),
151 GetParam().control_pts_y.end(), goal.spline.spline_y.begin());
152 spline_drivetrain_.SetGoal(goal);
Alex Perrycc3ee4c2019-02-09 21:20:41 -0800153
154 // Let the spline drivetrain compute the spline.
155 ::frc971::control_loops::DrivetrainQueue::Status status;
156 do {
157 ::std::this_thread::sleep_for(::std::chrono::milliseconds(5));
158 spline_drivetrain_.PopulateStatus(&status);
159 } while (status.trajectory_logging.planning_state !=
160 (int8_t)::frc971::control_loops::drivetrain::SplineDrivetrain::
161 PlanState::kPlannedTrajectory);
162 spline_drivetrain_.SetGoal(goal);
James Kuszmaul1057ce82019-02-09 17:58:24 -0800163 }
164
165 void TearDown() {
166 printf("Each iteration of the simulation took on average %f seconds.\n",
167 avg_time_.count());
168#if defined(SUPPORT_PLOT)
169 if (FLAGS_plot) {
170 matplotlibcpp::figure();
171 matplotlibcpp::plot(simulation_t_, simulation_vl_, {{"label", "Vl"}});
172 matplotlibcpp::plot(simulation_t_, simulation_vr_, {{"label", "Vr"}});
173 matplotlibcpp::legend();
174
175 matplotlibcpp::figure();
176 matplotlibcpp::plot(spline_x_, spline_y_, {{"label", "spline"}});
177 matplotlibcpp::plot(simulation_x_, simulation_y_, {{"label", "robot"}});
178 matplotlibcpp::plot(estimated_x_, estimated_y_,
179 {{"label", "estimation"}});
180 for (const Target & target : targets_) {
181 PlotPlotPts(target.PlotPoints(), {{"c", "g"}});
182 }
183 for (const Obstacle &obstacle : true_obstacles_) {
184 PlotPlotPts(obstacle.PlotPoints(), {{"c", "k"}});
185 }
186 // Go through and plot true/expected camera targets for a few select
187 // time-steps.
188 ::std::vector<const char *> colors{"m", "y", "c"};
189 int jj = 0;
190 for (size_t ii = 0; ii < simulation_x_.size(); ii += 400) {
191 *true_robot_pose_.mutable_pos() << simulation_x_[ii], simulation_y_[ii],
192 0.0;
193 true_robot_pose_.set_theta(simulation_theta_[ii]);
194 for (const TestCamera &camera : true_cameras_) {
195 for (const auto &plot_pts : camera.PlotPoints()) {
196 PlotPlotPts(plot_pts, {{"c", colors[jj]}});
197 }
198 }
199 for (const TestCamera &camera : robot_cameras_) {
200 *robot_pose_.mutable_pos() << estimated_x_[ii], estimated_y_[ii], 0.0;
201 robot_pose_.set_theta(estimated_theta_[ii]);
202 const auto &all_plot_pts = camera.PlotPoints();
203 *robot_pose_.mutable_pos() = true_robot_pose_.rel_pos();
204 robot_pose_.set_theta(true_robot_pose_.rel_theta());
205 for (const auto &plot_pts : all_plot_pts) {
206 PlotPlotPts(plot_pts, {{"c", colors[jj]}, {"ls", "--"}});
207 }
208 }
209 jj = (jj + 1) % colors.size();
210 }
211 matplotlibcpp::legend();
212
213 matplotlibcpp::figure();
214 matplotlibcpp::plot(
215 simulation_t_, spline_x_,
216 {{"label", "spline x"}, {"c", "g"}, {"ls", ""}, {"marker", "o"}});
217 matplotlibcpp::plot(simulation_t_, simulation_x_,
218 {{"label", "simulated x"}, {"c", "g"}});
219 matplotlibcpp::plot(simulation_t_, estimated_x_,
220 {{"label", "estimated x"}, {"c", "g"}, {"ls", "--"}});
221
222 matplotlibcpp::plot(
223 simulation_t_, spline_y_,
224 {{"label", "spline y"}, {"c", "b"}, {"ls", ""}, {"marker", "o"}});
225 matplotlibcpp::plot(simulation_t_, simulation_y_,
226 {{"label", "simulated y"}, {"c", "b"}});
227 matplotlibcpp::plot(simulation_t_, estimated_y_,
228 {{"label", "estimated y"}, {"c", "b"}, {"ls", "--"}});
229
230 matplotlibcpp::plot(simulation_t_, simulation_theta_,
231 {{"label", "simulated theta"}, {"c", "r"}});
232 matplotlibcpp::plot(
233 simulation_t_, estimated_theta_,
234 {{"label", "estimated theta"}, {"c", "r"}, {"ls", "--"}});
235 matplotlibcpp::legend();
236
237 matplotlibcpp::show();
238 }
239#endif
240 }
241
242 protected:
243 // Returns a random number with a gaussian distribution with a mean of zero
244 // and a standard deviation of std, if noisify = true.
245 // If noisify is false, then returns 0.0.
246 double Normal(double std) {
247 if (GetParam().noisify) {
248 return normal_(gen_) * std;
249 }
250 return 0.0;
251 }
252 // Adds random noise to the given target view.
253 void Noisify(TestCamera::TargetView *view) {
254 view->reading.heading += Normal(view->noise.heading);
255 view->reading.distance += Normal(view->noise.distance);
256 view->reading.height += Normal(view->noise.height);
257 view->reading.skew += Normal(view->noise.skew);
258 }
259 // The differential equation for the dynamics of the system.
260 TestLocalizer::State DiffEq(const TestLocalizer::State &X,
261 const TestLocalizer::Input &U) {
262 return localizer_.DiffEq(X, U);
263 }
264
265 Field field_;
266 ::std::array<Target, Field::kNumTargets> targets_;
267 // The obstacles that are passed to the camera objects for the localizer.
268 ::std::array<Obstacle, Field::kNumObstacles> modeled_obstacles_;
269 // The obstacles that are used for actually simulating the cameras.
270 ::std::array<Obstacle, Field::kNumObstacles> true_obstacles_;
271
272 DrivetrainConfig<double> dt_config_;
273
274 // Noise information for the actual simulated cameras (true_*) and the
275 // parameters that the localizer should use for modelling the cameras. Note
276 // how the max_viewable_distance is larger for the localizer, so that if
277 // there is any error in the estimation, it still thinks that it can see
278 // any targets that might actually be in range.
279 TestCamera::NoiseParameters true_noise_parameters_ = {
280 .max_viewable_distance = 10.0,
281 .heading_noise = 0.02,
282 .nominal_distance_noise = 0.06,
283 .nominal_skew_noise = 0.1,
284 .nominal_height_noise = 0.01};
285 TestCamera::NoiseParameters robot_noise_parameters_ = {
286 .max_viewable_distance = 11.0,
287 .heading_noise = 0.02,
288 .nominal_distance_noise = 0.06,
289 .nominal_skew_noise = 0.1,
290 .nominal_height_noise = 0.01};
291
292 // Standard deviations of the noise for the encoders/gyro.
293 double encoder_noise_, gyro_noise_;
294
295 Pose robot_pose_;
296 ::std::array<TestCamera, 4> robot_cameras_;
297 Pose true_robot_pose_;
298 ::std::array<TestCamera, 4> true_cameras_;
299
300 TestLocalizer localizer_;
301
302 ::frc971::control_loops::drivetrain::SplineDrivetrain spline_drivetrain_;
303
304 // All the data we want to end up plotting.
305 ::std::vector<double> simulation_t_;
306
307 ::std::vector<double> spline_x_;
308 ::std::vector<double> spline_y_;
309 ::std::vector<double> estimated_x_;
310 ::std::vector<double> estimated_y_;
311 ::std::vector<double> estimated_theta_;
312 ::std::vector<double> simulation_x_;
313 ::std::vector<double> simulation_y_;
314 ::std::vector<double> simulation_theta_;
315
316 ::std::vector<double> simulation_vl_;
317 ::std::vector<double> simulation_vr_;
318
319 // Simulation start time
320 ::aos::monotonic_clock::time_point t0_;
321
322 // Average duration of each iteration; used for debugging and getting a
323 // sanity-check on what performance looks like--uses a real system clock.
324 ::std::chrono::duration<double> avg_time_;
325
326 ::std::mt19937 gen_{static_cast<uint32_t>(::aos::testing::RandomSeed())};
327 ::std::normal_distribution<> normal_;
328};
329
James Kuszmaul6f941b72019-03-08 18:12:25 -0800330using ::std::chrono::milliseconds;
331
James Kuszmaul1057ce82019-02-09 17:58:24 -0800332// Tests that, when we attempt to follow a spline and use the localizer to
333// perform the state estimation, we end up roughly where we should and that
334// the localizer has a valid position estimate.
335TEST_P(ParameterizedLocalizerTest, SplineTest) {
336 // state stores the true state of the robot throughout the simulation.
337 TestLocalizer::State state = GetParam().true_start_state;
338
339 ::aos::monotonic_clock::time_point t = t0_;
340
341 // The period with which we should take frames from the cameras. Currently,
342 // we just trigger all the cameras at once, rather than offsetting them or
343 // anything.
344 const int camera_period = 5; // cycles
James Kuszmaul6f941b72019-03-08 18:12:25 -0800345 // The amount of time to delay the camera images from when they are taken, for
346 // each camera.
347 const ::std::array<milliseconds, 4> camera_latencies{
348 {milliseconds(45), milliseconds(50), milliseconds(55),
349 milliseconds(100)}};
James Kuszmaul1057ce82019-02-09 17:58:24 -0800350
James Kuszmaul6f941b72019-03-08 18:12:25 -0800351 // A queue of camera frames for each camera so that we can add a time delay to
352 // the data coming from the cameras.
353 ::std::array<
354 ::std::queue<::std::tuple<
355 ::aos::monotonic_clock::time_point, const TestCamera *,
356 ::aos::SizedArray<TestCamera::TargetView, kNumTargetsPerFrame>>>,
357 4>
358 camera_queues;
James Kuszmaul1057ce82019-02-09 17:58:24 -0800359
360 // Start time, for debugging.
361 const auto begin = ::std::chrono::steady_clock::now();
362
363 size_t i;
364 for (i = 0; !spline_drivetrain_.IsAtEnd(); ++i) {
365 // Get the current state estimate into a matrix that works for the
366 // trajectory code.
367 ::Eigen::Matrix<double, 5, 1> known_state;
368 TestLocalizer::State X_hat = localizer_.X_hat();
369 known_state << X_hat(StateIdx::kX, 0), X_hat(StateIdx::kY, 0),
370 X_hat(StateIdx::kTheta, 0), X_hat(StateIdx::kLeftVelocity, 0),
371 X_hat(StateIdx::kRightVelocity, 0);
372
373 spline_drivetrain_.Update(true, known_state);
374
375 ::frc971::control_loops::DrivetrainQueue::Output output;
376 output.left_voltage = 0;
377 output.right_voltage = 0;
378 spline_drivetrain_.SetOutput(&output);
379 TestLocalizer::Input U(output.left_voltage, output.right_voltage);
380
381 const ::Eigen::Matrix<double, 5, 1> goal_state =
382 spline_drivetrain_.CurrentGoalState();
383 simulation_t_.push_back(
384 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
385 t.time_since_epoch())
386 .count());
387 spline_x_.push_back(goal_state(0, 0));
388 spline_y_.push_back(goal_state(1, 0));
389 simulation_x_.push_back(state(StateIdx::kX, 0));
390 simulation_y_.push_back(state(StateIdx::kY, 0));
391 simulation_theta_.push_back(state(StateIdx::kTheta, 0));
392 estimated_x_.push_back(known_state(0, 0));
393 estimated_y_.push_back(known_state(1, 0));
394 estimated_theta_.push_back(known_state(StateIdx::kTheta, 0));
395
396 simulation_vl_.push_back(U(0));
397 simulation_vr_.push_back(U(1));
398 U(0, 0) = ::std::max(::std::min(U(0, 0), 12.0), -12.0);
399 U(1, 0) = ::std::max(::std::min(U(1, 0), 12.0), -12.0);
400
401 state = ::frc971::control_loops::RungeKuttaU(
James Kuszmaulfedc4612019-03-10 11:24:51 -0700402 [this](const ::Eigen::Matrix<double, 7, 1> &X,
James Kuszmaul1057ce82019-02-09 17:58:24 -0800403 const ::Eigen::Matrix<double, 2, 1> &U) { return DiffEq(X, U); },
404 state, U,
405 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
406 dt_config_.dt)
407 .count());
408
409 // Add arbitrary disturbances at some arbitrary interval. The main points of
410 // interest here are that we (a) stop adding disturbances at the very end of
411 // the trajectory, to allow us to actually converge to the goal, and (b)
412 // scale disturbances by the corruent velocity.
413 if (GetParam().disturb && i % 50 == 0) {
414 // Scale the disturbance so that when we have near-zero velocity we don't
415 // have much disturbance.
416 double disturbance_scale = ::std::min(
417 1.0, ::std::sqrt(::std::pow(state(StateIdx::kLeftVelocity, 0), 2) +
418 ::std::pow(state(StateIdx::kRightVelocity, 0), 2)) /
419 3.0);
420 TestLocalizer::State disturbance;
James Kuszmaulfedc4612019-03-10 11:24:51 -0700421 disturbance << 0.02, 0.02, 0.001, 0.03, 0.02, 0.0, 0.0;
James Kuszmaul1057ce82019-02-09 17:58:24 -0800422 disturbance *= disturbance_scale;
423 state += disturbance;
424 }
425
426 t += dt_config_.dt;
427 *true_robot_pose_.mutable_pos() << state(StateIdx::kX, 0),
428 state(StateIdx::kY, 0), 0.0;
429 true_robot_pose_.set_theta(state(StateIdx::kTheta, 0));
430 const double left_enc = state(StateIdx::kLeftEncoder, 0);
431 const double right_enc = state(StateIdx::kRightEncoder, 0);
432
433 const double gyro = (state(StateIdx::kRightVelocity, 0) -
434 state(StateIdx::kLeftVelocity, 0)) /
435 dt_config_.robot_radius / 2.0;
436
437 localizer_.UpdateEncodersAndGyro(left_enc + Normal(encoder_noise_),
438 right_enc + Normal(encoder_noise_),
439 gyro + Normal(gyro_noise_), U, t);
440
James Kuszmaul6f941b72019-03-08 18:12:25 -0800441 for (size_t cam_idx = 0; cam_idx < camera_queues.size(); ++cam_idx) {
442 auto &camera_queue = camera_queues[cam_idx];
443 // Clear out the camera frames that we are ready to pass to the localizer.
444 while (!camera_queue.empty() && ::std::get<0>(camera_queue.front()) <
445 t - camera_latencies[cam_idx]) {
446 const auto tuple = camera_queue.front();
447 camera_queue.pop();
448 ::aos::monotonic_clock::time_point t_obs = ::std::get<0>(tuple);
449 const TestCamera *camera = ::std::get<1>(tuple);
450 ::aos::SizedArray<TestCamera::TargetView, kNumTargetsPerFrame> views =
451 ::std::get<2>(tuple);
452 localizer_.UpdateTargets(*camera, views, t_obs);
453 }
James Kuszmaul1057ce82019-02-09 17:58:24 -0800454
James Kuszmaul6f941b72019-03-08 18:12:25 -0800455 // Actually take all the images and store them in the queue.
456 if (i % camera_period == 0) {
457 for (size_t jj = 0; jj < true_cameras_.size(); ++jj) {
458 const auto target_views = true_cameras_[jj].target_views();
459 ::aos::SizedArray<TestCamera::TargetView, kNumTargetsPerFrame>
460 pass_views;
461 for (size_t ii = 0;
462 ii < ::std::min(kNumTargetsPerFrame, target_views.size());
463 ++ii) {
464 TestCamera::TargetView view = target_views[ii];
465 Noisify(&view);
466 pass_views.push_back(view);
467 }
468 camera_queue.emplace(t, &robot_cameras_[jj], pass_views);
James Kuszmaul1057ce82019-02-09 17:58:24 -0800469 }
James Kuszmaul1057ce82019-02-09 17:58:24 -0800470 }
471 }
James Kuszmaul1057ce82019-02-09 17:58:24 -0800472 }
473
474 const auto end = ::std::chrono::steady_clock::now();
475 avg_time_ = (end - begin) / i;
476 TestLocalizer::State estimate_err = state - localizer_.X_hat();
477 EXPECT_LT(estimate_err.template topRows<7>().norm(),
478 GetParam().estimate_tolerance);
479 // Check that none of the states that we actually care about (x/y/theta, and
480 // wheel positions/speeds) are too far off, individually:
481 EXPECT_LT(estimate_err.template topRows<7>().cwiseAbs().maxCoeff(),
482 GetParam().estimate_tolerance / 3.0)
483 << "Estimate error: " << estimate_err.transpose();
484 Eigen::Matrix<double, 5, 1> final_trajectory_state;
485 final_trajectory_state << state(StateIdx::kX, 0), state(StateIdx::kY, 0),
486 state(StateIdx::kTheta, 0), state(StateIdx::kLeftVelocity, 0),
487 state(StateIdx::kRightVelocity, 0);
488 const Eigen::Matrix<double, 5, 1> final_trajectory_state_err =
489 final_trajectory_state - spline_drivetrain_.CurrentGoalState();
490 EXPECT_LT(final_trajectory_state_err.norm(), GetParam().goal_tolerance)
491 << "Goal error: " << final_trajectory_state_err.transpose();
492}
493
494INSTANTIATE_TEST_CASE_P(
495 LocalizerTest, ParameterizedLocalizerTest,
496 ::testing::Values(
497 // Checks a "perfect" scenario, where we should track perfectly.
498 LocalizerTestParams({
499 /*control_pts_x=*/{{0.0, 3.0, 3.0, 0.0, 1.0, 1.0}},
500 /*control_pts_y=*/{{-5.0, -5.0, 2.0, 2.0, 2.0, 3.0}},
James Kuszmaulfedc4612019-03-10 11:24:51 -0700501 (TestLocalizer::State() << 0.0, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800502 .finished(),
James Kuszmaulfedc4612019-03-10 11:24:51 -0700503 (TestLocalizer::State() << 0.0, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800504 .finished(),
505 /*noisify=*/false,
506 /*disturb=*/false,
507 /*estimate_tolerance=*/1e-5,
508 /*goal_tolerance=*/2e-2,
509 }),
510 // Checks "perfect" estimation, but start off the spline and check
511 // that we can still follow it.
512 LocalizerTestParams({
513 /*control_pts_x=*/{{0.0, 3.0, 3.0, 0.0, 1.0, 1.0}},
514 /*control_pts_y=*/{{-5.0, -5.0, 2.0, 2.0, 2.0, 3.0}},
James Kuszmaulfedc4612019-03-10 11:24:51 -0700515 (TestLocalizer::State() << 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800516 .finished(),
James Kuszmaulfedc4612019-03-10 11:24:51 -0700517 (TestLocalizer::State() << 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800518 .finished(),
519 /*noisify=*/false,
520 /*disturb=*/false,
521 /*estimate_tolerance=*/1e-5,
522 /*goal_tolerance=*/2e-2,
523 }),
524 // Repeats perfect scenario, but add sensor noise.
525 LocalizerTestParams({
526 /*control_pts_x=*/{{0.0, 3.0, 3.0, 0.0, 1.0, 1.0}},
527 /*control_pts_y=*/{{-5.0, -5.0, 2.0, 2.0, 2.0, 3.0}},
James Kuszmaulfedc4612019-03-10 11:24:51 -0700528 (TestLocalizer::State() << 0.0, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800529 .finished(),
James Kuszmaulfedc4612019-03-10 11:24:51 -0700530 (TestLocalizer::State() << 0.0, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800531 .finished(),
532 /*noisify=*/true,
533 /*disturb=*/false,
534 /*estimate_tolerance=*/0.2,
535 /*goal_tolerance=*/0.2,
536 }),
537 // Repeats perfect scenario, but add initial estimator error.
538 LocalizerTestParams({
539 /*control_pts_x=*/{{0.0, 3.0, 3.0, 0.0, 1.0, 1.0}},
540 /*control_pts_y=*/{{-5.0, -5.0, 2.0, 2.0, 2.0, 3.0}},
James Kuszmaulfedc4612019-03-10 11:24:51 -0700541 (TestLocalizer::State() << 0.0, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800542 .finished(),
James Kuszmaulfedc4612019-03-10 11:24:51 -0700543 (TestLocalizer::State() << 0.1, -5.1, -0.01, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800544 .finished(),
545 /*noisify=*/false,
546 /*disturb=*/false,
547 /*estimate_tolerance=*/1e-4,
548 /*goal_tolerance=*/2e-2,
549 }),
550 // Add disturbances while we are driving:
551 LocalizerTestParams({
552 /*control_pts_x=*/{{0.0, 3.0, 3.0, 0.0, 1.0, 1.0}},
553 /*control_pts_y=*/{{-5.0, -5.0, 2.0, 2.0, 2.0, 3.0}},
James Kuszmaulfedc4612019-03-10 11:24:51 -0700554 (TestLocalizer::State() << 0.0, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800555 .finished(),
James Kuszmaulfedc4612019-03-10 11:24:51 -0700556 (TestLocalizer::State() << 0.0, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800557 .finished(),
558 /*noisify=*/false,
559 /*disturb=*/true,
560 /*estimate_tolerance=*/2e-2,
561 /*goal_tolerance=*/0.15,
562 }),
563 // Add noise and some initial error in addition:
564 LocalizerTestParams({
565 /*control_pts_x=*/{{0.0, 3.0, 3.0, 0.0, 1.0, 1.0}},
566 /*control_pts_y=*/{{-5.0, -5.0, 2.0, 2.0, 2.0, 3.0}},
James Kuszmaulfedc4612019-03-10 11:24:51 -0700567 (TestLocalizer::State() << 0.0, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800568 .finished(),
James Kuszmaulfedc4612019-03-10 11:24:51 -0700569 (TestLocalizer::State() << 0.1, -5.1, 0.03, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800570 .finished(),
571 /*noisify=*/true,
572 /*disturb=*/true,
573 /*estimate_tolerance=*/0.15,
James Kuszmaulfedc4612019-03-10 11:24:51 -0700574 /*goal_tolerance=*/0.8,
James Kuszmaul1057ce82019-02-09 17:58:24 -0800575 }),
576 // Try another spline, just in case the one I was using is special for
577 // some reason; this path will also go straight up to a target, to
578 // better simulate what might happen when trying to score:
579 LocalizerTestParams({
580 /*control_pts_x=*/{{0.5, 3.5, 4.0, 8.0, 11.0, 10.2}},
581 /*control_pts_y=*/{{1.0, 1.0, -3.0, -2.0, -3.5, -3.65}},
James Kuszmaulfedc4612019-03-10 11:24:51 -0700582 (TestLocalizer::State() << 0.6, 1.01, 0.01, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800583 .finished(),
James Kuszmaulfedc4612019-03-10 11:24:51 -0700584 (TestLocalizer::State() << 0.5, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0)
James Kuszmaul1057ce82019-02-09 17:58:24 -0800585 .finished(),
586 /*noisify=*/true,
587 /*disturb=*/false,
James Kuszmaul6f941b72019-03-08 18:12:25 -0800588 /*estimate_tolerance=*/0.15,
James Kuszmaul1057ce82019-02-09 17:58:24 -0800589 /*goal_tolerance=*/0.5,
590 })));
591
592} // namespace testing
593} // namespace control_loops
594} // namespace y2019