blob: 4ec7eb9057cffed750b4e4f093ed0a17e2061759 [file] [log] [blame]
#include "frc971/control_loops/aiming/aiming.h"
#include "gtest/gtest.h"
#include "frc971/constants.h"
#include "frc971/control_loops/pose.h"
namespace frc971::control_loops::aiming::testing {
TEST(AimerTest, StandingStill) {
const Pose target({0.0, 0.0, 0.0}, 0.0);
Pose robot_pose({1.0, 0.0, 0.0}, 0.0);
const constants::Range range{-4.5, 4.5, -4.0, 4.0};
const double kBallSpeed = 10.0;
// Robot is ahead of target, should have to turret to 180 deg to shoot.
TurretGoal goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
EXPECT_FLOAT_EQ(M_PI, goal.position);
EXPECT_FLOAT_EQ(0.0, goal.velocity);
EXPECT_FLOAT_EQ(1.0, goal.virtual_shot_distance);
EXPECT_FLOAT_EQ(1.0, goal.target_distance);
// If there is a turret offset, it should get compensated out.
goal = AimerGoal(ShotConfig{target, ShotMode::kShootOnTheFly, range,
kBallSpeed, 0.0, M_PI},
RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
EXPECT_FLOAT_EQ(0.0, goal.position);
robot_pose = Pose({-1.0, 0.0, 0.0}, 1.0);
goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
EXPECT_FLOAT_EQ(-1.0, goal.position);
EXPECT_FLOAT_EQ(0.0, goal.velocity);
EXPECT_FLOAT_EQ(1.0, goal.virtual_shot_distance);
EXPECT_FLOAT_EQ(1.0, goal.target_distance);
// Test that we handle the case that where we are right on top of the target.
robot_pose = Pose({0.0, 0.0, 0.0}, 0.0);
goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
EXPECT_FLOAT_EQ(0.0, goal.position);
EXPECT_FLOAT_EQ(0.0, goal.velocity);
EXPECT_FLOAT_EQ(0.0, goal.virtual_shot_distance);
EXPECT_FLOAT_EQ(0.0, goal.target_distance);
}
// Test that spinning in place results in correct velocity goals.
TEST(AimerTest, SpinningRobot) {
const Pose target({0.0, 0.0, 0.0}, 0.0);
Pose robot_pose({-1.0, 0.0, 0.0}, 0.0);
const constants::Range range{-4.5, 4.5, -4.0, 4.0};
const double kBallSpeed = 10.0;
TurretGoal goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {0.0, 0.0}, 971.0, 0.0});
EXPECT_FLOAT_EQ(0.0, goal.position);
EXPECT_FLOAT_EQ(-971.0, goal.velocity);
}
// Tests that when we drive straight away from the target we don't have to spin
// the turret.
TEST(AimerTest, DrivingAwayFromTarget) {
const Pose target({0.0, 0.0, 0.0}, 0.0);
Pose robot_pose({-1.0, 0.0, 0.0}, 0.0);
const constants::Range range{-4.5, 4.5, -4.0, 4.0};
const double kBallSpeed = 10.0;
TurretGoal goal = AimerGoal(
ShotConfig{target, ShotMode::kStatic, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {-1.0, 0.0}, 0.0, 0.0});
EXPECT_FLOAT_EQ(0.0, goal.position);
EXPECT_FLOAT_EQ(0.0, goal.velocity);
EXPECT_FLOAT_EQ(1.0, goal.virtual_shot_distance);
EXPECT_FLOAT_EQ(1.0, goal.target_distance);
// Next, try with shooting-on-the-fly enabled--because we are driving straight
// away from the target, only the goal distance should be impacted.
goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {-1.0, 0.0}, 0.0, 0.0});
EXPECT_FLOAT_EQ(0.0, goal.position);
EXPECT_FLOAT_EQ(0.0, goal.velocity);
EXPECT_FLOAT_EQ(1.111, goal.virtual_shot_distance);
EXPECT_FLOAT_EQ(1.0, goal.target_distance);
}
// Tests that when we drive perpendicular to the target, we do have to spin.
TEST(AimerTest, DrivingLateralToTarget) {
const Pose target({0.0, 0.0, 0.0}, 0.0);
Pose robot_pose({0.0, -1.0, 0.0}, 0.0);
const constants::Range range{-4.5, 4.5, -4.0, 4.0};
const double kBallSpeed = 10.0;
TurretGoal goal = AimerGoal(
ShotConfig{target, ShotMode::kStatic, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {1.0, 0.0}, 0.0, 0.0});
EXPECT_FLOAT_EQ(M_PI_2, goal.position);
EXPECT_FLOAT_EQ(1.0, goal.velocity);
EXPECT_FLOAT_EQ(1.0, goal.virtual_shot_distance);
EXPECT_FLOAT_EQ(1.0, goal.target_distance);
// Next, test with shooting-on-the-fly enabled, The goal numbers should all be
// slightly offset due to the robot velocity.
goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {1.0, 0.0}, 0.0, 0.0});
// Confirm that the turret heading goal is a bit more than pi / 2, but not by
// too much.
EXPECT_LT(M_PI_2 + 0.01, goal.position);
EXPECT_GT(M_PI_2 + 0.5, goal.position);
// Similarly, the turret velocity goal should be a bit less than 1.0,
// since the turret is no longer at exactly a right angle.
EXPECT_LT(0.9, goal.velocity);
EXPECT_GT(0.999, goal.velocity);
// And the distance to the goal should be a bit greater than 1.0.
EXPECT_LT(1.00001, goal.virtual_shot_distance);
EXPECT_GT(1.1, goal.virtual_shot_distance);
EXPECT_FLOAT_EQ(1.0, goal.target_distance);
}
// Confirms that when we move the turret heading so that it would be entirely
// out of the normal range of motion that we send a valid (in-range) goal.
// I.e., test that we have some hysteresis, but that it doesn't take us
// out-of-range.
TEST(AimerTest, WrapWhenOutOfRange) {
// Start ourselves needing a turret angle of 0.0.
const Pose target({0.0, 0.0, 0.0}, 0.0);
Pose robot_pose({-1.0, 0.0, 0.0}, 0.0);
const constants::Range range{-5.5, 5.5, -5.0, 5.0};
const double kBallSpeed = 10.0;
TurretGoal goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {0.0, 0.0}, 0.0, 0.0});
EXPECT_FLOAT_EQ(0.0, goal.position);
EXPECT_FLOAT_EQ(0.0, goal.velocity);
// Rotate a bit...
robot_pose = Pose({-1.0, 0.0, 0.0}, 2.0);
goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {0.0, 0.0}, 0.0, goal.position});
EXPECT_FLOAT_EQ(-2.0, goal.position);
EXPECT_FLOAT_EQ(0.0, goal.velocity);
// Rotate to the soft stop.
robot_pose = Pose({-1.0, 0.0, 0.0}, 4.0);
goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {0.0, 0.0}, 0.0, goal.position});
EXPECT_FLOAT_EQ(-4.0, goal.position);
EXPECT_FLOAT_EQ(0.0, goal.velocity);
// Rotate past the hard stop.
robot_pose = Pose({-1.0, 0.0, 0.0}, 0.0);
goal = AimerGoal(
ShotConfig{target, ShotMode::kShootOnTheFly, range, kBallSpeed, 0.0, 0.0},
RobotState{robot_pose, {0.0, 0.0}, 0.0, goal.position});
EXPECT_FLOAT_EQ(0.0, goal.position);
EXPECT_FLOAT_EQ(0.0, goal.velocity);
}
} // namespace frc971::control_loops::aiming::testing