blob: 77f398863a99a3a50946099a9a89f4baeba78c63 [file] [log] [blame]
James Kuszmaul1798c072022-02-13 15:32:11 -08001#include "y2022/control_loops/drivetrain/localizer.h"
2
3#include <queue>
4
5#include "aos/events/logging/log_writer.h"
6#include "aos/network/message_bridge_server_generated.h"
7#include "aos/network/team_number.h"
8#include "aos/network/testing_time_converter.h"
9#include "frc971/control_loops/control_loop_test.h"
10#include "frc971/control_loops/drivetrain/drivetrain.h"
11#include "frc971/control_loops/drivetrain/drivetrain_test_lib.h"
12#include "frc971/control_loops/team_number_test_environment.h"
James Kuszmaul1798c072022-02-13 15:32:11 -080013#include "gtest/gtest.h"
14#include "y2022/control_loops/drivetrain/drivetrain_base.h"
James Kuszmaul2971b5a2023-01-29 15:49:32 -080015#include "y2022/localizer/localizer_output_generated.h"
James Kuszmaul1798c072022-02-13 15:32:11 -080016
17DEFINE_string(output_folder, "",
18 "If set, logs all channels to the provided logfile.");
James Kuszmaul2971b5a2023-01-29 15:49:32 -080019DECLARE_bool(die_on_malloc);
James Kuszmaul1798c072022-02-13 15:32:11 -080020
21namespace y2022 {
22namespace control_loops {
23namespace drivetrain {
24namespace testing {
25
26using frc971::control_loops::drivetrain::DrivetrainConfig;
27using frc971::control_loops::drivetrain::Goal;
28using frc971::control_loops::drivetrain::LocalizerControl;
29
30namespace {
31DrivetrainConfig<double> GetTest2022DrivetrainConfig() {
32 DrivetrainConfig<double> config = GetDrivetrainConfig();
33 return config;
34}
James Kuszmaul2971b5a2023-01-29 15:49:32 -080035} // namespace
James Kuszmaul1798c072022-02-13 15:32:11 -080036
37namespace chrono = std::chrono;
38using aos::monotonic_clock;
39using frc971::control_loops::drivetrain::DrivetrainLoop;
40using frc971::control_loops::drivetrain::testing::DrivetrainSimulation;
41
42// TODO(james): Make it so this actually tests the full system of the localizer.
43class LocalizedDrivetrainTest : public frc971::testing::ControlLoopTest {
44 protected:
45 // We must use the 2022 drivetrain config so that we don't have to deal
46 // with shifting:
47 LocalizedDrivetrainTest()
48 : frc971::testing::ControlLoopTest(
49 aos::configuration::ReadConfig(
50 "y2022/control_loops/drivetrain/simulation_config.json"),
51 GetTest2022DrivetrainConfig().dt),
52 roborio_(aos::configuration::GetNode(configuration(), "roborio")),
53 imu_(aos::configuration::GetNode(configuration(), "imu")),
54 test_event_loop_(MakeEventLoop("test", roborio_)),
55 imu_test_event_loop_(MakeEventLoop("test", imu_)),
56 drivetrain_goal_sender_(
57 test_event_loop_->MakeSender<Goal>("/drivetrain")),
58 localizer_output_sender_(
59 imu_test_event_loop_->MakeSender<frc971::controls::LocalizerOutput>(
60 "/localizer")),
61 drivetrain_goal_fetcher_(
62 test_event_loop_->MakeFetcher<Goal>("/drivetrain")),
63 drivetrain_status_fetcher_(
64 test_event_loop_
65 ->MakeFetcher<frc971::control_loops::drivetrain::Status>(
66 "/drivetrain")),
67 localizer_control_sender_(
68 test_event_loop_->MakeSender<LocalizerControl>("/drivetrain")),
69 drivetrain_event_loop_(MakeEventLoop("drivetrain", roborio_)),
70 dt_config_(GetTest2022DrivetrainConfig()),
71 localizer_(drivetrain_event_loop_.get(), dt_config_),
72 drivetrain_(dt_config_, drivetrain_event_loop_.get(), &localizer_),
73 drivetrain_plant_event_loop_(MakeEventLoop("plant", roborio_)),
74 drivetrain_plant_imu_event_loop_(MakeEventLoop("plant", imu_)),
75 drivetrain_plant_(drivetrain_plant_event_loop_.get(),
76 drivetrain_plant_imu_event_loop_.get(), dt_config_,
77 std::chrono::microseconds(500)) {
James Kuszmaul2971b5a2023-01-29 15:49:32 -080078 FLAGS_die_on_malloc = true;
James Kuszmaul1798c072022-02-13 15:32:11 -080079 set_team_id(frc971::control_loops::testing::kTeamNumber);
80 set_battery_voltage(12.0);
81
82 if (!FLAGS_output_folder.empty()) {
83 logger_event_loop_ = MakeEventLoop("logger", roborio_);
84 logger_ = std::make_unique<aos::logger::Logger>(logger_event_loop_.get());
85 logger_->StartLoggingOnRun(FLAGS_output_folder);
86 }
87
88 test_event_loop_->OnRun([this]() { SetStartingPosition({3.0, 2.0, 0.0}); });
89
90 imu_test_event_loop_
91 ->AddTimer([this]() {
92 auto builder = localizer_output_sender_.MakeBuilder();
93 frc971::controls::LocalizerOutput::Builder output_builder =
94 builder.MakeBuilder<frc971::controls::LocalizerOutput>();
95 output_builder.add_monotonic_timestamp_ns(
96 imu_test_event_loop_->monotonic_now().time_since_epoch().count());
97 output_builder.add_x(drivetrain_plant_.state()(0));
98 output_builder.add_y(drivetrain_plant_.state()(1));
99 output_builder.add_theta(drivetrain_plant_.state()(2));
James Kuszmaul2971b5a2023-01-29 15:49:32 -0800100 builder.CheckOk(builder.Send(output_builder.Finish()));
James Kuszmaul1798c072022-02-13 15:32:11 -0800101 })
102 ->Setup(imu_test_event_loop_->monotonic_now(),
103 std::chrono::milliseconds(5));
104 }
105
106 virtual ~LocalizedDrivetrainTest() override {}
107
108 void SetStartingPosition(const Eigen::Matrix<double, 3, 1> &xytheta) {
109 *drivetrain_plant_.mutable_state() << xytheta.x(), xytheta.y(),
110 xytheta(2, 0), 0.0, 0.0;
111 Eigen::Matrix<double, Localizer::HybridEkf::kNStates, 1> localizer_state;
112 localizer_state.setZero();
113 localizer_state.block<3, 1>(0, 0) = xytheta;
114 localizer_.Reset(monotonic_now(), localizer_state);
115 }
116
117 void VerifyNearGoal(double eps = 1e-2) {
118 drivetrain_goal_fetcher_.Fetch();
119 EXPECT_NEAR(drivetrain_goal_fetcher_->left_goal(),
120 drivetrain_plant_.GetLeftPosition(), eps);
121 EXPECT_NEAR(drivetrain_goal_fetcher_->right_goal(),
122 drivetrain_plant_.GetRightPosition(), eps);
123 }
124
125 ::testing::AssertionResult IsNear(double expected, double actual,
126 double epsilon) {
127 if (std::abs(expected - actual) < epsilon) {
128 return ::testing::AssertionSuccess();
129 } else {
130 return ::testing::AssertionFailure()
131 << "Expected " << expected << " but got " << actual
132 << " with a max difference of " << epsilon
133 << " and an actual difference of " << std::abs(expected - actual);
134 }
135 }
136 ::testing::AssertionResult VerifyEstimatorAccurate(double eps) {
137 const Eigen::Matrix<double, 5, 1> true_state = drivetrain_plant_.state();
138 ::testing::AssertionResult result(true);
139 if (!(result = IsNear(localizer_.x(), true_state(0), eps))) {
140 return result;
141 }
142 if (!(result = IsNear(localizer_.y(), true_state(1), eps))) {
143 return result;
144 }
145 if (!(result = IsNear(localizer_.theta(), true_state(2), eps))) {
146 return result;
147 }
148 if (!(result = IsNear(localizer_.left_velocity(), true_state(3), eps))) {
149 return result;
150 }
151 if (!(result = IsNear(localizer_.right_velocity(), true_state(4), eps))) {
152 return result;
153 }
154 return result;
155 }
156
157 const aos::Node *const roborio_;
158 const aos::Node *const imu_;
159
160 std::unique_ptr<aos::EventLoop> test_event_loop_;
161 std::unique_ptr<aos::EventLoop> imu_test_event_loop_;
162 aos::Sender<Goal> drivetrain_goal_sender_;
163 aos::Sender<frc971::controls::LocalizerOutput> localizer_output_sender_;
164 aos::Fetcher<Goal> drivetrain_goal_fetcher_;
165 aos::Fetcher<frc971::control_loops::drivetrain::Status>
166 drivetrain_status_fetcher_;
167 aos::Sender<LocalizerControl> localizer_control_sender_;
168
169 std::unique_ptr<aos::EventLoop> drivetrain_event_loop_;
170 const frc971::control_loops::drivetrain::DrivetrainConfig<double> dt_config_;
171
172 Localizer localizer_;
173 DrivetrainLoop drivetrain_;
174
175 std::unique_ptr<aos::EventLoop> drivetrain_plant_event_loop_;
176 std::unique_ptr<aos::EventLoop> drivetrain_plant_imu_event_loop_;
177 DrivetrainSimulation drivetrain_plant_;
178
179 void SendGoal(double left, double right) {
180 auto builder = drivetrain_goal_sender_.MakeBuilder();
181
182 Goal::Builder drivetrain_builder = builder.MakeBuilder<Goal>();
183 drivetrain_builder.add_controller_type(
184 frc971::control_loops::drivetrain::ControllerType::MOTION_PROFILE);
185 drivetrain_builder.add_left_goal(left);
186 drivetrain_builder.add_right_goal(right);
187
188 EXPECT_EQ(builder.Send(drivetrain_builder.Finish()),
189 aos::RawSender::Error::kOk);
190 }
191
192 private:
193 std::unique_ptr<aos::EventLoop> logger_event_loop_;
194 std::unique_ptr<aos::logger::Logger> logger_;
195};
196
197TEST_F(LocalizedDrivetrainTest, Nominal) {
198 SetEnabled(true);
199 EXPECT_TRUE(VerifyEstimatorAccurate(1e-7));
200
201 SendGoal(-1.0, 1.0);
202
203 RunFor(chrono::seconds(10));
204 VerifyNearGoal();
205 EXPECT_TRUE(VerifyEstimatorAccurate(5e-3));
206}
207
208} // namespace testing
209} // namespace drivetrain
210} // namespace control_loops
211} // namespace y2022