Skip to content
CrocoDocs
Starter GuideProgrammingEngineeringDesignPortfolio
Documentation

Introduction

IntroductionBIOBUZZ game overview

Getting started

Getting startedBlocksOnBot JavaAndroid StudioYour first OpModeJava basicsMotors and servosNonblocking codeDebugging robot code

Control systems

Control systemsJoystick mappingField-centric mecanum drivePID controlFeedforwardFlywheel velocity controlSystem identificationMotion profilingKalman filterLow-pass filter

Autonomous

AutonomousTimed and encoder movementOdometryLocalization troubleshootingMotion planningReliable autonomous sequencesPure PursuitSensor fusionAuto to TeleOp handoff

Season topics

Season topicsProjectile mathematicsTurret aimingShooting on the move

Code practices

Code practicesNaming conventionsCode organizationLoop timing and bulk readsFormattingComments and documentationTeam collaboration

Libraries

LibrariesNextFTCFTCLibSolversLibPedro PathingRoad RunnerFTC DashboardMercurial v2Mercurial v1PanelsSloth

Community software

Community softwareBlaze FTCOpenLoopHive Vision

Sensors and vision

Vision overviewVision basicsLimelight 3A
Starter Guide
Engineering
Design
Portfolio
Appearance

Feedforward

Add a measured gravity term to an arm controller and check its assumptions

Feedback reacts to error. Feedforward estimates the command needed for a known load or requested motion before that error grows. An arm holding a game piece against gravity needs some effort even when its measured angle equals the target. A gravity term can supply that effort while feedback corrects the remaining error.

Read PID control first. This page adds a gravity model; it does not replace motor-direction checks, homing, travel limits, or measured tuning.

Arm gravity

For an ideal rigid arm with a fixed center of mass, gravity varies with cos(angle), when angle is measured from horizontal. It is largest in magnitude near horizontal and approaches zero when the arm is vertical. A different angle reference requires a corresponding phase offset.

A simple normalized-power model is:

gravity power = kG × cos(measured angle from horizontal)
requested power = feedback power + gravity power

kG is a measured power coefficient for the actual arm, gearing, conditions, and load. There is no universal value. An extending arm or changing payload changes the torque; one fixed coefficient may be insufficient. Friction and motor deadband also limit the simple model.

Controller fragment

Place this calculation in the active control loop after obtaining a valid measured angle and feedback output. It assumes positive logical motor power raises the defined arm angle. Use radians inside trigonometric functions. angleDegrees is measured from horizontal, not an arbitrary zero.

// kG is measured for this arm. feedbackPower comes from the position controller.
double angleRadians = Math.toRadians(angleDegrees);
double gravityPower = kG * Math.cos(angleRadians);
double requestedPower = feedbackPower + gravityPower;
double limitedPower = Math.max(-0.25, Math.min(0.25, requestedPower));
// Apply measured travel limits before writing limitedPower to the motor.
// kG is measured for this arm. feedbackPower comes from the position controller.
val angleRadians = Math.toRadians(angleDegrees)
val gravityPower = kG * kotlin.math.cos(angleRadians)
val requestedPower = feedbackPower + gravityPower
val limitedPower = requestedPower.coerceIn(-0.25, 0.25)
// Apply measured travel limits before writing limitedPower to the motor.

The 0.25 cap is a practice bound, not proof that the arm can safely hold or move. If the needs more power than the cap allows, the command saturates and feedback cannot produce its requested correction. A vertical slide has approximately constant gravity load for a fixed payload, so copying an arm's cosine term into it is generally the wrong model.

Measurement and tuning

Support the arm and use a controlled test procedure appropriate to the mechanism. With integral gain zero, measure the small command needed to hold several known angles under a representative load. Compare the trend with the cosine model and investigate a wrong sign or zero reference before changing gains.

Log measured angle, target, feedback term, gravity term, unclamped request, and actual output. Test unloaded and loaded cases, several angles, and realistic battery conditions. Use a hold policy and physical support that remain safe if the stops or power is lost.

Velocity and acceleration feedforward can be added for a motion profile (kV × velocity and kA × acceleration) when the model and units support them. That is a separate identification problem; do not reuse unrelated gains because their names match.

System identification develops that measurement process: fit a voltage model, test it against separate recordings, then convert its output to motor power. Keep voltage-based gains separate from the normalised-power kG used in the arm fragment above.

Integral saturation

A finite integral-state cap reduces windup, but a blocked arm can still accumulate error while output is saturated. An optional anti-windup policy accepts a new integral update only when the output is unsaturated or the error would bring it back toward the allowed range. Account for feedforward and other terms when calculating the remaining output range.

Watch the I contribution, not only the stored integral: its effect depends on kI. Reset or manage controller state when disabling control, changing modes, or changing references. Test a blocked or unreachable target before relying on recovery behavior; do not intentionally an unsupported mechanism to tune it.

The fragments here use actual /Kotlin syntax but still require the -specific measurements and controller context described above.

Sources

  • CTRL ALT FTC: feedforward
  • CTRL ALT FTC: practical PID improvements
  • Author source and pseudocode notice

CTRL ALT FTC is a conceptual guide; its examples are explicitly pseudocode.

PID control

Build a feedback controller for an FTC arm one step at a time

Flywheel velocity control

Choose one velocity controller, convert encoder units, and wait for measured recovery before feeding

On this page

Arm gravityController fragmentMeasurement and tuningIntegral saturationSources