AccelLimiter can do three things:
Each thing you want to limit acceleration on should have its own instance of AccelLimiter (because each instance can only use one set of parameters at a time).
To create a new AccelLimiter object, you need to determine two things. You can use any unit you want, as long as you use the same unit everywhere. Typical units are m/s² and t/s² (ticks per second per second).
Pass these parameters into the constructor:
@Override
public void init() {
double maxAccel = 100; // t/s²
double maxDeltaVEachLoop = 10; // also t/s²
AccelLimiter exampleAL = new AccelLimiter(maxAccel, maxDeltaVEachLoop);
// to be continued...
There are two methods that you can call in every iteration of your loop to do this:
public double requestDeltaVel(double deltaVel, double currentTime)
- Takes a change in velocity and limits it based on the given maximum acceleration and how much time has passed since it was called last.public double requestVel(double newVel, double currentVel, double currentTime)
- Does the same thing, but takes and returns the entire velocity rather than only taking the change. (This is shorthand. Under the hood, this figures out the change in velocity, limits that, then adds it back to the previous velocity.)Let's limit the acceleration on a single motor using the AccelLimiter we created earlier. We'll use a DcMotor named "exampleMotor" and the requestVel() method.
Remember that our max. acceleration is 100 ticks per second. That will also be our maximum drive velocity, so it will take 1 second for the motor to reach full speed.
Also, since our maxDeltaVEachLoop is 10 t/s², if a loop takes longer than 100ms, it won't increase its speed more than that limit on this loop.
// We need to pass the time into the request*() methods so AccelLimiter knows how much time passes between calls.
ElapsedTime stopwatch = new ElapsedTime();
} // end init()
@Override
public void loop() {
// "Requested", as in what the driver wants to do
// Let's say that our drive velocity runs from -100 to +100 ticks per second.
double requestedDriveVelocity = (gamepad1.right_trigger - gamepad1.left_trigger) * 100;
// The user request can jump from 0 to 100.
// AccelLimiter will only change it as fast as maxAccel allows it to.
// Here, if a loop takes 50ms, it will only change it by 5 t/s that loop.
double limitedDriveVelocity = exampleAL.requestVel(requestedDriveVelocity, exampleMotor.getVelocity(), stopwatch.milliseconds());
// If this is a real motor, you should see it gradually ramp up and down as you press the triggers.
exampleMotor.setVelocity(limitedDriveVelocity);
}
To limit acceleration on multiple motors that act independently (like PixelArm - the pivot and elevator motors do not affect each other), create a separate AccelLimiter object for each. If the motors are connected together, like they are on the drivebase, continue to the next section.
This is done with the following method:
requestDeltaVelOnN(List deltaVelList, double currentTime)
(There is no requestVelOnN() method. You must use change in velocity.)
NOTE: This method is meant for multiple motors that are interconnected (the speed of one affects the rest). If your motors act independently (like PixelArm - the pivot and elevator motors do not affect each other), see the previous section.
This application is not common enough to include an example here. For an example implementation, look at the code of RRMecanumDriveImpl.applyDTS().
public double stoppingDistance(double currentVel, int resolution)
This method uses the maxAccel parameter already set for its AccelLimiter instance to determine how far the device will continue travelling while it's ramping down from the given velocity.
The resolution argument controls how precise the calculation is. A higher resolution yields a more accurate result but takes longer to calculate. 1000 seems to be a happy medium, getting close enough without taking too long.
stoppingDistance = exampleAL.stoppingDistance(motor.getVelocity(), 1000);
stoppingDistance now (roughly) equals the distance we will continue to travel while we are ramping down, or the distance we need to have available to stop safely.
One potential application of this is ramping down towards the end of an actuator's range. The stopping distance will tell you at what point you must start slowing down.
Let's consider an example that lets us easily test our example: Our maximum acceleration is 100 t/s², and we're going 100 t/s. The line of our deceleration will cut the square of 100 t/s over 1 second in half, making it a triangle with half the area. In other words, we will travel 50 ticks while we're still ramping down, or half the distance we would have traveled at full speed. That is our stopping distance. If we built the example above correctly, stoppingDistance should roughly equal 50.
If you're doing this on a single motor, you do not want to use AccelLimiter to do this. Use the SDK's DcMotor.setTargetPosition() method instead.
That said, if you have a fun issue like your motor's encoder counting backwards and the SDK won't work for you, this might be a stopgap solution until you can fix the motor. What this is really made for, however, is more complex applications such as moving the entire drivetrain (4 motors with different encoder positions and rotation directions) until a target position is reached on one of the dead wheels.
There is no blocking version of this (yet). It must be done asynchronously.
public void setupRampAlongDistance(double targetPos, double maxVelocity)
You can call this at any time to set the target position and the highest speed at which it will travel (perhaps we should call it "cruising speed"). This will reset the AccelLimiter object (!) and put it in the correct internal state for later updateRampAlongDistance() calls to work correctly.
public double updateRampAlongDistance(double currentPos)
Call this on every loop after setting up the ramp using the previous method. It will return the velocity that you should send to the motor each loop. See the basic example below:
// We want to go to encoder position 1000 at 100 t/s.
// We'd put this in our init().
exampleAL.setupRampAlongDistance(1000, 100);
// Here, we give it our position and it gives us the velocity.
// We'd put this in our loop().
double applyVel = exampleAL.updateRampAlongDistance(motor.getCurrentPosition());
// Here you could do any additional processing on the velocity if necessary.
motor.setVelocity(applyVel);