# How to Tune PID Controller Gains for Different Quadrotors in Peng

> Learn how to tune PID controller gains for different quadrotors in Peng by editing quad.yaml. Optimize performance with simulation and error monitoring.

- Repository: [Yang Zhou/peng](https://github.com/makeecat/peng)
- Tags: how-to-guide
- Published: 2026-03-06

---

**Tuning PID controller gains for different quadrotors in Peng requires editing the `pid_controller` section of [`config/quad.yaml`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/config/quad.yaml) (lines 24-34) to define platform-specific gains:

```yaml
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`](https://github.com/makeecat/peng/blob/main/src/lib.rs) (lines 62-69) accepts the gain matrices loaded from configuration:

```rust
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`](https://github.com/makeecat/peng/blob/main/src/lib.rs):

```rust
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`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/src/lib.rs) (`PIDController` struct, lines 21-38; control methods, lines 58-95), while configuration parsing happens in [`src/config.rs`](https://github.com/makeecat/peng/blob/main/src/config.rs)

## Frequently Asked Questions

### Where are the PID gains stored in Peng?

The gains are stored in the [`config/quad.yaml`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/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`](https://github.com/makeecat/peng/blob/main/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.