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