MojoScale Studio Docs
API Reference

control

Feedback control and PID controllers.

Studio Docs Runtime

import control

The control module provides lightweight closed-loop helpers for motors, temperature, position, speed, heading, distance, and similar systems. A control loop compares a target value with a measured value and produces an output that can drive a motor, heater, servo, PWM output, or another actuator. PID controllers use monotonic device timing internally, so the integral and derivative terms track elapsed time between update calls. Run controllers at a reasonably consistent interval with system.schedule.

Quick example

import control
    import system

    pid = control.pid(
        kp=1.0,
        ki=0.1,
        kd=0.05,
        output_min=0,
        output_max=100,
    )

    print(pid.update(target=50, measured=42))

    pid.set_target(120)

    def control_loop():
        measured = 86
        power = pid.update(measured=measured)
        print(power)

    system.schedule("pid", 20, 0, -1, control_loop)

    # Small errors can be ignored with a deadband.
    error = control.deadband(target - measured, 0.5)

    # Outputs can be constrained before sending them to hardware.
    power = control.clamp(power, 0, 100)

    # Raw inputs can be mapped into actuator ranges.
    speed = control.map(2048, 0, 4095, -100, 100)

Notes:
    PID output assumes error = target - measured, and that increasing output
    tends to increase the measured value. If the physical system moves in the
    opposite direction, invert the output before sending it to the actuator.

    Configure output limits for real actuators. Limits help keep motor power,
    heater power, steering, or other outputs inside the range the device can
    actually use.

    Reset a PID controller when restarting a stopped control operation so old
    integral or derivative state does not affect the next run.

importcontrol

Feedback control and PID controllers.

The control module provides lightweight closed-loop helpers for motors, temperature, position, speed, heading, distance, and similar systems. A control loop compares a target value with a measured value and produces an output that can drive a motor, heater, servo, PWM output, or another actuator. PID controllers use monotonic device timing internally, so the integral and derivative terms track elapsed time between update calls. Run controllers at a reasonably consistent interval with system.schedule.

API 14 available

control.pid(kp: float, ki: any = None, kd: any = None, output_min: any = None, output_max: any = None) -> pid_controller

Create a PID controller.

A PID controller converts a target value and a measured value into an output. It can be used as proportional-only, PI, or full PID control by choosing which gains are nonzero.

Parameters
Name Type Pass as Required Description
kp float positional or keyword Yes Required proportional gain. Controls how strongly output reacts to the current error.
ki any positional or keyword No Optional integral gain. Defaults to 0. Accumulates persistent error over time.
kd any positional or keyword No Optional derivative gain. Defaults to 0. Reacts to the rate of error change.
output_min any positional or keyword No Optional minimum controller output. Use None for no lower limit.
output_max any positional or keyword No Optional maximum controller output. Use None for no upper limit.
Returns

pid_controller pid_controller object.

control.pid().update(target=None, measured=None) -> float

Calculate the next controller output. Pass target and measured, or call set_target(value) first and then pass only measured. The controller uses elapsed monotonic time internally for integral and derivative behavior.

Parameters
Name Type Pass as Required Description
target any positional or keyword No Optional value. Defaults to None.
measured any positional or keyword No Optional value. Defaults to None.

control.pid().reset() -> bool

Clear accumulated integral error, previous error, previous output, and previous update time.

control.pid().set_target(value) -> bool

Store a persistent setpoint for measured-only update calls.

Parameters
Name Type Pass as Required Description
value any positional or keyword Yes Required value.

control.pid().target() -> float

Return the persistent target, or None if no target has been set.

control.pid().set_gains(kp, ki, kd) -> bool

Update proportional, integral, and derivative gains without recreating the controller.

Parameters
Name Type Pass as Required Description
kp any positional or keyword Yes Required value.
ki any positional or keyword Yes Required value.
kd any positional or keyword Yes Required value.

control.pid().set_limits(output_min=None, output_max=None) -> bool

Change output limits. Pass None for either side to remove that limit.

Parameters
Name Type Pass as Required Description
output_min any positional or keyword No Optional value. Defaults to None.
output_max any positional or keyword No Optional value. Defaults to None.

control.pid().error() -> float

Return the most recently calculated target-minus-measured error.

control.pid().output() -> float

Return the most recently calculated controller output.

control.pid().integral() -> float

Return the accumulated integral term for debugging and tuning.

control.pid().Examples

import control pid = control.pid( kp=1.0, ki=0.1, kd=0.05, output_min=0, output_max=100, ) power = pid.update(target=50, measured=42) print(power) pid.set_target(120) print(pid.update(measured=86)) pid.set_gains(kp=1.2, ki=0.15, kd=0.03) pid.set_limits(output_min=-100, output_max=100)

control.deadband(value: any, threshold: float) -> float

Return zero when a value is close enough to zero.

Parameters
Name Type Pass as Required Description
value any positional or keyword Yes Input value, usually an error term.
threshold float positional or keyword Yes Deadband around zero. Values between -threshold and threshold return 0.
Returns

float float Filtered value. Examples: error = target - measured error = control.deadband(error, 0.5)

control.clamp(value: any, minimum: any, maximum: any) -> float

Constrain a value to a numeric range.

Parameters
Name Type Pass as Required Description
value any positional or keyword Yes Input value.
minimum any positional or keyword Yes Lower bound.
maximum any positional or keyword Yes Upper bound.
Returns

float float Clamped value. Examples: power = control.clamp(power, 0, 100)

control.map(value: any, input_min: any, input_max: any, output_min: any, output_max: any) -> dict

Map a value from one numeric range to another.

Parameters
Name Type Pass as Required Description
value any positional or keyword Yes Input value.
input_min any positional or keyword Yes Minimum of the input range.
input_max any positional or keyword Yes Maximum of the input range.
output_min any positional or keyword Yes Minimum of the output range.
output_max any positional or keyword Yes Maximum of the output range.
Returns

dict float Mapped value. Examples: speed = control.map(joystick, 0, 4095, -100, 100)