Benjamin Fredrickson | a74b0bd | 2015-03-07 00:32:52 -0800 | [diff] [blame] | 1 | #include <cmath> |
| 2 | |
| 3 | #include "gtest/gtest.h" |
| 4 | |
| 5 | #include "aos/common/logging/logging.h" |
Brian Silverman | f5f8d8e | 2015-12-06 18:39:12 -0500 | [diff] [blame] | 6 | #include "aos/testing/test_logging.h" |
Brian Silverman | b691f5e | 2015-08-02 11:37:55 -0700 | [diff] [blame] | 7 | #include "y2015/util/kinematics.h" |
Benjamin Fredrickson | a74b0bd | 2015-03-07 00:32:52 -0800 | [diff] [blame] | 8 | #include "frc971/control_loops/team_number_test_environment.h" |
| 9 | |
| 10 | namespace aos { |
| 11 | namespace util { |
| 12 | namespace testing { |
| 13 | |
| 14 | // For verifcation against Mr. Schuh's internal tests. |
| 15 | // Please do not comment on this in a code review. We can remove this after the |
| 16 | // season to satisfy any OCD. |
| 17 | bool k_internal_debug = false; |
| 18 | |
| 19 | class KinematicsTest : public ::testing::Test { |
| 20 | public: |
| 21 | KinematicsTest() |
| 22 | : lower_height_limit_(0.01), |
| 23 | upper_height_limit_(0.65), |
| 24 | lower_angle_limit_(-M_PI / 2.0), |
| 25 | upper_angle_limit_(M_PI / 2.0) {} |
| 26 | |
| 27 | void SetUp() { |
Brian Silverman | f5f8d8e | 2015-12-06 18:39:12 -0500 | [diff] [blame] | 28 | ::aos::testing::EnableTestLogging(); |
Benjamin Fredrickson | a74b0bd | 2015-03-07 00:32:52 -0800 | [diff] [blame] | 29 | kinematics_ = ElevatorArmKinematics(arm_length_, upper_height_limit_, lower_height_limit_, |
| 30 | upper_angle_limit_, lower_angle_limit_); |
| 31 | } |
| 32 | |
| 33 | protected: |
| 34 | double lower_height_limit_; |
| 35 | double upper_height_limit_; |
| 36 | double lower_angle_limit_; |
| 37 | double upper_angle_limit_; |
| 38 | double arm_length_ = 0.7366; |
| 39 | |
| 40 | ElevatorArmKinematics kinematics_; |
| 41 | }; |
| 42 | |
| 43 | // Used for internal debugging and verification only not acctual testing. |
| 44 | // Please do not comment on this in a code review. We can remove this after |
| 45 | // the season to satisfy any OCD. |
| 46 | TEST_F(KinematicsTest, |
| 47 | PrintPointsSoWeCanHandVerifyThemUntilABetterTestIsMadeAndBrianIsHappy) { |
| 48 | if (k_internal_debug) { |
| 49 | for (double y = -1.0; y <= 1.0; y += 0.2) { |
| 50 | for (double x = -1.0; x <= 1.0; x += 0.2) { |
| 51 | ElevatorArmKinematics::KinematicResult res; |
| 52 | int region = kinematics_.InverseKinematic(x, y, 0.0, 0.0, &res); |
| 53 | printf( |
| 54 | " %12.3f %12.3f %8.3f %9.3f %8.2f %12d %12.4f %10.4f " |
| 55 | "%15.4f %16.4f\n", |
| 56 | x, y, res.elevator_height, res.arm_angle, |
| 57 | res.arm_angle * 180.0 / M_PI, region, res.fridge_x, res.fridge_h, |
| 58 | res.fridge_x - x, res.fridge_h - y); |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | // Make a set of calls to test the grabber arm intersection test code. |
| 63 | printf( |
| 64 | "# ArmAngle (degrees) ElevatorHeight ClawAngle (degrees) GrabberX " |
| 65 | "GrabberH intersectReturn SafeClawAngle (degrees)\n"); |
| 66 | for (double elevator_height = kinematics_.get_elevator_min(); |
| 67 | elevator_height <= kinematics_.get_elevator_max(); |
| 68 | elevator_height += 0.10) { |
| 69 | for (double arm_angle = kinematics_.get_lower_angle_limit(); |
| 70 | arm_angle <= kinematics_.get_upper_angle_limit() + 0.01; |
| 71 | arm_angle += M_PI * 0.5 / 9.0) { |
| 72 | double claw_angle = M_PI * 0.25; |
| 73 | double safe_claw_angle; |
| 74 | double intersectReturnValue = kinematics_.GrabberArmIntersectionCheck( |
| 75 | elevator_height, arm_angle, claw_angle, &safe_claw_angle); |
| 76 | Eigen::Vector2d grabber_location = |
| 77 | kinematics_.ForwardKinematicNoChecking(elevator_height, arm_angle); |
| 78 | |
| 79 | printf( |
| 80 | " %8.4f %8.2f %8.2f %14.4f %9.2f %9.2f %9.2f %10.3f %13.4f " |
| 81 | "%12.3f\n", |
| 82 | arm_angle, arm_angle * 180.0 / M_PI, elevator_height, claw_angle, |
| 83 | claw_angle * 180.0 / M_PI, grabber_location.x(), |
| 84 | grabber_location.y(), intersectReturnValue, safe_claw_angle, |
| 85 | safe_claw_angle * 180.0 / M_PI); |
| 86 | } |
| 87 | printf("\n"); |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | TEST_F(KinematicsTest, ValidIntersectCheckPointAtBottomOfElevatorRange) { |
| 93 | double safe_claw_angle; |
| 94 | double elevator_height = 0.01; |
| 95 | double arm_angle = 30.0 * M_PI / 180.0; |
| 96 | double claw_angle = M_PI * 0.25; |
| 97 | bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck( |
| 98 | elevator_height, arm_angle, claw_angle, &safe_claw_angle); |
| 99 | EXPECT_TRUE(intersectReturnValue); |
| 100 | EXPECT_EQ(safe_claw_angle, claw_angle); |
| 101 | } |
| 102 | |
| 103 | TEST_F(KinematicsTest, ValidIntersectCheckPointAtMiddleOfElevatorRange) { |
| 104 | double safe_claw_angle; |
| 105 | double elevator_height = 0.4; |
| 106 | double arm_angle = 30.0 * M_PI / 180.0; |
| 107 | double claw_angle = M_PI * 0.25; |
| 108 | bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck( |
| 109 | elevator_height, arm_angle, claw_angle, &safe_claw_angle); |
| 110 | EXPECT_TRUE(intersectReturnValue); |
| 111 | EXPECT_EQ(safe_claw_angle, claw_angle); |
| 112 | } |
| 113 | |
| 114 | TEST_F(KinematicsTest, |
| 115 | invalidIntersectCheckPointAtBottomOfElevatorRangeWithSafeClawAngle) { |
| 116 | double safe_claw_angle; |
| 117 | double elevator_height = 0.01; |
| 118 | double arm_angle = -20.0 * M_PI / 180.0; |
| 119 | double claw_angle = M_PI * 0.25; |
| 120 | bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck( |
| 121 | elevator_height, arm_angle, claw_angle, &safe_claw_angle); |
| 122 | EXPECT_FALSE(intersectReturnValue); |
| 123 | EXPECT_NEAR(safe_claw_angle, 0.0435733, 0.000001); |
| 124 | } |
| 125 | |
| 126 | TEST_F(KinematicsTest, |
| 127 | invalidIntersectCheckPointAtMiddleOfElevatorRangeWithSafeClawAngle) { |
| 128 | double safe_claw_angle; |
| 129 | double elevator_height = 0.41; |
| 130 | double arm_angle = -60.0 * M_PI / 180.0; |
| 131 | double claw_angle = M_PI * 0.25; |
| 132 | bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck( |
| 133 | elevator_height, arm_angle, claw_angle, &safe_claw_angle); |
| 134 | EXPECT_FALSE(intersectReturnValue); |
| 135 | EXPECT_NEAR(safe_claw_angle, 0.12655341, 0.000001); |
| 136 | } |
| 137 | |
| 138 | TEST_F(KinematicsTest, |
| 139 | invalidIntersectCheckPointAtBottomOfElevatorRangeNoSafeClawAngle) { |
| 140 | double safe_claw_angle; |
| 141 | double elevator_height = 0.01; |
| 142 | double arm_angle = -30.0 * M_PI / 180.0; |
| 143 | double claw_angle = M_PI * 0.25; |
| 144 | bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck( |
| 145 | elevator_height, arm_angle, claw_angle, &safe_claw_angle); |
| 146 | EXPECT_FALSE(intersectReturnValue); |
| 147 | EXPECT_EQ(safe_claw_angle, -1.0); |
| 148 | } |
| 149 | |
| 150 | TEST_F(KinematicsTest, |
| 151 | invalidIntersectCheckPointAtMiddleOfElevatorRangeNoSafeClawAngle) { |
| 152 | double safe_claw_angle; |
| 153 | double elevator_height = 0.41; |
| 154 | double arm_angle = -70.0 * M_PI / 180.0; |
| 155 | double claw_angle = M_PI * 0.25; |
| 156 | bool intersectReturnValue = kinematics_.GrabberArmIntersectionCheck( |
| 157 | elevator_height, arm_angle, claw_angle, &safe_claw_angle); |
| 158 | EXPECT_FALSE(intersectReturnValue); |
| 159 | EXPECT_EQ(safe_claw_angle, -1.0); |
| 160 | } |
| 161 | |
| 162 | // Tests that velocity calulations are correct |
| 163 | TEST_F(KinematicsTest, InverseKinematicVelocity) { |
| 164 | ElevatorArmKinematics::KinematicResult result; |
| 165 | // move striaght up and verify that only hieght changes |
| 166 | EXPECT_EQ(0, kinematics_.InverseKinematic(0.0, 0.2, 0.0, 0.7, &result)); |
| 167 | EXPECT_NEAR(0.0, result.arm_velocity, 0.00001); |
| 168 | EXPECT_NEAR(0.7, result.elevator_velocity, 0.00001); |
| 169 | // check the negative |
| 170 | EXPECT_EQ(0, kinematics_.InverseKinematic(0.0, 0.2, 0.0, -0.7, &result)); |
| 171 | EXPECT_NEAR(0.0, result.arm_velocity, 0.00001); |
| 172 | EXPECT_NEAR(-0.7, result.elevator_velocity, 0.00001); |
| 173 | // even with the arm out we should still just move up |
| 174 | EXPECT_EQ(0, kinematics_.InverseKinematic(M_PI / 6, 0.2, 0.0, 0.7, &result)); |
| 175 | EXPECT_NEAR(0.0, result.arm_velocity, 0.00001); |
| 176 | EXPECT_NEAR(0.7, result.elevator_velocity, 0.00001); |
| 177 | // even with the arm back we should still just move up |
| 178 | EXPECT_EQ(0, kinematics_.InverseKinematic(-M_PI / 6, 0.2, 0.0, 0.7, &result)); |
| 179 | EXPECT_NEAR(0.0, result.arm_velocity, 0.00001); |
| 180 | EXPECT_NEAR(0.7, result.elevator_velocity, 0.00001); |
| 181 | |
| 182 | // should move only angle forward |
| 183 | EXPECT_EQ(0, kinematics_.InverseKinematic(0.0, 0.2, 1.0, 0.0, &result)); |
| 184 | EXPECT_NEAR(-1.35759, result.arm_velocity, 0.00001); |
| 185 | EXPECT_NEAR(0.0, result.elevator_velocity, 0.00001); |
| 186 | // check the negative |
| 187 | EXPECT_EQ(0, kinematics_.InverseKinematic(0.0, 0.2, -1.0, 0.0, &result)); |
| 188 | EXPECT_NEAR(1.35759, result.arm_velocity, 0.00001); |
| 189 | EXPECT_NEAR(0.0, result.elevator_velocity, 0.00001); |
| 190 | // with the arm out a change in x should make arm angle greater and |
| 191 | // bring the evevator down. |
| 192 | EXPECT_EQ(0, kinematics_.InverseKinematic(0.2, 0.2, 1.0, 0.0, &result)); |
| 193 | EXPECT_GT(0.0, result.arm_velocity); |
| 194 | EXPECT_LT(0.0, result.elevator_velocity); |
| 195 | // with the arm out a change in x should make arm angle greater and |
| 196 | // bring the evevator down. |
| 197 | EXPECT_EQ(0, kinematics_.InverseKinematic(-0.2, 0.2, 1.0, 0.0, &result)); |
| 198 | EXPECT_GT(0.0, result.arm_velocity); |
| 199 | EXPECT_GT(0.0, result.elevator_velocity); |
| 200 | } |
| 201 | |
| 202 | // Tests that velocity calulations are correct |
| 203 | TEST_F(KinematicsTest, ForwardKinematicVelocity) { |
| 204 | ElevatorArmKinematics::KinematicResult result; |
| 205 | |
| 206 | // moving the arm forward at zero should result in x velocity |
| 207 | EXPECT_EQ(0, kinematics_.ForwardKinematic(0.2, 0.0, 0.0, 1.35759, &result)); |
| 208 | EXPECT_NEAR(-1.0, result.fridge_x_velocity, 0.00001); |
| 209 | EXPECT_NEAR(0.0, result.fridge_h_velocity, 0.00001); |
| 210 | // check the negative |
| 211 | EXPECT_EQ(0, kinematics_.ForwardKinematic(0.2, 0.0, 0.0, -1.35759, &result)); |
| 212 | EXPECT_NEAR(1.0, result.fridge_x_velocity, 0.00001); |
| 213 | EXPECT_NEAR(0.0, result.fridge_h_velocity, 0.00001); |
| 214 | // moving the arm up at zero should result in h velocity |
| 215 | EXPECT_EQ(0, kinematics_.ForwardKinematic(0.2, 0.0, 1.0, 0.0, &result)); |
| 216 | EXPECT_NEAR(0.0, result.fridge_x_velocity, 0.00001); |
| 217 | EXPECT_NEAR(1.0, result.fridge_h_velocity, 0.00001); |
| 218 | // check the negative |
| 219 | EXPECT_EQ(0, kinematics_.ForwardKinematic(0.2, 0.0, -1.0, 0.0, &result)); |
| 220 | EXPECT_NEAR(0.0, result.fridge_x_velocity, 0.00001); |
| 221 | EXPECT_NEAR(-1.0, result.fridge_h_velocity, 0.00001); |
| 222 | // arm is forward a negative angle should make x head forward and y head down. |
| 223 | EXPECT_EQ(0, kinematics_.ForwardKinematic(0.5, -0.2, 0.0, -1.0, &result)); |
| 224 | EXPECT_GT(result.fridge_x_velocity, 0.0); |
| 225 | EXPECT_LT(result.fridge_h_velocity, 0.0); |
| 226 | // arm is forward a positive angle should make x head backwardward and y head up. |
| 227 | EXPECT_EQ(0, kinematics_.ForwardKinematic(0.5, -0.2, 0.0, 1.0, &result)); |
| 228 | EXPECT_LT(result.fridge_x_velocity, 0.0); |
| 229 | EXPECT_GT(result.fridge_h_velocity, 0.0); |
| 230 | // arm is backward a negative angle should make x head forward and y head down. |
| 231 | EXPECT_EQ(0, kinematics_.ForwardKinematic(0.5, 0.2, 0.0, -1.0, &result)); |
| 232 | EXPECT_GT(result.fridge_x_velocity, 0.0); |
| 233 | EXPECT_GT(result.fridge_h_velocity, 0.0); |
| 234 | // arm is backward a negative angle should make x head forward and y head down. |
| 235 | EXPECT_EQ(0, kinematics_.ForwardKinematic(0.5, 0.2, 0.0, 1.0, &result)); |
| 236 | EXPECT_LT(result.fridge_x_velocity, 0.0); |
| 237 | EXPECT_LT(result.fridge_h_velocity, 0.0); |
| 238 | } |
| 239 | |
| 240 | } // namespace testing |
| 241 | } // namespace util |
| 242 | } // namespace aos |