Airhead
FRC 2023 robot whose arm and claw ran on pneumatics, gated by the shoulder encoder.
- Year
- 2023
- Category
- robotics
- Built at
- FRC Team 1458
- Team
- 8 people
Stack
- Problem
- A pneumatic cylinder has two states and no middle.
- Approach
- Pneumatics gated on an encoder the cylinders do not have.
- Result
- 23rd of 46 at the Sacramento Regional.
Team 1458's robot for the 2023 FRC season, CHARGED UP. Where the following two years moved things with motors, this one moved them with air: the arm extension and the claw were both double-acting pneumatic cylinders driven off a REV Pneumatic Hub, and the software's job was to decide when it was safe to fire them.
The problem
A pneumatic cylinder has two states and no middle. It cannot be commanded to a position, it cannot be slowed down, and it cannot be asked where it is — which is fine for a claw and dangerous for an arm that extends. Extend at the wrong shoulder angle and the arm drives itself into the floor or into the robot's own frame at full line pressure, and the only feedback the software gets is that something broke.
So the interesting part of this robot was not the pneumatics. It was the interlock around them: the motor-driven shoulder joint, which does have position feedback, had to be the thing that decided whether the air was allowed to fire at all.
Architecture
Pneumatics gated on an encoder the cylinders do not have. Solenoid wraps a WPILib
DoubleSolenoid on a REV Pneumatic Hub, and two of them are instantiated — one for arm
extension, one for the claw. Arm.extendArm() refuses to fire unless the shoulder encoder
reads above 42°, and manual descent below 42° is blocked while the arm is extended. The
cylinder is binary; the condition that releases it is not.
Arm angle from the motor's own integrated sensor, in degrees. The shoulder runs on a
TalonFX whose integrated sensor position is scaled to degrees by a single constant, and
presets at 105°, 90° and 45° are reached by a two-speed approach: full speed more than 10° out,
backed off inside that, and a ±1.5° deadband that returns the arm to idle. A state enum
(TOP/MIDDLE/BOTTOM/IDLE) means a driver input cancels a preset rather than fighting it.
Charge-station balance as an eight-state machine. Balancer reads pitch off the navX and
walks a fixed sequence — orient, drive on, detect the ramp when |pitch| exceeds 8°, climb,
then alternate between forward and backward corrections at 0.05 output until pitch settles
inside the small-angle band, then lock the wheels. Each transition is either a pitch
threshold or a timer, so a stall in any one state times out into the next rather than
hanging on the platform.
AprilTag alignment, partly. Aligner reads a Limelight for horizontal offset, vertical
offset and target rotation and drives the swerve to square up to a scoring tag. The rotate
stage worked; the translate stage did not ship and is commented out in the file.
LiDAR as a duty cycle. Lidar reads a LaserShark over a DIO channel as a duty-cycle
input scaled to a 4 m full scale — used in the autonomous approach to stop at the scoring
node.
What did not work
The robot ran out of air. Not once, as an accident — as a pattern, in the back half of matches, because too much of it was on pneumatics and a stored-air system does not refill on demand. What an on-board compressor may do during a match is limited by the rules, so the tank is a budget: every arm extension and every claw actuation spends from it, and by late in a match the pressure left was not enough to drive the arm reliably.
That reframes the interlock this page opens with. Gating extension on the shoulder encoder solved the mechanical failure — the arm never fired at an angle that would break something — and did nothing about the resource failure, because the constraint that actually bit was not when the cylinder fired but how many times. Nothing in the software counted actuations or knew what pressure remained; there is no pressure sensor read anywhere in this codebase.
The autonomous is the other unfinished piece. Autonomous.scoreStart() never got past its
"not tested" commit and would not have run if it had: the arm, swerve and lidar fields
it calls are never assigned, and its guard is written if (finishedScoring = false) — an
assignment rather than a comparison, which makes the condition always false. The Limelight
translate stage is in the same state, commented out in Aligner rather than deleted, leaving
alignment as rotate-only.
This is the direct reason the 2024 robot has no pneumatics at all.
Measured results
23rd of 46 at the Sacramento Regional.
The robot competed at the maximum allowed weight, as the 2025 robot did — a constraint the compressor, the tank and the air lines were all spending from. The 2024 robot, which dropped pneumatics entirely, came in under the cap.