1 unstable release

0.1.4 Jan 4, 2023
0.1.3 Aug 12, 2022
0.1.2 Aug 12, 2022
0.1.1 Aug 12, 2022
0.1.0 Aug 12, 2022

#823 in Embedded development

Download history 1/week @ 2024-07-20 52/week @ 2024-07-27 79/week @ 2024-08-03 16/week @ 2024-08-10 61/week @ 2024-08-17 91/week @ 2024-08-24 9/week @ 2024-08-31 13/week @ 2024-09-07 29/week @ 2024-09-14 74/week @ 2024-09-21 61/week @ 2024-09-28 40/week @ 2024-10-05 106/week @ 2024-10-12 21/week @ 2024-10-19 33/week @ 2024-10-26 25/week @ 2024-11-02

198 downloads per month

MIT/Apache

13KB
226 lines

Overview

A proportional-integral-derivative (PID) controller.

Inspired by pid-rs

With cleaner API and assumptions (constant time delta and symmetrical limits) dropped.

Features

  • Discrete time PID controller
  • Defined for generic float types
  • Attempts to conform to rust API Guidelines
  • #![no_std]
  • Limits for each of p, i, d terms and output
  • Calculates derivative term using measurement over error (no derivative kick on new setpoint)
  • Clamps time interval to between Float::epsilon() and Float::infinity()

Installation

cargo add pid-ctrl

Examples

use pid_ctrl;
use num_traits;

fn main() {
    let mut pid = pid_ctrl::PidCtrl::new_with_pid(3.0, 2.0, 1.0);

    let setpoint = 5.0;
    let prev_measurement = 0.0;
    // calling init optional. Setpoint and prev_measurement set to 0.0 by default.
    // Recommended to avoid derivative kick on startup
    pid.init(setpoint, prev_measurement);

    let measurement = 0.0;
    let time_delta = 1.0;
    assert_eq!(
        pid.step(pid_ctrl::PidIn::new(measurement, time_delta)), 
        pid_ctrl::PidOut::new(15.0, 10.0, 0.0, 25.0)
    );

    // changing pid constants
    pid.kp.set_scale(4.0);
    assert_eq!(
        pid.step(pid_ctrl::PidIn::new(measurement, time_delta)), 
        pid_ctrl::PidOut::new(20.0, 20.0, 0.0, 40.0)
    );

    // setting symmetrical limits around zero
    pid.kp.limits.set_limit(10.0);
    assert_eq!(
        pid.step(pid_ctrl::PidIn::new(measurement, time_delta)), 
        pid_ctrl::PidOut::new(10.0, 30.0, 0.0, 40.0)
    );

    let time_delta = 0.5;
    assert_eq!(
        pid.step(pid_ctrl::PidIn::new(measurement, time_delta)), 
        pid_ctrl::PidOut::new(10.0, 35.0, 0.0, 45.0)
    );

    // setting upper limits returns error if new value conflicts with lower limit
    pid.ki.limits.try_set_upper(28.0).unwrap();  
    assert_eq!(
        pid.step(pid_ctrl::PidIn::new(measurement, time_delta)), 
        pid_ctrl::PidOut::new(10.0, 28.0, 0.0, 38.0)
    );

    // time_delta gets clamped to Float::epsilon() - Float::infinity()
    let measurement = 1.0;
    let time_delta = -7.0;
    pid.kd.set_scale(num_traits::Float::epsilon());
    assert_eq!(pid.step(
        pid_ctrl::PidIn::new(measurement, time_delta)), 
        pid_ctrl::PidOut::new(10.0, 28.0, -1.0, 37.0)
    );

    // configure setpoint directly
    pid.setpoint = 1.0;
    assert_eq!(pid.step(
        pid_ctrl::PidIn::new(measurement, time_delta)), 
        pid_ctrl::PidOut::new(0.0, 28.0, 0.0, 28.0)
    );
}

Contribute

Feel free to raise issues.

Would like to make #![no_std] optional so types can impl Display trait.

Dependencies

~93–315KB