Skip to content
Mechanical, Build & Pneumatics·Lesson 33 of 47

Mini-Project 1: A Single-Jointed Arm From Math to Motion

Size a gearbox for a gravity-loaded arm, pick real parts, and write a ProfiledPIDController + ArmFeedforward control loop with cosine gravity compensation.

Sign in to track progress, earn XP, and save lessons.

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.

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.

Lesson quiz

Required

Answer all 3 questions correctly to complete this lesson.

1.In the worked single-jointed arm example, why is the reduction sized from the arm held horizontal rather than vertical?

2.How does WPILib's ArmFeedforward apply its gravity gain kG across the arm's range of motion?

3.Where 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.