Milind Upadhyay | 7c20522 | 2022-11-16 18:20:58 -0800 | [diff] [blame^] | 1 | #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 | |
| 9 | namespace 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. |
| 15 | class 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 |
| 59 | class 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. |
| 81 | class 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> ×tamped_robot_poses, |
| 107 | const std::vector<TimestampedDetection> ×tamped_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_ |