|
1 | 1 | package org.teamtators.rotator.control; |
2 | 2 |
|
| 3 | +import org.teamtators.rotator.config.ConfigException; |
3 | 4 | import org.teamtators.rotator.config.Configurable; |
4 | 5 |
|
5 | 6 | /** |
6 | 7 | * A controller for motion with a trapezoidal velocity graph |
7 | 8 | */ |
8 | 9 | public class TrapezoidController extends AbstractController implements Configurable<TrapezoidController.Config> { |
9 | | - private double startSpeed; |
10 | | - private double endSpeed; |
11 | | - private double maxVelocity; |
12 | | - private double accelerationEnd; |
13 | | - private double decelerationStart; |
14 | | - private double decelerationEnd; |
| 10 | + private double startVelocity; |
| 11 | + private double endVelocity; |
| 12 | + private double travelVelocity; |
| 13 | + private double maxAbsAcceleration; |
| 14 | + |
| 15 | + private boolean isTrapezoidal; |
| 16 | + private double time; |
| 17 | + private double totalTime; |
| 18 | + private double startSectionTime; |
| 19 | + private double flatTime; |
15 | 20 |
|
16 | 21 | public TrapezoidController(String name) { |
17 | 22 | super(name); |
| 23 | + time = 0; |
18 | 24 | } |
19 | 25 |
|
20 | 26 | public TrapezoidController() { |
21 | 27 | this("TrapezoidController"); |
22 | 28 | } |
23 | 29 |
|
| 30 | + public double getStartVelocity() { |
| 31 | + return startVelocity; |
| 32 | + } |
| 33 | + |
| 34 | + public void setStartVelocity(double startVelocity) { |
| 35 | + this.startVelocity = startVelocity; |
| 36 | + } |
| 37 | + |
| 38 | + public double getEndVelocity() { |
| 39 | + return endVelocity; |
| 40 | + } |
| 41 | + |
| 42 | + public void setEndVelocity(double endVelocity) { |
| 43 | + this.endVelocity = endVelocity; |
| 44 | + } |
| 45 | + |
| 46 | + public double getTravelVelocity() { |
| 47 | + return travelVelocity; |
| 48 | + } |
| 49 | + |
| 50 | + public void setTravelVelocity(double travelVelocity) { |
| 51 | + this.travelVelocity = travelVelocity; |
| 52 | + } |
| 53 | + |
| 54 | + public double getMaxAbsAcceleration() { |
| 55 | + return maxAbsAcceleration; |
| 56 | + } |
| 57 | + |
| 58 | + public void setMaxAbsAcceleration(double maxAbsAcceleration) { |
| 59 | + this.maxAbsAcceleration = maxAbsAcceleration; |
| 60 | + } |
| 61 | + |
| 62 | + @Override |
| 63 | + public void onEnable() { |
| 64 | + startSectionTime = Math.abs(travelVelocity - startVelocity) / maxAbsAcceleration; |
| 65 | + double endSectionTime = Math.abs(travelVelocity - endVelocity) / maxAbsAcceleration; |
| 66 | + double startDistance = startSectionTime * (travelVelocity + startVelocity) / 2; |
| 67 | + double endDistance = endSectionTime * (travelVelocity + endVelocity) / 2; |
| 68 | + double flatDistance = getSetpoint() - (startDistance + endDistance); |
| 69 | + flatTime = flatDistance / travelVelocity; |
| 70 | + isTrapezoidal = flatTime >= 0; |
| 71 | + if (!isTrapezoidal) { |
| 72 | + totalTime = Math.sqrt(getSetpoint() / maxAbsAcceleration) * 2; |
| 73 | + } |
| 74 | + super.onEnable(); |
| 75 | + } |
| 76 | + |
24 | 77 | @Override |
25 | 78 | protected double computeOutput(double delta) { |
26 | | - double input = getInput(); |
27 | | - if (input < accelerationEnd) { |
28 | | - return startSpeed |
29 | | - + (maxVelocity - startSpeed) * (input / accelerationEnd); |
30 | | - } else if (input >= accelerationEnd && input < decelerationStart) { |
31 | | - return maxVelocity; |
32 | | - } else if (input >= decelerationStart && input < decelerationEnd) { |
33 | | - return maxVelocity - (maxVelocity - endSpeed) |
34 | | - * (input - decelerationStart) |
35 | | - / (decelerationEnd - decelerationStart); |
| 79 | + time += delta; |
| 80 | + double velocity; |
| 81 | + if (isTrapezoidal) { |
| 82 | + if (time < startSectionTime) { |
| 83 | + // accelerating |
| 84 | + velocity = Math.signum(travelVelocity - startVelocity) * maxAbsAcceleration * time + startVelocity; |
| 85 | + } else if (time >= startSectionTime + flatTime) { |
| 86 | + // decelerating |
| 87 | + velocity = Math.signum(endVelocity - travelVelocity) * maxAbsAcceleration |
| 88 | + * (time - (startSectionTime + flatTime)) + endVelocity; |
| 89 | + } else { |
| 90 | + // constant velocity |
| 91 | + velocity = travelVelocity; |
| 92 | + } |
36 | 93 | } else { |
37 | | - return endSpeed; |
| 94 | + if (time < totalTime / 2) { |
| 95 | + velocity = Math.signum(travelVelocity - startVelocity) * maxAbsAcceleration * time + startVelocity; |
| 96 | + } else { |
| 97 | + velocity = Math.signum(endVelocity - travelVelocity) * maxAbsAcceleration * time + endVelocity; |
| 98 | + } |
38 | 99 | } |
| 100 | + return velocity; |
39 | 101 | } |
40 | 102 |
|
41 | 103 | @Override |
42 | 104 | public void configure(Config config) { |
43 | | - startSpeed = config.startSpeed; |
44 | | - endSpeed = config.endSpeed; |
45 | | - maxVelocity = config.maxVelocity; |
46 | | - accelerationEnd = config.accelerationEnd; |
47 | | - decelerationStart = config.decelerationStart; |
48 | | - decelerationEnd = config.decelerationEnd; |
| 105 | + travelVelocity = config.travelVelocity; |
| 106 | + maxAbsAcceleration = config.maxAbsAcceleration; |
| 107 | + startVelocity = config.startVelocity; |
| 108 | + endVelocity = config.endVelocity; |
| 109 | + if (maxAbsAcceleration <= 0) { |
| 110 | + throw new ConfigException("Absolute value of maxAbsAcceleration must be a positive number"); |
| 111 | + } |
| 112 | + if (travelVelocity == 0) { |
| 113 | + throw new ConfigException("Travel velocity must not be 0"); |
| 114 | + } |
49 | 115 | super.configure(config); |
50 | 116 | } |
51 | 117 |
|
52 | 118 | static class Config extends AbstractController.Config { |
53 | | - public double startSpeed = 0; |
54 | | - public double endSpeed = 0; |
55 | | - public double maxVelocity; |
56 | | - public double accelerationEnd = 0; |
57 | | - public double decelerationStart; |
58 | | - public double decelerationEnd; |
| 119 | + public double startVelocity = 0; |
| 120 | + public double endVelocity = 0; |
| 121 | + public double travelVelocity; |
| 122 | + public double maxAbsAcceleration; |
59 | 123 | } |
60 | 124 | } |
0 commit comments