package com.team254.frc2015.subsystems; import com.team254.frc2015.Constants; import com.team254.frc2015.subsystems.controllers.DriveFinishLineController; import com.team254.frc2015.subsystems.controllers.DrivePathController; import com.team254.frc2015.subsystems.controllers.DriveStraightController; import com.team254.frc2015.subsystems.controllers.TurnInPlaceController; import com.team254.lib.trajectory.Path; import com.team254.lib.util.*; import com.team254.lib.util.gyro.GyroThread; import edu.wpi.first.wpilibj.Encoder; public class Drive extends Subsystem implements Loopable { public interface DriveController { DriveSignal update(Pose pose); Pose getCurrentSetpoint(); public boolean onTarget(); } public CheesySpeedController m_left_motor; public CheesySpeedController m_right_motor; public Encoder m_left_encoder; public Encoder m_right_encoder; public GyroThread m_gyro; private DriveController m_controller = null; protected final double m_inches_per_tick = Constants.kDriveWheelSizeInches * Math.PI / Constants.kElevatorEncoderCountsPerRev; protected final double m_wheelbase_width = 26.0; // Get from CAD protected final double m_turn_slip_factor = 1.2; // Measure empirically private Pose m_cached_pose = new Pose(0, 0, 0, 0, 0, 0); // Don't allocate poses at 200Hz! public Drive(String name, CheesySpeedController left_drive, CheesySpeedController right_drive, Encoder left_encoder, Encoder right_encoder, GyroThread gyro) { super(name); this.m_left_motor = left_drive; this.m_right_motor = right_drive; this.m_left_encoder = left_encoder; this.m_right_encoder = right_encoder; this.m_left_encoder.setDistancePerPulse(m_inches_per_tick); this.m_right_encoder.setDistancePerPulse(m_inches_per_tick); this.m_gyro = gyro; } public void setOpenLoop(DriveSignal signal) { m_controller = null; setDriveOutputs(signal); } public void setDistanceSetpoint(double distance) { setDistanceSetpoint(distance, Constants.kDriveMaxSpeedInchesPerSec); } public void setDistanceSetpoint(double distance, double velocity) { // 0 < vel < max_vel double vel_to_use = Math.min(Constants.kDriveMaxSpeedInchesPerSec, Math.max(velocity, 0)); m_controller = new DriveStraightController( getPoseToContinueFrom(false), distance, vel_to_use); } public void setTurnSetPoint(double heading) { setTurnSetPoint(heading, Constants.kTurnMaxSpeedRadsPerSec); } public void setTurnSetPoint(double heading, double velocity) { velocity = Math.min(Constants.kTurnMaxSpeedRadsPerSec, Math.max(velocity, 0)); m_controller = new TurnInPlaceController(getPoseToContinueFrom(true), heading, velocity); } public void reset() { m_left_encoder.reset(); m_right_encoder.reset(); } public void setPathSetpoint(Path path) { reset(); m_controller = new DrivePathController(path); } public void setFinishLineSetpoint(double distance, double heading) { reset(); m_controller = new DriveFinishLineController(distance, heading, 1.0); } @Override public void getState(StateHolder states) { states.put("gyro_angle", m_gyro.getAngle()); states.put("left_encoder", m_left_encoder.getDistance()); states.put("left_encoder_rate", m_left_encoder.getRate()); states.put("right_encoder_rate", m_right_encoder.getRate()); states.put("right_encoder", m_right_encoder.getDistance()); Pose setPointPose = m_controller == null ? getPhysicalPose() : m_controller.getCurrentSetpoint(); states.put( "drive_set_point_pos", DriveStraightController.encoderDistance(setPointPose)); states.put("turn_set_point_pos", setPointPose.getHeading()); states.put("left_signal", m_left_motor.get()); states.put("right_signal", m_right_motor.get()); states.put("on_target", (m_controller != null && m_controller.onTarget()) ? 1.0 : 0.0); } @Override public void update() { if (m_controller == null) { return; } setDriveOutputs(m_controller.update(getPhysicalPose())); } private void setDriveOutputs(DriveSignal signal) { m_left_motor.set(signal.leftMotor); m_right_motor.set(-signal.rightMotor); } private Pose getPoseToContinueFrom(boolean for_turn_controller) { if (!for_turn_controller && m_controller instanceof TurnInPlaceController) { Pose pose_to_use = getPhysicalPose(); pose_to_use.m_heading = ((TurnInPlaceController) m_controller).getHeadingGoal(); pose_to_use.m_heading_velocity = 0; return pose_to_use; } else if (m_controller == null || (m_controller instanceof DriveStraightController && for_turn_controller)) { return getPhysicalPose(); } else if (m_controller instanceof DriveFinishLineController) { return getPhysicalPose(); } else if (m_controller.onTarget()) { return m_controller.getCurrentSetpoint(); } else { return getPhysicalPose(); } } /** * @return The pose according to the current sensor state */ public Pose getPhysicalPose() { m_cached_pose.reset( m_left_encoder.getDistance(), m_right_encoder.getDistance(), m_left_encoder.getRate(), m_right_encoder.getRate(), m_gyro.getAngle(), m_gyro.getRate()); return m_cached_pose; } public Drive.DriveController getController() { return m_controller; } @Override public void reloadConstants() { // TODO Auto-generated method stub } public boolean controllerOnTarget() { return m_controller != null && m_controller.onTarget(); } }