How to Troubleshoot Pedal Oscillation and Control Loop Stability in DIY Sim-Racing FFB Pedals

Eliminate pedal oscillation by disabling servo command smoothing (PR2.22/PR2.23), tuning PID gains in StepperMovementStrategy.h, and enabling oscillation detection (Pr6.37) via the SimHub plugin or firmware configuration.

Pedal oscillation—characterized by the pedal "wiggling," hunting around set points, or sudden snapping movements—indicates instability in the feedback control loop of the chrgri/diy-sim-racing-ffb-pedal project. This open-source force-feedback pedal uses a PID or MPC controller to drive an iSV-57 servo, and instability typically stems from mismatches between the controller gains, servo drive parameters, and mechanical stiffness settings. Understanding how to troubleshoot pedal oscillation and control loop stability requires examining the interaction between the firmware's payloadPedalConfig parameters and the low-level servo registers configured in isv57communication.cpp.

Common Causes of Instability

High-Frequency Pedal Jitter (Command Smoothing)

The most common source of rapid pedal oscillation is position-command smoothing active in the servo drive. Registers PR2.22 and PR2.23 apply digital filtering to step commands, introducing lag that causes the controller to over-compensate.

According to StepperParameterization/ServoParameterization.md, these registers must be set to 0 to deactivate smoothing. The source documentation explicitly warns: "PR2.22 = 0 (deactivate position command smoothing, as it lead to pedal oscillations…)".

In isv57communication.cpp (around line 294), the initialization sequence sets these parameters:

// Disable position command smoothing to prevent oscillation
retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+22, 0); // PR2.22
retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+23, 0); // PR2.23

Aggressive PID Tuning

When the pedal "sticks" or drifts slowly before snapping back, the PID gains are likely too aggressive or the output limit is too restrictive. The controller implementation in Firmware_for_V3/PedalFirmware/include/StepperMovementStrategy.h exposes tunings via tunePidValues() and myPID.SetTunings().

Solution: Reduce Kp (proportional gain) first, then Ki (integral gain). Keep Kd (derivative gain) at 0 or a small positive value. If the stepper hits the hard stop, increase PID_OUTPUT_LIMIT_FL32 (default is 0.5f).

// Conservative starting values in StepperMovementStrategy.h
#define PID_OUTPUT_LIMIT_FL32 0.5f
// Recommended initial tunings: Kp = 0.1, Ki = 20, Kd = 0
cfg.payLoadPedalConfig_.PID_p_gain = 0.1f;
cfg.payLoadPedalConfig_.PID_i_gain = 20.0f;
cfg.payLoadPedalConfig_.PID_d_gain = 0.0f;
myPID.SetTunings(Kp, Ki, Kd);

Oscillations During ABS or Traction Control Activation

If oscillation occurs only when ABS or traction control is active, the oscillation-detection level register (Pr6.37) is likely set to 0. This prevents the servo from signaling overload conditions, causing the controller to push continuously against mechanical limits.

In isv57communication.cpp, uncomment and set a non-zero detection threshold:

// Enable 0.1% oscillation detection (value = 10)
retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_6_00+37, 10);

Soft Pedal Feel and Sudden Snapback

A pedal that feels "soft" then suddenly stiffens indicates excessive Ratio-of-Inertia (Pr0.04). This parameter, controlled via the SimHub plugin's GeneralSetting_Servo.xaml slider, scales the effective mechanical stiffness.

Warning: The UI tooltip states: "Increase the value for stiffer pedal. Will increase likelihood of pedal oscillation." Keep this value near 1% (default) for stability; increasing it amplifies latency and reduces damping.

MPC-Specific Instability

For pedals using Model Predictive Control (MPC), wiggling indicates gains mismatched to the mechanical spring rate. The MPC implementation in StepperMovementStrategy.h uses three coefficients:

  • MPC_0th_order_gain: Position gain
  • MPC_1st_order_gain: Velocity gain
  • MPC_2nd_order_gain: Acceleration gain

Tuning strategy: Reduce MPC_1st_order_gain first, then MPC_0th_order_gain. Keep the second-order gain near zero unless using a very light spring.

Axis Enable Sequence Failures

Unexpected step command jumps often occur when the servo axis is not properly enabled at startup. The axis-enable logic in isv57communication.cpp (around line 1290) must complete successfully before commands are valid:

// Enable servo axis (0x0303 = enable command)
retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_4_00+8, 0x0303);

If you observe the message "Servo registered in NVM have been updated!" after flashing, a power cycle of both the servo and ESP32 is required to apply the new register values.

How the Control Loop Functions

Understanding the signal flow helps diagnose where instability originates:

  1. Sensor Acquisition: The load-cell reading (loadCellReadingKg) passes through an optional exponential filter at the top of StepperMovementStrategy.h.
  2. Setpoint Generation: The ForceCurve_Interpolated spline calculates target force based on current pedal position (stepperPosFraction).
  3. Controller Execution:
    • PID Path: MoveByPidStrategy normalizes error and calls myPID.Compute(), outputting a fractional position change.
    • MPC Path: MoveByInterpolatedStrategy performs a Newton-method solve (MAX_NUMBER_OF_NEWTON_STEPS) using the foot-spring model and MPC gains.
  4. Actuation: The resulting position (posStepperNew) writes to the iSV-57 via modbus.holdingRegisterWrite in isv57communication.cpp.
  5. Safety Hooks: Oscillation detection (pr_6_00+37) and position smoothing (PR2.22/PR2.23) operate at the servo level.

Practical Configuration Examples

Disabling Command Smoothing via Firmware

// isv57communication.cpp - servo initialization
bool configureServo(uint8_t slaveId) {
    bool success = true;
    // Critical: Disable FIR filters that cause phase lag
    success |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+22, 0);
    success |= modbus.checkAndReplaceParameter(slaveId, pr_2_00+23, 0);
    return success;
}

Runtime PID Adjustment

The SimHub plugin (GeneralSetting_PID.xaml.cs) provides sliders, but you can also adjust programmatically:

void adjustStability(DAP_config_st& cfg) {
    // Reduce proportional gain by 50% to eliminate hunting
    cfg.payLoadPedalConfig_.PID_p_gain *= 0.5f;
    
    // Re-apply to active controller
    myPID.SetTunings(
        cfg.payLoadPedalConfig_.PID_p_gain,
        cfg.payLoadPedalConfig_.PID_i_gain,
        cfg.payLoadPedalConfig_.PID_d_gain
    );
}

Enabling Oscillation Detection

// isv57communication.cpp
// Set detection threshold to 1% (value = 100) or 0.1% (value = 10)
retValue_b |= modbus.checkAndReplaceParameter(slaveId, pr_6_00 + 37, 10);

Adjusting Mechanical Stiffness

In GeneralSetting_Servo.xaml.cs, the slider event handler updates the payload:

private void Slider_ServoRatioOfInertia_ValueChanged(object sender, 
                                                     RoutedPropertyChangedEventArgs<double> e)
{
    // Constrain to safe range (1-100, where 1 = safest)
    tmp_config.payloadPedalConfig_.RatioOfInertia = Mathf.Clamp((float)e.NewValue, 1.0f, 100.0f);
    SendConfigToPedal(tmp_config);
}

Key Files for Stability Tuning

File Path Purpose Stability Relevance
Firmware_for_V3/PedalFirmware/include/StepperMovementStrategy.h PID/MPC implementations, tunePidValues() Core control algorithms and gain limits
Firmware_for_V3/PedalFirmware/include/DiyActivePedal_types.h payloadPedalConfig struct definition Container for all tunable parameters
Firmware_for_V3/PedalFirmware/src/isv57communication.cpp Servo register initialization PR2.22/PR2.23 smoothing, Pr6.37 detection, axis enable
StepperParameterization/ServoParameterization.md Default servo parameters Documentation of anti-oscillation settings
SimHubPlugin/UIFunction/GeneralSetting_PID.xaml.cs UI bindings for PID gains Runtime tuning without reflashing
SimHubPlugin/UIFunction/GeneralSetting_Servo.xaml Ratio of Inertia slider Mechanical stiffness adjustment
Firmware_for_V3/PedalFirmware/src/Main.cpp High-level pedal logic Line ~991 contains oscillation computation placeholder

Quick Stability Checklist

  1. Disable smoothing: Verify PR2.22 = 0 and PR2.23 = 0 in isv57communication.cpp.
  2. Enable protection: Set pr_6_00+37 to 10 (0.1% detection) to prevent runaway commands.
  3. Conservative PID start: Begin with Kp = 0.1, Ki = 20, Kd = 0; increase gradually.
  4. Check output limits: Ensure PID_OUTPUT_LIMIT_FL32 is 0.5f or higher to prevent clipping.
  5. Minimize inertia ratio: Keep RatioOfInertia near 1% for maximum damping.
  6. MPC tuning: Start with MPC_0th_order_gain = 1, MPC_1st_order_gain = 0.1, MPC_2nd_order_gain = 0.
  7. Power cycle: After any firmware flash showing "Servo registered in NVM have been updated!", reboot both servo and controller.

Summary

  • Command smoothing (PR2.22/PR2.23) is the primary cause of high-frequency oscillation and must be disabled by setting both registers to 0.
  • PID stability relies on conservative gains and adequate output limits defined in StepperMovementStrategy.h.
  • Oscillation detection (Pr6.37) protects against ABS/TC-induced hunting when set to a non-zero value (e.g., 10).
  • Mechanical stiffness via RatioOfInertia should remain near 1% unless the pedal requires specific compliance characteristics.
  • Axis initialization must complete successfully in isv57communication.cpp before stable control is possible.

Frequently Asked Questions

What causes the DIY Sim-Racing FFB pedal to vibrate or jitter at high frequencies?

High-frequency jitter typically results from position-command smoothing filters (PR2.22 and PR2.23) active in the iSV-57 servo drive. These filters introduce phase lag that destabilizes the PID controller. Disable them by setting both registers to 0 in isv57communication.cpp or via the SimHub Servo Settings panel.

Why does my pedal oscillate only when ABS or traction control activates?

This indicates the oscillation-detection register (Pr6.37) is set to 0, preventing the servo from reporting overload conditions. The controller continues applying force against the mechanical stop, causing hunting. Set pr_6_00+37 to 10 (0.1% threshold) in the firmware to enable automatic backoff during overload events.

How do I tune PID gains if the pedal feels soft then suddenly snaps?

Start with conservative values: Kp = 0.1, Ki = 20, Kd = 0. If the pedal drifts or sticks, reduce Kp by 50%. If the stepper hits hard stops, increase PID_OUTPUT_LIMIT_FL32 above 0.5f in StepperMovementStrategy.h. Adjust the Ratio of Inertia slider in SimHub to 1% to increase effective damping.

What is the correct startup sequence after flashing new servo parameters?

After flashing firmware that updates servo registers (indicated by the message "Servo registered in NVM have been updated!"), you must power cycle both the ESP32 microcontroller and the iSV-57 servo. This ensures the axis-enable sequence (pr_4_00+8 = 0x0303) runs correctly and applies the new anti-oscillation settings.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →