Skip to content

esw.visualization

Live plotting for bench testing, so you can watch a target and an actual value converge while a control loop runs.

Note

Nothing currently uses this module. The only reference is commented out in tools/scripts/send_can.py. It is documented here because it works and is the obvious tool for tuning a control loop, but expect to be its first user.

AsyncPlotter

A context manager that runs matplotlib in a separate process, so plotting cannot stall the loop feeding it data.

AsyncPlotter(
    labels: tuple[str, ...] = ("Target", "Actual"),
    max_size: int = 200,
    loop_delay: float = 0.05,
    x_label: str = "",
    y_label: str = "",
)
Argument Meaning
labels one line per label, and the arity send_data expects
max_size samples retained; older ones scroll off
loop_delay redraw interval in seconds
x_label, y_label axis labels
Method Signature
send_data send_data(*values: float)

send_data timestamps each sample relative to when the plotter started. Passing the wrong number of values logs a warning rather than raising.

Usage

from esw.can.canbus import CANBus
from esw.can.dbc import get_dbc
from esw.visualization.async_plotter import AsyncPlotter

TARGET = 1.0

with AsyncPlotter(labels=("Target", "Actual"), y_label="position (rad)") as plot:
    with CANBus(get_dbc(dbc_name="MRoverCAN"), "can0") as bus:
        while True:
            bus.send("BMCTargetCmd", {"target": TARGET, "target_valid": 1}, dest_id=0x67)
            msg = bus.recv(timeout=0.1)
            if msg and msg[0] == "BMCMotorState":
                plot.send_data(TARGET, msg[1]["position"])

The child process ignores SIGINT, so Ctrl+C is handled by the parent and the plot window shuts down with it.