예제 #1
0
class SetDistanceToBox(Command):
    """
    Drive until the robot is the given distance away from the box. Uses a local
    PID controller to run a simple PID loop that is only enabled while this
    command is running. The input is the averaged values of the left and right
    encoders.
    """
    def __init__(self, robot, distance):
        super().__init__()
        self.robot = robot

        self.requires(self.robot.drivetrain)
        self.pid = PIDController(
            -2,
            0,
            0,
        )
        self.pid.setSetpoint(distance)

    def initialize(self):
        """Called just before this Command runs the first time"""

        # Get everything in a safe starting state.
        self.robot.drivetrain.reset()
        self.pid.reset()
        self.pid.enable()

    def execute(self):
        """Called repeatedly when this Command is scheduled to run"""

    def isFinished(self):
        """Make this return true when this Command no longer needs to run execute()"""
        return self.pid.onTarget()

    def end(self):
        """Called once after isFinished returns true"""

        # Stop PID and the wheels
        self.pid.disable()
        self.robot.drivetrain.driveManual(0, 0)

    def interrupted(self):
        """Called when another command which requires one or more of the same
        subsystems is scheduled to run"""
        self.end()
예제 #2
0
파일: drive.py 프로젝트: frc1418/2020-robot
class Drive:
    """
    Handle robot drivetrain.
    All drive interaction must go through this class.
    """

    limelight: Limelight
    train: wpilib.drive.DifferentialDrive
    navx: navx.AHRS
    left_motors: wpilib.SpeedControllerGroup
    right_motors: wpilib.SpeedControllerGroup

    y = will_reset_to(0.0)
    rot = will_reset_to(0.0)
    aligning = will_reset_to(False)
    deadband = will_reset_to(0.1)

    auto = will_reset_to(False)
    left_voltage = will_reset_to(0)
    right_voltage = will_reset_to(0)

    speed_constant = will_reset_to(1.05)
    rotational_constant = will_reset_to(0.8)
    squared_inputs = False

    angle_p = ntproperty('/align/kp', 0.04)
    angle_i = ntproperty('/align/ki', 0)
    angle_d = ntproperty('/align/kd', 0.0)
    angle_reported = ntproperty('/align/angle', 0)
    angle_to = ntproperty('/align/angle_to', 0)

    def setup(self):
        """
        Run setup code on the injected variables (train)
        """
        self.angle_controller = PIDController(self.angle_p, self.angle_i,
                                              self.angle_d)
        self.angle_controller.setTolerance(2, 1)
        self.angle_controller.enableContinuousInput(0, 360)
        self.angle_setpoint = None
        self.calculated_pid = False

    def set_target(self, angle: float, relative=True):
        if relative and angle is not None:
            self.angle_setpoint = (self.angle + angle) % 360
            self.angle_to = self.angle_setpoint
        else:
            self.angle_setpoint = angle

        if angle is not None:
            self.angle_controller.setSetpoint(self.angle_setpoint)
        else:
            self.calculated_pid = False
            self.angle_controller.reset()

    def align(self):
        self.aligning = True
        self.deadband = 0

    def voltageDrive(self, left_voltage, right_voltage):
        self.left_voltage = left_voltage
        self.right_voltage = right_voltage
        self.auto = True
        self.deadband = 0

    def move(self, y: float, rot: float):
        """
        Move robot.
        :param y: Speed of motion in the y direction. [-1..1]
        :param rot: Speed of rotation. [-1..1]
        """
        self.y = y
        self.rot = rot

    @property
    def target_locked(self):
        # self.logger.info(f'SET {self.angle_controller.getSetpoint()} ANG {self.navx.getAngle()}')
        return self.angle_controller.atSetpoint() and self.calculated_pid

    @property
    def angle(self):
        raw = self.navx.getAngle()
        while raw < 0:
            raw += 360
        return raw % 360

    def execute(self):
        """
        Handle driving.
        """
        self.train.setDeadband(self.deadband)
        self.angle_reported = self.angle

        if self.aligning and self.angle_setpoint is not None:
            self.right_motors.setInverted(False)
            if self.angle_controller.atSetpoint() and self.calculated_pid:
                self.train.arcadeDrive(0, 0, squareInputs=False)
                return

            # Use new network tables variables for testing
            self.angle_controller.setP(self.angle_p)
            self.angle_controller.setD(self.angle_d)
            self.angle_controller.setI(self.angle_i)

            output = self.angle_controller.calculate(self.angle)
            self.logger.info(
                f'Output: {output}, Error: {self.angle_controller.getPositionError()}'
            )
            # Manual I-term zone (15 degrees)
            if abs(self.angle_controller.getPositionError()) <= 3:
                self.angle_controller.setI(self.angle_i)
                # Minumum and Maximum effect of integrator on output
                self.angle_controller.setIntegratorRange(-0.05, 0.05)
            else:
                self.angle_controller.setI(0)
                self.angle_controller.setIntegratorRange(0, 0)

            if output < 0:
                output = max(output, -0.35)
            else:
                output = min(output, 0.35)

            self.train.arcadeDrive(0, output, squareInputs=False)
            self.calculated_pid = True
        elif self.auto:
            self.right_motors.setInverted(True)
            self.right_motors.setVoltage(self.right_voltage)
            self.left_motors.setVoltage(self.left_voltage)
        else:
            self.right_motors.setInverted(False)
            self.train.arcadeDrive(
                self.speed_constant * self.y,
                self.rotational_constant * self.rot,
                squareInputs=self.squared_inputs,
            )

        if not self.limelight.targetExists():
            self.limelight.target_state = 0
        elif self.target_locked:
            self.limelight.target_state = 2
        else:
            self.limelight.target_state = 1
예제 #3
0
파일: turret.py 프로젝트: med-jed/rc2020-1
class Turret:
    '''
    The object thats responsible for managing the shooter
    '''
    def __init__(self):
        self.clockwise_limit_switch = DigitalInput(
            TURRET_CLOCKWISE_LIMIT_SWITCH)
        self.counterclockwise_limit_switch = DigitalInput(
            TURRET_COUNTERCLOCKWISE_LIMIT_SWITCH)

        self.turn_motor = SparkMax(TURRET_TURN_MOTOR)
        self.turn_pid = PIDController(0.4, 0.001, 0.02)

        self.shoot_motor_1 = Falcon(TURRET_SHOOT_MOTORS[0])
        self.shoot_motor_2 = Falcon(TURRET_SHOOT_MOTORS[1])
        self.timer = Timer()

        self.limelight = Limelight()

    def set_target_angle(self, angle):
        '''
        Sets the target angle of the turret. This will use a PID to turn the
        turret to the target angle.
        '''

        target_encoder = angle_to_encoder(angle)
        self.turn_pid.setSetpoint(target_encoder)

    def update(self):
        '''
        This is used to continuously update the turret's event loop.

        All this manages as of now is the turrets PID controller.

        The shoot motor is constantly running at a low percentage until we need it.
        '''

        motor_speed = self.turn_pid.calculate(
            self.limelight.get_target_screen_x())

        if self.clockwise_limit_switch.get() and motor_speed < 0:
            self.turn_motor.set_percent_output(motor_speed)

        elif self.counterclockwise_limit_switch.get() and motor_speed > 0:
            self.turn_motor.set_percent_output(motor_speed)

    def shoot(self):
        '''
        The wheel to shoot will rev up completely before balls start feeding
        in from the singulator.
        '''
        # One of the motors will be reversed, so make sure the shoot motor has the correct ID!
        speed = self.shoot_motor_1.get_percent_output()
        if speed < 1:
            speed += 0.02
        elif speed > 1:
            speed -= 0.02

        self.shoot_motor_1.set_percent_output(speed)
        self.shoot_motor_2.set_percent_output(-speed)

    def idle(self):
        '''
        Resets the motors back to their default state.
        '''
        speed = self.shoot_motor_1.get_percent_output()
        if speed < 0.5:
            speed += 0.02
        elif speed > 0.5:
            speed -= 0.02

        self.shoot_motor_1.set_percent_output(speed)
        self.shoot_motor_2.set_percent_output(-speed)

    def is_full_speed(self):
        '''
        Returns if the motor is at full speed or not.
        '''
        self.timer.get() > 0.4