blob: a4f7bf27dc178a50bd40e548e36e2c22c02fd096 [file] [log] [blame]
James Kuszmaul851b3962022-02-27 16:42:15 -08001#include "frc971/control_loops/aiming/aiming.h"
2
James Kuszmaul851b3962022-02-27 16:42:15 -08003#include "gtest/gtest.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07004
James Kuszmaul851b3962022-02-27 16:42:15 -08005#include "frc971/constants.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07006#include "frc971/control_loops/pose.h"
James Kuszmaul851b3962022-02-27 16:42:15 -08007
8namespace frc971::control_loops::aiming::testing {
9
10TEST(AimerTest, StandingStill) {
11 const Pose target({0.0, 0.0, 0.0}, 0.0);
12 Pose robot_pose({1.0, 0.0, 0.0}, 0.0);
13 const constants::Range range{-4.5, 4.5, -4.0, 4.0};
14 const double kBallSpeed = 10.0;
15 // Robot is ahead of target, should have to turret to 180 deg to shoot.
16 TurretGoal goal = AimerGoal(
17 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
18 RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
19 EXPECT_FLOAT_EQ(M_PI, goal.position);
20 EXPECT_FLOAT_EQ(0.0, goal.velocity);
21 EXPECT_FLOAT_EQ(1.0, goal.virtual_shot_distance);
22 EXPECT_FLOAT_EQ(1.0, goal.target_distance);
23
24 // If there is a turret offset, it should get compensated out.
25 goal = AimerGoal(ShotConfig{target, ShotMode::kShootOnTheFly, range,
26 kBallSpeed, 0.0, M_PI},
27 RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
28 EXPECT_FLOAT_EQ(0.0, goal.position);
29
30 robot_pose = Pose({-1.0, 0.0, 0.0}, 1.0);
31 goal = AimerGoal(
32 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
33 RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
34 EXPECT_FLOAT_EQ(-1.0, goal.position);
35 EXPECT_FLOAT_EQ(0.0, goal.velocity);
36 EXPECT_FLOAT_EQ(1.0, goal.virtual_shot_distance);
37 EXPECT_FLOAT_EQ(1.0, goal.target_distance);
38
39 // Test that we handle the case that where we are right on top of the target.
40 robot_pose = Pose({0.0, 0.0, 0.0}, 0.0);
41 goal = AimerGoal(
42 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
43 RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
44 EXPECT_FLOAT_EQ(0.0, goal.position);
45 EXPECT_FLOAT_EQ(0.0, goal.velocity);
46 EXPECT_FLOAT_EQ(0.0, goal.virtual_shot_distance);
47 EXPECT_FLOAT_EQ(0.0, goal.target_distance);
48}
49
50// Test that spinning in place results in correct velocity goals.
51TEST(AimerTest, SpinningRobot) {
52 const Pose target({0.0, 0.0, 0.0}, 0.0);
53 Pose robot_pose({-1.0, 0.0, 0.0}, 0.0);
54 const constants::Range range{-4.5, 4.5, -4.0, 4.0};
55 const double kBallSpeed = 10.0;
56 TurretGoal goal = AimerGoal(
57 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
58 RobotState{robot_pose, {0.0, 0.0}, 971.0, 0.0});
59 EXPECT_FLOAT_EQ(0.0, goal.position);
60 EXPECT_FLOAT_EQ(-971.0, goal.velocity);
61}
62
63// Tests that when we drive straight away from the target we don't have to spin
64// the turret.
65TEST(AimerTest, DrivingAwayFromTarget) {
66 const Pose target({0.0, 0.0, 0.0}, 0.0);
67 Pose robot_pose({-1.0, 0.0, 0.0}, 0.0);
68 const constants::Range range{-4.5, 4.5, -4.0, 4.0};
69 const double kBallSpeed = 10.0;
70 TurretGoal goal = AimerGoal(
71 ShotConfig{target, ShotMode::kStatic, range, kBallSpeed, 0.0, 0.0},
72 RobotState{robot_pose, {-1.0, 0.0}, 0.0, 0.0});
73 EXPECT_FLOAT_EQ(0.0, goal.position);
74 EXPECT_FLOAT_EQ(0.0, goal.velocity);
75 EXPECT_FLOAT_EQ(1.0, goal.virtual_shot_distance);
76 EXPECT_FLOAT_EQ(1.0, goal.target_distance);
77 // Next, try with shooting-on-the-fly enabled--because we are driving straight
78 // away from the target, only the goal distance should be impacted.
79 goal = AimerGoal(
80 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
81 RobotState{robot_pose, {-1.0, 0.0}, 0.0, 0.0});
82 EXPECT_FLOAT_EQ(0.0, goal.position);
83 EXPECT_FLOAT_EQ(0.0, goal.velocity);
84 EXPECT_FLOAT_EQ(1.111, goal.virtual_shot_distance);
85 EXPECT_FLOAT_EQ(1.0, goal.target_distance);
86}
87
88// Tests that when we drive perpendicular to the target, we do have to spin.
89TEST(AimerTest, DrivingLateralToTarget) {
90 const Pose target({0.0, 0.0, 0.0}, 0.0);
91 Pose robot_pose({0.0, -1.0, 0.0}, 0.0);
92 const constants::Range range{-4.5, 4.5, -4.0, 4.0};
93 const double kBallSpeed = 10.0;
94 TurretGoal goal = AimerGoal(
95 ShotConfig{target, ShotMode::kStatic, range, kBallSpeed, 0.0, 0.0},
96 RobotState{robot_pose, {1.0, 0.0}, 0.0, 0.0});
97 EXPECT_FLOAT_EQ(M_PI_2, goal.position);
98 EXPECT_FLOAT_EQ(1.0, goal.velocity);
99 EXPECT_FLOAT_EQ(1.0, goal.virtual_shot_distance);
100 EXPECT_FLOAT_EQ(1.0, goal.target_distance);
101 // Next, test with shooting-on-the-fly enabled, The goal numbers should all be
102 // slightly offset due to the robot velocity.
103 goal = AimerGoal(
104 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
105 RobotState{robot_pose, {1.0, 0.0}, 0.0, 0.0});
106 // Confirm that the turret heading goal is a bit more than pi / 2, but not by
107 // too much.
108 EXPECT_LT(M_PI_2 + 0.01, goal.position);
109 EXPECT_GT(M_PI_2 + 0.5, goal.position);
110 // Similarly, the turret velocity goal should be a bit less than 1.0,
111 // since the turret is no longer at exactly a right angle.
112 EXPECT_LT(0.9, goal.velocity);
113 EXPECT_GT(0.999, goal.velocity);
114 // And the distance to the goal should be a bit greater than 1.0.
115 EXPECT_LT(1.00001, goal.virtual_shot_distance);
116 EXPECT_GT(1.1, goal.virtual_shot_distance);
117 EXPECT_FLOAT_EQ(1.0, goal.target_distance);
118}
119
120// Confirms that when we move the turret heading so that it would be entirely
121// out of the normal range of motion that we send a valid (in-range) goal.
122// I.e., test that we have some hysteresis, but that it doesn't take us
123// out-of-range.
124TEST(AimerTest, WrapWhenOutOfRange) {
125 // Start ourselves needing a turret angle of 0.0.
126 const Pose target({0.0, 0.0, 0.0}, 0.0);
127 Pose robot_pose({-1.0, 0.0, 0.0}, 0.0);
128 const constants::Range range{-5.5, 5.5, -5.0, 5.0};
129 const double kBallSpeed = 10.0;
130 TurretGoal goal = AimerGoal(
131 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
132 RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
133 EXPECT_FLOAT_EQ(0.0, goal.position);
134 EXPECT_FLOAT_EQ(0.0, goal.velocity);
135
136 // Rotate a bit...
137 robot_pose = Pose({-1.0, 0.0, 0.0}, 2.0);
138 goal = AimerGoal(
139 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
140 RobotState{robot_pose, {0.0, 0.0}, 0.0, goal.position});
141 EXPECT_FLOAT_EQ(-2.0, goal.position);
142 EXPECT_FLOAT_EQ(0.0, goal.velocity);
143
144 // Rotate to the soft stop.
145 robot_pose = Pose({-1.0, 0.0, 0.0}, 4.0);
146 goal = AimerGoal(
147 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
148 RobotState{robot_pose, {0.0, 0.0}, 0.0, goal.position});
149 EXPECT_FLOAT_EQ(-4.0, goal.position);
150 EXPECT_FLOAT_EQ(0.0, goal.velocity);
151
152 // Rotate past the hard stop.
153 robot_pose = Pose({-1.0, 0.0, 0.0}, 0.0);
154 goal = AimerGoal(
155 ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
156 RobotState{robot_pose, {0.0, 0.0}, 0.0, goal.position});
157 EXPECT_FLOAT_EQ(0.0, goal.position);
158 EXPECT_FLOAT_EQ(0.0, goal.velocity);
159}
160
Stephan Pleines8ef61ed2024-02-19 20:13:06 -0800161// Tests that we fail if the ballspeed is zero. Can't hit anything if the ball
162// isn't moving.
163TEST(AimerDeathTest, ZeroBallSpeed) {
164 const Pose target({0.0, 0.0, 0.0}, 0.0);
165 Pose robot_pose({1.0, 1.0, 1.0}, 0.0);
166 const constants::Range range{-2.36, 2.36, -2.16, 2.16};
167 const double kBallSpeed = 0.0;
168 EXPECT_DEATH(AimerGoal(ShotConfig{target, ShotMode::kShootOnTheFly, range,
169 kBallSpeed, 0.0, 0.0},
170 RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0}),
171 "Ball speed must be positive.");
172}
173
James Kuszmaul851b3962022-02-27 16:42:15 -0800174} // namespace frc971::control_loops::aiming::testing