The goal#
Design a single-jointed arm: a 24 in aluminum arm carrying a 3 lb game piece at the tip, pivoting from horizontal (0 rad) up to vertical (1.57 rad). We'll size the reduction, pick parts, then write closed-loop code.
Step 1 — Worst-case torque#
Gravity torque is highest when the arm is horizontal: tau = m * g * r. The arm itself (~2 lb at its 12 in center of mass) plus a 3 lb load at 24 in:
- Arm:
0.91 kg * 9.81 * 0.305 m = 2.72 Nm - Load:
1.36 kg * 9.81 * 0.61 m = 8.14 Nm - Total holding torque ≈ 10.9 Nm at the joint.
Step 2 — Motor + reduction#
A single NEO Vortex (REV-21-1652) produces 3.6 Nm stall torque and 6784 RPM free speed (per REV's published motor specs). You never run a motor at stall, so target ~20-25% of stall as a usable continuous holding torque: ~0.8 Nm at the motor. Required reduction:
10.9 Nm / 0.8 Nm ≈ 14:1 minimum; round up for margin to a clean 48:1.
Build this with a REV MAXPlanetary (system kit REV-21-2100): stack a 4:1 and 3:1 cartridge = 12:1, then a final 16T:64T sprocket/chain stage = 4:1, for a total 48:1. MAXPlanetary cartridges are sold individually as 3:1 (REV-21-2101), 4:1 (REV-21-2102), 5:1 (REV-21-2103), and 9:1. To drive the MAXPlanetary with a NEO Vortex you also need the Vortex MAXPlanetary Input Kit (REV-21-2130), which couples the Vortex's keyed shaft into the gearbox input stage.
Step 3 — Sensing#
Mount a REV Through Bore Encoder V2 (REV-11-3174) on the dead axle so it reads true joint angle regardless of chain stretch. It has a 1/2 in hex through bore and outputs both absolute (pulse-width) and incremental (quadrature) signals; read its absolute output through your controller's data/IO port and convert pulse width to radians in code.
Step 4 — The control code (WPILib Java)#
Use a ProfiledPIDController for smooth motion and ArmFeedforward for gravity. The key insight: ArmFeedforward's kG term is multiplied by cos(angle) internally, because gravity torque scales with the horizontal projection. The constructor order is (kS, kG, kV, kA).
private final ProfiledPIDController pid =
new ProfiledPIDController(
4.0, 0.0, 0.1,
new TrapezoidProfile.Constraints(2.0, 5.0)); // rad/s, rad/s^2
private final ArmFeedforward ff =
new ArmFeedforward(0.2, 0.45, 1.9, 0.05); // kS, kG, kV, kA (volts)
public void setGoal(double goalRad) { pid.setGoal(goalRad); }
@Override
public void periodic() {
double measured = encoder.getPosition(); // radians, absolute
double pidVolts = pid.calculate(measured);
var sp = pid.getSetpoint();
double ffVolts = ff.calculate(sp.position, sp.velocity); // cos(angle) handled inside
motor.setVoltage(pidVolts + ffVolts);
}
The example gains above are placeholders; measure your own with SysId.
Step 5 — Tune in order#
Per WPILib's feedforward guidance: find kG first (increase until the arm just holds horizontal without drifting), then kV (matches steady-velocity slope), then kA (matches acceleration curves), then add kP until slight overshoot and back off ~20%. Set a smart current limit of 40 A on the controller so the planetary and chain survive a stall.
the part worth keeping
Key takeaways
- Size reductions from worst-case (horizontal) gravity torque, then target ~20-25% of motor stall torque as usable holding torque.
- ArmFeedforward's constructor order is (kS, kG, kV, kA), and kG is multiplied by cos(angle) automatically; pass it the profiled setpoint position and velocity.
- Read joint angle with an absolute Through Bore Encoder V2 (REV-11-3174) on the dead axle so chain stretch never corrupts the angle.
Mechanical, Build & PneumaticsWorked Examples & Mini-Projectslesson 1 of 5
Keep going
Take the quiz+10 XP with an accountMore in Worked Examples & Mini-Projects
where this came from
Sources and corrections
This lesson is AI-assisted: drafted from primary sources, then reviewed and edited by hand. Errors still get through. When one is reported we fix it and write down what changed, in public, in the corrections log.
sources and further reading
- github.wpilib.orgWPILib ArmFeedforward API
- docs.revrobotics.comREV MAXPlanetary System
- revrobotics.comREV Through Bore Encoder V2 (REV-11-3174)
- docs.wpilib.orgWPILib Feedforward Control
clipped to this lesson
Articles that go further on this
The lesson gets you through the topic. These go wider on it, and they read in one sitting.
- 16 min readFRC Elevator and Arm Design: Staging, Rigging, Motors, Gravity, and SafetyA primary-source FRC guide to designing elevators and arms: cascade vs continuous rigging, staging, motor and gear-ratio sizing, gravity math, and safe holding./blogread it
- 7 min readFRC Motion Profiling Explained: TrapezoidProfile and ProfiledPIDControllerHow WPILib's TrapezoidProfile and ProfiledPIDController smooth mechanism motion, how to pick velocity and acceleration constraints, and when a profile beats plain PID./blogread it
- 20 min readREV Robotics for FRC: NEO Motors, Spark MAX/Flex, PDH & MAXSwerveA practical guide to the REV Robotics FRC ecosystem: NEO, NEO Vortex and NEO 550 motors, Spark MAX and Spark Flex controllers, the PDH, and MAXSwerve./blogread it
answer sheet
Lesson quiz
All 3 right completes the lesson. Miss one and only that question comes back, anything you already answered correctly stays banked.
0 of 3 answered
01In the worked single-jointed arm example, why is the reduction sized from the arm held horizontal rather than vertical?
02How does WPILib's ArmFeedforward apply its gravity gain kG across the arm's range of motion?
03Where should the absolute Through Bore Encoder V2 (REV-11-3174) be mounted to read true joint angle on the arm?
Answer every question to submit.
All 47 lessons in Mechanical, Build & Pneumaticsopenclose
01 / prerequisites
02 / drivetrains
03 / power-transmission
04 / structure-materials-fasteners
05 / mechanisms-fabrication-assembly
06 / pneumatics-fundamentals
07 / pneumatic-components
08 / build-wire-program
09 / safety-rules-testing
10 / worked-examples-mini-projects
- Not read yet:Mini-Project 1: A Single-Jointed Arm From Math to Motion
- Not read yet:Mini-Project 2: A Two-Stage Cascade Elevator
- Not read yet:Mini-Project 3: A Velocity-Controlled Flywheel Shooter
- Not read yet:Mini-Project 4: A Pivoting Roller Intake
- Not read yet:Mini-Project 5: Integrating a COTS Swerve Module
11 / common-mistakes-troubleshooting
- Not read yet:Pneumatics Won't Fire: A Full Diagnostic Tree
- Not read yet:The Robot Won't Drive Straight (and Other Drivetrain Sins)
- Not read yet:Gearboxes That Grenade and Fasteners That Vibrate Loose
- Not read yet:Closed-Loop Mechanisms That Oscillate, Sag, or Stall
- Not read yet:Field-Ready Reliability: Inspection, Spares, and the Pit Checklist
12 / advanced-techniques-case-studies
- Not read yet:Characterizing Any Mechanism with SysId
- Not read yet:Simulation-Driven Design with WPILib Physics Models
- Not read yet:Motion Profiling and Superstructure Coordination
- Not read yet:Designing for Weight, Stiffness, and Manufacturability
- Not read yet:Case Studies: Learning From Open Alliance Robots