Skip to main content

Introduction

PID (Proportional-Integral-Derivative) control is the foundation of movement algorithms in Mars-RS. The PID controller continuously calculates an error value and applies corrections to minimize that error.

PID Structure

PidConstants

The PidConstants struct defines the tuning parameters for a PID controller:
pub struct PidConstants {
    pub p: f32,                    // Proportional gain
    pub i: f32,                    // Integral gain
    pub d: f32,                    // Derivative gain
    pub tolerance: f32,            // Error threshold for integral reset
    pub integralThreshold: f32,    // Error threshold for integral accumulation
    pub maxIntegral: f32,          // Maximum integral value (anti-windup)
}
All fields are public (pub) to allow easy configuration and experimentation with different tuning parameters.

Pid Controller

The Pid struct maintains the controller state:
pub struct Pid {
    prevError: f32,      // Previous error for derivative calculation
    derivative: f32,     // Current derivative term
    integral: f32,       // Accumulated integral
    constants: PidConstants
}

PID Algorithm

Initialization

Create a new PID controller with specific constants:
let mut controller = util::Pid::new(util::PidConstants {
    p: 0.05,
    i: 0.0,
    d: 0.0,
    tolerance: 0.0,
    integralThreshold: 0.0,
    maxIntegral: 0.0
});

Output Calculation

The out method computes the control signal:
pub fn out(&mut self, error: f32) -> f32 {
    // Reset integral if within tolerance
    if error.abs() < self.constants.tolerance {
        self.integral = 0.0
    }
    // Accumulate integral if within threshold
    else if error.abs() < self.constants.integralThreshold {
        self.integral += error
    };
    
    // Anti-windup: cap integral
    if self.integral > self.constants.maxIntegral {
        self.integral = self.constants.maxIntegral
    };
    
    // Calculate derivative
    self.derivative = error - self.prevError;
    self.prevError = error;
    
    // PID formula
    error * self.constants.p  
        + self.integral * self.constants.i 
        + self.derivative * self.constants.d
}

PID Terms Explained

Proportional (P)

The proportional term provides an output proportional to the current error:
P_out = Kp × error
Effect: Higher P values make the system respond more aggressively to errors but can cause oscillation.
Characteristics:
  • Immediate response to current error
  • Cannot eliminate steady-state error alone
  • Dominant term in most Mars-RS controllers

Integral (I)

The integral term accounts for accumulated past errors:
I_out = Ki × Σ(error)
Effect: Eliminates steady-state error but can cause overshoot and instability if too high.
Special Features in Mars-RS:
  1. Integral Threshold: Only accumulates when error is below threshold
    else if error.abs() < self.constants.integralThreshold {
        self.integral += error
    };
    
  2. Tolerance Reset: Clears integral when error is very small
    if error.abs() < self.constants.tolerance {
        self.integral = 0.0
    }
    
  3. Anti-Windup: Caps integral to prevent excessive accumulation
    if self.integral > self.constants.maxIntegral {
        self.integral = self.constants.maxIntegral
    };
    
In many Mars-RS movement algorithms, the integral term is set to 0.0, relying primarily on proportional and derivative control.

Derivative (D)

The derivative term predicts future error based on the rate of change:
D_out = Kd × (error - previous_error)
Effect: Dampens oscillations and improves stability, but can amplify noise.
Implementation:
self.derivative = error - self.prevError;
self.prevError = error;

Tuning Guidelines

Manual Tuning Process

1

Start with P only

Set I and D to zero. Increase P until the system responds quickly but oscillates slightly.
2

Add D if needed

Increase D to dampen oscillations while maintaining responsiveness.
3

Add I if necessary

Only add I if steady-state error persists. Start very small.
4

Configure integral limits

Set integralThreshold to when you want integral to activate, and maxIntegral to prevent windup.

Example Configurations

From moveToPurePursuit implementation:
let lConstants = util::PidConstants {
    p: 0.05,              // Moderate proportional gain
    i: 0.0,               // No integral
    d: 0.0,               // No derivative
    tolerance: 0.0,
    integralThreshold: 0.0,
    maxIntegral: 0.0
};
Notice the angular (rotation) controller has a lower P value than the linear controller. This is because rotational control is typically more sensitive.

Common Tuning Issues

Problem: P gain too highSolution: Reduce P or increase D to dampen oscillations
Problem: P gain too lowSolution: Gradually increase P until response improves
Problem: P and D cannot eliminate final errorSolution: Add small I term with appropriate threshold and max values
Problem: I term too large or no anti-windupSolution: Reduce I, set appropriate maxIntegral, or increase integralThreshold

Dual PID System

Mars-RS uses separate PID controllers for different aspects of motion:
let mut lCont = util::Pid::new(lConstants);  // Linear velocity
let mut rCont = util::Pid::new(rConstants);  // Angular velocity
The outputs are combined to generate differential wheel velocities:
let linearVel = lCont.out(linearError);
let angularVel = rCont.out(rotationError);
let rVel = linearVel - angularVel;
let lVel = linearVel + angularVel;
This allows independent tuning of:
  • Linear control: How aggressively the robot moves toward targets
  • Angular control: How quickly the robot corrects heading errors
Start with conservative values (low P) and gradually increase. It’s easier to add aggression than to fix an unstable system.

Mathematical Formula

The complete PID formula implemented in Mars-RS: u(t)=Kpe(t)+Kie(t)+Kd(e(t)e(t1))u(t) = K_p \cdot e(t) + K_i \cdot \sum e(t) + K_d \cdot (e(t) - e(t-1)) Where:
  • u(t)u(t) = control output
  • e(t)e(t) = current error
  • KpK_p = proportional gain
  • KiK_i = integral gain
  • KdK_d = derivative gain

Movement Algorithms

See PID in action with movement functions

Robot Physics

Understand how PID output controls the robot

Build docs developers (and LLMs) love