How to Integrate RK4 vs Euler for Quadrotor Dynamics in Peng

Peng supports both explicit Euler and fourth-order Runge-Kutta (RK4) integration for quadrotor state propagation, selectable via boolean configuration flags in src/config.rs that dispatch to either update_dynamics_with_controls_euler or update_dynamics_with_controls_rk4 defined in src/lib.rs.

The makeecat/peng repository provides a Rust-based quadrotor simulator that lets you swap numerical integrators without refactoring physics code. Configuring these methods allows you to trade computational speed for integration accuracy depending on your simulation step size and real-time constraints.

Understanding the Integration Methods

Peng implements two distinct numerical integrators for the Quadrotor state update, both residing in src/lib.rs and accepting identical control inputs.

Euler integration implements a first-order explicit step: state_{n+1} = state_n + \dot state·Δt. Found at lines 85‑100 in src/lib.rs, this method requires minimal computation and is sufficient for high-frequency real-time loops or coarse-grained testing when Δt ≤ 1 ms.

RK4 integration implements the classic fourth-order Runge-Kutta algorithm at lines 203‑252 in src/lib.rs. It evaluates the state derivative four times per step (k1 through k4), combining them as state_{n+1} = state_n + (Δt/6)*(k1 + 2k2 + 2k3 + k4). This yields significantly higher accuracy for stiff dynamics or larger step sizes, essential for trajectory optimization pipelines.

Both functions share the same signature, accepting control_thrust: f32 and control_torque: &Vector3<f32>.

Configuration Flags for Integrator Selection

The simulation behavior is governed by two boolean fields defined at lines 35‑38 in src/config.rs:

pub use_rk4_for_dynamics_control: bool,
pub use_rk4_for_dynamics_update: bool,
  • use_rk4_for_dynamics_control: Toggles RK4 for the controlled dynamics update when thrust and torque inputs are present.
  • use_rk4_for_dynamics_update: Toggles RK4 for uncontrolled state propagation when no control signal is applied.

These flags are typically read from config/quad.yaml and loaded via Config::from_yaml.

Implementing the Selection Logic

The dispatch pattern appears in src/main.rs at lines 140‑148, where the simulation loop checks the configuration and calls the appropriate method:

if config.use_rk4_for_dynamics_control {
    quad.update_dynamics_with_controls_rk4(thrust, &torque);
} else {
    quad.update_dynamics_with_controls_euler(thrust, &torque);
}

This branching applies identically to the uncontrolled update path when evaluating use_rk4_for_dynamics_update.

Practical Configuration Examples

Enabling RK4 via YAML

To activate RK4 for controlled dynamics without recompiling, edit your configuration file:


# config/quad.yaml

use_rk4_for_dynamics_control: true   # Use RK4 when applying thrust/torque

use_rk4_for_dynamics_update: false  # Use Euler for passive propagation

After loading the configuration, the simulation driver automatically selects the specified integrator.

Programmatic Runtime Switching

For applications requiring dynamic selection, implement a wrapper function that accepts a boolean flag:

fn step_quad(quad: &mut Quadrotor, thrust: f32, torque: &Vector3<f32>, use_rk4: bool) {
    if use_rk4 {
        quad.update_dynamics_with_controls_rk4(thrust, torque);
    } else {
        quad.update_dynamics_with_controls_euler(thrust, torque);
    }
}

You can toggle use_rk4 based on command-line arguments, real-time performance metrics, or mission phase requirements.

Complete RK4 Integration Example

The following snippet demonstrates initializing a quadrotor and performing a single RK4 step:

use nalgebra::Vector3;
use peng_quad::{Quadrotor, SimulationError};

fn main() -> Result<(), SimulationError> {
    let (dt, mass, g, drag) = (0.01, 1.3, 9.81, 0.01);
    let inertia = [0.0347563, 0.0, 0.0,
                   0.0, 0.0458929, 0.0,
                   0.0, 0.0, 0.0977];

    let mut quad = Quadrotor::new(dt, mass, g, drag, inertia)?;
    let thrust = mass * g;              // Hover thrust
    let torque = Vector3::zeros();     // No rotation

    // High-fidelity RK4 step
    quad.update_dynamics_with_controls_rk4(thrust, &torque);
    println!("Position after RK4: {:?}", quad.position);
    Ok(())
}

When to Choose Each Method

Select the integrator based on your accuracy and performance constraints:

  • Use Euler when running real-time demonstrations or when your simulation step is already very small (e.g., Δt ≤ 1 ms). The method at src/lib.rs:85 minimizes CPU overhead for maximum frame rates.
  • Use RK4 for high-fidelity simulations, trajectory optimization, or when propagating states over larger time steps. The implementation at src/lib.rs:203 reduces integration error accumulation in stiff rotational dynamics.

Summary

  • Peng implements both Euler and RK4 integrators in src/lib.rs with identical function signatures for controlled updates.
  • Configuration flags use_rk4_for_dynamics_control and use_rk4_for_dynamics_update in src/config.rs (lines 35‑38) govern the selection without code changes.
  • Dispatch logic in src/main.rs (lines 140‑148) branches between update_dynamics_with_controls_euler and update_dynamics_with_controls_rk4 based on these booleans.
  • YAML configuration in config/quad.yaml allows persistent integrator selection across simulation runs.
  • Euler offers speed for real-time loops; RK4 provides accuracy for larger steps or stiff dynamics.

Frequently Asked Questions

What is the difference between RK4 and Euler integration in Peng?

According to the makeecat/peng source code, Euler performs a single derivative evaluation per step (state + derivative * dt), while RK4 performs four evaluations (k1 through k4) and combines them with weighted averaging. This makes RK4 significantly more accurate for the same step size, particularly for the stiff rotational dynamics of quadrotors, at the cost of approximately four times the computation.

How do I enable RK4 integration in the configuration?

Set use_rk4_for_dynamics_control: true in your YAML configuration file (e.g., config/quad.yaml). This flag is defined at line 36 in src/config.rs and is read by the simulation driver at startup. When true, the driver calls update_dynamics_with_controls_rk4 instead of update_dynamics_with_controls_euler during the main simulation loop.

Can I switch between integrators at runtime?

Yes, though the standard implementation reads the configuration at startup. You can implement runtime switching by passing a boolean parameter to a wrapper function that conditionally calls either quad.update_dynamics_with_controls_rk4(thrust, &torque) or quad.update_dynamics_with_controls_euler(thrust, &torque) based on your runtime conditions, as both methods are public and available on the Quadrotor struct.

Which integrator should I use for real-time simulation?

For real-time applications where maintaining a high frame rate is critical, Euler integration is recommended, especially if your time step is small (≤ 1 ms). If you observe instability or accumulated drift in the quadrotor attitude, switch to RK4 by setting use_rk4_for_dynamics_control: true, accepting the additional computational cost for improved numerical stability.

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 →