Skip to content

Commit 6f73b0f

Browse files
Merge branch 'main' into remove-wrist-limit-switch
2 parents 9cf1f96 + 57e71e1 commit 6f73b0f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2653
-2655
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
- main
77
pull_request:
88
merge_group:
9+
workflow_dispatch:
910

1011
jobs:
1112
test:

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ minimum_pre_commit_version: '3.2.0'
77

88
repos:
99
- repo: https://github.com/pre-commit/pre-commit-hooks
10-
rev: v5.0.0
10+
rev: v6.0.0
1111
hooks:
1212
# Side effects
1313
- id: fix-byte-order-marker
@@ -22,7 +22,7 @@ repos:
2222
- id: check-yaml
2323

2424
- repo: https://github.com/astral-sh/ruff-pre-commit
25-
rev: v0.12.0
25+
rev: v0.12.11
2626
hooks:
2727
- id: ruff
2828
args:

README.md

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# py2025
1+
# pyreefscape
22

33
The Drop Bears' robot code for FRC 2025
44

@@ -11,19 +11,14 @@ This includes the Python version, and any Python packages such as `wpilib`.
1111

1212
Install `uv` by following the [`uv` docs](https://docs.astral.sh/uv/).
1313

14-
After installing `uv`, use it to create a virtual environment and install our dependencies.
14+
After installing `uv`, download the roboRIO dependencies.
1515

1616
```sh
17-
uv sync
18-
```
19-
20-
Then, download the roboRIO dependencies.
21-
22-
```sh
23-
uv run python -m ensurepip
2417
uv run robotpy sync --no-install
2518
```
2619

20+
`uv run` will automatically create a virtual environment and install our dependencies for local development.
21+
2722
### pre-commit
2823

2924
[pre-commit][] is configured to run our formatters and linters.
@@ -33,7 +28,7 @@ To use pre-commit, you must install it outside of this project's virtual environ
3328
Either use your system package manager, or use `uv tool`:
3429

3530
```sh
36-
uv tool install pre-commit
31+
uv tool install pre-commit --with pre-commit-uv
3732
```
3833

3934
You can then set up the pre-commit hooks to run on commit:
@@ -59,15 +54,13 @@ pre-commit install
5954

6055
### Simulation
6156

62-
Before your first run, copy the `*.json.orig` files to the main directory and remove the `.orig` extension.
63-
6457
```
6558
uv run robotpy sim
6659
```
6760

6861
### Deploy to Robot
6962

70-
Once on robots network
63+
First, connect to the robot network. Then run:
7164

7265
```
7366
uv run robotpy deploy

autonomous/auto_base.py

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
import choreo
44
import wpilib
55
from choreo.trajectory import SwerveSample, SwerveTrajectory
6-
from magicbot import AutonomousStateMachine, state, timed_state
6+
from magicbot import AutonomousStateMachine, state, timed_state, tunable
77
from wpilib import RobotBase
88
from wpimath.controller import PIDController
99
from wpimath.geometry import Pose2d
1010
from wpimath.kinematics import ChassisSpeeds
1111

1212
from components.chassis import ChassisComponent
13-
from components.coral_depositor import CoralDepositorComponent
1413
from components.injector import InjectorComponent
1514
from components.shooter import ShooterComponent
1615
from controllers.algae_shooter import AlgaeShooter
@@ -28,19 +27,25 @@ class AutoBase(AutonomousStateMachine):
2827
algae_shooter: AlgaeShooter
2928
reef_intake: ReefIntake
3029

31-
coral_depositor_component: CoralDepositorComponent
3230
shooter_component: ShooterComponent
3331
injector_component: InjectorComponent
3432
chassis: ChassisComponent
3533

3634
field: wpilib.Field2d
3735

3836
DISTANCE_TOLERANCE = 0.1 # metres
37+
SHOOT_DISTANCE_TOLERANCE = 0.4 # metres
38+
SHOOT_ANGLE_TOLERANCE = math.radians(10)
3939
ANGLE_TOLERANCE = math.radians(3)
4040
CORAL_DISTANCE_TOLERANCE = 0.2 # metres
4141
TRANSLATIONAL_SPEED_TOLERANCE = 0.2
4242
ROTATIONAL_SPEED_TOLERANCE = 0.1
4343

44+
is_shooting_leg = tunable(False)
45+
is_in_distance_tolerance = tunable(False)
46+
is_in_angle_tolerance = tunable(False)
47+
is_in_second_half_of_leg = tunable(False)
48+
4449
def __init__(self, trajectory_names: list[str]) -> None:
4550
# We want to parameterise these by paths and potentially a sequence of events
4651
super().__init__()
@@ -59,6 +64,7 @@ def __init__(self, trajectory_names: list[str]) -> None:
5964

6065
def setup(self) -> None:
6166
# setup path tracking controllers
67+
self.auto_sample_field_obj = self.field.getObject("auto_sample")
6268

6369
# init any other defaults
6470
pass
@@ -106,49 +112,71 @@ def tracking_trajectory(self, initial_call, state_tm) -> None:
106112
if initial_call:
107113
self.current_leg += 1
108114

109-
if self.current_leg == len(self.trajectories):
110-
self.done()
111-
return
115+
if self.current_leg == len(self.trajectories):
116+
self.done()
117+
return
118+
119+
trajectory = (
120+
self.trajectories[self.current_leg].flipped()
121+
if game.is_red()
122+
else self.trajectories[self.current_leg]
123+
)
124+
125+
trajectory_poses = trajectory.get_poses()
126+
self.field.getObject("trajectory").setPoses(trajectory_poses)
112127

113-
# get next leg on entry
114-
current_pose = self.chassis.get_pose()
115128
final_pose = self.trajectories[self.current_leg].get_final_pose(game.is_red())
116129
if final_pose is None:
117130
self.done()
118131
return
119132

133+
# get next leg on entry
134+
current_pose = self.chassis.get_pose()
135+
120136
distance = current_pose.translation().distance(final_pose.translation())
121137
angle_error = (final_pose.rotation() - current_pose.rotation()).radians()
122-
velocity = self.chassis.get_velocity()
123-
speed = math.sqrt(math.pow(velocity.vx, 2.0) + math.pow(velocity.vy, 2.0))
124138

125-
if self.current_leg % 2 != 0:
139+
self.is_shooting_leg = self.current_leg % 2 != 0
140+
141+
if self.is_shooting_leg:
126142
self.algae_shooter.shoot()
127143
else:
128144
self.reef_intake.intake()
129145
# Check if we are close enough to deposit coral
130146
if distance < self.CORAL_DISTANCE_TOLERANCE:
131147
self.reef_intake.holding_coral = False
132148

149+
self.is_in_distance_tolerance = (
150+
distance < self.SHOOT_DISTANCE_TOLERANCE
151+
if self.is_shooting_leg
152+
else distance < self.DISTANCE_TOLERANCE
153+
)
154+
155+
self.is_in_angle_tolerance = (
156+
math.isclose(angle_error, 0.0, abs_tol=self.SHOOT_ANGLE_TOLERANCE)
157+
if self.is_shooting_leg
158+
else math.isclose(angle_error, 0.0, abs_tol=self.ANGLE_TOLERANCE)
159+
)
160+
self.is_in_second_half_of_leg = (
161+
state_tm > self.trajectories[self.current_leg].get_total_time() / 2.0
162+
)
163+
133164
if (
134-
distance < self.DISTANCE_TOLERANCE
135-
and math.isclose(angle_error, 0.0, abs_tol=self.ANGLE_TOLERANCE)
136-
and math.isclose(speed, 0.0, abs_tol=self.TRANSLATIONAL_SPEED_TOLERANCE)
137-
and math.isclose(
138-
velocity.omega, 0.0, abs_tol=self.ROTATIONAL_SPEED_TOLERANCE
139-
)
140-
and state_tm > self.trajectories[self.current_leg].get_total_time() / 2.0
165+
self.is_in_distance_tolerance
166+
and self.is_in_angle_tolerance
167+
and self.chassis.is_stationary()
168+
and self.is_in_second_half_of_leg
141169
):
142170
# run cycles of pick up -> shoot
143-
if self.injector_component.has_algae():
171+
if self.is_shooting_leg:
144172
self.next_state("shooting_algae")
145173
else:
146174
self.next_state("intaking_algae")
147-
return
148175

149176
sample = self.trajectories[self.current_leg].sample_at(state_tm, game.is_red())
150177
if sample is not None:
151178
self.follow_trajectory(sample)
179+
self.auto_sample_field_obj.setPose(sample.get_pose())
152180

153181
def follow_trajectory(self, sample: SwerveSample):
154182
# track path
@@ -170,15 +198,15 @@ def follow_trajectory(self, sample: SwerveSample):
170198

171199
@timed_state(duration=1.0, next_state="tracking_trajectory")
172200
def intaking_algae(self) -> None:
173-
if self.current_leg == 0:
174-
self.coral_depositor_component.deposit()
175201
if self.injector_component.has_algae():
176-
self.coral_depositor_component.tuck()
177202
self.next_state("tracking_trajectory")
178203

179204
@timed_state(duration=1.0, next_state="tracking_trajectory")
180205
def shooting_algae(self) -> None:
181206
self.algae_shooter.shoot()
182207

183-
if not self.algae_shooter.is_executing:
208+
if (
209+
not self.injector_component.has_algae()
210+
and not self.algae_shooter.is_executing
211+
):
184212
self.next_state("tracking_trajectory")

components/ballistics.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,27 @@ class BallisticsComponent:
4848
# Tuples are values corresponding to the distances above
4949
# fmt: off
5050
FLYWHEEL_TOP_SPEED_LOOKUP = {
51-
ALGAE_MIN_DIAMETER: (17, 19, 26.0),
52-
ALGAE_MAX_DIAMETER: (21, 22, 24.0),
51+
ALGAE_MIN_DIAMETER: (19, 22, 26.0),
52+
ALGAE_MAX_DIAMETER: (25, 28, 28.0),
5353
}
5454

5555
FLYWHEEL_BOTTOM_SPEED_LOOKUP = {
56-
ALGAE_MIN_DIAMETER: (33, 36, 44.0),
57-
ALGAE_MAX_DIAMETER: (36, 37, 44.0),
56+
ALGAE_MIN_DIAMETER: (35, 37, 42.0),
57+
ALGAE_MAX_DIAMETER: (40, 40, 44.0),
5858
}
5959

6060
FLYWHEEL_ANGLE_LOOKUP = {
61-
ALGAE_MIN_DIAMETER: (math.radians(-10), math.radians(-15), math.radians(-25)),
61+
ALGAE_MIN_DIAMETER: (
62+
math.radians(-10),
63+
math.radians(-15),
64+
math.radians(-25)
65+
),
6266
# 16.5: (math.radians(-11.0), math.radians(-19), -25),
63-
ALGAE_MAX_DIAMETER: (math.radians(-10), math.radians(-15), math.radians(-25)),
67+
ALGAE_MAX_DIAMETER: (
68+
math.radians(-10),
69+
math.radians(-15),
70+
math.radians(-25)
71+
),
6472
}
6573
# fmt: on
6674
BALL_SIZES = list(FLYWHEEL_ANGLE_LOOKUP.keys())
@@ -104,7 +112,7 @@ def corrected_range(self) -> float:
104112
robot_to_barge_X_offset
105113
/ (robot_pose.rotation() + self.robot_to_shooter).cos()
106114
)
107-
return distance + 0.3
115+
return distance
108116

109117
@feedback
110118
def is_aligned(self) -> bool:

components/chassis.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import magicbot
66
import ntcore
77
import wpilib
8-
from magicbot import feedback
8+
from magicbot import feedback, tunable
99
from phoenix6.configs import (
1010
ClosedLoopGeneralConfigs,
1111
FeedbackConfigs,
@@ -28,6 +28,8 @@
2828
SwerveModuleState,
2929
)
3030

31+
import utilities
32+
import utilities.scalers
3133
from ids import CancoderId, TalonId
3234
from utilities.ctre import FALCON_FREE_RPS
3335
from utilities.functions import rate_limit_module
@@ -229,6 +231,9 @@ class ChassisComponent:
229231

230232
DRIVE_CURRENT_THRESHOLD = 35
231233

234+
MIN_ALIGN_Y_AXIS_SPEED = tunable(0.3)
235+
MAX_ALIGN_Y_AXIS_SPEED = tunable(1.5)
236+
232237
HEADING_TOLERANCE = math.radians(1)
233238

234239
swerve_config: SwerveConfig
@@ -381,6 +386,23 @@ def drive_local(self, vx: float, vy: float, omega: float) -> None:
381386
"""Robot oriented drive commands"""
382387
self.chassis_speeds = ChassisSpeeds(vx, vy, omega)
383388

389+
def align_on_y(self, offset: float, distance_tol: float, angle_tol: float) -> None:
390+
if math.isclose(offset, 0.0, abs_tol=distance_tol):
391+
self.chassis_speeds.vy = 0
392+
393+
elif math.isclose(self.heading_controller.getError(), 0.0, abs_tol=angle_tol):
394+
# move in direction opposite to offset, proportional to offset
395+
self.chassis_speeds.vy = -math.copysign(
396+
utilities.scalers.scale_value(
397+
abs(offset),
398+
0,
399+
1.5,
400+
self.MIN_ALIGN_Y_AXIS_SPEED,
401+
self.MAX_ALIGN_Y_AXIS_SPEED,
402+
),
403+
offset,
404+
)
405+
384406
def limit_to_positive_longitudinal_velocity(self) -> None:
385407
self.chassis_speeds.vy = 0.0
386408
self.chassis_speeds.omega = 0.0

components/coral_depositor.py

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

components/injector.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class InjectorComponent:
1919
INJECTOR_RPS_TOLERANCE = 0.5
2020
INJECTOR_MAX_ACCEL = 0.5
2121

22+
cycle_counter = tunable(0)
23+
cycle_segment = tunable("intaking")
24+
2225
def __init__(self) -> None:
2326
self.algae_limit_switch = DigitalInput(DioChannel.ALGAE_INTAKE_SWITCH)
2427

@@ -55,6 +58,14 @@ def __init__(self) -> None:
5558

5659
def on_enable(self) -> None:
5760
self.has_seen_algae = False
61+
self.cycle_counter = 0
62+
63+
def increment_segment(self) -> None:
64+
if self.cycle_segment == "intaking":
65+
self.cycle_segment = "shooting"
66+
else:
67+
self.cycle_segment = "intaking"
68+
self.cycle_counter += 1
5869

5970
@feedback
6071
def _algae_limit_switch_pressed(self) -> bool:

0 commit comments

Comments
 (0)