Skip to content

Commit fb61381

Browse files
committed
Ope
1 parent 3c332db commit fb61381

File tree

3 files changed

+556
-0
lines changed

3 files changed

+556
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) PhotonVision
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
#include <random>
26+
27+
#include "cpnp/constrained_pnp.h"
28+
#include <gtest/gtest.h>
29+
30+
#include <iostream>
31+
#include <vector>
32+
33+
#include <frc/geometry/Transform2d.h>
34+
#include <frc/geometry/Translation2d.h>
35+
#include <units/length.h>
36+
37+
/// @brief Get the positions of the corners of a tag relative to the tag origin, in homogeneous coordinates.
38+
/// @return A 4x4 matrix where the first three rows are the x, y, and z coordinates of the corners, and the last row is all ones.
39+
Eigen::Matrix4d tagCenterToCorners() {
40+
const units::meter_t width{6.0_in};
41+
const units::meter_t height{6.0_in};
42+
43+
Eigen::Matrix<double, 4, 3> corners {};
44+
corners << 0, -width.value(), -height.value(),
45+
0, width.value(), -height.value(),
46+
0, width.value(), height.value(),
47+
0, -width.value(), height.value();
48+
49+
Eigen::Matrix4d ret {};
50+
ret.block(0, 0, 3, 4) = corners.transpose();
51+
ret.row(3) = Eigen::Matrix<double, 1, 4>::Ones();
52+
53+
return ret;
54+
}
55+
56+
/// @brief Get a list of test tags' corners in homogeneous coordinates. The locations of these tags is hard-coded, because I'm lazy
57+
/// @return A 4xN matrix where the first three rows are the x, y, and z coordinates of the corners, and the last row is all ones.
58+
Eigen::Matrix<double, 4, Eigen::Dynamic> getTestTags() {
59+
// change me to add more tags
60+
Eigen::Matrix<double, 4, Eigen::Dynamic> ret(4, 8);
61+
62+
auto tagCorners = tagCenterToCorners();
63+
64+
// Add all the corners of tag 1, located at (2, 0, 1) and rotated 180 degrees
65+
// about +Z
66+
auto tag1pose = frc::Pose3d{frc::Translation3d{2_m, 0_m, 1_m},
67+
frc::Rotation3d{0_deg, 0_deg, 180_deg}}
68+
.ToMatrix();
69+
70+
ret.block(0, 0, 4, 4) = tag1pose * tagCorners;
71+
72+
// Add all the corners of tag 2, located at (2, 1, 1) and rotated 180 degrees
73+
// about +Z
74+
auto tag2pose = frc::Pose3d{frc::Translation3d{2_m, 1_m, 1_m},
75+
frc::Rotation3d{0_deg, 0_deg, 180_deg}}
76+
.ToMatrix();
77+
78+
ret.block(0, 4, 4, 4) = tag2pose * tagCorners;
79+
80+
return ret;
81+
}
82+
83+
84+
/// @brief Project the corners of the tags into the camera frame.
85+
/// @param K OpenCV camera calibration matrix
86+
/// @param field2camera_wpi The location of the camera in the field frame. This is the "wpi" camera pose, with X forward, Y left, and Z up.
87+
/// @param field2corners The locations of the corners of the tags in the field frame
88+
/// @return Observed pixel locations
89+
Eigen::Matrix<double, 2, Eigen::Dynamic>
90+
projectPoints(double f_x,
91+
double f_y,
92+
double c_x,
93+
double c_y,
94+
Eigen::Matrix4d field2camera_wpi,
95+
Eigen::Matrix<double, 4, Eigen::Dynamic> field2corners) {
96+
// robot is ENU, cameras are SDE
97+
Eigen::Matrix4d camera2opencv{
98+
{0, 0, 1, 0},
99+
{-1, 0, 0, 0},
100+
{0, -1, 0, 0},
101+
{0, 0, 0, 1},
102+
};
103+
Eigen::Matrix4d field2camera = field2camera_wpi * camera2opencv;
104+
105+
// transform the points to camera space
106+
auto camera2corners = field2camera.inverse() * field2corners;
107+
108+
// project the points. This is verbose but whatever
109+
Eigen::Matrix<double, 3, 3> K;
110+
K << f_x, 0, c_x, 0, f_y, c_y, 0, 0, 1;
111+
auto pointsUnnormalized =
112+
K * camera2corners.block(0, 0, 3, camera2corners.cols());
113+
auto u =
114+
pointsUnnormalized.row(0).array() / pointsUnnormalized.row(2).array();
115+
auto v =
116+
pointsUnnormalized.row(1).array() / pointsUnnormalized.row(2).array();
117+
118+
Eigen::Matrix<double, 2, Eigen::Dynamic> ret(2, camera2corners.cols());
119+
ret.row(0) = u;
120+
ret.row(1) = v;
121+
return ret;
122+
}
123+
124+
TEST(PoseTest, Projection) {
125+
// cpnp::ProblemParams params(4);
126+
127+
// params.K << 599.375, 0., 479.5, 0., 599.16666667, 359.5, 0., 0., 1.;
128+
// params.K << 100, 0., 0, 0., 100, 0, 0., 0., 1.;
129+
130+
// params.f_x = 100;
131+
// params.f_y = 100;
132+
// params.c_x = 0;
133+
// params.c_y = 0;
134+
135+
// params.worldPoints = getTestTags();
136+
137+
// frc::Transform3d robot2camera {};
138+
// params.imagePoints = projectPoints(params.f_x, params.f_y, params.c_x, params.c_y, robot2camera.ToMatrix(), params.worldPoints);
139+
140+
// std::cout << "world points:\n" << params.worldPoints << std::endl;
141+
// std::cout << "image points:\n" << params.imagePoints << std::endl;
142+
}
143+
144+
TEST(PoseTest, Naive) {
145+
for (int j = 0; j < 20; j++) {
146+
147+
cpnp::ProblemParams params;
148+
149+
params.f_x = 100;
150+
params.f_y = 100;
151+
params.c_x = 0;
152+
params.c_y = 0;
153+
154+
auto tags = getTestTags();
155+
frc::Transform3d robot2camera {frc::Pose3d{}, frc::Pose3d{frc::Translation3d{-1.3_m, 0.25_m, 0_m}, frc::Rotation3d{0_rad, 0_rad, -1.5_rad}}};
156+
auto imgpoints = projectPoints(params.f_x, params.f_y, params.c_x, params.c_y, robot2camera.ToMatrix(), tags);
157+
158+
// Add +-1 px of noise to the image points
159+
std::random_device rd;
160+
std::mt19937 rng(rd());
161+
std::uniform_real_distribution<double> dist(-1.0, 1.0);
162+
163+
for (int i = 0; i < tags.cols(); ++i) {
164+
// Add some noise to imgpoints, and add it to our list
165+
params.imagePoints.push_back(imgpoints(0, i) + dist(rng));
166+
params.imagePoints.push_back(imgpoints(1, i) + dist(rng));
167+
168+
params.worldPoints.push_back(tags(0, i));
169+
params.worldPoints.push_back(tags(1, i));
170+
params.worldPoints.push_back(tags(2, i));
171+
}
172+
173+
auto t0 = std::chrono::high_resolution_clock::now();
174+
auto polynomial_ret = cpnp::solve_polynomial(params);
175+
auto t1 = std::chrono::high_resolution_clock::now();
176+
177+
fmt::println("Polynomial solve time: {}ms", std::chrono::duration_cast<std::chrono::nanoseconds>(t1-t0).count() / 1e6);
178+
179+
// auto t2 = std::chrono::high_resolution_clock::now();
180+
// auto naive_ret = cpnp::solve_naive(params);
181+
// auto t3 = std::chrono::high_resolution_clock::now();
182+
183+
// fmt::println("Naive solve time: {}ms", std::chrono::duration_cast<std::chrono::nanoseconds>(t3-t2).count() / 1e6);
184+
fmt::println("Polynomial method says robot is at:\n{}", polynomial_ret.ToMatrix());
185+
// fmt::println("Naive method says robot is at:\n{}", naive_ret.ToMatrix());
186+
}
187+
}

0 commit comments

Comments
 (0)