blob: cdb9334379f7d5e57a4f85c629d7c58820aaca90 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2014-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "Encoder.h"
9#include "Jaguar.h"
10#include "PIDController.h"
11#include "Talon.h"
12#include "TestBench.h"
13#include "Timer.h"
14#include "Victor.h"
15#include "gtest/gtest.h"
16
17using namespace frc;
18
19enum MotorEncoderTestType { TEST_VICTOR, TEST_JAGUAR, TEST_TALON };
20
21std::ostream& operator<<(std::ostream& os, MotorEncoderTestType const& type) {
22 switch (type) {
23 case TEST_VICTOR:
24 os << "Victor";
25 break;
26 case TEST_JAGUAR:
27 os << "Jaguar";
28 break;
29 case TEST_TALON:
30 os << "Talon";
31 break;
32 }
33
34 return os;
35}
36
37static constexpr double kMotorTime = 0.5;
38
39/**
40 * A fixture that includes a PWM speed controller and an encoder connected to
41 * the same motor.
42 */
43class MotorEncoderTest : public testing::TestWithParam<MotorEncoderTestType> {
44 protected:
45 SpeedController* m_speedController;
46 Encoder* m_encoder;
47
48 void SetUp() override {
49 switch (GetParam()) {
50 case TEST_VICTOR:
51 m_speedController = new Victor(TestBench::kVictorChannel);
52 m_encoder = new Encoder(TestBench::kVictorEncoderChannelA,
53 TestBench::kVictorEncoderChannelB);
54 break;
55
56 case TEST_JAGUAR:
57 m_speedController = new Jaguar(TestBench::kJaguarChannel);
58 m_encoder = new Encoder(TestBench::kJaguarEncoderChannelA,
59 TestBench::kJaguarEncoderChannelB);
60 break;
61
62 case TEST_TALON:
63 m_speedController = new Talon(TestBench::kTalonChannel);
64 m_encoder = new Encoder(TestBench::kTalonEncoderChannelA,
65 TestBench::kTalonEncoderChannelB);
66 break;
67 }
68 }
69
70 void TearDown() override {
71 delete m_speedController;
72 delete m_encoder;
73 }
74
75 void Reset() {
76 m_speedController->Set(0.0);
77 m_encoder->Reset();
78 }
79};
80
81/**
82 * Test if the encoder value increments after the motor drives forward
83 */
84TEST_P(MotorEncoderTest, Increment) {
85 Reset();
86
87 /* Drive the speed controller briefly to move the encoder */
88 m_speedController->Set(0.2f);
89 Wait(kMotorTime);
90 m_speedController->Set(0.0);
91
92 /* The encoder should be positive now */
93 EXPECT_GT(m_encoder->Get(), 0)
94 << "Encoder should have incremented after the motor moved";
95}
96
97/**
98 * Test if the encoder value decrements after the motor drives backwards
99 */
100TEST_P(MotorEncoderTest, Decrement) {
101 Reset();
102
103 /* Drive the speed controller briefly to move the encoder */
104 m_speedController->Set(-0.2);
105 Wait(kMotorTime);
106 m_speedController->Set(0.0);
107
108 /* The encoder should be positive now */
109 EXPECT_LT(m_encoder->Get(), 0.0)
110 << "Encoder should have decremented after the motor moved";
111}
112
113/**
114 * Test if motor speeds are clamped to [-1,1]
115 */
116TEST_P(MotorEncoderTest, ClampSpeed) {
117 Reset();
118
119 m_speedController->Set(2.0);
120 Wait(kMotorTime);
121
122 EXPECT_FLOAT_EQ(1.0, m_speedController->Get());
123
124 m_speedController->Set(-2.0);
125 Wait(kMotorTime);
126
127 EXPECT_FLOAT_EQ(-1.0, m_speedController->Get());
128}
129
130/**
131 * Test if position PID loop works
132 */
133TEST_P(MotorEncoderTest, PositionPIDController) {
134 Reset();
135 double goal = 1000;
136 m_encoder->SetPIDSourceType(PIDSourceType::kDisplacement);
137 PIDController pid(0.001, 0.0005, 0.0, m_encoder, m_speedController);
138 pid.SetAbsoluteTolerance(50.0);
139 pid.SetOutputRange(-0.2, 0.2);
140 pid.SetSetpoint(goal);
141
142 /* 10 seconds should be plenty time to get to the setpoint */
143 pid.Enable();
144 Wait(10.0);
145 pid.Disable();
146
147 RecordProperty("PIDError", pid.GetError());
148
149 EXPECT_TRUE(pid.OnTarget())
150 << "PID loop did not converge within 10 seconds. Goal was: " << goal
151 << " Error was: " << pid.GetError();
152}
153
154/**
155 * Test if velocity PID loop works
156 */
157TEST_P(MotorEncoderTest, VelocityPIDController) {
158 Reset();
159
160 m_encoder->SetPIDSourceType(PIDSourceType::kRate);
161 PIDController pid(1e-5, 0.0, 3e-5, 8e-5, m_encoder, m_speedController);
162 pid.SetAbsoluteTolerance(200.0);
163 pid.SetToleranceBuffer(50);
164 pid.SetOutputRange(-0.3, 0.3);
165 pid.SetSetpoint(600);
166
167 /* 10 seconds should be plenty time to get to the setpoint */
168 pid.Enable();
169 Wait(10.0);
170 pid.Disable();
171 RecordProperty("PIDError", pid.GetError());
172
173 EXPECT_TRUE(pid.OnTarget())
174 << "PID loop did not converge within 10 seconds. Goal was: " << 600
175 << " Error was: " << pid.GetError();
176}
177
178/**
179 * Test resetting encoders
180 */
181TEST_P(MotorEncoderTest, Reset) {
182 Reset();
183
184 EXPECT_EQ(0, m_encoder->Get()) << "Encoder did not reset to 0";
185}
186
187INSTANTIATE_TEST_CASE_P(Test, MotorEncoderTest,
188 testing::Values(TEST_VICTOR, TEST_JAGUAR, TEST_TALON));