예제 #1
0
class LifterComponent(Events):
    class EVENTS:
        on_control_move = "on_control_move"
        on_manual_move = "on_manual move"

    # RPM Multipliers
    # ELEVATOR_MULTIPLIER = 1635.15 / 6317.67
    ELEVATOR_MULTIPLIER = 2102.35 / 3631.33
    CARRIAGE_MULTIPLIER = 1.0 - ELEVATOR_MULTIPLIER

    # Max heights of each stage.
    ELEVATOR_MAX_HEIGHT = 40
    CARRIAGE_MAX_HEIGHT = 40

    # Conversion factors (counts to inches)
    # CHANGE THESE VALUES
    ELEVATOR_CONV_FACTOR = 0.00134
    CARRIAGE_CONV_FACTOR = 0.000977

    el_down = -1
    el_up = 1
    carriage_down = -1
    carriage_up = 1
    MAX_SPEED = 0.5
    ELEVATOR_ZERO = 0.0
    CARRIAGE_ZERO = 0.0
    TIMEOUT_MS = 0

    ELEVATOR_kF = 0.0
    ELEVATOR_kP = 0.1
    ELEVATOR_kI = 0.0
    ELEVATOR_kD = 0.0

    # ALLOWABLE_ERROR = 2

    CARRIAGE_ALLOWABLE_ERROR = int(2 / CARRIAGE_CONV_FACTOR)
    ELEVATOR_ALLOWABLE_ERROR = int(2 / ELEVATOR_CONV_FACTOR)

    # positions = {
    #     "floor": 2.0,
    #     "portal": 34.0,
    #     "scale_low": 48.0,
    #     "scale_mid": 60.0,
    #     "scale_high": 72.0,
    #     "max_height": 84.0
    # }

    positions = {
        "floor": 0.5,
        "portal": 34.0,
        "scale_low": 52.0,
        "scale_mid": 68.0,
        "scale_high": 78.0
    }

    def __init__(self):
        Events.__init__(self)
        self.elevator_motor = WPI_TalonSRX(5)
        self.elevator_bottom_switch = DigitalInput(9)

        self.carriage_motor = WPI_TalonSRX(3)
        self.carriage_bottom_switch = DigitalInput(1)
        self.carriage_top_switch = DigitalInput(2)

        self._create_events([
            LifterComponent.EVENTS.on_control_move,
            LifterComponent.EVENTS.on_manual_move
        ])

        self._is_reset = False

        # configure elevator motor and encoder

        self.elevator_motor.setNeutralMode(NeutralMode.Brake)

        self.elevator_motor.configSelectedFeedbackSensor(
            FeedbackDevice.CTRE_MagEncoder_Relative, 0,
            LifterComponent.TIMEOUT_MS)

        self.elevator_motor.setSensorPhase(True)
        self.elevator_motor.setInverted(True)

        self.elevator_motor.configNominalOutputForward(
            LifterComponent.ELEVATOR_ZERO, LifterComponent.TIMEOUT_MS)
        self.elevator_motor.configNominalOutputReverse(
            LifterComponent.ELEVATOR_ZERO, LifterComponent.TIMEOUT_MS)
        self.elevator_motor.configPeakOutputForward(LifterComponent.el_up,
                                                    LifterComponent.TIMEOUT_MS)
        self.elevator_motor.configPeakOutputReverse(LifterComponent.el_down,
                                                    LifterComponent.TIMEOUT_MS)

        self.elevator_motor.configNominalOutputForward(
            LifterComponent.ELEVATOR_ZERO, LifterComponent.TIMEOUT_MS)
        self.elevator_motor.configNominalOutputReverse(
            LifterComponent.ELEVATOR_ZERO, LifterComponent.TIMEOUT_MS)
        self.elevator_motor.configPeakOutputForward(LifterComponent.el_up,
                                                    LifterComponent.TIMEOUT_MS)
        self.elevator_motor.configPeakOutputReverse(LifterComponent.el_down,
                                                    LifterComponent.TIMEOUT_MS)

        self.elevator_motor.configAllowableClosedloopError(
            0, LifterComponent.ELEVATOR_ALLOWABLE_ERROR,
            LifterComponent.TIMEOUT_MS)

        self.elevator_motor.configForwardSoftLimitThreshold(
            int(LifterComponent.ELEVATOR_MAX_HEIGHT /
                LifterComponent.ELEVATOR_CONV_FACTOR), 0)

        self.elevator_motor.config_kF(0, LifterComponent.ELEVATOR_kF,
                                      LifterComponent.TIMEOUT_MS)
        self.elevator_motor.config_kP(0, LifterComponent.ELEVATOR_kP,
                                      LifterComponent.TIMEOUT_MS)
        self.elevator_motor.config_kI(0, LifterComponent.ELEVATOR_kI,
                                      LifterComponent.TIMEOUT_MS)
        self.elevator_motor.config_kD(0, LifterComponent.ELEVATOR_kD,
                                      LifterComponent.TIMEOUT_MS)

        # self.elevator_motor.setCurrentLimit()
        # self.elevator_motor.EnableCurrentLimit()
        # self.elevator_motor.configVoltageCompSaturation(4, LifterComponent.TIMEOUT_MS)

        # configure carriage motor and encoder

        self.carriage_motor.setNeutralMode(NeutralMode.Brake)

        self.carriage_motor.configSelectedFeedbackSensor(
            FeedbackDevice.CTRE_MagEncoder_Relative, 0,
            LifterComponent.TIMEOUT_MS)

        self.carriage_motor.setSensorPhase(True)
        self.carriage_motor.setInverted(True)

        self.carriage_motor.configNominalOutputForward(
            LifterComponent.ELEVATOR_ZERO, LifterComponent.TIMEOUT_MS)
        self.carriage_motor.configNominalOutputReverse(
            LifterComponent.ELEVATOR_ZERO, LifterComponent.TIMEOUT_MS)
        self.carriage_motor.configPeakOutputForward(LifterComponent.el_up,
                                                    LifterComponent.TIMEOUT_MS)
        self.carriage_motor.configPeakOutputReverse(LifterComponent.el_down,
                                                    LifterComponent.TIMEOUT_MS)

        self.carriage_motor.configNominalOutputForward(
            LifterComponent.ELEVATOR_ZERO, LifterComponent.TIMEOUT_MS)
        self.carriage_motor.configNominalOutputReverse(
            LifterComponent.ELEVATOR_ZERO, LifterComponent.TIMEOUT_MS)
        self.carriage_motor.configPeakOutputForward(LifterComponent.el_up,
                                                    LifterComponent.TIMEOUT_MS)
        self.carriage_motor.configPeakOutputReverse(LifterComponent.el_down,
                                                    LifterComponent.TIMEOUT_MS)

        self.carriage_motor.configAllowableClosedloopError(
            0, LifterComponent.CARRIAGE_ALLOWABLE_ERROR,
            LifterComponent.TIMEOUT_MS)

        self.carriage_motor.configForwardSoftLimitThreshold(
            int(LifterComponent.CARRIAGE_MAX_HEIGHT /
                LifterComponent.CARRIAGE_CONV_FACTOR), 0)

        self.carriage_motor.config_kF(0, LifterComponent.ELEVATOR_kF,
                                      LifterComponent.TIMEOUT_MS)
        self.carriage_motor.config_kP(0, LifterComponent.ELEVATOR_kP,
                                      LifterComponent.TIMEOUT_MS)
        self.carriage_motor.config_kI(0, LifterComponent.ELEVATOR_kI,
                                      LifterComponent.TIMEOUT_MS)
        self.carriage_motor.config_kD(0, LifterComponent.ELEVATOR_kD,
                                      LifterComponent.TIMEOUT_MS)

        # self.carriage_motor.setCurrentLimit()
        # self.carriage_motor.EnableCurrentLimit()
        # self.carriage_motor.configVoltageCompSaturation(4, LifterComponent.TIMEOUT_MS)

    def set_elevator_speed(self, speed):
        if (speed > 0 and self.current_elevator_position >= LifterComponent.ELEVATOR_MAX_HEIGHT - 2) \
                or (speed < 0 and self.elevator_bottom_switch.get()):
            self.elevator_motor.set(0)
        else:
            self.elevator_motor.set(speed)
        self.trigger_event(LifterComponent.EVENTS.on_manual_move)

    def set_carriage_speed(self, speed):
        if (speed > 0 and self.carriage_top_switch.get()) \
                or (speed < 0 and self.carriage_bottom_switch.get()):
            self.carriage_motor.set(0)
        else:
            self.carriage_motor.set(speed)
        self.trigger_event(LifterComponent.EVENTS.on_manual_move)

    def reset_sensors(self):
        self.carriage_motor.setSelectedSensorPosition(
            0, 0, LifterComponent.TIMEOUT_MS)
        self.elevator_motor.setSelectedSensorPosition(
            0, 0, LifterComponent.TIMEOUT_MS)
        self._is_reset = True

    @property
    def current_elevator_position(self) -> float:
        return self.elevator_motor.getSelectedSensorPosition(
            0) * LifterComponent.ELEVATOR_CONV_FACTOR

    @property
    def current_carriage_position(self) -> float:
        return self.carriage_motor.getSelectedSensorPosition(
            0) * LifterComponent.CARRIAGE_CONV_FACTOR

    @property
    def current_position(self) -> float:
        return self.current_elevator_position + self.current_carriage_position

    def stop_lift(self):
        self.elevator_motor.stopMotor()
        self.carriage_motor.stopMotor()

    def elevator_to_target_position(self, inches: float):
        if self._is_reset:
            self.elevator_motor.set(
                WPI_TalonSRX.ControlMode.Position,
                inches / LifterComponent.ELEVATOR_CONV_FACTOR)
            self.trigger_event(LifterComponent.EVENTS.on_control_move)

    def carriage_to_target_position(self, inches: float):
        if self._is_reset:
            self.carriage_motor.set(
                WPI_TalonSRX.ControlMode.Position,
                inches / LifterComponent.CARRIAGE_CONV_FACTOR)
            self.trigger_event(LifterComponent.EVENTS.on_control_move)

    def lift_to_distance(self, inches):
        i = inches + 6
        elevator = min(i * LifterComponent.ELEVATOR_MULTIPLIER,
                       LifterComponent.ELEVATOR_MAX_HEIGHT)
        carriage = i - elevator

        print("lift_to_distance carriage" + str(carriage))
        print("lift_to_distance elevate" + str(elevator))
        print("lift_to_distance lifter" + str(carriage + elevator))

        self.elevator_to_target_position(elevator)
        self.carriage_to_target_position(carriage)
        self.trigger_event(LifterComponent.EVENTS.on_control_move)

    def is_at_position(self, position: str) -> bool:
        return self.is_at_distance(LifterComponent.positions[position])

    def is_at_distance(self, inches: float) -> bool:
        return inches - 5 < self.current_position < inches + 5

    def closest_position(self) -> tuple:
        # returns the key for the position closest to the current position
        positions = [(key, position)
                     for key, position in LifterComponent.positions.items()]
        return min(
            positions,
            key=lambda position: abs(self.current_position - position[1]))

    def next_position(self) -> str:
        position = self.closest_position()
        positions = [(key, position)
                     for key, position in LifterComponent.positions.items()]
        index = positions.index(position)
        if index == len(positions) - 1:
            return position[0]
        return positions[index + 1][0]

    def prev_position(self) -> str:
        position = self.closest_position()
        positions = [(key, position)
                     for key, position in LifterComponent.positions.items()]
        index = positions.index(position)
        if index == 0:
            return position[0]
        return positions[index - 1][0]
예제 #2
0
class Robot(wpilib.IterativeRobot):

    #: Which PID slot to pull gains from. Starting 2018, you can choose from
    #: 0,1,2 or 3. Only the first two (0,1) are visible in web-based
    #: configuration.
    kSlotIdx = 0

    #: Talon SRX/ Victor SPX will supported multiple (cascaded) PID loops. For
    #: now we just want the primary one.
    kPIDLoopIdx = 0

    #: set to zero to skip waiting for confirmation, set to nonzero to wait and
    #: report to DS if action fails.
    kTimeoutMs = 10

    def robotInit(self):
        self.talon = WPI_TalonSRX(3)
        self.joy = wpilib.Joystick(0)

        self.loops = 0
        self.lastButton1 = False
        self.targetPos = 0

        # choose the sensor and sensor direction
        self.talon.configSelectedFeedbackSensor(
            WPI_TalonSRX.FeedbackDevice.CTRE_MagEncoder_Relative,
            self.kPIDLoopIdx,
            self.kTimeoutMs,
        )

        # choose to ensure sensor is positive when output is positive
        self.talon.setSensorPhase(True)

        # choose based on what direction you want forward/positive to be.
        # This does not affect sensor phase.
        self.talon.setInverted(False)

        # set the peak and nominal outputs, 12V means full
        self.talon.configNominalOutputForward(0, self.kTimeoutMs)
        self.talon.configNominalOutputReverse(0, self.kTimeoutMs)
        self.talon.configPeakOutputForward(1, self.kTimeoutMs)
        self.talon.configPeakOutputReverse(-1, self.kTimeoutMs)

        # Set the allowable closed-loop error, Closed-Loop output will be
        # neutral within this range. See Table in Section 17.2.1 for native
        # units per rotation.
        self.talon.configAllowableClosedloopError(0, self.kPIDLoopIdx,
                                                  self.kTimeoutMs)

        # set closed loop gains in slot0, typically kF stays zero - see documentation */
        self.talon.selectProfileSlot(self.kSlotIdx, self.kPIDLoopIdx)
        self.talon.config_kF(0, 0, self.kTimeoutMs)
        self.talon.config_kP(0, 0.1, self.kTimeoutMs)
        self.talon.config_kI(0, 0, self.kTimeoutMs)
        self.talon.config_kD(0, 0, self.kTimeoutMs)

        # zero the sensor
        self.talon.setSelectedSensorPosition(0, self.kPIDLoopIdx,
                                             self.kTimeoutMs)

    def teleopPeriodic(self):
        """
            This function is called periodically during operator control
        """
        # get gamepad axis - forward stick is positive
        leftYstick = self.joy.getY()
        # calculate the percent motor output
        motorOutput = self.talon.getMotorOutputPercent()
        button1 = self.joy.getRawButton(1)
        button2 = self.joy.getRawButton(2)

        # deadband gamepad
        if abs(leftYstick) < 0.1:
            leftYstick = 0

        # prepare line to print
        sb = []
        sb.append("\tOut%%: %.3f" % motorOutput)
        sb.append("\tPos: %.3fu" %
                  self.talon.getSelectedSensorPosition(self.kPIDLoopIdx))

        if self.lastButton1 and button1:
            # Position mode - button just pressed

            # 10 Rotations * 4096 u/rev in either direction
            self.targetPos = leftYstick * 4096 * 10.0
            self.talon.set(WPI_TalonSRX.ControlMode.Position, self.targetPos)

        # on button2 just straight drive
        if button2:
            # Percent voltage mode
            self.talon.set(WPI_TalonSRX.ControlMode.PercentOutput, leftYstick)

        if self.talon.getControlMode() == WPI_TalonSRX.ControlMode.Position:
            # append more signals to print when in speed mode.
            sb.append("\terr: %s" %
                      self.talon.getClosedLoopError(self.kPIDLoopIdx))
            sb.append("\ttrg: %.3f" % self.targetPos)

        # periodically print to console
        self.loops += 1
        if self.loops >= 10:
            self.loops = 0
            print(" ".join(sb))

        # save button state for on press detect
        self.lastButton1 = button1