blob: 55d3c7eb1d31beb0681c05ea6430d53afb3317cd [file] [log] [blame]
Milind Upadhyay7c205222022-11-16 18:20:58 -08001#ifndef FRC971_VISION_TARGET_MAPPER_H_
2#define FRC971_VISION_TARGET_MAPPER_H_
3
4#include <unordered_map>
5
6#include "aos/events/simulated_event_loop.h"
7#include "frc971/vision/ceres/types.h"
8
9namespace frc971::vision {
10
11// Estimates positions of vision targets (ex. April Tags) using
12// target detections relative to a robot (which were computed using robot
13// positions at the time of those detections). Solves SLAM problem to estimate
14// target locations using deltas between consecutive target detections.
15class TargetMapper {
16 public:
17 using TargetId = int;
18
19 struct TargetPose {
20 TargetId id;
21 ceres::examples::Pose2d pose;
22 };
23
24 // target_poses are initial guesses for the actual locations of the targets on
25 // the field.
26 // target_constraints are the deltas between consecutive target detections,
27 // and are usually prepared by the DataAdapter class below.
28 TargetMapper(std::map<TargetId, ceres::examples::Pose2d> target_poses,
29 std::vector<ceres::examples::Constraint2d> target_constraints);
30
31 void Solve();
32
33 static std::optional<TargetPose> GetTargetPoseById(
34 std::vector<TargetPose> target_poses, TargetId target_id);
35
36 std::map<TargetId, ceres::examples::Pose2d> target_poses() {
37 return target_poses_;
38 }
39
40 private:
41 // Output the poses to std::cout with format: ID: x y yaw_radians.
42 static void OutputPoses(const std::map<int, ceres::examples::Pose2d> &poses);
43
44 // Constructs the nonlinear least squares optimization problem from the
45 // pose graph constraints.
46 void BuildOptimizationProblem(
47 std::map<TargetId, ceres::examples::Pose2d> *target_poses,
48 const std::vector<ceres::examples::Constraint2d> &constraints,
49 ceres::Problem *problem);
50
51 // Returns true if the solve was successful.
52 bool SolveOptimizationProblem(ceres::Problem *problem);
53
54 std::map<TargetId, ceres::examples::Pose2d> target_poses_;
55 std::vector<ceres::examples::Constraint2d> target_constraints_;
56};
57
58// Utility functions for dealing with ceres::examples::Pose2d structs
59class PoseUtils {
60 public:
61 // Embeds a 2d pose into a 3d affine transformation to be used in 3d
62 // computation
63 static Eigen::Affine3d Pose2dToAffine3d(ceres::examples::Pose2d pose2d);
64 // Assumes only x and y translation, and only z rotation (yaw)
65 static ceres::examples::Pose2d Affine3dToPose2d(Eigen::Affine3d H);
66
67 // Computes pose_2 relative to pose_1. This is equivalent to (pose_1^-1 *
68 // pose_2)
69 static ceres::examples::Pose2d ComputeRelativePose(
70 ceres::examples::Pose2d pose_1, ceres::examples::Pose2d pose_2);
71
72 // Computes pose_2 given a pose_1 and pose_2 relative to pose_1. This is
73 // equivalent to (pose_1 * pose_2_relative)
74 static ceres::examples::Pose2d ComputeOffsetPose(
75 ceres::examples::Pose2d pose_1, ceres::examples::Pose2d pose_2_relative);
76};
77
78// Transforms robot position and target detection data into target constraints
79// to be used for mapping. Interpolates continous-time data, converting it to
80// discrete detection time steps.
81class DataAdapter {
82 public:
83 // Pairs pose with a time point
84 struct TimestampedPose {
85 aos::distributed_clock::time_point time;
86 ceres::examples::Pose2d pose;
87 };
88
89 // Pairs target detection with a time point
90 struct TimestampedDetection {
91 aos::distributed_clock::time_point time;
92 // Pose of target relative to robot
93 Eigen::Affine3d H_robot_target;
94 TargetMapper::TargetId id;
95 };
96
97 // Pairs consecutive target detections into constraints, and interpolates
98 // robot poses based on time points to compute motion between detections. This
99 // prepares data to be used by TargetMapper. Also returns vector of delta
100 // robot poses corresponding to each constraint, to be used for testing.
101 //
102 // Assumes both inputs are in chronological order.
103 static std::pair<std::vector<ceres::examples::Constraint2d>,
104 std::vector<ceres::examples::Pose2d>>
105 MatchTargetDetections(
106 const std::vector<TimestampedPose> &timestamped_robot_poses,
107 const std::vector<TimestampedDetection> &timestamped_target_detections);
108
109 // Computes inverse of covariance matrix, assuming there was a target
110 // detection between robot movement over the given time period. Ceres calls
111 // this matrix the "information"
112 static Eigen::Matrix3d ComputeConfidence(
113 aos::distributed_clock::time_point start,
114 aos::distributed_clock::time_point end);
115
116 private:
117 static ceres::examples::Pose2d InterpolatePose(
118 const TimestampedPose &pose_start, const TimestampedPose &pose_end,
119 aos::distributed_clock::time_point time);
120
121 // Computes the constraint between the start and end pose of the targets: the
122 // relative pose between the start and end target locations in the frame of
123 // the start target. Takes into account the robot motion in the time between
124 // the two detections.
125 static ceres::examples::Constraint2d ComputeTargetConstraint(
126 const TimestampedDetection &target_detection_start,
127 const Eigen::Affine3d &H_robotstart_robotend,
128 const TimestampedDetection &target_detection_end,
129 const Eigen::Matrix3d &confidence);
130};
131
132} // namespace frc971::vision
133
134#endif // FRC971_VISION_TARGET_MAPPER_H_