blob: 9b3b51f35538cf7455543f4dc80eae6ac893a1da [file] [log] [blame]
James Kuszmaul9f9676d2019-01-25 21:27:58 -08001#include "frc971/control_loops/pose.h"
2
3#include "gtest/gtest.h"
4
Stephan Pleinesf63bde82024-01-13 15:59:33 -08005namespace frc971::control_loops::testing {
James Kuszmaul9f9676d2019-01-25 21:27:58 -08006
7// Test that basic accessors on an individual Pose object work as expected.
8TEST(PoseTest, BasicPoseTest) {
9 // Provide a basic Pose with non-zero components for everything.
10 Pose pose({1, 1, 0.5}, 0.5);
11 // The xy_norm should just be based on the x/y positions, not the Z; hence
12 // sqrt(2) rather than sqrt(1^2 + 1^2 + 0.5^2).
13 EXPECT_DOUBLE_EQ(::std::sqrt(2.0), pose.xy_norm());
14 // Similarly, heading should just be atan2(y, x).
15 EXPECT_DOUBLE_EQ(M_PI / 4.0, pose.heading());
16 // Global and relative poses should be the same since we did not construct
17 // this off of a separate Pose.
18 EXPECT_EQ(1.0, pose.rel_pos().x());
19 EXPECT_EQ(1.0, pose.rel_pos().y());
20 EXPECT_EQ(0.5, pose.rel_pos().z());
21
22 EXPECT_EQ(1.0, pose.abs_pos().x());
23 EXPECT_EQ(1.0, pose.abs_pos().y());
James Kuszmaul090563a2019-02-09 14:43:20 -080024 EXPECT_EQ(1.0, pose.abs_xy().x());
25 EXPECT_EQ(1.0, pose.abs_xy().y());
James Kuszmaul9f9676d2019-01-25 21:27:58 -080026 EXPECT_EQ(0.5, pose.abs_pos().z());
27
28 EXPECT_EQ(0.5, pose.rel_theta());
29 EXPECT_EQ(0.5, pose.abs_theta());
30
31 pose.set_theta(3.14);
32 EXPECT_EQ(3.14, pose.rel_theta());
33 pose.mutable_pos()->x() = 9.71;
34 EXPECT_EQ(9.71, pose.rel_pos().x());
James Kuszmaul090563a2019-02-09 14:43:20 -080035
36 EXPECT_EQ(nullptr, pose.base());
37 Pose new_base;
38 pose.set_base(&new_base);
39 EXPECT_EQ(&new_base, pose.base());
James Kuszmaul9f9676d2019-01-25 21:27:58 -080040}
41
42// Check that Poses behave as expected when constructed relative to another
43// POse.
44TEST(PoseTest, BaseTest) {
45 // Tolerance for the EXPECT_NEARs. Because we are doing enough trig operations
46 // under the hood we actually start to lose some precision.
47 constexpr double kEps = 1e-15;
48 // The points we will construct have absolute positions at:
49 // base1: (1, 1)
50 // base2: (-1, 1)
51 // rel1: (0, 2)
52 // Where rel1 is expressed as compared to base1, noting that because base1
53 // has a yaw of M_PI, the position of rel1 compared to base1 is (1, -1)
54 // rather than (-1, 1).
55 Pose base1({1, 1, 0}, M_PI);
56 Pose base2({-1, 1, 0}, -M_PI / 2.0);
57 Pose rel1(&base1, {1, -1, 0}, 0.0);
58 EXPECT_NEAR(0.0, rel1.abs_pos().x(), kEps);
59 EXPECT_NEAR(2.0, rel1.abs_pos().y(), kEps);
60 EXPECT_NEAR(M_PI, rel1.abs_theta(), kEps);
61 // Check that, when rebasing to base2, the absolute position does not change
62 // and the relative POse changes to be relative to base2.
63 Pose rel2 = rel1.Rebase(&base2);
64 EXPECT_NEAR(rel1.abs_pos().x(), rel2.abs_pos().x(), kEps);
65 EXPECT_NEAR(rel1.abs_pos().y(), rel2.abs_pos().y(), kEps);
66 EXPECT_NEAR(rel1.abs_pos().z(), rel2.abs_pos().z(), kEps);
67 EXPECT_NEAR(rel1.abs_theta(), rel2.abs_theta(), kEps);
68 EXPECT_NEAR(-1.0, rel2.rel_pos().x(), kEps);
69 EXPECT_NEAR(1.0, rel2.rel_pos().y(), kEps);
70 EXPECT_NEAR(-M_PI / 2.0, rel2.rel_theta(), kEps);
71 // Check that rebasing onto nullptr results in a Pose based in the global
72 // frame.
73 Pose abs = rel1.Rebase(nullptr);
74 EXPECT_NEAR(rel1.abs_pos().x(), abs.abs_pos().x(), kEps);
75 EXPECT_NEAR(rel1.abs_pos().y(), abs.abs_pos().y(), kEps);
76 EXPECT_NEAR(rel1.abs_pos().z(), abs.abs_pos().z(), kEps);
77 EXPECT_NEAR(rel1.abs_theta(), abs.abs_theta(), kEps);
78 EXPECT_NEAR(rel1.abs_pos().x(), abs.rel_pos().x(), kEps);
79 EXPECT_NEAR(rel1.abs_pos().y(), abs.rel_pos().y(), kEps);
80 EXPECT_NEAR(rel1.abs_pos().z(), abs.rel_pos().z(), kEps);
81 EXPECT_NEAR(rel1.abs_theta(), abs.rel_theta(), kEps);
82}
83
James Kuszmaul3ca28612020-02-15 17:52:27 -080084// Tests that we can go between transformation matrices and Pose objects.
85TEST(PoseTest, TransformationMatrixTest) {
86 // First, sanity check the basic case.
87 Pose pose({0, 0, 0}, 0);
88 typedef Eigen::Matrix<double, 4, 4> TransformationMatrix;
89 ASSERT_EQ(TransformationMatrix::Identity(), pose.AsTransformationMatrix());
90 Pose reproduced_pose(pose.AsTransformationMatrix());
91 ASSERT_EQ(reproduced_pose.rel_pos(), pose.rel_pos());
92 ASSERT_EQ(reproduced_pose.rel_theta(), pose.rel_theta());
93 // Check a basic case of rotation + translation.
94 *pose.mutable_pos() << 1, 2, 3;
95 pose.set_theta(M_PI_2);
96 TransformationMatrix expected;
97 expected << 0, -1, 0, 1, 1, 0, 0, 2, 0, 0, 1, 3, 0, 0, 0, 1;
Philipp Schrader790cb542023-07-05 21:06:52 -070098 TransformationMatrix pose_transformation = pose.AsTransformationMatrix();
James Kuszmaul3ca28612020-02-15 17:52:27 -080099 ASSERT_LT((expected - pose_transformation).norm(), 1e-15)
100 << "expected:\n"
101 << expected << "\nBut got:\n"
102 << pose_transformation;
103 ASSERT_EQ(Eigen::Vector4d(1, 2, 3, 1),
104 pose_transformation * Eigen::Vector4d(0, 0, 0, 1));
105 ASSERT_LT((Eigen::Vector4d(0, 3, 3, 1) -
106 pose_transformation * Eigen::Vector4d(1, 1, 0, 1))
107 .norm(),
108 1e-15)
109 << "got " << pose_transformation * Eigen::Vector4d(1, 1, 0, 1);
110
111 // Also, confirm that setting a new base does not affect the pose.
112 Pose faux_base({1, 1, 1}, 1);
113 pose.set_base(&faux_base);
114
115 ASSERT_EQ(pose_transformation, pose.AsTransformationMatrix());
116
117 reproduced_pose = Pose(pose_transformation);
118 ASSERT_EQ(reproduced_pose.rel_pos(), pose.rel_pos());
119 ASSERT_EQ(reproduced_pose.rel_theta(), pose.rel_theta());
120 // And check that if we introduce a pitch to the transformation matrix that it
121 // does not impact the resulting Pose (which only has a yaw component).
122 pose_transformation.block<3, 3>(0, 0) =
123 Eigen::AngleAxis<double>(0.5, Eigen::Vector3d::UnitX()) *
124 pose_transformation.block<3, 3>(0, 0);
125 reproduced_pose = Pose(pose_transformation);
126 ASSERT_EQ(reproduced_pose.rel_pos(), pose.rel_pos());
127 ASSERT_EQ(reproduced_pose.rel_theta(), pose.rel_theta());
128}
129
James Kuszmaul090563a2019-02-09 14:43:20 -0800130// Tests that basic accessors for LineSegment behave as expected.
131TEST(LineSegmentTest, BasicAccessorTest) {
132 LineSegment l;
133 EXPECT_EQ(0.0, l.pose1().rel_theta());
134 l.mutable_pose1()->set_theta(1.234);
135 EXPECT_EQ(1.234, l.pose1().rel_theta());
136 EXPECT_EQ(0.0, l.pose2().rel_theta());
137 l.mutable_pose2()->set_theta(5.678);
138 EXPECT_EQ(5.678, l.pose2().rel_theta());
139
140 const ::std::vector<Pose> plot_pts = l.PlotPoints();
141 ASSERT_EQ(2u, plot_pts.size());
142 EXPECT_EQ(l.pose1().rel_theta(), plot_pts[0].rel_theta());
143 EXPECT_EQ(l.pose2().rel_theta(), plot_pts[1].rel_theta());
144}
145
James Kuszmaul9f9676d2019-01-25 21:27:58 -0800146// Tests that basic checks for intersection function as expected.
147TEST(LineSegmentTest, TrivialIntersectTest) {
148 Pose p1({0, 0, 0}, 0.0), p2({2, 0, 0}, 0.0);
149 // A line segment from (0, 0) to (0, 2).
150 LineSegment l1(p1, p2);
151 Pose q1({1, -1, 0}, 0.0), q2({1, 1, 0}, 0.0);
152 // A line segment from (1, -1) to (1, 1).
153 LineSegment l2(q1, q2);
154 // The two line segments should intersect.
155 EXPECT_TRUE(l1.Intersects(l2));
156 EXPECT_TRUE(l2.Intersects(l1));
157
158 // If we switch around the orderings such that the line segments are
159 // (0, 0) -> (1, -1) and (2, 0)->(1, 1) then the line segments do not
160 // intersect.
161 LineSegment l3(p1, q1);
162 LineSegment l4(p2, q2);
163 EXPECT_FALSE(l3.Intersects(l4));
164 EXPECT_FALSE(l4.Intersects(l3));
165}
166
167// Check that when we construct line segments that are collinear, both with
168// overlapping bits and without overlapping bits, they register as not
169// intersecting.
170// We may want this behavior to change in the future, but for now check for
171// consistency.
172TEST(LineSegmentTest, CollinearIntersectTest) {
173 Pose p1({0, 0, 0}, 0.0), p2({1, 0, 0}, 0.0), p3({2, 0, 0}, 0.0),
174 p4({3, 0, 0}, 0.0);
175 // These two line segments overlap and are collinear, one going from 0 to 2
176 // and the other from 1 to 3 on the X-axis.
177 LineSegment l1(p1, p3);
178 LineSegment l2(p2, p4);
179 EXPECT_FALSE(l1.Intersects(l2));
180 EXPECT_FALSE(l2.Intersects(l1));
181
182 // These two line segments do not overlap and are collinear, one going from 0
183 // to 1 and the other from 2 to 3 on the X-axis.
184 LineSegment l3(p1, p2);
185 LineSegment l4(p3, p4);
186 EXPECT_FALSE(l3.Intersects(l4));
187 EXPECT_FALSE(l4.Intersects(l3));
188
189 // Test when one line segment is completely contained within the other.
190 LineSegment l5(p1, p4);
191 LineSegment l6(p3, p2);
192 EXPECT_FALSE(l5.Intersects(l6));
193 EXPECT_FALSE(l6.Intersects(l5));
194}
195
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800196} // namespace frc971::control_loops::testing