blob: 38465c9ce4e98dfb976ada80edf517e31b6ed486 [file] [log] [blame]
Yash Chainanid5c7f0d2022-11-19 17:05:57 -08001#include "aos/events/logging/log_reader.h"
2#include "aos/events/simulated_event_loop.h"
3#include "aos/init.h"
4#include "frc971/control_loops/pose.h"
milind-u16e3a082023-01-21 13:53:43 -08005#include "frc971/vision/calibration_generated.h"
Yash Chainanid5c7f0d2022-11-19 17:05:57 -08006#include "frc971/vision/charuco_lib.h"
7#include "frc971/vision/target_mapper.h"
8#include "opencv2/aruco.hpp"
9#include "opencv2/calib3d.hpp"
10#include "opencv2/core/eigen.hpp"
11#include "opencv2/features2d.hpp"
12#include "opencv2/highgui.hpp"
13#include "opencv2/highgui/highgui.hpp"
14#include "opencv2/imgproc.hpp"
milind-u09fb1252023-01-28 19:21:41 -080015#include "y2023/vision/aprilrobotics.h"
milind-u16e3a082023-01-21 13:53:43 -080016#include "y2023/vision/calibration_data.h"
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080017
18DEFINE_string(json_path, "target_map.json",
19 "Specify path for json with initial pose guesses.");
milind-u09fb1252023-01-28 19:21:41 -080020DECLARE_int32(team_number);
Milind Upadhyayc6e42ee2022-12-27 00:02:11 -080021
milind-u16e3a082023-01-21 13:53:43 -080022namespace y2023 {
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080023namespace vision {
milind-u16e3a082023-01-21 13:53:43 -080024using frc971::vision::CharucoExtractor;
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080025using frc971::vision::DataAdapter;
milind-u16e3a082023-01-21 13:53:43 -080026using frc971::vision::ImageCallback;
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080027using frc971::vision::PoseUtils;
milind-u09fb1252023-01-28 19:21:41 -080028using frc971::vision::TargetMap;
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080029using frc971::vision::TargetMapper;
milind-u16e3a082023-01-21 13:53:43 -080030namespace calibration = frc971::vision::calibration;
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080031
32// Change reference frame from camera to robot
Milind Upadhyayebf93ee2023-01-05 14:12:58 -080033Eigen::Affine3d CameraToRobotDetection(Eigen::Affine3d H_camrob_target,
Milind Upadhyayc5beba12022-12-17 17:41:20 -080034 Eigen::Affine3d extrinsics) {
35 const Eigen::Affine3d H_robot_camrob = extrinsics;
Milind Upadhyayebf93ee2023-01-05 14:12:58 -080036 const Eigen::Affine3d H_robot_target = H_robot_camrob * H_camrob_target;
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080037 return H_robot_target;
38}
39
40// Add detected apriltag poses relative to the robot to
41// timestamped_target_detections
milind-u09fb1252023-01-28 19:21:41 -080042void HandleAprilTag(const TargetMap &map,
43 aos::distributed_clock::time_point pi_distributed_time,
Milind Upadhyay915d6002022-12-26 20:37:43 -080044 std::vector<DataAdapter::TimestampedDetection>
45 *timestamped_target_detections,
Milind Upadhyayc5beba12022-12-17 17:41:20 -080046 Eigen::Affine3d extrinsics) {
milind-u09fb1252023-01-28 19:21:41 -080047 for (const auto *target_pose : *map.target_poses()) {
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080048 Eigen::Translation3d T_camera_target = Eigen::Translation3d(
milind-u09fb1252023-01-28 19:21:41 -080049 target_pose->x(), target_pose->y(), target_pose->z());
50 Eigen::Quaterniond R_camera_target =
51 PoseUtils::EulerAnglesToQuaternion(Eigen::Vector3d(
52 target_pose->roll(), target_pose->pitch(), target_pose->yaw()));
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080053
milind-u09fb1252023-01-28 19:21:41 -080054 Eigen::Affine3d H_camcv_target = T_camera_target * R_camera_target;
Milind Upadhyayebf93ee2023-01-05 14:12:58 -080055 // With X, Y, Z being robot axes and x, y, z being camera axes,
56 // x = -Y, y = -Z, z = X
57 static const Eigen::Affine3d H_camcv_camrob =
58 Eigen::Affine3d((Eigen::Matrix4d() << 0.0, -1.0, 0.0, 0.0, 0.0, 0.0,
59 -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)
60 .finished());
61 Eigen::Affine3d H_camrob_target = H_camcv_camrob.inverse() * H_camcv_target;
Milind Upadhyayc5beba12022-12-17 17:41:20 -080062 Eigen::Affine3d H_robot_target =
63 CameraToRobotDetection(H_camrob_target, extrinsics);
Milind Upadhyayebf93ee2023-01-05 14:12:58 -080064
Milind Upadhyayc5beba12022-12-17 17:41:20 -080065 ceres::examples::Pose3d target_pose_camera =
66 PoseUtils::Affine3dToPose3d(H_camrob_target);
67 double distance_from_camera = target_pose_camera.p.norm();
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080068
milind-u09fb1252023-01-28 19:21:41 -080069 CHECK(map.has_monotonic_timestamp_ns())
70 << "Need detection timestamps for mapping";
71
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080072 timestamped_target_detections->emplace_back(
Milind Upadhyayebf93ee2023-01-05 14:12:58 -080073 DataAdapter::TimestampedDetection{
74 .time = pi_distributed_time,
75 .H_robot_target = H_robot_target,
76 .distance_from_camera = distance_from_camera,
milind-u09fb1252023-01-28 19:21:41 -080077 .id = static_cast<TargetMapper::TargetId>(target_pose->id())});
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080078 }
79}
80
milind-u16e3a082023-01-21 13:53:43 -080081const calibration::CameraCalibration *FindCameraCalibration(
82 const calibration::CalibrationData *calibration_data,
83 std::string_view node_name) {
84 for (const calibration::CameraCalibration *candidate :
85 *calibration_data->camera_calibrations()) {
86 if (candidate->node_name()->string_view() != node_name) {
87 continue;
88 }
89 if (candidate->team_number() != FLAGS_team_number) {
90 continue;
91 }
92 return candidate;
93 }
94 LOG(FATAL) << ": Failed to find camera calibration for " << node_name
95 << " on " << FLAGS_team_number;
96}
97
Milind Upadhyayc5beba12022-12-17 17:41:20 -080098Eigen::Affine3d CameraExtrinsics(
Yash Chainanid5c7f0d2022-11-19 17:05:57 -080099 const calibration::CameraCalibration *camera_calibration) {
milind-u16e3a082023-01-21 13:53:43 -0800100 const frc971::vision::calibration::TransformationMatrix *transform =
101 camera_calibration->has_turret_extrinsics()
102 ? camera_calibration->turret_extrinsics()
103 : camera_calibration->fixed_extrinsics();
104
105 cv::Mat result(
106 4, 4, CV_32F,
107 const_cast<void *>(static_cast<const void *>(transform->data()->data())));
108 result.convertTo(result, CV_64F);
109 CHECK_EQ(result.total(), transform->data()->size());
110
111 Eigen::Matrix4d result_eigen;
112 cv::cv2eigen(result, result_eigen);
113 return Eigen::Affine3d(result_eigen);
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800114}
115
116// Get images from pi and pass apriltag positions to HandleAprilTag()
117void HandlePiCaptures(
milind-u09fb1252023-01-28 19:21:41 -0800118 aos::EventLoop *pi_event_loop, aos::logger::LogReader *reader,
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800119 std::vector<DataAdapter::TimestampedDetection>
120 *timestamped_target_detections,
milind-u09fb1252023-01-28 19:21:41 -0800121 std::vector<std::unique_ptr<AprilRoboticsDetector>> *detectors) {
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800122 const aos::FlatbufferSpan<calibration::CalibrationData> calibration_data(
123 CalibrationData());
milind-u09fb1252023-01-28 19:21:41 -0800124
125 const auto node_name = pi_event_loop->node()->name()->string_view();
126 const calibration::CameraCalibration *calibration =
127 FindCameraCalibration(&calibration_data.message(), node_name);
Milind Upadhyayc5beba12022-12-17 17:41:20 -0800128 const auto extrinsics = CameraExtrinsics(calibration);
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800129
Milind Upadhyayc6e42ee2022-12-27 00:02:11 -0800130 // TODO(milind): change to /camera once we log at full frequency
131 static constexpr std::string_view kImageChannel = "/camera/decimated";
milind-u09fb1252023-01-28 19:21:41 -0800132 detectors->emplace_back(
133 std::make_unique<AprilRoboticsDetector>(pi_event_loop, kImageChannel));
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800134
milind-u09fb1252023-01-28 19:21:41 -0800135 pi_event_loop->MakeWatcher("/camera", [=](const TargetMap &map) {
136 aos::distributed_clock::time_point pi_distributed_time =
137 reader->event_loop_factory()
138 ->GetNodeEventLoopFactory(pi_event_loop->node())
139 ->ToDistributedClock(aos::monotonic_clock::time_point(
140 aos::monotonic_clock::duration(map.monotonic_timestamp_ns())));
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800141
milind-u09fb1252023-01-28 19:21:41 -0800142 HandleAprilTag(map, pi_distributed_time, timestamped_target_detections,
143 extrinsics);
144 });
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800145}
146
147void MappingMain(int argc, char *argv[]) {
148 std::vector<std::string> unsorted_logfiles =
149 aos::logger::FindLogs(argc, argv);
150
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800151 std::vector<DataAdapter::TimestampedDetection> timestamped_target_detections;
152
153 // open logfiles
154 aos::logger::LogReader reader(aos::logger::SortParts(unsorted_logfiles));
155 reader.Register();
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800156
milind-u09fb1252023-01-28 19:21:41 -0800157 std::vector<std::unique_ptr<AprilRoboticsDetector>> detectors;
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800158
159 const aos::Node *pi1 =
160 aos::configuration::GetNode(reader.configuration(), "pi1");
161 std::unique_ptr<aos::EventLoop> pi1_event_loop =
162 reader.event_loop_factory()->MakeEventLoop("pi1", pi1);
milind-u09fb1252023-01-28 19:21:41 -0800163 HandlePiCaptures(pi1_event_loop.get(), &reader,
164 &timestamped_target_detections, &detectors);
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800165
166 const aos::Node *pi2 =
167 aos::configuration::GetNode(reader.configuration(), "pi2");
168 std::unique_ptr<aos::EventLoop> pi2_event_loop =
169 reader.event_loop_factory()->MakeEventLoop("pi2", pi2);
milind-u09fb1252023-01-28 19:21:41 -0800170 HandlePiCaptures(pi2_event_loop.get(), &reader,
171 &timestamped_target_detections, &detectors);
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800172
173 const aos::Node *pi3 =
174 aos::configuration::GetNode(reader.configuration(), "pi3");
175 std::unique_ptr<aos::EventLoop> pi3_event_loop =
176 reader.event_loop_factory()->MakeEventLoop("pi3", pi3);
milind-u09fb1252023-01-28 19:21:41 -0800177 HandlePiCaptures(pi3_event_loop.get(), &reader,
178 &timestamped_target_detections, &detectors);
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800179
180 const aos::Node *pi4 =
181 aos::configuration::GetNode(reader.configuration(), "pi4");
182 std::unique_ptr<aos::EventLoop> pi4_event_loop =
183 reader.event_loop_factory()->MakeEventLoop("pi4", pi4);
milind-u09fb1252023-01-28 19:21:41 -0800184 HandlePiCaptures(pi4_event_loop.get(), &reader,
185 &timestamped_target_detections, &detectors);
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800186
187 reader.event_loop_factory()->Run();
188
189 auto target_constraints =
Milind Upadhyayc5beba12022-12-17 17:41:20 -0800190 DataAdapter::MatchTargetDetections(timestamped_target_detections);
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800191
192 frc971::vision::TargetMapper mapper(FLAGS_json_path, target_constraints);
milind-u09fb1252023-01-28 19:21:41 -0800193 mapper.Solve("charged_up");
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800194
milind-u09fb1252023-01-28 19:21:41 -0800195 // Clean up all the pointers
196 for (auto &detector_ptr : detectors) {
197 detector_ptr.reset();
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800198 }
199}
200
201} // namespace vision
milind-u16e3a082023-01-21 13:53:43 -0800202} // namespace y2023
Yash Chainanid5c7f0d2022-11-19 17:05:57 -0800203
204int main(int argc, char **argv) {
205 aos::InitGoogle(&argc, &argv);
milind-u16e3a082023-01-21 13:53:43 -0800206 y2023::vision::MappingMain(argc, argv);
Milind Upadhyay915d6002022-12-26 20:37:43 -0800207}