Skip to content

Commit b4827db

Browse files
optimization: utilize math library for faster math (#111)
1 parent 1dfbe78 commit b4827db

File tree

5 files changed

+61
-144
lines changed

5 files changed

+61
-144
lines changed

pybot/autos.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
DEFAULT_TRAJECTORY = 'outwayred'
77

88
class FollowTrajectory(commands2.Command):
9-
def __init__(self, drivetrain, intake, traj, is_red_alliance):
9+
def __init__(self, drivetrain, intake, traj, is_red_alliance) -> None:
1010
"""
1111
Initializes the AutonomousCommand.
1212
@@ -20,12 +20,14 @@ def __init__(self, drivetrain, intake, traj, is_red_alliance):
2020
self.trajectory = choreo.load_swerve_trajectory(traj)
2121
self.timer = wpilib.Timer()
2222
self.is_red_alliance = is_red_alliance
23-
self.addRequirements(self.drivetrain) # Ensure the drivetrain is a requirement for this command
2423
self.laststamp = 0
2524
self.event_markers = []
2625
self.triggered_events = set()
2726

28-
def initialize(self):
27+
self.addRequirements(self.drivetrain) # Ensure the drivetrain is a requirement for this command
28+
29+
30+
def initialize(self) -> None:
2931
"""
3032
This autonomous runs the autonomous command selected by your RobotContainer class.
3133
"""
@@ -43,7 +45,7 @@ def initialize(self):
4345
# Reset and start the timer when the autonomous period begins
4446
self.timer.restart()
4547

46-
def execute(self):
48+
def execute(self) -> None:
4749
"""
4850
This function is called periodically during autonomous.
4951
"""
@@ -67,7 +69,7 @@ def execute(self):
6769
else:
6870
self.drivetrain.stop()
6971

70-
def triggerEvent(self, event):
72+
def triggerEvent(self, event) -> None:
7173
"""
7274
Trigger the action associated with the event.
7375
"""
@@ -81,7 +83,7 @@ def triggerEvent(self, event):
8183
elif event == "ResetHeading":
8284
self.drivetrain.seed_field_centric()
8385

84-
def isFinished(self):
86+
def isFinished(self) -> bool:
8587
"""
8688
Returns true when the command should end.
8789
"""

pybot/lifter.py

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,23 @@
1-
import logging
21
from phoenix6.hardware import talon_fx
32
from phoenix6.controls.follower import Follower
43
from phoenix6.signals import NeutralModeValue
54

6-
# Configure logging
7-
logging.basicConfig(level=logging.INFO)
8-
logger = logging.getLogger(__name__)
9-
105
class Lifter:
11-
def __init__(self, master_id, follower_id):
6+
def __init__(self, master_id, follower_id) -> None:
127
self.motor = talon_fx.TalonFX(master_id)
138
self.motor.setNeutralMode(NeutralModeValue.BRAKE)
149
self.follower = talon_fx.TalonFX(follower_id)
1510
self.follower.set_control(Follower(master_id, True))
1611
self.follower.setNeutralMode(NeutralModeValue.BRAKE)
1712

18-
def moveDown(self):
19-
try:
20-
if self.motor.get_reverse_limit() == 0:
21-
logger.warning("Reverse limit reached; cannot move down")
22-
self.stop()
23-
else:
24-
logger.info("Moving down")
25-
self.motor.set(-0.2) # Gentle downward movement
26-
except Exception as e:
27-
logger.error(f"Error moving down: {e}")
28-
29-
def moveUp(self):
30-
try:
31-
if self.motor.get_forward_limit() == 0:
32-
logger.warning("Forward limit reached; cannot move up")
33-
self.stop()
34-
else:
35-
logger.info("Moving up")
36-
self.motor.set(0.3) # Upward movement
37-
except Exception as e:
38-
logger.error(f"Error moving up: {e}")
13+
def moveDown(self) -> None:
14+
self.motor.set(-0.2)
3915

16+
def moveUp(self) -> None:
17+
self.motor.set(0.3)
4018

41-
def stop(self):
42-
try:
19+
def stop(self) -> None:
4320
self.motor.stopMotor()
44-
except Exception as e:
45-
logger.error(f"Error stopping motor: {e}")
4621

47-
def setMotor(self, value):
48-
try:
22+
def setMotor(self, value) -> None:
4923
self.motor.set(value)
50-
except Exception as e:
51-
logger.error(f"Error setting motor: {e}")

pybot/robot.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ def robotInit(self) -> None:
2424
self.container = RobotContainer()
2525
self.scheduler = commands2.CommandScheduler.getInstance()
2626
self.registerTrajectories()
27-
wpilib.CameraServer.launch ('vision.py:main')
2827

2928
def registerTrajectories(self) -> None:
3029
self.chooser = autos.createChooser()

pybot/robotcontainer.py

Lines changed: 46 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,27 @@
77
import commands2
88
import commands2.button
99
import commands2.cmd
10-
from commands2.sysid import SysIdRoutine
10+
#from commands2.sysid import SysIdRoutine
1111
from generated.tuner_constants import TunerConstants
1212
from telemetry import Telemetry
1313

14-
from phoenix6 import swerve, SignalLogger
14+
from phoenix6 import swerve#, SignalLogger
1515
from wpimath.units import rotationsToRadians
1616
from lifter import Lifter # Import the Lifter class
1717
import wpilib
1818
import logging
19-
2019
import math
2120

2221
# Configure logging
2322
logging.basicConfig(level=logging.DEBUG)
2423

24+
MAX_SPEED_SCALING = 0.35
25+
DEAD_BAND = 0.1
26+
ELEVATOR_MOTOR_ID_1 = 20
27+
ELEVATOR_MOTOR_ID_2 = 16
28+
29+
INTAKE_MOTOR_ID_TOP = 18
30+
INTAKE_MOTOR_ID_BOTTOM = 22
2531

2632
class RobotContainer:
2733
"""
@@ -33,14 +39,14 @@ class RobotContainer:
3339

3440
def __init__(self) -> None:
3541
self._max_speed = (
36-
TunerConstants.speed_at_12_volts * .35
42+
TunerConstants.speed_at_12_volts * MAX_SPEED_SCALING
3743
) # speed_at_12_volts desired top speed
3844
self._max_angular_rate = rotationsToRadians(
3945
TunerConstants.angular_at_12_volts * .1
4046
)
4147

4248
# Setting up bindings for necessary control of the swerve drive platform
43-
self._deadband = 0.1 # The input deadband
49+
self._deadband = DEAD_BAND # The input deadband
4450
self._exponent = 3.0 # Exponential factor (try 2, 2.5, or 3)
4551

4652
self._drive = (
@@ -61,13 +67,32 @@ def __init__(self) -> None:
6167
self.drivetrain = TunerConstants.create_drivetrain()
6268

6369
# Initialize the elevator with motor IDs
64-
self.elevator = Lifter(20, 16)
70+
self.elevator = Lifter(ELEVATOR_MOTOR_ID_1, ELEVATOR_MOTOR_ID_2)
6571
# Initialize the intake with motor IDs
66-
self.intake = Lifter(18, 22)
67-
# making it closer to 1 will make the decay slower and smoother.
68-
72+
self.intake = Lifter(INTAKE_MOTOR_ID_TOP, INTAKE_MOTOR_ID_BOTTOM)
73+
6974
# Setup telemetry
70-
self._registerTelemetery()
75+
self._registerTelemetry()
76+
77+
def calculateJoystick(self) -> tuple[float, float]:
78+
x0, y0 = self._joystick.getLeftX(), self._joystick.getLeftY()
79+
magnitude = self.applyExponential(math.hypot(x0, y0), self._deadband, self._exponent) * self._max_speed
80+
theta = math.atan2(y0, x0)
81+
82+
x1 = magnitude * math.cos(theta)
83+
y1 = magnitude * math.sin(theta)
84+
85+
return x1, y1
86+
87+
def applyRequest(self) -> swerve.requests.SwerveRequest:
88+
(new_vx, new_vy) = self.calculateJoystick()
89+
90+
return (self._drive.with_velocity_x(self._driveMultiplier * new_vy # Drive left with negative X (left)
91+
) .with_velocity_y(self._driveMultiplier * new_vx) # Drive forward with negative Y (forward)
92+
.with_rotational_rate(
93+
self._driveMultiplier * self.applyExponential(self._joystick.getRightX(), self._deadband, self._exponent) * self._max_angular_rate
94+
)) # Drive counterclockwise with negative X (left)
95+
7196

7297
def configureButtonBindings(self) -> None:
7398
"""
@@ -76,36 +101,14 @@ def configureButtonBindings(self) -> None:
76101
if not hasattr(self, '_joystick') or self._joystick is None:
77102
self._joystick = commands2.button.CommandXboxController(0)
78103

104+
79105
# Cache the multiplier
80106
self._driveMultiplier = -1.0 if self.isRedAlliance() else 1.0
81-
82-
def calculateJoystick():
83-
x0 = self._joystick.getLeftX()
84-
y0 = self._joystick.getLeftY()
85-
86-
mag = self.apply_exponential(math.sqrt((x0 * x0) + (y0 * y0)), self._deadband, self._exponent) * self._max_speed
87-
theta = math.atan2(y0, x0)
88-
89-
x1 = mag * math.cos(theta)
90-
y1 = mag * math.sin(theta)
91-
92-
print('0: %f, %f 1: %f %f' % (x0, y0, x1, y1))
93-
94-
return (x1, y1)
95-
96-
def applyRequest():
97-
(new_vx, new_vy) = calculateJoystick()
98-
99-
return (self._drive.with_velocity_x(self._driveMultiplier * new_vy) # Drive forward with negative Y (forward)
100-
.with_velocity_y(self._driveMultiplier * new_vx) # Drive left with negative X (left)
101-
.with_rotational_rate(
102-
self._driveMultiplier * self.apply_exponential(self._joystick.getRightX(), self._deadband, self._exponent) * self._max_angular_rate
103-
)) # Drive counterclockwise with negative X (left)
104-
107+
105108
# Note that X is defined as forward according to WPILib convention,
106109
# and Y is defined as to the left according to WPILib convention.
107-
self.drivetrain.setDefaultCommand(self.drivetrain.apply_request(applyRequest))
108-
# reset the field-centric heading on left bumper press
110+
self.drivetrain.setDefaultCommand(self.drivetrain.apply_request(self.applyRequest))
111+
# reset the field-centric heading on x button press
109112
self._joystick.x().onTrue(
110113
self.drivetrain.runOnce(lambda: self.resetHeading())
111114
)
@@ -133,7 +136,7 @@ def applyRequest():
133136
lambda: self.intake.stop()
134137
))
135138

136-
def resetHeading(self):
139+
def resetHeading(self) -> None:
137140
self.drivetrain.seed_field_centric()
138141

139142
def getAutonomousCommand(self, selected: str) -> commands2.Command:
@@ -143,15 +146,15 @@ def getAutonomousCommand(self, selected: str) -> commands2.Command:
143146
selected,
144147
is_red_alliance = self.isRedAlliance())
145148

146-
def isRedAlliance(self):
149+
def isRedAlliance(self) -> bool:
147150
return wpilib.DriverStation.getAlliance() == wpilib.DriverStation.Alliance.kRed
148151

149-
def _registerTelemetery (self):
152+
def _registerTelemetry (self) -> None:
150153
self.drivetrain.register_telemetry(
151154
lambda state: self._logger.telemeterize(state)
152155
)
153156

154-
def apply_exponential(self, input: float, deadband: float, exponent: float) -> float:
157+
def applyExponential(self, input: float, deadband: float, exponent: float) -> float:
155158
"""
156159
Apply an exponential response curve with a deadband on the input
157160
such that the final output goes smoothly from 0 -> ±1 without
@@ -162,19 +165,9 @@ def apply_exponential(self, input: float, deadband: float, exponent: float) -> f
162165
:param exponent: The exponent for the curve (e.g. 2.0 for a squared curve).
163166
:return: A smoothly scaled & exponentiated value in [-1, 1].
164167
"""
165-
# 1. Apply input deadband (stick within ±deadband => output = 0)
166168
if abs(input) < deadband:
167169
return 0.0
168170

169-
# 2. Preserve sign and work with magnitude
170-
sign = 1 if input > 0 else -1
171-
magnitude = abs(input)
172-
173-
# 3. Scale from [deadband .. 1] to [0 .. 1]
174-
scaled = (magnitude - deadband) / (1.0 - deadband) # in [0..1]
175-
176-
# 4. Apply exponential. e.g. exponent=2 => x^2, exponent=3 => x^3
177-
curved = scaled ** exponent
178-
179-
# 5. Reapply sign to restore forward/backward
180-
return sign * curved
171+
sign = math.copysign(1, input) # Use math.copysign for clarity
172+
scaled = (abs(input) - deadband) / (1.0 - deadband)
173+
return sign * math.pow(scaled, exponent)

pybot/vision.py

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)