blob: ab06c5b2e82b893e9936f8b3450c34436cb79567 [file] [log] [blame]
Milind Upadhyay7c205222022-11-16 18:20:58 -08001#include "frc971/vision/target_mapper.h"
2
3#include <random>
4
5#include "aos/events/simulated_event_loop.h"
6#include "aos/testing/random_seed.h"
7#include "glog/logging.h"
8#include "gtest/gtest.h"
9
10namespace frc971::vision {
11
12namespace {
13constexpr double kToleranceMeters = 0.05;
14constexpr double kToleranceRadians = 0.05;
Milind Upadhyay05652cb2022-12-07 20:51:51 -080015constexpr std::string_view kFieldName = "test";
Milind Upadhyay7c205222022-11-16 18:20:58 -080016} // namespace
17
18#define EXPECT_POSE_NEAR(pose1, pose2) \
19 EXPECT_NEAR(pose1.x, pose2.x, kToleranceMeters); \
20 EXPECT_NEAR(pose1.y, pose2.y, kToleranceMeters); \
21 EXPECT_NEAR(pose1.yaw_radians, pose2.yaw_radians, kToleranceRadians);
22
23#define EXPECT_POSE_EQ(pose1, pose2) \
24 EXPECT_DOUBLE_EQ(pose1.x, pose2.x); \
25 EXPECT_DOUBLE_EQ(pose1.y, pose2.y); \
26 EXPECT_DOUBLE_EQ(pose1.yaw_radians, pose2.yaw_radians);
27
28#define EXPECT_BETWEEN_EXCLUSIVE(value, a, b) \
29 { \
30 auto low = std::min(a, b); \
31 auto high = std::max(a, b); \
32 EXPECT_GT(value, low); \
33 EXPECT_LT(value, high); \
34 }
35
36namespace {
37// Expects angles to be normalized
38double DeltaAngle(double a, double b) {
39 double delta = std::abs(a - b);
40 return std::min(delta, (2.0 * M_PI) - delta);
41}
42} // namespace
43
44// Expects angles to be normalized
45#define EXPECT_ANGLE_BETWEEN_EXCLUSIVE(theta, a, b) \
46 EXPECT_LT(DeltaAngle(a, theta), DeltaAngle(a, b)); \
47 EXPECT_LT(DeltaAngle(b, theta), DeltaAngle(a, b));
48
49#define EXPECT_POSE_IN_RANGE(interpolated_pose, pose_start, pose_end) \
50 EXPECT_BETWEEN_EXCLUSIVE(interpolated_pose.x, pose_start.x, pose_end.x); \
51 EXPECT_BETWEEN_EXCLUSIVE(interpolated_pose.y, pose_start.y, pose_end.y); \
52 EXPECT_ANGLE_BETWEEN_EXCLUSIVE(interpolated_pose.yaw_radians, \
53 pose_start.yaw_radians, \
54 pose_end.yaw_radians);
55
56// Both confidence matrixes should have the same dimensions and be square
57#define EXPECT_CONFIDENCE_GT(confidence1, confidence2) \
58 { \
59 ASSERT_EQ(confidence1.rows(), confidence2.rows()); \
60 ASSERT_EQ(confidence1.rows(), confidence1.cols()); \
61 ASSERT_EQ(confidence2.rows(), confidence2.cols()); \
62 for (size_t i = 0; i < confidence1.rows(); i++) { \
63 EXPECT_GT(confidence1(i, i), confidence2(i, i)); \
64 } \
65 }
66
67namespace {
68ceres::examples::Pose2d MakePose(double x, double y, double yaw_radians) {
69 return ceres::examples::Pose2d{x, y, yaw_radians};
70}
71
Milind Upadhyayebf93ee2023-01-05 14:12:58 -080072// Assumes camera and robot origin are the same
73DataAdapter::TimestampedDetection MakeTimestampedDetection(
74 aos::distributed_clock::time_point time, Eigen::Affine3d H_robot_target,
75 TargetMapper::TargetId id) {
76 auto target_pose = PoseUtils::Affine3dToPose2d(H_robot_target);
77 return DataAdapter::TimestampedDetection{
78 time, H_robot_target,
79 std::sqrt(std::pow(target_pose.x, 2) + std::pow(target_pose.y, 2)), id};
80}
81
Milind Upadhyay7c205222022-11-16 18:20:58 -080082bool TargetIsInView(TargetMapper::TargetPose target_detection) {
83 // And check if it is within the fov of the robot /
84 // camera, assuming camera is pointing in the
85 // positive x-direction of the robot
86 double angle_to_target =
87 atan2(target_detection.pose.y, target_detection.pose.x);
88
89 // Simulated camera field of view, in radians
90 constexpr double kCameraFov = M_PI_2;
91 if (fabs(angle_to_target) <= kCameraFov / 2.0) {
92 VLOG(2) << "Found target in view, based on T = " << target_detection.pose.x
93 << ", " << target_detection.pose.y << " with angle "
94 << angle_to_target;
95 return true;
96 } else {
97 return false;
98 }
99}
100
101aos::distributed_clock::time_point TimeInMs(size_t ms) {
102 return aos::distributed_clock::time_point(std::chrono::milliseconds(ms));
103}
104
105} // namespace
106
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800107TEST(DataAdapterTest, ComputeConfidence) {
108 // Check the confidence matrices. Don't check the actual values
109 // in case the constants change, just check that the confidence of contraints
110 // decreases as time period or distances from camera increase.
111 {
112 // Vary time period
113 constexpr double kDistanceStart = 0.5;
114 constexpr double kDistanceEnd = 2.0;
115
116 Eigen::Matrix3d last_confidence = Eigen::Matrix3d::Zero();
117 for (size_t dt = 0; dt < 15; dt++) {
118 Eigen::Matrix3d confidence = DataAdapter::ComputeConfidence(
119 TimeInMs(0), TimeInMs(dt), kDistanceStart, kDistanceEnd);
120
121 if (dt != 0) {
122 // Confidence only decreases every 5ms (control loop period)
123 if (dt % 5 == 0) {
124 EXPECT_CONFIDENCE_GT(last_confidence, confidence);
125 } else {
126 EXPECT_EQ(last_confidence, confidence);
127 }
128 }
129 last_confidence = confidence;
130 }
131 }
132
133 {
134 // Vary distance at start
135 constexpr int kDt = 3;
136 constexpr double kDistanceEnd = 1.5;
137 Eigen::Matrix3d last_confidence = Eigen::Matrix3d::Zero();
138 for (double distance_start = 0.0; distance_start < 3.0;
139 distance_start += 0.5) {
140 Eigen::Matrix3d confidence = DataAdapter::ComputeConfidence(
141 TimeInMs(0), TimeInMs(kDt), distance_start, kDistanceEnd);
142 if (distance_start != 0.0) {
143 EXPECT_CONFIDENCE_GT(last_confidence, confidence);
144 }
145 last_confidence = confidence;
146 }
147 }
148
149 {
150 // Vary distance at end
151 constexpr int kDt = 2;
152 constexpr double kDistanceStart = 2.5;
153 Eigen::Matrix3d last_confidence = Eigen::Matrix3d::Zero();
154 for (double distance_end = 0.0; distance_end < 3.0; distance_end += 0.5) {
155 Eigen::Matrix3d confidence = DataAdapter::ComputeConfidence(
156 TimeInMs(0), TimeInMs(kDt), kDistanceStart, distance_end);
157 if (distance_end != 0.0) {
158 EXPECT_CONFIDENCE_GT(last_confidence, confidence);
159 }
160 last_confidence = confidence;
161 }
162 }
163}
164
Milind Upadhyayec493912022-12-18 21:33:15 -0800165TEST(DataAdapterTest, MatchTargetDetections) {
Milind Upadhyay7c205222022-11-16 18:20:58 -0800166 std::vector<DataAdapter::TimestampedPose> timestamped_robot_poses = {
167 {TimeInMs(0), ceres::examples::Pose2d{1.0, 2.0, 0.0}},
168 {TimeInMs(5), ceres::examples::Pose2d{1.0, 2.0, 0.0}},
169 {TimeInMs(10), ceres::examples::Pose2d{3.0, 1.0, M_PI_2}},
170 {TimeInMs(15), ceres::examples::Pose2d{5.0, -2.0, -M_PI}},
171 {TimeInMs(20), ceres::examples::Pose2d{5.0, -2.0, -M_PI}},
172 {TimeInMs(25), ceres::examples::Pose2d{10.0, -32.0, M_PI_2}},
173 {TimeInMs(30), ceres::examples::Pose2d{-15.0, 12.0, 0.0}},
174 {TimeInMs(35), ceres::examples::Pose2d{-15.0, 12.0, 0.0}}};
175 std::vector<DataAdapter::TimestampedDetection> timestamped_target_detections =
176 {{TimeInMs(5),
177 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{5.0, -4.0, 0.0}),
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800178 1.0, 0},
Milind Upadhyay7c205222022-11-16 18:20:58 -0800179 {TimeInMs(9),
180 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{5.0, -4.0, 0.0}),
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800181 1.0, 1},
Milind Upadhyay7c205222022-11-16 18:20:58 -0800182 {TimeInMs(9),
183 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{5.0, -4.0, 0.0}),
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800184 1.0, 2},
Milind Upadhyay7c205222022-11-16 18:20:58 -0800185 {TimeInMs(15),
186 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{5.0, -4.0, 0.0}),
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800187 1.0, 0},
Milind Upadhyay7c205222022-11-16 18:20:58 -0800188 {TimeInMs(16),
189 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{5.0, -4.0, 0.0}),
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800190 1.0, 2},
Milind Upadhyay7c205222022-11-16 18:20:58 -0800191 {TimeInMs(27),
192 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{5.0, -4.0, 0.0}),
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800193 1.0, 1}};
Milind Upadhyay7c205222022-11-16 18:20:58 -0800194 auto [target_constraints, robot_delta_poses] =
195 DataAdapter::MatchTargetDetections(timestamped_robot_poses,
196 timestamped_target_detections);
197
198 // Check that target constraints got inserted in the
199 // correct spots
200 EXPECT_EQ(target_constraints.size(),
201 timestamped_target_detections.size() - 1);
202 for (auto it = target_constraints.begin(); it < target_constraints.end();
203 it++) {
204 auto timestamped_it = timestamped_target_detections.begin() +
205 (it - target_constraints.begin());
206 EXPECT_EQ(it->id_begin, timestamped_it->id);
207 EXPECT_EQ(it->id_end, (timestamped_it + 1)->id);
208 }
209
210 // Check that poses were interpolated correctly.
211 // Keep track of the computed robot pose by adding the delta poses
212 auto computed_robot_pose = timestamped_robot_poses[1].pose;
213
214 computed_robot_pose =
215 PoseUtils::ComputeOffsetPose(computed_robot_pose, robot_delta_poses[0]);
216 EXPECT_POSE_IN_RANGE(computed_robot_pose, timestamped_robot_poses[1].pose,
217 timestamped_robot_poses[2].pose);
218
219 computed_robot_pose =
220 PoseUtils::ComputeOffsetPose(computed_robot_pose, robot_delta_poses[1]);
221 EXPECT_POSE_IN_RANGE(computed_robot_pose, timestamped_robot_poses[1].pose,
222 timestamped_robot_poses[2].pose);
223 EXPECT_POSE_EQ(robot_delta_poses[1], MakePose(0.0, 0.0, 0.0));
224
225 computed_robot_pose =
226 PoseUtils::ComputeOffsetPose(computed_robot_pose, robot_delta_poses[2]);
227 EXPECT_POSE_EQ(computed_robot_pose, timestamped_robot_poses[3].pose);
228
229 computed_robot_pose =
230 PoseUtils::ComputeOffsetPose(computed_robot_pose, robot_delta_poses[3]);
231 EXPECT_POSE_EQ(computed_robot_pose, timestamped_robot_poses[4].pose);
232
233 computed_robot_pose =
234 PoseUtils::ComputeOffsetPose(computed_robot_pose, robot_delta_poses[4]);
235 EXPECT_POSE_IN_RANGE(computed_robot_pose, timestamped_robot_poses[5].pose,
236 timestamped_robot_poses[6].pose);
Milind Upadhyay7c205222022-11-16 18:20:58 -0800237}
238
Milind Upadhyayec493912022-12-18 21:33:15 -0800239TEST(DataAdapterTest, MatchTargetDetectionsWithoutRobotPosition) {
240 std::vector<DataAdapter::TimestampedDetection> timestamped_target_detections =
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800241 {MakeTimestampedDetection(
242 TimeInMs(5),
243 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{5.0, -5.0, 0.0}),
244 2),
245 MakeTimestampedDetection(TimeInMs(6),
246 PoseUtils::Pose2dToAffine3d(
247 ceres::examples::Pose2d{5.0, -4.0, M_PI}),
248 0),
249 MakeTimestampedDetection(TimeInMs(10),
250 PoseUtils::Pose2dToAffine3d(
251 ceres::examples::Pose2d{3.0, -3.0, M_PI}),
252 1),
253 MakeTimestampedDetection(TimeInMs(13),
254 PoseUtils::Pose2dToAffine3d(
255 ceres::examples::Pose2d{4.0, -7.0, M_PI_2}),
256 2),
257 MakeTimestampedDetection(TimeInMs(14),
258 PoseUtils::Pose2dToAffine3d(
259 ceres::examples::Pose2d{4.0, -4.0, M_PI_2}),
260 2)};
Milind Upadhyayec493912022-12-18 21:33:15 -0800261
262 constexpr auto kMaxDt = std::chrono::milliseconds(3);
263 auto target_constraints =
264 DataAdapter::MatchTargetDetections(timestamped_target_detections, kMaxDt);
265
266 // The constraint between the detection at 6ms and the one at 10 ms is skipped
267 // because dt > kMaxDt.
268 // Also, the constraint between the last two detections is skipped because
269 // they are the same target
270 EXPECT_EQ(target_constraints.size(),
271 timestamped_target_detections.size() - 3);
272
273 // Between 5ms and 6ms detections
274 EXPECT_DOUBLE_EQ(target_constraints[0].x, 0.0);
275 EXPECT_DOUBLE_EQ(target_constraints[0].y, 1.0);
276 EXPECT_DOUBLE_EQ(target_constraints[0].yaw_radians, -M_PI);
277 EXPECT_EQ(target_constraints[0].id_begin, 2);
278 EXPECT_EQ(target_constraints[0].id_end, 0);
279
280 // Between 10ms and 13ms detections
281 EXPECT_DOUBLE_EQ(target_constraints[1].x, -1.0);
282 EXPECT_DOUBLE_EQ(target_constraints[1].y, 4.0);
283 EXPECT_DOUBLE_EQ(target_constraints[1].yaw_radians, -M_PI_2);
284 EXPECT_EQ(target_constraints[1].id_begin, 1);
285 EXPECT_EQ(target_constraints[1].id_end, 2);
286}
287
Milind Upadhyay7c205222022-11-16 18:20:58 -0800288TEST(TargetMapperTest, TwoTargetsOneConstraint) {
289 std::map<TargetMapper::TargetId, ceres::examples::Pose2d> target_poses;
290 target_poses[0] = ceres::examples::Pose2d{5.0, 0.0, M_PI};
291 target_poses[1] = ceres::examples::Pose2d{-5.0, 0.0, 0.0};
292
293 std::vector<DataAdapter::TimestampedPose> timestamped_robot_poses = {
294 {TimeInMs(5), ceres::examples::Pose2d{2.0, 0.0, 0.0}},
295 {TimeInMs(10), ceres::examples::Pose2d{-1.0, 0.0, 0.0}},
296 {TimeInMs(15), ceres::examples::Pose2d{-1.0, 0.0, 0.0}}};
297 std::vector<DataAdapter::TimestampedDetection> timestamped_target_detections =
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800298 {MakeTimestampedDetection(
299 TimeInMs(5),
300 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{3.0, 0.0, M_PI}),
301 0),
302 MakeTimestampedDetection(
303 TimeInMs(10),
304 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{-4.0, 0.0, 0.0}),
305 1)};
Milind Upadhyay7c205222022-11-16 18:20:58 -0800306 auto target_constraints =
307 DataAdapter::MatchTargetDetections(timestamped_robot_poses,
308 timestamped_target_detections)
309 .first;
310
311 frc971::vision::TargetMapper mapper(target_poses, target_constraints);
Milind Upadhyay05652cb2022-12-07 20:51:51 -0800312 mapper.Solve(kFieldName);
Milind Upadhyay7c205222022-11-16 18:20:58 -0800313
314 ASSERT_EQ(mapper.target_poses().size(), 2);
315 EXPECT_POSE_NEAR(mapper.target_poses()[0], MakePose(5.0, 0.0, M_PI));
316 EXPECT_POSE_NEAR(mapper.target_poses()[1], MakePose(-5.0, 0.0, 0.0));
317}
318
319TEST(TargetMapperTest, TwoTargetsTwoConstraints) {
320 std::map<TargetMapper::TargetId, ceres::examples::Pose2d> target_poses;
321 target_poses[0] = ceres::examples::Pose2d{5.0, 0.0, M_PI};
322 target_poses[1] = ceres::examples::Pose2d{-5.0, 0.0, -M_PI_2};
323
324 std::vector<DataAdapter::TimestampedPose> timestamped_robot_poses = {
325 {TimeInMs(5), ceres::examples::Pose2d{-1.0, 0.0, 0.0}},
326 {TimeInMs(10), ceres::examples::Pose2d{3.0, 0.0, 0.0}},
327 {TimeInMs(15), ceres::examples::Pose2d{4.0, 0.0, 0.0}},
328 {TimeInMs(20), ceres::examples::Pose2d{-1.0, 0.0, 0.0}}};
329 std::vector<DataAdapter::TimestampedDetection> timestamped_target_detections =
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800330 {MakeTimestampedDetection(
331 TimeInMs(5),
332 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{6.0, 0.0, M_PI}),
333 0),
334 MakeTimestampedDetection(
335 TimeInMs(10),
336 PoseUtils::Pose2dToAffine3d(
337 ceres::examples::Pose2d{-8.0, 0.0, -M_PI_2}),
338 1),
339 MakeTimestampedDetection(
340 TimeInMs(15),
341 PoseUtils::Pose2dToAffine3d(ceres::examples::Pose2d{1.0, 0.0, M_PI}),
342 0)};
Milind Upadhyay7c205222022-11-16 18:20:58 -0800343 auto target_constraints =
344 DataAdapter::MatchTargetDetections(timestamped_robot_poses,
345 timestamped_target_detections)
346 .first;
347
348 frc971::vision::TargetMapper mapper(target_poses, target_constraints);
Milind Upadhyay05652cb2022-12-07 20:51:51 -0800349 mapper.Solve(kFieldName);
Milind Upadhyay7c205222022-11-16 18:20:58 -0800350
351 ASSERT_EQ(mapper.target_poses().size(), 2);
352 EXPECT_POSE_NEAR(mapper.target_poses()[0], MakePose(5.0, 0.0, M_PI));
353 EXPECT_POSE_NEAR(mapper.target_poses()[1], MakePose(-5.0, 0.0, -M_PI_2));
354}
355
356TEST(TargetMapperTest, TwoTargetsOneNoisyConstraint) {
357 std::map<TargetMapper::TargetId, ceres::examples::Pose2d> target_poses;
358 target_poses[0] = ceres::examples::Pose2d{5.0, 0.0, M_PI};
359 target_poses[1] = ceres::examples::Pose2d{-5.0, 0.0, 0.0};
360
361 std::vector<DataAdapter::TimestampedPose> timestamped_robot_poses = {
362 {TimeInMs(5), ceres::examples::Pose2d{1.99, 0.0, 0.0}},
363 {TimeInMs(10), ceres::examples::Pose2d{-1.0, 0.0, 0.0}},
364 {TimeInMs(15), ceres::examples::Pose2d{-1.01, -0.01, 0.004}}};
365 std::vector<DataAdapter::TimestampedDetection> timestamped_target_detections =
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800366 {MakeTimestampedDetection(
367 TimeInMs(5),
368 PoseUtils::Pose2dToAffine3d(
369 ceres::examples::Pose2d{3.01, 0.001, M_PI - 0.001}),
370 0),
371 MakeTimestampedDetection(TimeInMs(10),
372 PoseUtils::Pose2dToAffine3d(
373 ceres::examples::Pose2d{-4.01, 0.0, 0.0}),
374 1)};
Milind Upadhyay7c205222022-11-16 18:20:58 -0800375
376 auto target_constraints =
377 DataAdapter::MatchTargetDetections(timestamped_robot_poses,
378 timestamped_target_detections)
379 .first;
380
381 frc971::vision::TargetMapper mapper(target_poses, target_constraints);
Milind Upadhyay05652cb2022-12-07 20:51:51 -0800382 mapper.Solve(kFieldName);
Milind Upadhyay7c205222022-11-16 18:20:58 -0800383
384 ASSERT_EQ(mapper.target_poses().size(), 2);
385 EXPECT_POSE_NEAR(mapper.target_poses()[0], MakePose(5.0, 0.0, M_PI));
386 EXPECT_POSE_NEAR(mapper.target_poses()[1], MakePose(-5.0, 0.0, 0.0));
387}
388
389TEST(TargetMapperTest, MultiTargetCircleMotion) {
390 // Build set of target locations wrt global origin
391 // For simplicity, do this on a grid of the field
392 double field_half_length = 7.5; // half length of the field
393 double field_half_width = 5.0; // half width of the field
394 std::map<TargetMapper::TargetId, ceres::examples::Pose2d> target_poses;
395 std::vector<TargetMapper::TargetPose> actual_target_poses;
396 for (int i = 0; i < 3; i++) {
397 for (int j = 0; j < 3; j++) {
398 TargetMapper::TargetId target_id = i * 3 + j;
399 TargetMapper::TargetPose target_pose{
400 target_id, ceres::examples::Pose2d{field_half_length * (1 - i),
401 field_half_width * (1 - j), 0.0}};
402 actual_target_poses.emplace_back(target_pose);
403 target_poses[target_id] = target_pose.pose;
404 VLOG(2) << "VERTEX_SE2 " << target_id << " " << target_pose.pose.x << " "
405 << target_pose.pose.y << " " << target_pose.pose.yaw_radians;
406 }
407 }
408
409 // Now, create a bunch of robot poses and target
410 // observations
411 size_t dt = 1;
412
413 std::vector<DataAdapter::TimestampedPose> timestamped_robot_poses;
414 std::vector<DataAdapter::TimestampedDetection> timestamped_target_detections;
415
416 constexpr size_t kTotalSteps = 100;
417 for (size_t step_count = 0; step_count < kTotalSteps; step_count++) {
418 size_t t = dt * step_count;
419 // Circle clockwise around the center of the field
420 double robot_theta = t;
421 double robot_x = (field_half_length / 2.0) * cos(robot_theta);
422 double robot_y = (-field_half_width / 2.0) * sin(robot_theta);
423
424 ceres::examples::Pose2d robot_pose{robot_x, robot_y, robot_theta};
425 for (TargetMapper::TargetPose target_pose : actual_target_poses) {
426 TargetMapper::TargetPose target_detection = {
427 .id = target_pose.id,
428 .pose = PoseUtils::ComputeRelativePose(robot_pose, target_pose.pose)};
429 if (TargetIsInView(target_detection)) {
430 // Define random generator with Gaussian
431 // distribution
432 const double mean = 0.0;
433 const double stddev = 1.0;
434 // Can play with this to see how it impacts
435 // randomness
436 constexpr double kNoiseScale = 0.01;
437 std::default_random_engine generator(aos::testing::RandomSeed());
438 std::normal_distribution<double> dist(mean, stddev);
439
440 target_detection.pose.x += dist(generator) * kNoiseScale;
441 target_detection.pose.y += dist(generator) * kNoiseScale;
442 robot_pose.x += dist(generator) * kNoiseScale;
443 robot_pose.y += dist(generator) * kNoiseScale;
444
445 auto time_point =
446 aos::distributed_clock::time_point(std::chrono::milliseconds(t));
447 timestamped_robot_poses.emplace_back(DataAdapter::TimestampedPose{
448 .time = time_point, .pose = robot_pose});
Milind Upadhyayebf93ee2023-01-05 14:12:58 -0800449 timestamped_target_detections.emplace_back(MakeTimestampedDetection(
450 time_point, PoseUtils::Pose2dToAffine3d(target_detection.pose),
451 target_detection.id));
Milind Upadhyay7c205222022-11-16 18:20:58 -0800452 }
453 }
454 }
455
456 {
457 // Add in a robot pose after all target poses
458 auto final_robot_pose =
459 timestamped_robot_poses[timestamped_robot_poses.size() - 1];
460 timestamped_robot_poses.emplace_back(DataAdapter::TimestampedPose{
461 .time = final_robot_pose.time + std::chrono::milliseconds(dt),
462 .pose = final_robot_pose.pose});
463 }
464
465 auto target_constraints =
466 DataAdapter::MatchTargetDetections(timestamped_robot_poses,
467 timestamped_target_detections)
468 .first;
469 frc971::vision::TargetMapper mapper(target_poses, target_constraints);
Milind Upadhyay05652cb2022-12-07 20:51:51 -0800470 mapper.Solve(kFieldName);
Milind Upadhyay7c205222022-11-16 18:20:58 -0800471
472 for (auto [target_pose_id, mapper_target_pose] : mapper.target_poses()) {
473 TargetMapper::TargetPose actual_target_pose =
474 TargetMapper::GetTargetPoseById(actual_target_poses, target_pose_id)
475 .value();
476 EXPECT_POSE_NEAR(mapper_target_pose, actual_target_pose.pose);
477 }
478
479 //
480 // See what happens when we don't start with the
481 // correct values
482 //
483 for (auto [target_id, target_pose] : target_poses) {
484 // Skip first pose, since that needs to be correct
485 // and is fixed in the solver
486 if (target_id != 0) {
487 ceres::examples::Pose2d bad_pose{0.0, 0.0, M_PI / 2.0};
488 target_poses[target_id] = bad_pose;
489 }
490 }
491
492 frc971::vision::TargetMapper mapper_bad_poses(target_poses,
493 target_constraints);
Milind Upadhyay05652cb2022-12-07 20:51:51 -0800494 mapper_bad_poses.Solve(kFieldName);
Milind Upadhyay7c205222022-11-16 18:20:58 -0800495
496 for (auto [target_pose_id, mapper_target_pose] :
497 mapper_bad_poses.target_poses()) {
498 TargetMapper::TargetPose actual_target_pose =
499 TargetMapper::GetTargetPoseById(actual_target_poses, target_pose_id)
500 .value();
501 EXPECT_POSE_NEAR(mapper_target_pose, actual_target_pose.pose);
502 }
503}
504
505} // namespace frc971::vision