PID Control for Peristaltic Pump and Balance

  1. Introduction
  2. Imports
  3. Device Configuration
  4. PID Controller
  5. PID Control Loop
  6. Modification of PID Parameters

Introduction

This guide walks you through a sample code that demonstrates how to implement PID control to control the rate of a peristaltic pump based on the weight reading from a balance. The code performs tasks like setting up the devices, configuring PID parameters, and running control loops.

pid_controller_filling

Imports

The first step is to import all the necessary modules from the Aqueduct library.

# Import necessary modules import time from aqueduct.core.aq import Aqueduct from aqueduct.core.aq import InitParams from aqueduct.core.pid import Pid from aqueduct.core.pid import PidController from aqueduct.core.pid import Schedule from aqueduct.core.pid import ScheduleConstraints from aqueduct.core.pid import ScheduleParameters from aqueduct.devices.balance import Balance from aqueduct.devices.base.utils import DeviceTypes from aqueduct.devices.pump.peristaltic import PeristalticPump

Initialization

Here, initialization parameters are parsed from the command line and used to initialize the Aqueduct instance.

# Parse the initialization parameters from the command line params = InitParams.parse() # Initialize the Aqueduct instance with the provided parameters aq = Aqueduct(params.user_id, params.ip_address, params.port)

Device Configuration

Clear Setup

Any existing setup should be cleared before adding new devices.

# Clear the existing setup aq.clear_setup()

Add Devices

This section adds a peristaltic pump and a balance to the setup and then retrieves the newly added devices.

# Add Peristaltic Pump and Balance devices aq.add_device(DeviceTypes.PERISTALTIC_PUMP, PUMP_NAME, 1) aq.add_device(DeviceTypes.BALANCE, BAL_NAME, 1) # Retrieve the setup to confirm the added devices aq.get_setup() # Retrieve PeristalticPump and Balance instances pp: PeristalticPump = aq.devices.get(PUMP_NAME) bal: Balance = aq.devices.get(BAL_NAME)

PID Controller

PID Parameters and Schedule

Define the PID schedule parameters and constraints. Each PID controller can have multiple schedules. Constraints help to determine which schedule is used at each time step.

# Create a PID controller params = ScheduleParameters() params.kp = 10.0 params.kd = 5.0 constraints = ScheduleConstraints() sched = Schedule(params, constraints)

Creating the PID Controller

The PID controller is then created with the defined schedules.

# Get process and control values for PID process = bal.to_pid_process_value(index=0) control = pp.to_pid_control_output(index=0) pid = PidController("fill_controller", process, control, pid)

PID Control Loop

The code then runs a PID control loop to control the device for a duration.

# Perform PID control loop for a duration start = time.monotonic_ns() while time.monotonic_ns() < start + 30 * 1e9: # set the sim rate of change of the balance based on the pump rate bal.set_sim_rates_of_change( [ pp.get_ml_min()[0] / 60, ] ) time.sleep(0.01)

Modification of PID Parameters

Finally, the code shows how to modify PID parameters and rerun the control loop.

# Change PID parameters and setpoint pid.pid.schedule[0].change_parameters(kp=30, kd=10) pid.change_setpoint(40)

Here's the full code:

""" Demo code showcasing the usage of Aqueduct for PID control of a Peristaltic Pump and Balance. """ # Import necessary modules import time from aqueduct.core.aq import Aqueduct from aqueduct.core.aq import InitParams from aqueduct.core.pid import Pid from aqueduct.core.pid import PidController from aqueduct.core.pid import Schedule from aqueduct.core.pid import ScheduleConstraints from aqueduct.core.pid import ScheduleParameters from aqueduct.devices.balance import Balance from aqueduct.devices.base.utils import DeviceTypes from aqueduct.devices.pump.peristaltic import PeristalticPump # Parse the initialization parameters from the command line params = InitParams.parse() # Initialize the Aqueduct instance with the provided parameters aq = Aqueduct(params.user_id, params.ip_address, params.port) # Perform system initialization if specified aq.initialize(params.init) # Set a delay between sending commands to the pump aq.set_command_delay(0.05) # Define names for devices PUMP_NAME = "PUMP" BAL_NAME = "BALANCE" # Clear the existing setup and add Peristaltic Pump and Balance devices aq.clear_setup() aq.add_device(DeviceTypes.PERISTALTIC_PUMP, PUMP_NAME, 1) aq.add_device(DeviceTypes.BALANCE, BAL_NAME, 1) # Retrieve the setup to confirm the added devices aq.get_setup() # Retrieve PeristalticPump and Balance instances pp: PeristalticPump = aq.devices.get(PUMP_NAME) bal: Balance = aq.devices.get(BAL_NAME) # Create a start command for the Peristaltic Pump c = pp.make_start_command( mode=pp.MODE.Continuous, direction=pp.STATUS.Clockwise, rate_value=1, rate_units=pp.RATE_UNITS.MlMin, ) commands = pp.make_commands() pp.set_command(commands, 0, c) pp.start(commands=commands) # Get process and control values for PID process = bal.to_pid_process_value(index=0) control = pp.to_pid_control_output(index=0) # Create a PID controller params = ScheduleParameters() params.kp = 10.0 params.kd = 5.0 constraints = ScheduleConstraints() sched = Schedule(params, constraints) pid = Pid(20) pid.output_limits = (0, 100) pid.add_schedule(sched) pid.enabled = True pid = PidController("fill_controller", process, control, pid) # Set noise level for simulation bal.set_sim_noise( [ 0, ] ) # Wait for some time time.sleep(1) # Create PID controller on Aqueduct aq.create_pid_controller(pid) # Perform PID control loop for a duration start = time.monotonic_ns() while time.monotonic_ns() < start + 30 * 1e9: bal.set_sim_rates_of_change( [ pp.get_ml_min()[0] / 60, ] ) time.sleep(0.01) # Change PID parameters and setpoint pid.pid.schedule[0].change_parameters(kp=30, kd=10) pid.change_setpoint(40) # Perform PID control loop again for a duration start = time.monotonic_ns() while time.monotonic_ns() < start + 30 * 1e9: bal.set_sim_rates_of_change( [ pp.get_ml_min()[0] / 60, ] ) time.sleep(0.01) # Delete the created PID controller pid.delete()