Update for new actions

This commit is contained in:
Ryan Brott
2023-01-01 22:40:18 -08:00
parent 2d9c57a373
commit d0b06e2d59
6 changed files with 167 additions and 564 deletions

View File

@ -1,261 +0,0 @@
package org.firstinspires.ftc.teamcode;
import com.acmerobotics.dashboard.FtcDashboard;
import com.acmerobotics.dashboard.canvas.Canvas;
import com.acmerobotics.dashboard.telemetry.TelemetryPacket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public interface Action {
void init();
default boolean loop(TelemetryPacket p) {
return false;
}
default void draw(Canvas c) {
}
default void runBlocking() {
if (Thread.currentThread().isInterrupted()) return;
init();
boolean b = true;
while (!Thread.currentThread().isInterrupted() && b) {
TelemetryPacket p = new TelemetryPacket();
draw(p.fieldOverlay());
b = loop(p);
FtcDashboard.getInstance().sendTelemetryPacket(p);
}
}
default double clock() {
return 1e-9 * System.nanoTime();
}
// not persistent!
interface IBuilder<T extends IBuilder<T>> {
T add(Action a);
}
// parent-less, generic-less SeqBuilder
class Builder implements IBuilder<Builder> {
private final List<Action> as = new ArrayList<>();
@Override
public Builder add(Action a) {
as.add(a);
return this;
}
public SeqBuilder<Builder> seq() {
return new SeqBuilder<>(this);
}
public ParBuilder<Builder> par() {
return new ParBuilder<>(this);
}
public Builder sleep(double duration) {
return add(new SleepAction(duration));
}
public Builder after(double duration, Action a) {
return seq().sleep(duration).add(a).end();
}
public Action build() {
return new SequentialAction(as);
}
}
class SeqBuilder<T extends IBuilder<T>> implements IBuilder<SeqBuilder<T>> {
private final T parent;
private final List<Action> as = new ArrayList<>();
private SeqBuilder(T parent) {
this.parent = parent;
}
@Override
public SeqBuilder<T> add(Action a) {
as.add(a);
return this;
}
public T end() {
parent.add(new SequentialAction(as));
return parent;
}
public SeqBuilder<SeqBuilder<T>> seq() {
return new SeqBuilder<>(this);
}
public ParBuilder<SeqBuilder<T>> par() {
return new ParBuilder<>(this);
}
public SeqBuilder<T> sleep(double duration) {
return add(new SleepAction(duration));
}
public SeqBuilder<T> after(double duration, Action a) {
return seq().sleep(duration).add(a).end();
}
}
final class ParBuilder<T extends IBuilder<T>> implements IBuilder<ParBuilder<T>> {
private final T parent;
private final List<Action> as = new ArrayList<>();
private ParBuilder(T parent) {
this.parent = parent;
}
@Override
public ParBuilder<T> add(Action a) {
as.add(a);
return this;
}
public T end() {
parent.add(new ParallelAction(as));
return parent;
}
public SeqBuilder<ParBuilder<T>> seq() {
return new SeqBuilder<>(this);
}
public ParBuilder<ParBuilder<T>> par() {
return new ParBuilder<>(this);
}
public ParBuilder<T> sleep(double duration) {
return add(new SleepAction(duration));
}
public ParBuilder<T> after(double duration, Action a) {
return seq().sleep(duration).add(a).end();
}
}
}
final class SleepAction implements Action {
public final double duration;
private double endTs;
public SleepAction(double duration) {
this.duration = duration;
}
@Override
public void init() {
endTs = clock() + duration;
}
@Override
public boolean loop(TelemetryPacket p) {
return clock() <= endTs;
}
}
final class ParallelAction implements Action {
public final List<Action> actions;
private List<Action> remaining;
public ParallelAction(List<Action> actions) {
this.actions = Collections.unmodifiableList(actions);
}
public ParallelAction(Action... actions) {
this(Arrays.asList(actions));
}
@Override
public void init() {
remaining = new ArrayList<>(actions);
for (Action a : remaining) {
a.init();
}
}
@Override
public boolean loop(TelemetryPacket p) {
List<Action> newRem = new ArrayList<>();
for (Action a : remaining) {
if (a.loop(p)) {
newRem.add(a);
}
}
remaining = newRem;
return remaining.size() > 0;
}
@Override
public void draw(Canvas c) {
for (Action a : actions) {
a.draw(c);
}
}
}
final class SequentialAction implements Action {
public final List<Action> actions;
private int index;
private boolean needsInit;
public SequentialAction(List<Action> actions) {
this.actions = Collections.unmodifiableList(actions);
}
public SequentialAction(Action... actions) {
this(Arrays.asList(actions));
}
@Override
public void init() {
if (actions.isEmpty()) {
return;
}
actions.get(0).init();
}
@Override
public boolean loop(TelemetryPacket p) {
if (index >= actions.size()) {
return false;
}
Action a = actions.get(index);
if (needsInit) {
a.init();
needsInit = false;
}
if (!a.loop(p)) {
index++;
needsInit = true;
}
return index < actions.size();
}
@Override
public void draw(Canvas c) {
for (Action a : actions) {
a.draw(c);
}
}
}

View File

@ -0,0 +1,26 @@
package org.firstinspires.ftc.teamcode;
import com.acmerobotics.dashboard.FtcDashboard;
import com.acmerobotics.dashboard.canvas.Canvas;
import com.acmerobotics.dashboard.telemetry.TelemetryPacket;
import com.acmerobotics.roadrunner.Action;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
public abstract class ActionOpMode extends LinearOpMode {
private final FtcDashboard dash = FtcDashboard.getInstance();
protected void runBlocking(Action a) {
Canvas c = new Canvas();
a.preview(c);
boolean b = true;
while (b && !isStopRequested()) {
TelemetryPacket p = new TelemetryPacket();
p.fieldOverlay().getOperations().addAll(c.getOperations());
b = a.run(p);
dash.sendTelemetryPacket(p);
}
}
}

View File

@ -1,9 +1,13 @@
package org.firstinspires.ftc.teamcode; package org.firstinspires.ftc.teamcode;
import androidx.annotation.NonNull;
import com.acmerobotics.dashboard.canvas.Canvas; import com.acmerobotics.dashboard.canvas.Canvas;
import com.acmerobotics.dashboard.config.Config; import com.acmerobotics.dashboard.config.Config;
import com.acmerobotics.dashboard.telemetry.TelemetryPacket; import com.acmerobotics.dashboard.telemetry.TelemetryPacket;
import com.acmerobotics.roadrunner.AccelConstraint; import com.acmerobotics.roadrunner.AccelConstraint;
import com.acmerobotics.roadrunner.Action;
import com.acmerobotics.roadrunner.Actions;
import com.acmerobotics.roadrunner.AngularVelConstraint; import com.acmerobotics.roadrunner.AngularVelConstraint;
import com.acmerobotics.roadrunner.DualNum; import com.acmerobotics.roadrunner.DualNum;
import com.acmerobotics.roadrunner.HolonomicController; import com.acmerobotics.roadrunner.HolonomicController;
@ -13,15 +17,11 @@ import com.acmerobotics.roadrunner.MotorFeedforward;
import com.acmerobotics.roadrunner.Pose2d; import com.acmerobotics.roadrunner.Pose2d;
import com.acmerobotics.roadrunner.Pose2dDual; import com.acmerobotics.roadrunner.Pose2dDual;
import com.acmerobotics.roadrunner.ProfileAccelConstraint; import com.acmerobotics.roadrunner.ProfileAccelConstraint;
import com.acmerobotics.roadrunner.Profiles;
import com.acmerobotics.roadrunner.Rotation2d;
import com.acmerobotics.roadrunner.Rotation2dDual;
import com.acmerobotics.roadrunner.SafeTrajectoryBuilder;
import com.acmerobotics.roadrunner.Time; import com.acmerobotics.roadrunner.Time;
import com.acmerobotics.roadrunner.TimeProfile;
import com.acmerobotics.roadrunner.TimeTrajectory; import com.acmerobotics.roadrunner.TimeTrajectory;
import com.acmerobotics.roadrunner.Trajectory; import com.acmerobotics.roadrunner.TimeTurn;
import com.acmerobotics.roadrunner.TrajectoryBuilder; import com.acmerobotics.roadrunner.TrajectoryActionBuilder;
import com.acmerobotics.roadrunner.TurnConstraints;
import com.acmerobotics.roadrunner.Twist2d; import com.acmerobotics.roadrunner.Twist2d;
import com.acmerobotics.roadrunner.Twist2dDual; import com.acmerobotics.roadrunner.Twist2dDual;
import com.acmerobotics.roadrunner.Twist2dIncrDual; import com.acmerobotics.roadrunner.Twist2dIncrDual;
@ -81,6 +81,8 @@ public final class MecanumDrive {
public final MotorFeedforward feedforward = new MotorFeedforward(kS, kV, kA); public final MotorFeedforward feedforward = new MotorFeedforward(kS, kV, kA);
public final TurnConstraints defaultTurnConstraints = new TurnConstraints(
MAX_ANG_VEL, -MAX_ANG_ACCEL, MAX_ANG_ACCEL);
public final VelConstraint defaultVelConstraint = public final VelConstraint defaultVelConstraint =
new MinVelConstraint(Arrays.asList( new MinVelConstraint(Arrays.asList(
kinematics.new WheelVelConstraint(MAX_WHEEL_VEL), kinematics.new WheelVelConstraint(MAX_WHEEL_VEL),
@ -194,16 +196,13 @@ public final class MecanumDrive {
} }
public final class FollowTrajectoryAction implements Action { public final class FollowTrajectoryAction implements Action {
public final Trajectory trajectory; public final TimeTrajectory timeTrajectory;
private TimeTrajectory timeTrajectory; private double beginTs = -1;
private double beginTs;
private boolean active;
private final double[] xPoints, yPoints; private final double[] xPoints, yPoints;
public FollowTrajectoryAction(Trajectory t) { public FollowTrajectoryAction(TimeTrajectory t) {
this.trajectory = t; timeTrajectory = t;
timeTrajectory = new TimeTrajectory(t);
List<Double> disps = com.acmerobotics.roadrunner.Math.range( List<Double> disps = com.acmerobotics.roadrunner.Math.range(
0, t.path.length(), 0, t.path.length(),
@ -218,23 +217,21 @@ public final class MecanumDrive {
} }
@Override @Override
public void init() { public boolean run(@NonNull TelemetryPacket p) {
beginTs = clock(); double t;
if (beginTs < 0) {
beginTs = Actions.now();
t = 0;
} else {
t = Actions.now() - beginTs;
}
active = true;
}
@Override
public boolean loop(TelemetryPacket p) {
double t = clock() - beginTs;
if (t >= timeTrajectory.duration) { if (t >= timeTrajectory.duration) {
leftFront.setPower(0); leftFront.setPower(0);
leftBack.setPower(0); leftBack.setPower(0);
rightBack.setPower(0); rightBack.setPower(0);
rightFront.setPower(0); rightFront.setPower(0);
active = false;
return false; return false;
} }
@ -274,66 +271,50 @@ public final class MecanumDrive {
c.setStroke("#3F51B5"); c.setStroke("#3F51B5");
drawRobot(c, pose); drawRobot(c, pose);
c.setStroke("#4CAF50FF");
c.setStrokeWidth(1);
c.strokePolyline(xPoints, yPoints);
return true; return true;
} }
public void cancel() {
double t = clock() - beginTs;
double s = timeTrajectory.profile.get(t).value();
beginTs += t;
timeTrajectory = new TimeTrajectory(trajectory.cancel(s));
}
@Override @Override
public void draw(Canvas c) { public void preview(Canvas c) {
c.setStroke("#4CAF507A");
c.setStrokeWidth(1); c.setStrokeWidth(1);
c.setStroke(active ? "#4CAF50FF" : "#4CAF507A");
c.strokePolyline(xPoints, yPoints); c.strokePolyline(xPoints, yPoints);
} }
} }
public final class TurnAction implements Action { public final class TurnAction implements Action {
private final Pose2d beginPose; private final TimeTurn turn;
private final TimeProfile profile; private double beginTs = -1;
private double beginTs;
private final boolean reversed;
private boolean active; public TurnAction(TimeTurn turn) {
this.turn = turn;
public TurnAction(Pose2d beginPose, double angle) {
this.beginPose = beginPose;
profile = new TimeProfile(Profiles.constantProfile(Math.abs(angle), 0, MAX_ANG_VEL, -MAX_ANG_ACCEL, MAX_ANG_ACCEL).baseProfile);
reversed = angle < 0;
} }
@Override @Override
public void init() { public boolean run(@NonNull TelemetryPacket p) {
beginTs = clock(); double t;
if (beginTs < 0) {
beginTs = Actions.now();
t = 0;
} else {
t = Actions.now() - beginTs;
}
active = true; if (t >= turn.duration) {
}
@Override
public boolean loop(TelemetryPacket p) {
double t = clock() - beginTs;
if (t >= profile.duration) {
leftFront.setPower(0); leftFront.setPower(0);
leftBack.setPower(0); leftBack.setPower(0);
rightBack.setPower(0); rightBack.setPower(0);
rightFront.setPower(0); rightFront.setPower(0);
active = false;
return false; return false;
} }
DualNum<Time> x = profile.get(t); Pose2dDual<Time> txWorldTarget = turn.get(t);
if (reversed) {
x = x.unaryMinus();
}
Pose2dDual<Time> txWorldTarget = Rotation2dDual.exp(x).times(beginPose);
Twist2d robotVelRobot = updatePoseEstimateAndGetActualVel(); Twist2d robotVelRobot = updatePoseEstimateAndGetActualVel();
@ -359,13 +340,16 @@ public final class MecanumDrive {
c.setStroke("#3F51B5"); c.setStroke("#3F51B5");
drawRobot(c, pose); drawRobot(c, pose);
c.setStroke("#7C4DFFFF");
c.fillCircle(turn.beginPose.trans.x, turn.beginPose.trans.y, 2);
return true; return true;
} }
@Override @Override
public void draw(Canvas c) { public void preview(Canvas c) {
c.setFill(active ? "#7C4DFFFF" : "#7C4DFF7A"); c.setStroke("#7C4DFF7A");
c.fillCircle(beginPose.trans.x, beginPose.trans.y, 2); c.fillCircle(turn.beginPose.trans.x, turn.beginPose.trans.y, 2);
} }
} }
@ -410,73 +394,14 @@ public final class MecanumDrive {
c.strokeLine(p1.x, p1.y, p2.x, p2.y); c.strokeLine(p1.x, p1.y, p2.x, p2.y);
} }
public TurnAction turn(Pose2d beginPose, double angle) { public TrajectoryActionBuilder actionBuilder(Pose2d beginPose) {
return new TurnAction(beginPose, angle); return new TrajectoryActionBuilder(
} TurnAction::new,
public TurnAction turnTo(Pose2d beginPose, Rotation2d rot) { FollowTrajectoryAction::new,
return new TurnAction(beginPose, rot.minus(beginPose.rot)); beginPose, 1e-6,
} defaultTurnConstraints,
public TurnAction turnTo(Pose2d beginPose, double rot) { defaultVelConstraint, defaultAccelConstraint,
return turnTo(beginPose, Rotation2d.exp(rot)); 0.25
} );
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, Rotation2d beginTangent, TrajectoryBuilder.PoseMap poseMap) {
return new SafeTrajectoryBuilder(
beginPose, beginTangent, 1e-6,
0.0, defaultVelConstraint, defaultAccelConstraint, 0.25, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, double beginTangent, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(beginPose, Rotation2d.exp(beginTangent), poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, boolean reversed, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(beginPose, beginPose.rot.plus(reversed ? Math.PI : 0), poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(beginPose, false, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, Rotation2d beginTangent) {
return new SafeTrajectoryBuilder(
beginPose, beginTangent, 1e-6,
0.0, defaultVelConstraint, defaultAccelConstraint, 0.25);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, double beginTangent) {
return trajectoryBuilder(beginPose, Rotation2d.exp(beginTangent));
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, boolean reversed) {
return trajectoryBuilder(beginPose, beginPose.rot.plus(reversed ? Math.PI : 0));
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose) {
return trajectoryBuilder(beginPose, false);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, Rotation2d beginTangent, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(traj.path.end(1).value(), beginTangent, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, double beginTangent, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(traj.path.end(1).value(), beginTangent, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, boolean reversed, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(traj.path.end(1).value(), reversed, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(traj.path.end(1).value(), poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, Rotation2d beginTangent) {
return trajectoryBuilder(traj.path.end(1).value(), beginTangent);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, double beginTangent) {
return trajectoryBuilder(traj.path.end(1).value(), beginTangent);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, boolean reversed) {
return trajectoryBuilder(traj.path.end(1).value(), reversed);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj) {
return trajectoryBuilder(traj.path.end(1).value());
}
public FollowTrajectoryAction followTrajectory(Trajectory trajectory) {
return new FollowTrajectoryAction(trajectory);
} }
} }

View File

@ -1,9 +1,13 @@
package org.firstinspires.ftc.teamcode; package org.firstinspires.ftc.teamcode;
import androidx.annotation.NonNull;
import com.acmerobotics.dashboard.canvas.Canvas; import com.acmerobotics.dashboard.canvas.Canvas;
import com.acmerobotics.dashboard.config.Config; import com.acmerobotics.dashboard.config.Config;
import com.acmerobotics.dashboard.telemetry.TelemetryPacket; import com.acmerobotics.dashboard.telemetry.TelemetryPacket;
import com.acmerobotics.roadrunner.AccelConstraint; import com.acmerobotics.roadrunner.AccelConstraint;
import com.acmerobotics.roadrunner.Action;
import com.acmerobotics.roadrunner.Actions;
import com.acmerobotics.roadrunner.AngularVelConstraint; import com.acmerobotics.roadrunner.AngularVelConstraint;
import com.acmerobotics.roadrunner.Arclength; import com.acmerobotics.roadrunner.Arclength;
import com.acmerobotics.roadrunner.DualNum; import com.acmerobotics.roadrunner.DualNum;
@ -12,17 +16,13 @@ import com.acmerobotics.roadrunner.MotorFeedforward;
import com.acmerobotics.roadrunner.Pose2d; import com.acmerobotics.roadrunner.Pose2d;
import com.acmerobotics.roadrunner.Pose2dDual; import com.acmerobotics.roadrunner.Pose2dDual;
import com.acmerobotics.roadrunner.ProfileAccelConstraint; import com.acmerobotics.roadrunner.ProfileAccelConstraint;
import com.acmerobotics.roadrunner.Profiles;
import com.acmerobotics.roadrunner.RamseteController; import com.acmerobotics.roadrunner.RamseteController;
import com.acmerobotics.roadrunner.Rotation2d;
import com.acmerobotics.roadrunner.Rotation2dDual;
import com.acmerobotics.roadrunner.SafeTrajectoryBuilder;
import com.acmerobotics.roadrunner.TankKinematics; import com.acmerobotics.roadrunner.TankKinematics;
import com.acmerobotics.roadrunner.Time; import com.acmerobotics.roadrunner.Time;
import com.acmerobotics.roadrunner.TimeProfile;
import com.acmerobotics.roadrunner.TimeTrajectory; import com.acmerobotics.roadrunner.TimeTrajectory;
import com.acmerobotics.roadrunner.Trajectory; import com.acmerobotics.roadrunner.TimeTurn;
import com.acmerobotics.roadrunner.TrajectoryBuilder; import com.acmerobotics.roadrunner.TrajectoryActionBuilder;
import com.acmerobotics.roadrunner.TurnConstraints;
import com.acmerobotics.roadrunner.Twist2d; import com.acmerobotics.roadrunner.Twist2d;
import com.acmerobotics.roadrunner.Twist2dDual; import com.acmerobotics.roadrunner.Twist2dDual;
import com.acmerobotics.roadrunner.Twist2dIncrDual; import com.acmerobotics.roadrunner.Twist2dIncrDual;
@ -81,6 +81,8 @@ public final class TankDrive {
public final MotorFeedforward feedforward = new MotorFeedforward(kS, kV, kA); public final MotorFeedforward feedforward = new MotorFeedforward(kS, kV, kA);
public final TurnConstraints defaultTurnConstraints = new TurnConstraints(
MAX_ANG_VEL, -MAX_ANG_ACCEL, MAX_ANG_ACCEL);
public final VelConstraint defaultVelConstraint = public final VelConstraint defaultVelConstraint =
new MinVelConstraint(Arrays.asList( new MinVelConstraint(Arrays.asList(
kinematics.new WheelVelConstraint(MAX_WHEEL_VEL), kinematics.new WheelVelConstraint(MAX_WHEEL_VEL),
@ -211,16 +213,13 @@ public final class TankDrive {
} }
public final class FollowTrajectoryAction implements Action { public final class FollowTrajectoryAction implements Action {
public final Trajectory trajectory; public final TimeTrajectory timeTrajectory;
private TimeTrajectory timeTrajectory; private double beginTs = -1;
private double beginTs;
private boolean active;
private final double[] xPoints, yPoints; private final double[] xPoints, yPoints;
public FollowTrajectoryAction(Trajectory t) { public FollowTrajectoryAction(TimeTrajectory t) {
this.trajectory = t; timeTrajectory = t;
timeTrajectory = new TimeTrajectory(t);
List<Double> disps = com.acmerobotics.roadrunner.Math.range( List<Double> disps = com.acmerobotics.roadrunner.Math.range(
0, t.path.length(), 0, t.path.length(),
@ -235,15 +234,15 @@ public final class TankDrive {
} }
@Override @Override
public void init() { public boolean run(@NonNull TelemetryPacket p) {
beginTs = clock(); double t;
if (beginTs < 0) {
beginTs = Actions.now();
t = 0;
} else {
t = Actions.now() - beginTs;
}
active = true;
}
@Override
public boolean loop(TelemetryPacket p) {
double t = clock() - beginTs;
if (t >= timeTrajectory.duration) { if (t >= timeTrajectory.duration) {
for (DcMotorEx m : leftMotors) { for (DcMotorEx m : leftMotors) {
m.setPower(0); m.setPower(0);
@ -252,8 +251,6 @@ public final class TankDrive {
m.setPower(0); m.setPower(0);
} }
active = false;
return false; return false;
} }
@ -294,50 +291,41 @@ public final class TankDrive {
c.setStroke("#3F51B5"); c.setStroke("#3F51B5");
drawRobot(c, pose); drawRobot(c, pose);
c.setStroke("#4CAF50FF");
c.setStrokeWidth(1);
c.strokePolyline(xPoints, yPoints);
return true; return true;
} }
public void cancel() {
double t = clock() - beginTs;
double s = timeTrajectory.profile.get(t).value();
beginTs += t;
timeTrajectory = new TimeTrajectory(trajectory.cancel(s));
}
@Override @Override
public void draw(Canvas c) { public void preview(Canvas c) {
c.setStroke("#4CAF507A");
c.setStrokeWidth(1); c.setStrokeWidth(1);
c.setStroke(active ? "#4CAF50FF" : "#4CAF507A");
c.strokePolyline(xPoints, yPoints); c.strokePolyline(xPoints, yPoints);
} }
} }
public final class TurnAction implements Action { public final class TurnAction implements Action {
private final Pose2d beginPose; private final TimeTurn turn;
private final TimeProfile profile; private double beginTs = -1;
private double beginTs;
private final boolean reversed;
private boolean active; public TurnAction(TimeTurn turn) {
this.turn = turn;
public TurnAction(Pose2d beginPose, double angle) {
this.beginPose = beginPose;
profile = new TimeProfile(Profiles.constantProfile(Math.abs(angle), 0, MAX_ANG_VEL, -MAX_ANG_ACCEL, MAX_ANG_ACCEL).baseProfile);
reversed = angle < 0;
} }
@Override @Override
public void init() { public boolean run(@NonNull TelemetryPacket p) {
beginTs = clock(); double t;
if (beginTs < 0) {
beginTs = Actions.now();
t = 0;
} else {
t = Actions.now() - beginTs;
}
active = true; if (t >= turn.duration) {
}
@Override
public boolean loop(TelemetryPacket p) {
double t = clock() - beginTs;
if (t >= profile.duration) {
for (DcMotorEx m : leftMotors) { for (DcMotorEx m : leftMotors) {
m.setPower(0); m.setPower(0);
} }
@ -345,24 +333,18 @@ public final class TankDrive {
m.setPower(0); m.setPower(0);
} }
active = false;
return false; return false;
} }
DualNum<Time> x = profile.get(t); Pose2dDual<Time> txWorldTarget = turn.get(t);
if (reversed) {
x = x.unaryMinus();
}
Twist2d robotVelRobot = updatePoseEstimateAndGetActualVel(); Twist2d robotVelRobot = updatePoseEstimateAndGetActualVel();
Rotation2dDual<Time> target = Rotation2dDual.<Time>constant(beginPose.rot, 3).plus(x);
Twist2dDual<Time> command = new Twist2dDual<>( Twist2dDual<Time> command = new Twist2dDual<>(
Vector2dDual.constant(new Vector2d(0, 0), 3), Vector2dDual.constant(new Vector2d(0, 0), 3),
target.velocity().plus( txWorldTarget.rot.velocity().plus(
TURN_GAIN * pose.rot.minus(target.value()) + TURN_GAIN * pose.rot.minus(txWorldTarget.rot.value()) +
TURN_VEL_GAIN * (robotVelRobot.rotVel - target.velocity().value()) TURN_VEL_GAIN * (robotVelRobot.rotVel - txWorldTarget.rot.velocity().value())
) )
); );
@ -379,18 +361,21 @@ public final class TankDrive {
drawPoseHistory(c); drawPoseHistory(c);
c.setStroke("#4CAF50"); c.setStroke("#4CAF50");
drawRobot(c, new Pose2d(beginPose.trans, beginPose.rot.plus(x.value()))); drawRobot(c, txWorldTarget.value());
c.setStroke("#3F51B5"); c.setStroke("#3F51B5");
drawRobot(c, pose); drawRobot(c, pose);
c.setStroke("#7C4DFFFF");
c.fillCircle(turn.beginPose.trans.x, turn.beginPose.trans.y, 2);
return true; return true;
} }
@Override @Override
public void draw(Canvas c) { public void preview(Canvas c) {
c.setFill(active ? "#7C4DFFFF" : "#7C4DFF7A"); c.setStroke("#7C4DFF7A");
c.fillCircle(beginPose.trans.x, beginPose.trans.y, 2); c.fillCircle(turn.beginPose.trans.x, turn.beginPose.trans.y, 2);
} }
} }
@ -435,73 +420,14 @@ public final class TankDrive {
c.strokeLine(p1.x, p1.y, p2.x, p2.y); c.strokeLine(p1.x, p1.y, p2.x, p2.y);
} }
public TurnAction turn(Pose2d beginPose, double angle) { public TrajectoryActionBuilder actionBuilder(Pose2d beginPose) {
return new TurnAction(beginPose, angle); return new TrajectoryActionBuilder(
} TurnAction::new,
public TurnAction turnTo(Pose2d beginPose, Rotation2d rot) { FollowTrajectoryAction::new,
return new TurnAction(beginPose, rot.minus(beginPose.rot)); beginPose, 1e-6,
} defaultTurnConstraints,
public TurnAction turnTo(Pose2d beginPose, double rot) { defaultVelConstraint, defaultAccelConstraint,
return turnTo(beginPose, Rotation2d.exp(rot)); 0.25
} );
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, Rotation2d beginTangent, TrajectoryBuilder.PoseMap poseMap) {
return new SafeTrajectoryBuilder(
beginPose, beginTangent, 1e-6,
0.0, defaultVelConstraint, defaultAccelConstraint, 0.25, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, double beginTangent, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(beginPose, Rotation2d.exp(beginTangent), poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, boolean reversed, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(beginPose, beginPose.rot.plus(reversed ? Math.PI : 0), poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(beginPose, false, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, Rotation2d beginTangent) {
return new SafeTrajectoryBuilder(
beginPose, beginTangent, 1e-6,
0.0, defaultVelConstraint, defaultAccelConstraint, 0.25);
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, double beginTangent) {
return trajectoryBuilder(beginPose, Rotation2d.exp(beginTangent));
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose, boolean reversed) {
return trajectoryBuilder(beginPose, beginPose.rot.plus(reversed ? Math.PI : 0));
}
public SafeTrajectoryBuilder trajectoryBuilder(Pose2d beginPose) {
return trajectoryBuilder(beginPose, false);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, Rotation2d beginTangent, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(traj.path.end(1).value(), beginTangent, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, double beginTangent, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(traj.path.end(1).value(), beginTangent, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, boolean reversed, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(traj.path.end(1).value(), reversed, poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, TrajectoryBuilder.PoseMap poseMap) {
return trajectoryBuilder(traj.path.end(1).value(), poseMap);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, Rotation2d beginTangent) {
return trajectoryBuilder(traj.path.end(1).value(), beginTangent);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, double beginTangent) {
return trajectoryBuilder(traj.path.end(1).value(), beginTangent);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj, boolean reversed) {
return trajectoryBuilder(traj.path.end(1).value(), reversed);
}
public SafeTrajectoryBuilder trajectoryBuilder(Trajectory traj) {
return trajectoryBuilder(traj.path.end(1).value());
}
public TankDrive.FollowTrajectoryAction followTrajectory(Trajectory trajectory) {
return new TankDrive.FollowTrajectoryAction(trajectory);
} }
} }

View File

@ -1,13 +1,12 @@
package org.firstinspires.ftc.teamcode.tuning; package org.firstinspires.ftc.teamcode.tuning;
import com.acmerobotics.roadrunner.Trajectory;
import com.acmerobotics.roadrunner.Pose2d; import com.acmerobotics.roadrunner.Pose2d;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import org.firstinspires.ftc.teamcode.ActionOpMode;
import org.firstinspires.ftc.teamcode.MecanumDrive; import org.firstinspires.ftc.teamcode.MecanumDrive;
import org.firstinspires.ftc.teamcode.TankDrive; import org.firstinspires.ftc.teamcode.TankDrive;
public final class ManualFeedbackTuner extends LinearOpMode { public final class ManualFeedbackTuner extends ActionOpMode {
public static double DISTANCE = 64; public static double DISTANCE = 64;
@Override @Override
@ -15,36 +14,26 @@ public final class ManualFeedbackTuner extends LinearOpMode {
if (TuningOpModes.DRIVE_CLASS.equals(MecanumDrive.class)) { if (TuningOpModes.DRIVE_CLASS.equals(MecanumDrive.class)) {
MecanumDrive drive = new MecanumDrive(hardwareMap, new Pose2d(0, 0, 0)); MecanumDrive drive = new MecanumDrive(hardwareMap, new Pose2d(0, 0, 0));
Trajectory forward = drive.trajectoryBuilder(drive.pose)
.forward(DISTANCE)
.build();
Trajectory backward = drive.trajectoryBuilder(forward, true)
.forward(-DISTANCE)
.build();
waitForStart(); waitForStart();
while (opModeIsActive()) { while (opModeIsActive()) {
drive.followTrajectory(forward).runBlocking(); runBlocking(
drive.followTrajectory(backward).runBlocking(); drive.actionBuilder(drive.pose)
.forward(DISTANCE)
.forward(-DISTANCE)
.build());
} }
} else if (TuningOpModes.DRIVE_CLASS.equals(TankDrive.class)) { } else if (TuningOpModes.DRIVE_CLASS.equals(TankDrive.class)) {
TankDrive drive = new TankDrive(hardwareMap, new Pose2d(0, 0, 0)); TankDrive drive = new TankDrive(hardwareMap, new Pose2d(0, 0, 0));
Trajectory forward = drive.trajectoryBuilder(drive.pose)
.forward(DISTANCE)
.build();
Trajectory backward = drive.trajectoryBuilder(forward, true)
.forward(-DISTANCE)
.build();
waitForStart(); waitForStart();
while (opModeIsActive()) { while (opModeIsActive()) {
drive.followTrajectory(forward).runBlocking(); runBlocking(
drive.followTrajectory(backward).runBlocking(); drive.actionBuilder(drive.pose)
.forward(DISTANCE)
.forward(-DISTANCE)
.build());
} }
} else { } else {
throw new AssertionError(); throw new AssertionError();

View File

@ -1,13 +1,13 @@
package org.firstinspires.ftc.teamcode.tuning; package org.firstinspires.ftc.teamcode.tuning;
import com.acmerobotics.roadrunner.Vector2d;
import com.acmerobotics.roadrunner.Pose2d; import com.acmerobotics.roadrunner.Pose2d;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; import com.acmerobotics.roadrunner.Vector2d;
import org.firstinspires.ftc.teamcode.ActionOpMode;
import org.firstinspires.ftc.teamcode.MecanumDrive; import org.firstinspires.ftc.teamcode.MecanumDrive;
import org.firstinspires.ftc.teamcode.TankDrive; import org.firstinspires.ftc.teamcode.TankDrive;
public final class SplineTest extends LinearOpMode { public final class SplineTest extends ActionOpMode {
@Override @Override
public void runOpMode() throws InterruptedException { public void runOpMode() throws InterruptedException {
if (TuningOpModes.DRIVE_CLASS.equals(MecanumDrive.class)) { if (TuningOpModes.DRIVE_CLASS.equals(MecanumDrive.class)) {
@ -15,23 +15,21 @@ public final class SplineTest extends LinearOpMode {
waitForStart(); waitForStart();
drive.followTrajectory( runBlocking(
drive.trajectoryBuilder(drive.pose) drive.actionBuilder(drive.pose)
.splineTo(new Vector2d(30, 30), Math.PI / 2) .splineTo(new Vector2d(30, 30), Math.PI / 2)
.splineTo(new Vector2d(60, 0), Math.PI) .splineTo(new Vector2d(60, 0), Math.PI)
.build() .build());
).runBlocking();
} else if (TuningOpModes.DRIVE_CLASS.equals(TankDrive.class)) { } else if (TuningOpModes.DRIVE_CLASS.equals(TankDrive.class)) {
TankDrive drive = new TankDrive(hardwareMap, new Pose2d(0, 0, 0)); TankDrive drive = new TankDrive(hardwareMap, new Pose2d(0, 0, 0));
waitForStart(); waitForStart();
drive.followTrajectory( runBlocking(
drive.trajectoryBuilder(drive.pose) drive.actionBuilder(drive.pose)
.splineTo(new Vector2d(30, 30), Math.PI / 2) .splineTo(new Vector2d(30, 30), Math.PI / 2)
.splineTo(new Vector2d(60, 0), Math.PI) .splineTo(new Vector2d(60, 0), Math.PI)
.build() .build());
).runBlocking();
} else { } else {
throw new AssertionError(); throw new AssertionError();
} }