How to Tune PID Controller Gains for Different Quadrotors in Peng

Tuning PID controller gains for different quadrotors in Peng requires editing the pid_controller section of config/quad.yaml to adjust proportional, derivative, and integral gains for both position and attitude control loops, then iterating through simulation runs while monitoring rise time, overshoot, and steady-state error.

The Peng quadrotor simulator implements a cascaded PID architecture where gains are decoupled from the core Rust logic and loaded via YAML configuration. This design allows you to stabilize diverse drone platforms—from lightweight agile racers to heavy-lift cargo vehicles—without recompiling the simulator source code.

Peng's PID Control Architecture

At the heart of the system lies the PIDController struct defined in src/lib.rs, which maintains separate gain matrices for translational and rotational dynamics.

The PIDController Struct

According to the Peng source code in src/lib.rs (lines 21-38), the controller stores:

  • kpid_pos: A 3×3 matrix of proportional, derivative, and integral gains for the x, y, and z axes
  • kpid_att: Corresponding gains for roll, pitch, and yaw
  • Integral error accumulators: Track cumulative position and attitude errors
  • Max-integral limits: Clamp values to prevent windup (pos_max_int and att_max_int)

The controller computes commands through two primary methods also located in src/lib.rs:

  1. compute_position_control (lines 58-78): Implements the position PID law acc = Kp·e_pos + Kd·e_vel + Ki·∫e_pos to generate thrust and desired orientation
  2. compute_attitude_control (lines 84-95): Implements the attitude PID law using Euler-angle error to produce torque vectors

These methods receive the discrete time step dt derived from control_frequency defined in config/quad.yaml (lines 11-14).

Step-by-Step Tuning Workflow

Systematic tuning of PID controller gains in Peng follows a structured approach that accounts for physical platform differences.

1. Characterize the Physical Platform

Before adjusting gains, identify the quadrotor's mass, inertia, and drag coefficient under the quadrotor section of config/quad.yaml. Heavier drones require larger proportional gains to overcome inertia, while high-drag configurations need aggressive integral action to eliminate steady-state bias.

2. Establish a Baseline

The repository ships with default values (position kp ≈ 7–11, attitude kp ≈ 1.5) that provide stable flight for a standard 1kg platform. Use these as your starting reference point.

3. Tune Position Gains First

Adjust the pos_gains block in config/quad.yaml using this sequence:

  • Proportional (kp): Increase until the drone reaches setpoints with minimal steady-state error but stops before oscillation begins
  • Derivative (kd): Raise after kp to dampen overshoot and mitigate ringing
  • Integral (ki): Add sparingly to eliminate residual error from drag or asymmetry; always pair with appropriate pos_max_int limits to prevent windup

4. Tune Attitude Gains

Modify the att_gains block similarly, noting that attitude dynamics respond faster than position. Typical kp values remain lower (≈ 1.5–2.0) because rotational inertia is usually smaller than translational mass.

5. Iterate Through Simulation

Run short hover or step-response tests after each modification, monitoring:

  • Rise time: How quickly error drops to 10% of initial value
  • Overshoot: Should remain below 5% for most applications
  • Steady-state error: Target less than 1cm for position, less than 1° for attitude

6. Verify Integral Limits

Check that integral_pos_error and integral_att_error never saturate against pos_max_int or att_max_int. If saturation occurs, either lower the gain or raise the limit.

7. Optimize Control Frequency

If the system appears choppy or unstable, increase control_frequency in the YAML file (e.g., from 200Hz to 400Hz). Higher frequencies permit finer dt steps, allowing slightly higher gains without destabilizing the discrete-time controller.

Platform-Specific Tuning Strategies

Different quadrotor configurations demand specific gain adjustments in Peng:

  • Heavy platforms (≥ 2kg): Multiply pos_gains.kp by 1.5–2× and increase pos_gains.kd proportionally to maintain damping ratios
  • Lightweight agile drones: Reduce pos_gains.kp to prevent aggressive thrust spikes; increase att_gains.kd for smoother orientation tracking
  • High-drag environments: Modestly increase pos_gains.ki and raise pos_max_int to compensate for constant velocity bias
  • Low-frequency control (100Hz): Keep gains conservative (≤ 70% of 200Hz values) because the larger dt amplifies discretization errors

Implementation Examples

Configuring Gains in YAML

Edit config/quad.yaml (lines 24-34) to define platform-specific gains:

quadrotor:
  mass: 2.0
  gravity: 9.81
  drag_coefficient: 0.02

pid_controller:
  pos_gains:
    kp: [10.0, 10.0, 15.0]
    kd: [3.0, 3.0, 8.0]
    ki: [0.1, 0.1, 0.1]
  att_gains:
    kp: [2.0, 2.0, 1.2]
    kd: [0.2, 0.2, 0.15]
    ki: [0.0, 0.0, 0.0]
  pos_max_int: [15.0, 15.0, 15.0]
  att_max_int: [1.0, 1.0, 1.0]

control_frequency: 400

Instantiating the Controller in Rust

The PIDController::new constructor in src/lib.rs (lines 62-69) accepts the gain matrices loaded from configuration:

use peng_quad::{PIDController, Config};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config::from_yaml("config/quad.yaml")?;
    
    let kpid_pos = [
        config.pid_controller.pos_gains.kp,
        config.pid_controller.pos_gains.kd,
        config.pid_controller.pos_gains.ki,
    ];
    let kpid_att = [
        config.pid_controller.att_gains.kp,
        config.pid_controller.att_gains.kd,
        config.pid_controller.att_gains.ki,
    ];

    let pid = PIDController::new(
        kpid_pos,
        kpid_att,
        config.pid_controller.pos_max_int,
        config.pid_controller.att_max_int,
        config.quadrotor.mass,
        config.quadrotor.gravity,
    );
    
    Ok(())
}

Running the Control Loop

Integrate the controller into your simulation loop using the methods defined in src/lib.rs:

let dt = 1.0 / config.simulation.control_frequency as f32;

let (thrust, desired_orientation) = pid.compute_position_control(
    &Vector3::new(0.0, 0.0, 1.0),
    &Vector3::zeros(),
    0.0,
    &quad.position,
    &quad.velocity,
    dt,
);

let torque = pid.compute_attitude_control(
    &desired_orientation,
    &quad.orientation,
    &quad.angular_velocity,
    dt,
);

quad.update_dynamics_with_controls_euler(thrust, &torque);

Summary

  • PID gains in Peng are defined in config/quad.yaml under pid_controller.pos_gains and pid_controller.att_gains, with separate proportional, derivative, and integral values for each axis
  • Tuning sequence matters: establish proportional gains first, add derivative to dampen oscillations, then introduce integral action with strict limits to eliminate steady-state error
  • Physical parameters including mass and drag coefficients must be updated in the same YAML file to ensure the controller accounts for platform-specific dynamics
  • Control frequency directly impacts stability; higher frequencies (400Hz) support more aggressive gains than lower frequencies (100Hz)
  • Source locations: The core logic resides in src/lib.rs (PIDController struct, lines 21-38; control methods, lines 58-95), while configuration parsing happens in src/config.rs

Frequently Asked Questions

Where are the PID gains stored in Peng?

The gains are stored in the config/quad.yaml file under the pid_controller section, specifically within pos_gains and att_gains subsections. Each subsection contains arrays for kp, kd, and ki corresponding to the x/y/z axes for position and roll/pitch/yaw for attitude.

How do I prevent integral windup when tuning?

Set appropriate values for pos_max_int and att_max_int in config/quad.yaml to clamp the maximum accumulated error. If the drone exhibits oscillatory behavior after reaching steady state, reduce the integral gain or lower these limits to restrict how much error the controller can accumulate.

Should I tune position or attitude gains first?

Always tune position gains first because the attitude controller tracks orientation setpoints generated by the position loop. If the inner attitude loop is too sluggish, the outer position loop cannot achieve accurate tracking regardless of its gains. Start with attitude gains at conservative values, optimize position response, then refine attitude gains for crisp orientation tracking.

How does control frequency affect PID tuning?

The control_frequency parameter in config/quad.yaml determines the discrete time step dt passed to compute_position_control and compute_attitude_control. Lower frequencies (e.g., 100Hz) require more conservative gains because the larger dt increases discretization error and delay. When increasing control frequency from 200Hz to 400Hz, you can typically raise proportional gains by 20-30% while maintaining equivalent stability margins.

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 →