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