robotswerve
Container to hold the main robot code.
Controller Map


1""" 2Container to hold the main robot code. 3 4## Controller Map 5 6 7 8 9""" 10 11# Native imports 12import json 13import os 14from pathlib import Path 15from typing import Callable 16 17import wpimath 18 19# Internal imports 20from config import HoodConfig 21from constants.swerve_constants import HoodConstants 22from data.telemetry import Telemetry 23from commands.default_swerve_drive import DefaultDrive 24from subsystem.drivetrain.swerve_drivetrain import SwerveDrivetrain 25from subsystem.mechanisms.shooter.hood import createHood 26from utils.input import InputFactory 27 28# Third-party imports 29import commands2 30import wpilib 31from commands2.button import Trigger 32from pathplannerlib.auto import AutoBuilder 33 34 35class RobotSwerve: 36 # forward declare critical types for editors 37 drivetrain: SwerveDrivetrain 38 39 def __init__(self, is_disabled: Callable[[], bool]) -> None: 40 # networktables setup 41 self.field = wpilib.Field2d() 42 wpilib.SmartDashboard.putData("Field", self.field) 43 44 # Subsystem instantiation 45 self.drivetrain = SwerveDrivetrain() 46 self.hood = createHood(HoodConstants, HoodConfig) 47 48 # Alliance instantiation 49 self.updateAlliance() 50 51 52 # Initialize timer 53 self.timer = wpilib.Timer() 54 self.timer.start() 55 56 # HID setup — config-driven via InputFactory 57 wpilib.DriverStation.silenceJoystickConnectionWarning(True) 58 self.factory = InputFactory(config_path="data/inputs/2026bot.yaml") 59 60 # Speed toggle state 61 self._drive_scale_slow = 0.25 62 self._drive_scale_fast = 1 63 self._drive_is_slow = False 64 65 # TODO: Move input retrieval and binding into commands/{subsystem}_controls.py 66 # files as part of the subsystem registry refactor. Each subsystem's controls 67 # module should own its own factory.get*() calls and command wiring. 68 self._configure_controls() 69 70 # Autonomous setup 71 self.auto_command = None 72 self.auto_chooser = AutoBuilder.buildAutoChooser() 73 wpilib.SmartDashboard.putData("Select auto routine", self.auto_chooser) 74 75 # Telemetry setup 76 wpilib.SmartDashboard.putNumber("Drivetrain speed", self._drive_scale_fast) 77 self.enableTelemetry = wpilib.SmartDashboard.getBoolean("enableTelemetry", True) 78 if self.enableTelemetry: 79 self.telemetry = Telemetry( 80 driveTrain=self.drivetrain, 81 driverController=self.factory.getController(0), 82 mechController=self.factory.getController(1), 83 ) 84 85 wpilib.SmartDashboard.putString("Robot Version", self.getDeployInfo("git-hash")) 86 wpilib.SmartDashboard.putString("Git Branch", self.getDeployInfo("git-branch")) 87 wpilib.SmartDashboard.putString( 88 "Deploy Host", self.getDeployInfo("deploy-host") 89 ) 90 wpilib.SmartDashboard.putString( 91 "Deploy User", self.getDeployInfo("deploy-user") 92 ) 93 94 # Update drivetrain motor idle modes 3 seconds after the robot has been disabled. 95 # to_break should be False at competitions where the robot is turned off between matches 96 Trigger(is_disabled()).debounce(3).onTrue( 97 commands2.cmd.runOnce( 98 self.drivetrain.set_motor_stop_modes( 99 to_drive=True, to_break=True, all_motor_override=True, burn_flash=True 100 ), 101 self.drivetrain 102 ) 103 ) 104 105 def robotPeriodic(self): 106 if self.enableTelemetry and self.telemetry: 107 self.telemetry.runDefaultDataCollections() 108 109 self.field.setRobotPose(self.drivetrain.current_pose()) 110 111 def disabledInit(self): 112 self.updateAlliance() 113 self.drivetrain.set_motor_stop_modes(to_drive=True, to_break=True, all_motor_override=True, burn_flash=False) 114 self.drivetrain.stop_driving() 115 116 def disabledPeriodic(self): 117 pass 118 119 def autonomousInit(self): 120 self.updateAlliance() 121 self.auto_command = self.auto_chooser.getSelected() 122 if self.auto_command: 123 self.auto_command.schedule() 124 else: 125 self.drivetrain.reset_pose_estimator(self.drivetrain.get_default_starting_pose()) 126 127 def autonomousPeriodic(self): 128 pass 129 130 def teleopInit(self): 131 self.updateAlliance() 132 if self.auto_command: 133 self.auto_command.cancel() 134 135 self.drivetrain.setDefaultCommand( 136 DefaultDrive( 137 self.drivetrain, 138 self.translate_x, 139 self.translate_y, 140 self.rotate, 141 lambda: not self.robot_relative_btn() 142 ) 143 ) 144 145 def teleopPeriodic(self): 146 pass 147 148 def testInit(self): 149 #TODO Move to NT listener on change listener 150 self.updateAlliance() 151 commands2.CommandScheduler.getInstance().cancelAll() 152 self.drivetrain.setDefaultCommand( 153 DefaultDrive( 154 self.drivetrain, 155 lambda: wpimath.applyDeadband(-1 * self.driver_controller.getLeftY(), 0.06), 156 lambda: wpimath.applyDeadband(-1 * self.driver_controller.getLeftX(), 0.06), 157 lambda: wpimath.applyDeadband(-1 * self.driver_controller.getRightX(), 0.1), 158 lambda: not self.driver_controller.getRightBumperButton() 159 ) 160 ) 161 commands2.cmd.run(lambda: self.drivetrain.drive(2, 0, 0, False), self.drivetrain).withTimeout(5).schedule() 162 163 def testPeriodic(self): 164 pass 165 166 def _configure_controls(self) -> None: 167 """Retrieve managed inputs from the factory and wire command bindings. 168 169 TODO: Move into commands/{subsystem}_controls.py files as part of the 170 subsystem registry refactor. Each subsystem's controls module would 171 call register_controls(subsystem, container) and own its own 172 factory.get*() calls and command wiring. 173 """ 174 # Managed drive inputs 175 self.translate_x = self.factory.getAnalog("drivetrain.translate_x") 176 self.translate_y = self.factory.getAnalog("drivetrain.translate_y") 177 self.rotate = self.factory.getAnalog("drivetrain.rotate") 178 self.robot_relative_btn = self.factory.getRawButton("drivetrain.robot_relative") 179 180 # Cancel-all: event-driven via Trigger instead of polling 181 self.factory.getButton("drivetrain.cancel_all").onTrue( 182 commands2.cmd.runOnce( 183 lambda: commands2.CommandScheduler.getInstance().cancelAll() 184 ) 185 ) 186 187 # Speed toggle: Y button switches between slow and fast scale 188 self.factory.getButton("drivetrain.speed_toggle").onTrue( 189 commands2.cmd.runOnce(self._toggle_drive_scale) 190 ) 191 192 # Map all drive axes' scale to a shared SmartDashboard entry. 193 # Dashboard changes and Y-button toggles both write to this path; 194 # the factory auto-syncs the value into all three analogs each cycle. 195 _SPEED_NT = "/SmartDashboard/Drivetrain speed" 196 for analog in (self.translate_x, self.translate_y, self.rotate): 197 analog.mapParamToNtPath(_SPEED_NT, "scale") 198 199 def _toggle_drive_scale(self) -> None: 200 """Toggle between slow and fast drive scale presets. 201 202 Writes the new scale to SmartDashboard; the factory auto-syncs 203 it into all three drive analogs via mapParamToNtPath each cycle. 204 """ 205 self._drive_is_slow = not self._drive_is_slow 206 scale = self._drive_scale_slow if self._drive_is_slow else self._drive_scale_fast 207 wpilib.SmartDashboard.putNumber("Drivetrain speed", scale) 208 209 def getDeployInfo(self, key: str) -> str: 210 """Gets the Git SHA of the deployed robot by parsing ~/deploy.json and returning the git-hash from the JSON key OR if deploy.json is unavailable will return "unknown" 211 example deploy.json: '{"deploy-host": "DESKTOP-80HA89O", "deploy-user": "ehsra", "deploy-date": "2023-03-02T17:54:14", "code-path": "blah", "git-hash": "3f4e89f138d9d78093bd4869e0cac9b61becd2b9", "git-desc": "3f4e89f-dirty", "git-branch": "fix-recal-nbeasley"} 212 213 Args: 214 key (str): The desired json key to get. Popular onces are git-hash, deploy-host, deploy-user 215 216 Returns: 217 str: Returns the value of the desired deploy key 218 """ 219 json_object = None 220 home = str(Path.home()) + os.path.sep 221 releaseFile = home + 'py' + os.path.sep + "deploy.json" 222 try: 223 # Read from ~/deploy.json 224 with open(releaseFile, "r") as openfile: 225 json_object = json.load(openfile) 226 print(json_object) 227 print(type(json_object)) 228 if key in json_object: 229 return json_object[key] 230 else: 231 return f"Key: {key} Not Found in JSON" 232 except OSError: 233 return "unknown" 234 except json.JSONDecodeError: 235 return "bad json in deploy file check for unescaped " 236 237 def updateAlliance(self) -> None: 238 """ 239 Update the alliance the robot is on 240 """ 241 self.alliance = wpilib.DriverStation.getAlliance() 242 self.drivetrain.update_alliance_flag(self.alliance)
class
RobotSwerve:
36class RobotSwerve: 37 # forward declare critical types for editors 38 drivetrain: SwerveDrivetrain 39 40 def __init__(self, is_disabled: Callable[[], bool]) -> None: 41 # networktables setup 42 self.field = wpilib.Field2d() 43 wpilib.SmartDashboard.putData("Field", self.field) 44 45 # Subsystem instantiation 46 self.drivetrain = SwerveDrivetrain() 47 self.hood = createHood(HoodConstants, HoodConfig) 48 49 # Alliance instantiation 50 self.updateAlliance() 51 52 53 # Initialize timer 54 self.timer = wpilib.Timer() 55 self.timer.start() 56 57 # HID setup — config-driven via InputFactory 58 wpilib.DriverStation.silenceJoystickConnectionWarning(True) 59 self.factory = InputFactory(config_path="data/inputs/2026bot.yaml") 60 61 # Speed toggle state 62 self._drive_scale_slow = 0.25 63 self._drive_scale_fast = 1 64 self._drive_is_slow = False 65 66 # TODO: Move input retrieval and binding into commands/{subsystem}_controls.py 67 # files as part of the subsystem registry refactor. Each subsystem's controls 68 # module should own its own factory.get*() calls and command wiring. 69 self._configure_controls() 70 71 # Autonomous setup 72 self.auto_command = None 73 self.auto_chooser = AutoBuilder.buildAutoChooser() 74 wpilib.SmartDashboard.putData("Select auto routine", self.auto_chooser) 75 76 # Telemetry setup 77 wpilib.SmartDashboard.putNumber("Drivetrain speed", self._drive_scale_fast) 78 self.enableTelemetry = wpilib.SmartDashboard.getBoolean("enableTelemetry", True) 79 if self.enableTelemetry: 80 self.telemetry = Telemetry( 81 driveTrain=self.drivetrain, 82 driverController=self.factory.getController(0), 83 mechController=self.factory.getController(1), 84 ) 85 86 wpilib.SmartDashboard.putString("Robot Version", self.getDeployInfo("git-hash")) 87 wpilib.SmartDashboard.putString("Git Branch", self.getDeployInfo("git-branch")) 88 wpilib.SmartDashboard.putString( 89 "Deploy Host", self.getDeployInfo("deploy-host") 90 ) 91 wpilib.SmartDashboard.putString( 92 "Deploy User", self.getDeployInfo("deploy-user") 93 ) 94 95 # Update drivetrain motor idle modes 3 seconds after the robot has been disabled. 96 # to_break should be False at competitions where the robot is turned off between matches 97 Trigger(is_disabled()).debounce(3).onTrue( 98 commands2.cmd.runOnce( 99 self.drivetrain.set_motor_stop_modes( 100 to_drive=True, to_break=True, all_motor_override=True, burn_flash=True 101 ), 102 self.drivetrain 103 ) 104 ) 105 106 def robotPeriodic(self): 107 if self.enableTelemetry and self.telemetry: 108 self.telemetry.runDefaultDataCollections() 109 110 self.field.setRobotPose(self.drivetrain.current_pose()) 111 112 def disabledInit(self): 113 self.updateAlliance() 114 self.drivetrain.set_motor_stop_modes(to_drive=True, to_break=True, all_motor_override=True, burn_flash=False) 115 self.drivetrain.stop_driving() 116 117 def disabledPeriodic(self): 118 pass 119 120 def autonomousInit(self): 121 self.updateAlliance() 122 self.auto_command = self.auto_chooser.getSelected() 123 if self.auto_command: 124 self.auto_command.schedule() 125 else: 126 self.drivetrain.reset_pose_estimator(self.drivetrain.get_default_starting_pose()) 127 128 def autonomousPeriodic(self): 129 pass 130 131 def teleopInit(self): 132 self.updateAlliance() 133 if self.auto_command: 134 self.auto_command.cancel() 135 136 self.drivetrain.setDefaultCommand( 137 DefaultDrive( 138 self.drivetrain, 139 self.translate_x, 140 self.translate_y, 141 self.rotate, 142 lambda: not self.robot_relative_btn() 143 ) 144 ) 145 146 def teleopPeriodic(self): 147 pass 148 149 def testInit(self): 150 #TODO Move to NT listener on change listener 151 self.updateAlliance() 152 commands2.CommandScheduler.getInstance().cancelAll() 153 self.drivetrain.setDefaultCommand( 154 DefaultDrive( 155 self.drivetrain, 156 lambda: wpimath.applyDeadband(-1 * self.driver_controller.getLeftY(), 0.06), 157 lambda: wpimath.applyDeadband(-1 * self.driver_controller.getLeftX(), 0.06), 158 lambda: wpimath.applyDeadband(-1 * self.driver_controller.getRightX(), 0.1), 159 lambda: not self.driver_controller.getRightBumperButton() 160 ) 161 ) 162 commands2.cmd.run(lambda: self.drivetrain.drive(2, 0, 0, False), self.drivetrain).withTimeout(5).schedule() 163 164 def testPeriodic(self): 165 pass 166 167 def _configure_controls(self) -> None: 168 """Retrieve managed inputs from the factory and wire command bindings. 169 170 TODO: Move into commands/{subsystem}_controls.py files as part of the 171 subsystem registry refactor. Each subsystem's controls module would 172 call register_controls(subsystem, container) and own its own 173 factory.get*() calls and command wiring. 174 """ 175 # Managed drive inputs 176 self.translate_x = self.factory.getAnalog("drivetrain.translate_x") 177 self.translate_y = self.factory.getAnalog("drivetrain.translate_y") 178 self.rotate = self.factory.getAnalog("drivetrain.rotate") 179 self.robot_relative_btn = self.factory.getRawButton("drivetrain.robot_relative") 180 181 # Cancel-all: event-driven via Trigger instead of polling 182 self.factory.getButton("drivetrain.cancel_all").onTrue( 183 commands2.cmd.runOnce( 184 lambda: commands2.CommandScheduler.getInstance().cancelAll() 185 ) 186 ) 187 188 # Speed toggle: Y button switches between slow and fast scale 189 self.factory.getButton("drivetrain.speed_toggle").onTrue( 190 commands2.cmd.runOnce(self._toggle_drive_scale) 191 ) 192 193 # Map all drive axes' scale to a shared SmartDashboard entry. 194 # Dashboard changes and Y-button toggles both write to this path; 195 # the factory auto-syncs the value into all three analogs each cycle. 196 _SPEED_NT = "/SmartDashboard/Drivetrain speed" 197 for analog in (self.translate_x, self.translate_y, self.rotate): 198 analog.mapParamToNtPath(_SPEED_NT, "scale") 199 200 def _toggle_drive_scale(self) -> None: 201 """Toggle between slow and fast drive scale presets. 202 203 Writes the new scale to SmartDashboard; the factory auto-syncs 204 it into all three drive analogs via mapParamToNtPath each cycle. 205 """ 206 self._drive_is_slow = not self._drive_is_slow 207 scale = self._drive_scale_slow if self._drive_is_slow else self._drive_scale_fast 208 wpilib.SmartDashboard.putNumber("Drivetrain speed", scale) 209 210 def getDeployInfo(self, key: str) -> str: 211 """Gets the Git SHA of the deployed robot by parsing ~/deploy.json and returning the git-hash from the JSON key OR if deploy.json is unavailable will return "unknown" 212 example deploy.json: '{"deploy-host": "DESKTOP-80HA89O", "deploy-user": "ehsra", "deploy-date": "2023-03-02T17:54:14", "code-path": "blah", "git-hash": "3f4e89f138d9d78093bd4869e0cac9b61becd2b9", "git-desc": "3f4e89f-dirty", "git-branch": "fix-recal-nbeasley"} 213 214 Args: 215 key (str): The desired json key to get. Popular onces are git-hash, deploy-host, deploy-user 216 217 Returns: 218 str: Returns the value of the desired deploy key 219 """ 220 json_object = None 221 home = str(Path.home()) + os.path.sep 222 releaseFile = home + 'py' + os.path.sep + "deploy.json" 223 try: 224 # Read from ~/deploy.json 225 with open(releaseFile, "r") as openfile: 226 json_object = json.load(openfile) 227 print(json_object) 228 print(type(json_object)) 229 if key in json_object: 230 return json_object[key] 231 else: 232 return f"Key: {key} Not Found in JSON" 233 except OSError: 234 return "unknown" 235 except json.JSONDecodeError: 236 return "bad json in deploy file check for unescaped " 237 238 def updateAlliance(self) -> None: 239 """ 240 Update the alliance the robot is on 241 """ 242 self.alliance = wpilib.DriverStation.getAlliance() 243 self.drivetrain.update_alliance_flag(self.alliance)
RobotSwerve(is_disabled: Callable[[], bool])
40 def __init__(self, is_disabled: Callable[[], bool]) -> None: 41 # networktables setup 42 self.field = wpilib.Field2d() 43 wpilib.SmartDashboard.putData("Field", self.field) 44 45 # Subsystem instantiation 46 self.drivetrain = SwerveDrivetrain() 47 self.hood = createHood(HoodConstants, HoodConfig) 48 49 # Alliance instantiation 50 self.updateAlliance() 51 52 53 # Initialize timer 54 self.timer = wpilib.Timer() 55 self.timer.start() 56 57 # HID setup — config-driven via InputFactory 58 wpilib.DriverStation.silenceJoystickConnectionWarning(True) 59 self.factory = InputFactory(config_path="data/inputs/2026bot.yaml") 60 61 # Speed toggle state 62 self._drive_scale_slow = 0.25 63 self._drive_scale_fast = 1 64 self._drive_is_slow = False 65 66 # TODO: Move input retrieval and binding into commands/{subsystem}_controls.py 67 # files as part of the subsystem registry refactor. Each subsystem's controls 68 # module should own its own factory.get*() calls and command wiring. 69 self._configure_controls() 70 71 # Autonomous setup 72 self.auto_command = None 73 self.auto_chooser = AutoBuilder.buildAutoChooser() 74 wpilib.SmartDashboard.putData("Select auto routine", self.auto_chooser) 75 76 # Telemetry setup 77 wpilib.SmartDashboard.putNumber("Drivetrain speed", self._drive_scale_fast) 78 self.enableTelemetry = wpilib.SmartDashboard.getBoolean("enableTelemetry", True) 79 if self.enableTelemetry: 80 self.telemetry = Telemetry( 81 driveTrain=self.drivetrain, 82 driverController=self.factory.getController(0), 83 mechController=self.factory.getController(1), 84 ) 85 86 wpilib.SmartDashboard.putString("Robot Version", self.getDeployInfo("git-hash")) 87 wpilib.SmartDashboard.putString("Git Branch", self.getDeployInfo("git-branch")) 88 wpilib.SmartDashboard.putString( 89 "Deploy Host", self.getDeployInfo("deploy-host") 90 ) 91 wpilib.SmartDashboard.putString( 92 "Deploy User", self.getDeployInfo("deploy-user") 93 ) 94 95 # Update drivetrain motor idle modes 3 seconds after the robot has been disabled. 96 # to_break should be False at competitions where the robot is turned off between matches 97 Trigger(is_disabled()).debounce(3).onTrue( 98 commands2.cmd.runOnce( 99 self.drivetrain.set_motor_stop_modes( 100 to_drive=True, to_break=True, all_motor_override=True, burn_flash=True 101 ), 102 self.drivetrain 103 ) 104 )
def
teleopInit(self):
131 def teleopInit(self): 132 self.updateAlliance() 133 if self.auto_command: 134 self.auto_command.cancel() 135 136 self.drivetrain.setDefaultCommand( 137 DefaultDrive( 138 self.drivetrain, 139 self.translate_x, 140 self.translate_y, 141 self.rotate, 142 lambda: not self.robot_relative_btn() 143 ) 144 )
def
testInit(self):
149 def testInit(self): 150 #TODO Move to NT listener on change listener 151 self.updateAlliance() 152 commands2.CommandScheduler.getInstance().cancelAll() 153 self.drivetrain.setDefaultCommand( 154 DefaultDrive( 155 self.drivetrain, 156 lambda: wpimath.applyDeadband(-1 * self.driver_controller.getLeftY(), 0.06), 157 lambda: wpimath.applyDeadband(-1 * self.driver_controller.getLeftX(), 0.06), 158 lambda: wpimath.applyDeadband(-1 * self.driver_controller.getRightX(), 0.1), 159 lambda: not self.driver_controller.getRightBumperButton() 160 ) 161 ) 162 commands2.cmd.run(lambda: self.drivetrain.drive(2, 0, 0, False), self.drivetrain).withTimeout(5).schedule()
def
getDeployInfo(self, key: str) -> str:
210 def getDeployInfo(self, key: str) -> str: 211 """Gets the Git SHA of the deployed robot by parsing ~/deploy.json and returning the git-hash from the JSON key OR if deploy.json is unavailable will return "unknown" 212 example deploy.json: '{"deploy-host": "DESKTOP-80HA89O", "deploy-user": "ehsra", "deploy-date": "2023-03-02T17:54:14", "code-path": "blah", "git-hash": "3f4e89f138d9d78093bd4869e0cac9b61becd2b9", "git-desc": "3f4e89f-dirty", "git-branch": "fix-recal-nbeasley"} 213 214 Args: 215 key (str): The desired json key to get. Popular onces are git-hash, deploy-host, deploy-user 216 217 Returns: 218 str: Returns the value of the desired deploy key 219 """ 220 json_object = None 221 home = str(Path.home()) + os.path.sep 222 releaseFile = home + 'py' + os.path.sep + "deploy.json" 223 try: 224 # Read from ~/deploy.json 225 with open(releaseFile, "r") as openfile: 226 json_object = json.load(openfile) 227 print(json_object) 228 print(type(json_object)) 229 if key in json_object: 230 return json_object[key] 231 else: 232 return f"Key: {key} Not Found in JSON" 233 except OSError: 234 return "unknown" 235 except json.JSONDecodeError: 236 return "bad json in deploy file check for unescaped "
Gets the Git SHA of the deployed robot by parsing ~/deploy.json and returning the git-hash from the JSON key OR if deploy.json is unavailable will return "unknown" example deploy.json: '{"deploy-host": "DESKTOP-80HA89O", "deploy-user": "ehsra", "deploy-date": "2023-03-02T17:54:14", "code-path": "blah", "git-hash": "3f4e89f138d9d78093bd4869e0cac9b61becd2b9", "git-desc": "3f4e89f-dirty", "git-branch": "fix-recal-nbeasley"}
Args: key (str): The desired json key to get. Popular onces are git-hash, deploy-host, deploy-user
Returns: str: Returns the value of the desired deploy key