Skip to content

Commit

Permalink
changes for supporting multiple cameras
Browse files Browse the repository at this point in the history
  • Loading branch information
jhhbrown1 committed Jan 28, 2025
1 parent 5c84bf2 commit 636b57b
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 92 deletions.
12 changes: 10 additions & 2 deletions src/main/java/frc/robot/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,18 @@ public static final class DriveConstants {
public static final class RobotConstants {

/**
* The {@code Transform3d} expressing the pose of the camera relative to the
* pose of the robot.
* The {@code Transform3d} expressing the pose of the first camera relative to
* the pose of the robot.
*/
public static Transform3d kRobotToCamera1 = new Transform3d(new Translation3d(0.0, -0.1, 0.2),
new Rotation3d(0, Units.degreesToRadians(-20), 0));

/**
* The {@code Transform3d} expressing the pose of the second camera relative to
* the pose of the robot.
*/
public static Transform3d kRobotToCamera2 = new Transform3d(new Translation3d(-0.5, -0.0, 0.2),
new Rotation3d(0, Units.degreesToRadians(-20), Units.degreesToRadians(180)));

}
}
23 changes: 13 additions & 10 deletions src/main/java/frc/robot/Robot.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package frc.robot;

import static edu.wpi.first.wpilibj2.command.Commands.*;
import static frc.robot.Constants.RobotConstants.*;
import static frc.robot.subsystems.PoseEstimationSubsystem.*;

Expand All @@ -22,12 +23,12 @@
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.CommandScheduler;
import edu.wpi.first.wpilibj2.command.WaitCommand;
import edu.wpi.first.wpilibj2.command.button.CommandPS4Controller;
import frc.robot.Constants.ControllerConstants;
import frc.robot.Constants.ControllerConstants.Button;
import frc.robot.commands.AlignCommand;
import frc.robot.commands.DriveCommand;
import frc.robot.subsystems.DriveSimulator;
import frc.robot.subsystems.DriveSubsystem;
import frc.robot.subsystems.PhotonCameraSimulator;
import frc.robot.subsystems.PoseEstimationSubsystem;
Expand All @@ -38,12 +39,13 @@ public class Robot extends TimedRobot {
private final CommandPS4Controller m_driverController = new CommandPS4Controller(
ControllerConstants.kDriverControllerPort);
private final PowerDistribution m_pdh = new PowerDistribution();
DriveSimulator driveSimulator = new DriveSimulator(m_driveSubsystem, pose(1, 1, 0), 0.01);
PhotonCamera m_camera1 = RobotBase.isSimulation()
? new PhotonCameraSimulator("Camera1", kRobotToCamera1, m_driveSubsystem,
pose(1, 1, 0),
0.01, 3,
0.1)
? new PhotonCameraSimulator("Camera1", kRobotToCamera1, driveSimulator, 3, 0.1)
: new PhotonCamera("Cool camera");
PhotonCamera m_camera2 = RobotBase.isSimulation()
? new PhotonCameraSimulator("Camera2", kRobotToCamera2, driveSimulator, 3, 0.1)
: new PhotonCamera("Cool camera2");
private final PoseEstimationSubsystem m_poseEstimationSubystem = new PoseEstimationSubsystem(m_driveSubsystem,
m_camera1);

Expand Down Expand Up @@ -79,7 +81,9 @@ public void bindDriveControls() {
DriveCommand.moveForward(m_driveSubsystem, 1, distanceTolerance, angleTolerance));
m_driverController.button(Button.kCircle) // 2m away
.whileTrue(
DriveCommand.moveForward(m_driveSubsystem, 2, distanceTolerance, angleTolerance));
sequence(
DriveCommand.moveForward(m_driveSubsystem, 2, distanceTolerance, angleTolerance),
DriveCommand.moveForward(m_driveSubsystem, -2, distanceTolerance, angleTolerance)));

double angleOfCoverageInDegrees = 90;
double distanceThresholdInMeters = 4;
Expand Down Expand Up @@ -151,10 +155,9 @@ public void teleopExit() {
@Override
public void testInit() {
CommandScheduler.getInstance().cancelAll();
new WaitCommand(0)
.andThen(m_driveSubsystem.testCommand()) // F, B, SL, SR, RL, RR
.andThen(DriveCommand.testCommand(m_driveSubsystem))
.schedule();
sequence(
m_driveSubsystem.testCommand(), // F, B, SL, SR, RL, RR
DriveCommand.testCommand(m_driveSubsystem)).schedule();
}

@Override
Expand Down
104 changes: 104 additions & 0 deletions src/main/java/frc/robot/subsystems/DriveSimulator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package frc.robot.subsystems;

import java.util.Random;

import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Transform2d;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.StructPublisher;
import edu.wpi.first.wpilibj2.command.CommandScheduler;
import edu.wpi.first.wpilibj2.command.SubsystemBase;

/**
* A {@code DriveSimulator} aims to provide realistic simulations of the
* {@code Pose2d} of the robot on the field.
*/
public class DriveSimulator extends SubsystemBase {

/**
* The {@code DriveSubsystem} used by this {@code DriveSimulator}.
*/
private final DriveSubsystem m_driveSubsystem;

/**
* The {@code Pose2d} of the robot in simulation.
*/
private Pose2d m_pose;

/**
* The previous {@code Pose2d} of the robot from the {@code DriveSubsystem}.
*/
private Pose2d m_previousOdometryPose = null;

/**
* The error ratio in measurements for updating the odometry of the robot.
*/
private double m_measurmentErrorRatio;

/**
* The {@code StructPublisher} for reporting the {@code Pose2d} of the
* robot in simulation.
*/
private final StructPublisher<Pose2d> m_posePublisher;

/**
* The {@code Random} instance used by this {@code DriveSimulator}.
*/
private final Random random = new Random(31);

/**
* Constructs a {@code DriveSimulator}.
*
* @param driveSubsystem the {@code DriveSubsystem} to be used by the
* {@code PhotonCameraSimulator}
* @param initialPose the initial {@code Pose2d} of the robot in simulation
* @param measurmentErrorRatio the error ratio in measurements for
* updating the odometry of the robot
*/
public DriveSimulator(DriveSubsystem driveSubsystem, Pose2d initialPose, double measurmentErrorRatio) {
m_driveSubsystem = driveSubsystem;
m_pose = initialPose;
m_measurmentErrorRatio = measurmentErrorRatio;
m_posePublisher = NetworkTableInstance.getDefault()
.getStructTopic("/SmartDashboard/Pose@Simulation", Pose2d.struct)
.publish();
}

/**
* Is invoked periodically by the {@link CommandScheduler}.
*/
@Override
public void periodic() {
var p = m_driveSubsystem.getPose();
if (m_previousOdometryPose != null) {
var t = p.minus(m_previousOdometryPose);
t = new Transform2d(change(t.getX(), m_measurmentErrorRatio), change(t.getY(), m_measurmentErrorRatio),
Rotation2d.fromDegrees(change(t.getRotation().getDegrees(), m_measurmentErrorRatio)));
m_pose = m_pose.plus(t);
}
m_previousOdometryPose = p;
m_posePublisher.set(m_pose);
}

/**
* Returns the most recent simulated {@code Pose2d} of the robot on the field.
*
* @return the most recent simulated {@code Pose2d} of the robot on the field
*/
public Pose2d getSimulatedPose() {
return m_pose;
}

/**
* Randomly changes the specified value using the specified ratio.
*
* @param x a value
* @param r the ratio by which the value can be increased or decreased
* @return the resulting value
*/
private double change(double x, double r) {
return x * (1 - r + 2 * r * random.nextDouble());
}

}
94 changes: 14 additions & 80 deletions src/main/java/frc/robot/subsystems/PhotonCameraSimulator.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,18 @@
import org.photonvision.targeting.PhotonPipelineResult;
import org.photonvision.targeting.PhotonTrackedTarget;

import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Pose3d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Rotation3d;
import edu.wpi.first.math.geometry.Transform2d;
import edu.wpi.first.math.geometry.Transform3d;
import edu.wpi.first.networktables.NetworkTableInstance;
import edu.wpi.first.networktables.StructPublisher;
import edu.wpi.first.wpilibj.RobotBase;

/**
* A {@code PhotonCameraSimulator} aims to provide realistic simulations of a
* {@code PhotonCamera} with artificially injected auglar errors and delays.
*/
public class PhotonCameraSimulator extends PhotonCamera {

/**
* The {@code PhotonCamera} used by this {@code PhotonCameraSimulator}.
*/
private final DriveSubsystem m_driveSubsystem;

/**
* The {@code PhotonCameraSim} used by this {@code PhotonCameraSimulator}.
*/
Expand Down Expand Up @@ -69,39 +60,13 @@ public class PhotonCameraSimulator extends PhotonCamera {
*/
private PhotonPipelineResult m_latestResult = new PhotonPipelineResult();

/**
* The {@code Pose2d} of the robot in simulation.
*/
private Pose2d m_pose;

/**
* The previous {@code Pose2d} of the robot from the {@code DriveSubsystem}.
*/
private Pose2d m_previousOdometryPose = null;

/**
* The error ratio in measurements for updating the odometry of the
* robot.
*/
private double m_measurmentErrorRatio;

/**
* The {@code StructPublisher} for reporting the {@code Pose2d} of the
* robot in simulation.
*/
private final StructPublisher<Pose2d> m_posePublisher;

/**
* The {@code StructPublisher} for reporting the {@code Pose2d} of the
* robot in simulation.
*/
private final StructPublisher<Pose3d> m_cameraPosePublisher;

/**
* The {@code Transform3d} expressing the pose of the
* camera relative to the pose of the robot.
*/
private Transform3d m_robotToCamera;
private DriveSimulator m_driveSimulator;

/**
* Constructs a {@code PhotonCameraSimulator}.
Expand All @@ -119,34 +84,22 @@ public class PhotonCameraSimulator extends PhotonCamera {
* inject in degrees
* @param delayInSeconds the artificial delay to add in seconds
*/
public PhotonCameraSimulator(String cameraName, Transform3d robotToCamera, DriveSubsystem driveSubsystem,
Pose2d initialPose,
double measurmentErrorRatio,
public PhotonCameraSimulator(String cameraName, Transform3d robotToCamera,
DriveSimulator driveSimulator,
double maxAngularErrorInDegrees,
double delayInSeconds) {
super(cameraName);
m_robotToCamera = robotToCamera;
m_driveSubsystem = driveSubsystem;
m_pose = initialPose;
m_measurmentErrorRatio = measurmentErrorRatio;
m_driveSimulator = driveSimulator;
m_maxAngularError = maxAngularErrorInDegrees * Math.PI / 180;
m_DelayInSeconds = delayInSeconds;
if (RobotBase.isSimulation()) {
m_cameraSim = new PhotonCameraSim(this);
m_cameraSim.enableProcessedStream(true);
m_cameraSim.enableDrawWireframe(true);
m_sim = new VisionSystemSim(cameraName);
m_sim.addAprilTags(kFieldLayout);
m_sim.addCamera(m_cameraSim, robotToCamera);
} else {
m_sim = null;
m_cameraSim = null;
}
m_posePublisher = NetworkTableInstance.getDefault()
.getStructTopic("/SmartDashboard/Pose@Simulation", Pose2d.struct)
.publish();
m_cameraSim = new PhotonCameraSim(this);
m_cameraSim.enableProcessedStream(true);
m_cameraSim.enableDrawWireframe(true);
m_sim = new VisionSystemSim(cameraName);
m_sim.addAprilTags(kFieldLayout);
m_sim.addCamera(m_cameraSim, robotToCamera);
m_cameraPosePublisher = NetworkTableInstance.getDefault()
.getStructTopic("/SmartDashboard/CameraPose@Simulation", Pose3d.struct)
.getStructTopic("/SmartDashboard/" + cameraName + "@Simulation", Pose3d.struct)
.publish();
}

Expand Down Expand Up @@ -184,17 +137,6 @@ public List<PhotonPipelineResult> getAllUnreadResults() {
return r;
}

/**
* Randomly changes the specified value using the specified ratio.
*
* @param x a value
* @param r the ratio by which the value can be increased or decreased
* @return the resulting value
*/
private double change(double x, double r) {
return x * (1 - r + 2 * r * Math.random());
}

/**
* Updates this {@code PhotonCameraSimulator} using the specified
* {@code PhotonPipelineResult}s.
Expand All @@ -213,17 +155,9 @@ private void update(List<PhotonPipelineResult> l) {
m_unreadResults.remove();
}
}
var p = m_driveSubsystem.getPose();
if (m_previousOdometryPose != null) {
var t = p.minus(m_previousOdometryPose);
t = new Transform2d(change(t.getX(), m_measurmentErrorRatio), change(t.getY(), m_measurmentErrorRatio),
Rotation2d.fromDegrees(change(t.getRotation().getDegrees(), m_measurmentErrorRatio)));
m_pose = m_pose.plus(t);
}
m_previousOdometryPose = p;
m_sim.update(m_pose);
m_posePublisher.set(m_pose);
m_cameraPosePublisher.set(new Pose3d(m_pose).plus(m_robotToCamera));
var pose = m_driveSimulator.getSimulatedPose();
m_sim.update(pose);
m_cameraPosePublisher.set(m_sim.getCameraPose(m_cameraSim).get());
}

/**
Expand Down

0 comments on commit 636b57b

Please sign in to comment.