PIE: Simulating a PID Controller in Python

Klarity Insider - Oct 25 - - Dev Community

Hello friends, welcome to the first article in the Programming In Engineering, (PIE) series.

The purpose of this series is to provide clarity to those who have been looking to understand the practical application of their engineering coursework or just seeking to explore innovative ideas. The series relates engineering concepts and formulas to programming and theorized applications.

This article will focus on a concept in the Mechatronics Engineering field, PID control.
You will learn of its importance, and how to simulate a PID controller in Python using Scipy and Matplotlib to visualize its performance.

What is a PID Controller?

A PID (Proportional-Integral-Derivative) Controller is a device that adjusts the a control system's behaviour to achieve a setpoint by minimising the error between the desired and actual values.

In mechatronics, control systems are essential for managing the behaviour of dynamic systems, such as motors, temperature control units, or robotic arms. One of the most widely used control methods is the PID controller.

A PID controller uses three terms to adjust the control input:

  1. Proportional (P): Corrects the error based on its current value. If the error is large, the controller responds aggressively.

  2. Integral (I): Corrects accumulated past errors. It ensures the error is eliminated over time.

  3. Derivative (D): Reacts to the rate of change of the error. It helps reduce overshoot and dampens oscillations.

These three terms work together to adjust the control input, bringing the system closer to the setpoint with minimal error and oscillation.

The Formula of a PID Controller

The PID formula can be written as:

Formula of a PID controller

Where:

  • 𝑢(𝑡) is the PID control variable,
  • 𝐾𝑝 is the proportional constant,
  • 𝐾𝑖 is the integral constant,
  • 𝐾𝑑 is the derivative constant,
  • 𝑒(𝑡) is the error at time 𝑡 (i.e., the difference between the desired setpoint and the current output).

Why Simulate a PID Controller?

Simulating a PID controller allows engineers to:

  • Understand the behavior of a system before implementing a physical controller.
  • Tune PID parameters effectively.
  • Visualize the system response and optimize control strategies.

Hands-on:

Firstly, let's summarize your desired algorithm. You will simulate a simple first-order plant model that responds to the control input (e.g., temperature adjusting in an oven). The system will be defined by the function, plant_model, which represents the temperature dynamics.
You will also define a function, pid_controller, to implement the PID control formula. It will calculate the error, integral, and derivative terms and then return the control input.
Eventually, you will define a simulation loop, simulate_pid, to simulate the system's response over time, adjusting the control input at each time step based on the PID controller.
Finally, you will plot the results, being the temperature response over time, and visualize how the PID controller drives the system to reach the desired setpoint (50°C).

Installing Required Libraries

We will use the following Python libraries for our simulation:

  • numpy: This is used for handling numerical operations and arrays. This script helps us define the time steps and perform mathematical calculations.
  • matplotlib.pyplot: This library plots graphs to visualize the system's behavior over time.
  • scipy.integrate.odeint: This function helps solve ordinary differential equations (ODEs). We use it to simulate the plant’s dynamics, i.e., how the system responds to the control input.

To install these, run:
pip install numpy scipy matplotlib

Simulating a PID Controller for Temperature Control

In this example, we’ll simulate a PID controller that controls the temperature of a system (such as an oven) and ensures it reaches the desired temperature.

  • Import the necessary libraries
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
Enter fullscreen mode Exit fullscreen mode
  • Define the PID controller parameters
Kp = 1.2   
Ki = 1.0   
Kd = 0.01 
Enter fullscreen mode Exit fullscreen mode
  • Kp (Proportional gain): Controls how aggressively the system responds to the error.
  • Ki (Integral gain): Accumulates past errors to eliminate any steady-state error.
  • Kd (Derivative gain): Reacts to the rate of change of the error, helping to smooth out oscillations and overshoots.

These PID parameters are the tuning parameters for the PID controller

  • Define a Setpoint and time array
setpoint = 50.0  # Desired temperature (°C)
time = np.linspace(0, 50, 500)  # 50 seconds, 500 points
Enter fullscreen mode Exit fullscreen mode

The setpoint is the target value we want the system to reach (50°C in this case, simulating a temperature control scenario) while the time is an array containing 500 equally spaced time points over a range of 50 seconds. It defines the simulation period and time resolution.

  • Define the plant model
def plant_model(y, t, u):
    tau = 5.0  # Time constant of the system
    dydt = (-y + u) / tau
    return dydt
Enter fullscreen mode Exit fullscreen mode
  • y: The current system output (temperature).
  • t: Time (needed by odeint for numerical integration).
  • u: The control input from the PID controller.
  • tau: The system’s time constant, which determines how quickly the system responds to changes in control input.
  • dydt: This is the rate of change of the system’s output. The system tends to reach the control input with a lag based on the time constant.

The plant_model function simulates simulates the dynamics of the system being controlled (like an oven heating up).

  • Define the PID Controller Logic
def pid_controller(setpoint, y_current, integral_error, previous_error, dt):
    error = setpoint - y_current
    integral_error += error * dt
    derivative_error = (error - previous_error) / dt

    u = Kp * error + Ki * integral_error + Kd * derivative_error

    return u, integral_error, error
Enter fullscreen mode Exit fullscreen mode

To better understand this function, you need to understand how the PID formula works.

  • error = setpoint - y_current: This calculates the difference between the desired temperature and the current temperature (proportional term).
  • integral_error += error * dt: This accumulates the error over time (integral term).
  • derivative_error = (error - previous_error) / dt: This calculates how fast the error is changing (derivative term).
  • u = Kp * error + Ki * integral_error + Kd * derivative_error: This is the final control input calculated using the PID formula, where the control input depends on the proportional, integral, and derivative terms.

At the end of it, the function returns the control input, u, the updated integral, integral_error and the current error to be used in the next step, error.

  • Define a function to simulate the PID controller
def simulate_pid():
    y_current = 0.0  # Initial temperature
    integral_error = 0.0
    previous_error = 0.0
    dt = time[1] - time[0]  # Time step

    y_values = []  # Store temperature values for plotting

    for t in time:
        u, integral_error, previous_error = pid_controller(
            setpoint, y_current, integral_error, previous_error, dt)

        # Simulate the plant for the next time step
        y_current = odeint(plant_model, y_current, [t, t + dt], args=(u,))[-1]

        y_values.append(y_current)

    return y_values
Enter fullscreen mode Exit fullscreen mode

The loop runs through each time step, updating the system’s temperature based on the PID control. At each time step, the PID controller calculates the new control input,(u), using the current temperature and the error.
The system (plant) dynamics are simulated using odeint, which solves the differential equation to find the next temperature value (y_current).
The new temperature is stored in y_values for later plotting.
Return: The function returns y_values, which is the list of temperatures over time.

  • Run the Simulation and Plot the Results
y_sim = simulate_pid()

# Plot the results
plt.figure(figsize=(10, 5))
plt.plot(time, y_sim, label='Temperature (°C)')
plt.axhline(y=setpoint, color='r', linestyle='--', label='Setpoint (50°C)')
plt.title('PID Controller Simulation for Temperature Control')
plt.xlabel('Time (seconds)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.show()
Enter fullscreen mode Exit fullscreen mode

As a final output, you’ll observe an initial rise in temperature, a potential overshoot beyond 50°C and a gradual stabilization at the setpoint as the controller reduces the error.
The graph shows how the temperature (y-axis) changes over time (x-axis) as the PID controller works to bring the system to the desired setpoint (50°C).

This visualization helps in understanding the PID controller's performance and how it tunes the system to meet the setpoint. You can adjust the PID gains (Kp, Ki, Kd) to observe their effects on the system's response.

Conclusion

This article and Python script provide a hands-on introduction to simulating PID controllers in Python. PID controllers are a vital part of mechatronics systems, and simulating them allows for better tuning and understanding of system dynamics before real-world implementation. By modifying and experimenting with the code, you can expand your knowledge of control systems and how PID controllers work.

.