diff --git a/examples/can-arcade-drive/robot.py b/examples/can-arcade-drive/robot.py new file mode 100644 index 0000000..d026616 --- /dev/null +++ b/examples/can-arcade-drive/robot.py @@ -0,0 +1,63 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2017-2018 FIRST. All Rights Reserved. +# Open Source Software - may be modified and shared by FRC teams. The code +# must be accompanied by the FIRST BSD license file in the root directory of +# the project. +# ---------------------------------------------------------------------------- + +import rev +import wpilib +from wpilib.drive import DifferentialDrive + + +class Robot(wpilib.TimedRobot): + def robotInit(self): + # SPARK MAX controllers are intialized over CAN by constructing a + # CANSparkMax object + # + # The CAN ID, which can be configured using the SPARK MAX Client, is passed + # as the first parameter + # + # The motor type is passed as the second parameter. + # Motor type can either be: + # rev.MotorType.kBrushless + # rev.MotorType.kBrushed + # + # The example below initializes four brushless motors with CAN IDs + # 1, 2, 3, 4. Change these parameters to match your setup + self.leftLeadMotor = rev.CANSparkMax(1, rev.MotorType.kBrushless) + self.rightLeadMotor = rev.CANSparkMax(3, rev.MotorType.kBrushless) + self.leftFollowMotor = rev.CANSparkMax(2, rev.MotorType.kBrushless) + self.rightFollowMotor = rev.CANSparkMax(4, rev.MotorType.kBrushless) + + # Passing in the lead motors into DifferentialDrive allows any + # commmands sent to the lead motors to be sent to the follower motors. + self.driveTrain = DifferentialDrive(self.leftLeadMotor, self.rightLeadMotor) + self.joystick = wpilib.Joystick(0) + + # The RestoreFactoryDefaults method can be used to reset the + # configuration parameters in the SPARK MAX to their factory default + # state. If no argument is passed, these parameters will not persist + # between power cycles + self.leftLeadMotor.restoreFactoryDefaults() + self.rightLeadMotor.restoreFactoryDefaults() + self.leftFollowMotor.restoreFactoryDefaults() + self.rightFollowMotor.restoreFactoryDefaults() + + # In CAN mode, one SPARK MAX can be configured to follow another. This + # is done by calling the follow() method on the SPARK MAX you want to + # configure as a follower, and by passing as a parameter the SPARK MAX + # you want to configure as a leader. + # + # This is shown in the example below, where one motor on each side of + # our drive train is configured to follow a lead motor. + self.leftFollowMotor.follow(self.leftLeadMotor) + self.rightFollowMotor.follow(self.rightLeadMotor) + + def teleopPeriodic(self): + # Drive with arcade style + self.driveTrain.arcadeDrive(-self.joystick.getY(), self.joystick.getX()) + + +if __name__ == "__main__": + wpilib.run(Robot) diff --git a/examples/can-tank-drive/robot.py b/examples/can-tank-drive/robot.py new file mode 100644 index 0000000..13b4df2 --- /dev/null +++ b/examples/can-tank-drive/robot.py @@ -0,0 +1,48 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2017-2018 FIRST. All Rights Reserved. +# Open Source Software - may be modified and shared by FRC teams. The code +# must be accompanied by the FIRST BSD license file in the root directory of +# the project. +# ---------------------------------------------------------------------------- + +import rev +import wpilib +from wpilib.drive import DifferentialDrive + + +class Robot(wpilib.TimedRobot): + def robotInit(self): + # SPARK MAX controllers are intialized over CAN by constructing a + # CANSparkMax object + # + # The CAN ID, which can be configured using the SPARK MAX Client, is passed + # as the first parameter + # + # The motor type is passed as the second parameter. + # Motor type can either be: + # rev.MotorType.kBrushless + # rev.MotorType.kBrushed + # + # The example below initializes two brushless motors with CAN IDs + # 1 and 2. Change these parameters to match your setup + self.leftMotor = rev.CANSparkMax(1, rev.MotorType.kBrushless) + self.rightMotor = rev.CANSparkMax(2, rev.MotorType.kBrushless) + + # The RestoreFactoryDefaults method can be used to reset the + # configuration parameters in the SPARK MAX to their factory default + # state. If no argument is passed, these parameters will not persist + # between power cycles + self.leftMotor.restoreFactoryDefaults() + self.rightMotor.restoreFactoryDefaults() + + self.driveTrain = DifferentialDrive(self.leftMotor, self.rightMotor) + self.l_stick = wpilib.Joystick(0) + self.r_stick = wpilib.Joystick(1) + + def teleopPeriodic(self): + # Create tank drive + self.driveTrain.tankDrive(self.l_stick.getY(), self.r_stick.getY()) + + +if __name__ == "__main__": + wpilib.run(Robot) diff --git a/examples/get-set-params/robot.py b/examples/get-set-params/robot.py new file mode 100644 index 0000000..029c0dd --- /dev/null +++ b/examples/get-set-params/robot.py @@ -0,0 +1,63 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2017-2018 FIRST. All Rights Reserved. +# Open Source Software - may be modified and shared by FRC teams. The code +# must be accompanied by the FIRST BSD license file in the root directory of +# the project. +# ---------------------------------------------------------------------------- + +import rev +import wpilib + + +class Robot(wpilib.TimedRobot): + def robotInit(self): + # Create motor + self.motor = rev.CANSparkMax(1, rev.MotorType.kBrushless) + + self.joystick = wpilib.Joystick(0) + + # The restoreFactoryDefaults method can be used to reset the + # configuration parameters in the SPARK MAX to their factory default + # state. If no argument is passed, these parameters will not persist + # between power cycles + self.motor.restoreFactoryDefaults() + + # Parameters can be set by calling the appropriate set() method on the + # CANSparkMax object whose properties you want to change + # + # Set methods will return one of three CANError values which will let + # you know if the parameter was successfully set: + # CANError.kOk + # CANError.kError + # CANError.kTimeout + if self.motor.setIdleMode(rev.IdleMode.kCoast) is not rev.CANError.kOk: + wpilib.SmartDashboard.putString("Idle Mode", "Error") + + # Similarly, parameters will have a get() method which allows you to + # retrieve their values from the controller + if self.motor.getIdleMode() is rev.IdleMode.kCoast: + wpilib.SmartDashboard.putString("Idle Mode", "Coast") + else: + wpilib.SmartDashboard.putString("Idle Mode", "Brake") + + # Set ramp rate to 0 + if self.motor.setOpenLoopRampRate(0) is not rev.CANError.kOk: + wpilib.SmartDashboard.putString("Ramp Rate", "Error") + + # Read back ramp value + wpilib.SmartDashboard.putString( + "Ramp Rate", str(self.motor.getOpenLoopRampRate()) + ) + + def teleopPeriodic(self): + # Pair motor and the joystick's Y Axis + self.motor.set(self.joystick.getY()) + + # Put Voltage, Temperature, and Motor Output onto SmartDashboard + wpilib.SmartDashboard.putNumber("Voltage", self.motor.getBusVoltage()) + wpilib.SmartDashboard.putNumber("Temperature", self.motor.getMotorTemperature()) + wpilib.SmartDashboard.putNumber("Output", self.motor.getAppliedOutput()) + + +if __name__ == "__main__": + wpilib.run(Robot) diff --git a/examples/limit-switch/robot.py b/examples/limit-switch/robot.py new file mode 100644 index 0000000..68de3bf --- /dev/null +++ b/examples/limit-switch/robot.py @@ -0,0 +1,76 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2017-2018 FIRST. All Rights Reserved. +# Open Source Software - may be modified and shared by FRC teams. The code +# must be accompanied by the FIRST BSD license file in the root directory of +# the project. +# ---------------------------------------------------------------------------- + +import rev +import wpilib + +# Before Running: +# Open Shuffleboard, select File->Load Layout and select the +# shuffleboard.json that is in the root directory of this example + + +class Robot(wpilib.TimedRobot): + def robotInit(self): + # Create motor + self.motor = rev.CANSparkMax(1, rev.MotorType.kBrushless) + + self.joystick = wpilib.Joystick(0) + + # A CANDigitalInput object is constructed using the + # GetForwardLimitSwitch() or + # GetReverseLimitSwitch() method on an existing CANSparkMax object, + # depending on which direction you would like to limit + # + # Limit switches can be configured to one of two polarities: + # rev.CANDigitalInput.LimitSwitchPolarity.kNormallyOpen + # rev.CANDigitalInput.LimitSwitchPolarity.kNormallyClosed + self.forwardLimit = self.motor.getForwardLimitSwitch( + rev.LimitSwitchPolarity.kNormallyClosed + ) + self.reverseLimit = self.motor.getReverseLimitSwitch( + rev.LimitSwitchPolarity.kNormallyClosed + ) + + self.forwardLimit.enableLimitSwitch(False) + self.reverseLimit.enableLimitSwitch(False) + + wpilib.SmartDashboard.putBoolean( + "Forward Limit Enabled", self.forwardLimit.isLimitSwitchEnabled() + ) + wpilib.SmartDashboard.putBoolean( + "Reverse Limit Enabled", self.forwardLimit.isLimitSwitchEnabled() + ) + + def teleopPeriodic(self): + # Pair motor and the joystick's Y Axis + self.motor.set(self.joystick.getY()) + + # enable/disable limit switches based on value read from SmartDashboard + self.forwardLimit.enableLimitSwitch( + wpilib.SmartDashboard.getBoolean("Forward Limit Enabled", False) + ) + self.reverseLimit.enableLimitSwitch( + wpilib.SmartDashboard.getBoolean("Reverse Limit Enabled", False) + ) + + # The get() method can be used on a CANDigitalInput object to read the + # state of the switch. + # + # In this example, the polarity of the switches are set to normally + # closed. In this case, get() will return true if the switch is + # pressed. It will also return true if you do not have a switch + # connected. get() will return false when the switch is released. + wpilib.SmartDashboard.putBoolean( + "Forward Limit Switch", self.forwardLimit.get() + ) + wpilib.SmartDashboard.putBoolean( + "Reverse Limit Switch", self.reverseLimit.get() + ) + + +if __name__ == "__main__": + wpilib.run(Robot) diff --git a/examples/limit-switch/shuffleboard.json b/examples/limit-switch/shuffleboard.json new file mode 100644 index 0000000..c10ded5 --- /dev/null +++ b/examples/limit-switch/shuffleboard.json @@ -0,0 +1,72 @@ +{ + "tabPane": [ + { + "title": "SmartDashboard", + "autoPopulate": true, + "autoPopulatePrefix": "SmartDashboard/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "tiles": { + "0,0": { + "size": [ + 2, + 3 + ], + "content": { + "_type": "List Layout", + "_title": "Limit Switch", + "Layout/Label position": "BOTTOM", + "_children": [ + { + "_type": "Toggle Switch", + "_source0": "network_table:///SmartDashboard/Forward Limit Enabled", + "_title": "Forward Limit Enabled" + }, + { + "_type": "Toggle Switch", + "_source0": "network_table:///SmartDashboard/Reverse Limit Enabled", + "_title": "Reverse Limit Enabled" + }, + { + "_type": "Boolean Box", + "_source0": "network_table:///SmartDashboard/Forward Limit Switch", + "_title": "Forward Limit Switch", + "Colors/Color when true": "#7CFC00FF", + "Colors/Color when false": "#8B0000FF" + }, + { + "_type": "Boolean Box", + "_source0": "network_table:///SmartDashboard/Reverse Limit Switch", + "_title": "Reverse Limit Switch", + "Colors/Color when true": "#7CFC00FF", + "Colors/Color when false": "#8B0000FF" + } + ] + } + } + } + } + }, + { + "title": "LiveWindow", + "autoPopulate": true, + "autoPopulatePrefix": "LiveWindow/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "tiles": {} + } + } + ], + "windowGeometry": { + "x": -1640.0, + "y": 54.0, + "width": 1540.0, + "height": 832.0 + } + } \ No newline at end of file diff --git a/examples/position-pid-control/robot.py b/examples/position-pid-control/robot.py new file mode 100644 index 0000000..2d43dfa --- /dev/null +++ b/examples/position-pid-control/robot.py @@ -0,0 +1,116 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2017-2018 FIRST. All Rights Reserved. +# Open Source Software - may be modified and shared by FRC teams. The code +# must be accompanied by the FIRST BSD license file in the root directory of +# the project. +# ---------------------------------------------------------------------------- + +import rev +import wpilib + +# Before Running: +# Open Shuffleboard, select File->Load Layout and select the +# shuffleboard.json that is in the root directory of this example + + +class Robot(wpilib.TimedRobot): + def robotInit(self): + # Create motors + self.motor = rev.CANSparkMax(1, rev.MotorType.kBrushless) + + # You must call getPIDController() on an existing CANSparkMax or + # SparkMax object to fully use PID functionality + self.pidController = self.motor.getPIDController() + + # Instantiate built-in encoder to display position + self.encoder = self.motor.getEncoder() + + # PID Coefficents and Controller Output Range + self.kP = 0.1 + self.kI = 1e-4 + self.kD = 0 + self.kIz = 0 + self.kFF = 0 + self.kMinOutput = -1 + self.kMaxOutput = 1 + + # The restoreFactoryDefaults() method can be used to reset the + # configuration parameters in the SPARK MAX to their factory default + # state. If no argument is passed, these parameters will not persist + # between power cycles + self.motor.restoreFactoryDefaults() + + # Set PID Constants + self.pidController.setP(self.kP) + self.pidController.setI(self.kI) + self.pidController.setD(self.kD) + self.pidController.setIZone(self.kIz) + self.pidController.setFF(self.kFF) + self.pidController.setOutputRange(self.kMinOutput, self.kMaxOutput) + + # Push PID Coefficients to SmartDashboard + wpilib.SmartDashboard.putNumber("P Gain", self.kP) + wpilib.SmartDashboard.putNumber("I Gain", self.kI) + wpilib.SmartDashboard.putNumber("D Gain", self.kD) + wpilib.SmartDashboard.putNumber("I Zone", self.kIz) + wpilib.SmartDashboard.putNumber("Feed Forward", self.kFF) + wpilib.SmartDashboard.putNumber("Min Output", self.kMinOutput) + wpilib.SmartDashboard.putNumber("Max Output", self.kMaxOutput) + wpilib.SmartDashboard.putNumber("Set Rotations", 0) + + def teleopPeriodic(self): + # Read data from SmartDashboard + p = wpilib.SmartDashboard.getNumber("P Gain", 0) + i = wpilib.SmartDashboard.getNumber("I Gain", 0) + d = wpilib.SmartDashboard.getNumber("D Gain", 0) + iz = wpilib.SmartDashboard.getNumber("I Zone", 0) + ff = wpilib.SmartDashboard.getNumber("Feed Forward", 0) + min_out = wpilib.SmartDashboard.getNumber("Min Output", 0) + max_out = wpilib.SmartDashboard.getNumber("Max Output", 0) + rotations = wpilib.SmartDashboard.getNumber("Set Rotations", 0) + + # Update PIDController datapoints with the latest from SmartDashboard + if p != self.kP: + self.pidController.setP(p) + self.kP = p + if i != self.kI: + self.pidController.setI(i) + self.kI = i + if d != self.kD: + self.pidController.setD(d) + self.kD = d + if iz != self.kIz: + self.pidController.setIZone(iz) + self.kIz = iz + if ff != self.kFF: + self.pidController.setFF(ff) + self.kFF = ff + if (min_out != self.kMinOutput) or (max_out != self.kMaxOutput): + self.pidController.setOutputRange(min_out, max_out) + self.kMinOutput = min_out + self.kMaxOutput = max_out + + # PIDController objects are commanded to a set point using the + # setReference() method. + # + # The first parameter is the value of the set point, whose units vary + # depending on the control type set in the second parameter. + # + # The second parameter is the control type can be set to one of four + # parameters: + # rev.ControlType.kDutyCycle + # rev.ControlType.kPosition + # rev.ControlType.kVelocity + # rev.ControlType.kVoltage + # + # For more information on what these types are, refer to the Spark Max + # documentation. + self.pidController.setReference(rotations, rev.ControlType.kPosition) + + # Push Setpoint and the motor's current position to SmartDashboard. + wpilib.SmartDashboard.putNumber("SetPoint", rotations) + wpilib.SmartDashboard.putNumber("ProcessVariable", self.encoder.getPosition()) + + +if __name__ == "__main__": + wpilib.run(Robot) diff --git a/examples/position-pid-control/shuffleboard.json b/examples/position-pid-control/shuffleboard.json new file mode 100644 index 0000000..cd23b8d --- /dev/null +++ b/examples/position-pid-control/shuffleboard.json @@ -0,0 +1,103 @@ +{ + "tabPane": [ + { + "title": "SmartDashboard", + "autoPopulate": true, + "autoPopulatePrefix": "SmartDashboard/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "tiles": { + "0,0": { + "size": [ + 3, + 3 + ], + "content": { + "_type": "Graph", + "_source0": "network_table:///SmartDashboard/ProcessVariable", + "_source1": "network_table:///SmartDashboard/SetPoint", + "_title": "ProcessVariable", + "Graph/Visible time": 30.0, + "Visible data/SmartDashboard/ProcessVariable": true, + "Visible data/SmartDashboard/SetPoint": true + } + }, + "3,0": { + "size": [ + 2, + 3 + ], + "content": { + "_type": "List Layout", + "_title": "PID Control", + "Layout/Label position": "LEFT", + "_children": [ + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/P Gain", + "_title": "P Gain" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/I Gain", + "_title": "I Gain" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/D Gain", + "_title": "D Gain" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/I Zone", + "_title": "I Zone" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Feed Forward", + "_title": "Feed Forward" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Min Output", + "_title": "Min Output" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Max Output", + "_title": "Max Output" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Set Rotations", + "_title": "Set Rotations" + } + ] + } + } + } + } + }, + { + "title": "LiveWindow", + "autoPopulate": true, + "autoPopulatePrefix": "LiveWindow/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "tiles": {} + } + } + ], + "windowGeometry": { + "x": -1640.0, + "y": 54.0, + "width": 1540.0, + "height": 832.0 + } + } \ No newline at end of file diff --git a/examples/read-encoder-values/robot.py b/examples/read-encoder-values/robot.py new file mode 100644 index 0000000..3268625 --- /dev/null +++ b/examples/read-encoder-values/robot.py @@ -0,0 +1,52 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2017-2018 FIRST. All Rights Reserved. +# Open Source Software - may be modified and shared by FRC teams. The code +# must be accompanied by the FIRST BSD license file in the root directory of +# the project. +# ---------------------------------------------------------------------------- + +import rev +import wpilib + + +class Robot(wpilib.TimedRobot): + # This sample program displays the position and velocity of the integrated + # encoder onto the SmartDashboard. + # + # Position is displayed in revolutions (of the motor's axle) and velocity + # is displayed in revolutions per minute (RPM) + # + # Optionally, if you call the setPositionConversionFactor() method on the + # encoder and give it a measurement of how far one revolution is, the + # getVelocity() and getPosition() methods return a scaled output in the + # units of your choice. + def robotInit(self): + # Instantiate SPARK MAX object + self.motor = rev.CANSparkMax(1, rev.MotorType.kBrushless) + + self.motor.restoreFactoryDefaults() + self.encoder = self.motor.getEncoder() + + self.joystick = wpilib.Joystick(0) + + def teleopPeriodic(self): + # Set motor output to the joystick's Y-axis + self.motor.set(self.joystick.getY()) + + # Encoder position is read from a CANEncoder object by calling the + # getPosition() method. + # + # getPosition() returns the position of the encoder in units of + # revolutions (unless overridden) + wpilib.SmartDashboard.putNumber("Encoder Position", self.encoder.getPosition()) + + # Encoder velocity is read from a CANEncoder object by calling the + # getVelocity() method. + # + # getVelocity() returns the position of the encoder in units of + # revolutions (unless overridden) + wpilib.SmartDashboard.putNumber("Encoder Velocity", self.encoder.getVelocity()) + + +if __name__ == "__main__": + wpilib.run(Robot) diff --git a/examples/velocity-pid-control/robot.py b/examples/velocity-pid-control/robot.py new file mode 100644 index 0000000..bd82784 --- /dev/null +++ b/examples/velocity-pid-control/robot.py @@ -0,0 +1,117 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2017-2018 FIRST. All Rights Reserved. +# Open Source Software - may be modified and shared by FRC teams. The code +# must be accompanied by the FIRST BSD license file in the root directory of +# the project. +# ---------------------------------------------------------------------------- + +import rev +import wpilib + + +class Robot(wpilib.TimedRobot): + def robotInit(self): + # Create motors + self.motor = rev.CANSparkMax(1, rev.MotorType.kBrushless) + + # You must call getPIDController() on an existing CANSparkMax or + # SparkMax object to fully use PID functionality + self.pidController = self.motor.getPIDController() + + # Instantiate built-in encoder to display position + self.encoder = self.motor.getEncoder() + + self.joystick = wpilib.Joystick(0) + + # PID Coefficents and Controller Output Range + self.kP = 0.1 + self.kI = 1e-4 + self.kD = 0 + self.kIz = 0 + self.kFF = 0 + self.kMinOutput = -1 + self.kMaxOutput = 1 + + # Motor max RPM + self.maxRPM = 5700 + + # The restoreFactoryDefaults() method can be used to reset the + # configuration parameters in the SPARK MAX to their factory default + # state. If no argument is passed, these parameters will not persist + # between power cycles + self.motor.restoreFactoryDefaults() + + # Set PID Constants + self.pidController.setP(self.kP) + self.pidController.setI(self.kI) + self.pidController.setD(self.kD) + self.pidController.setIZone(self.kIz) + self.pidController.setFF(self.kFF) + self.pidController.setOutputRange(self.kMinOutput, self.kMaxOutput) + + # Push PID Coefficients to SmartDashboard + wpilib.SmartDashboard.putNumber("P Gain", self.kP) + wpilib.SmartDashboard.putNumber("I Gain", self.kI) + wpilib.SmartDashboard.putNumber("D Gain", self.kD) + wpilib.SmartDashboard.putNumber("I Zone", self.kIz) + wpilib.SmartDashboard.putNumber("Feed Forward", self.kFF) + wpilib.SmartDashboard.putNumber("Min Output", self.kMinOutput) + wpilib.SmartDashboard.putNumber("Max Output", self.kMaxOutput) + + def teleopPeriodic(self): + # Read data from SmartDashboard + p = wpilib.SmartDashboard.getNumber("P Gain", 0) + i = wpilib.SmartDashboard.getNumber("I Gain", 0) + d = wpilib.SmartDashboard.getNumber("D Gain", 0) + iz = wpilib.SmartDashboard.getNumber("I Zone", 0) + ff = wpilib.SmartDashboard.getNumber("Feed Forward", 0) + min_out = wpilib.SmartDashboard.getNumber("Min Output", 0) + max_out = wpilib.SmartDashboard.getNumber("Max Output", 0) + + # Update PIDController datapoints with the latest from SmartDashboard + if p != self.kP: + self.pidController.setP(p) + self.kP = p + if i != self.kI: + self.pidController.setI(i) + self.kI = i + if d != self.kD: + self.pidController.setD(d) + self.kD = d + if iz != self.kIz: + self.pidController.setIZone(iz) + self.kIz = iz + if ff != self.kFF: + self.pidController.setFF(ff) + self.kFF = ff + if (min_out != self.kMinOutput) or (max_out != self.kMaxOutput): + self.pidController.setOutputRange(min_out, max_out) + self.kMinOutput = min_out + self.kMaxOutput = max_out + + setpoint = self.maxRPM * self.joystick.getY() + + # PIDController objects are commanded to a set point using the + # setReference() method. + # + # The first parameter is the value of the set point, whose units vary + # depending on the control type set in the second parameter. + # + # The second parameter is the control type can be set to one of four + # parameters: + # rev.ControlType.kDutyCycle + # rev.ControlType.kPosition + # rev.ControlType.kVelocity + # rev.ControlType.kVoltage + # + # For more information on what these types are, refer to the Spark Max + # documentation. + self.pidController.setReference(setpoint, rev.ControlType.kVelocity) + + # Push Setpoint and the motor's current velocity to SmartDashboard. + wpilib.SmartDashboard.putNumber("SetPoint", setpoint) + wpilib.SmartDashboard.putNumber("ProcessVariable", self.encoder.getVelocity()) + + +if __name__ == "__main__": + wpilib.run(Robot) diff --git a/examples/velocity-pid-control/shuffleboard.json b/examples/velocity-pid-control/shuffleboard.json new file mode 100644 index 0000000..37de452 --- /dev/null +++ b/examples/velocity-pid-control/shuffleboard.json @@ -0,0 +1,98 @@ +{ + "tabPane": [ + { + "title": "SmartDashboard", + "autoPopulate": true, + "autoPopulatePrefix": "SmartDashboard/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "tiles": { + "0,0": { + "size": [ + 3, + 3 + ], + "content": { + "_type": "Graph", + "_source0": "network_table:///SmartDashboard/ProcessVariable", + "_source1": "network_table:///SmartDashboard/SetPoint", + "_title": "ProcessVariable", + "Graph/Visible time": 30.0, + "Visible data/SmartDashboard/ProcessVariable": true, + "Visible data/SmartDashboard/SetPoint": true + } + }, + "3,0": { + "size": [ + 2, + 3 + ], + "content": { + "_type": "List Layout", + "_title": "PID Control", + "Layout/Label position": "LEFT", + "_children": [ + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/P Gain", + "_title": "P Gain" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/I Gain", + "_title": "I Gain" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/D Gain", + "_title": "D Gain" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/I Zone", + "_title": "I Zone" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Feed Forward", + "_title": "Feed Forward" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Min Output", + "_title": "Min Output" + }, + { + "_type": "Text View", + "_source0": "network_table:///SmartDashboard/Max Output", + "_title": "Max Output" + } + ] + } + } + } + } + }, + { + "title": "LiveWindow", + "autoPopulate": true, + "autoPopulatePrefix": "LiveWindow/", + "widgetPane": { + "gridSize": 128.0, + "showGrid": true, + "hgap": 16.0, + "vgap": 16.0, + "tiles": {} + } + } + ], + "windowGeometry": { + "x": -1640.0, + "y": 54.0, + "width": 1540.0, + "height": 832.0 + } + } \ No newline at end of file