Bota Control Toolkit Python =========================== Definitions ----------- BotaControlBlock ^^^^^^^^^^^^^^^^ ``BotaControlBlock`` is defined as a parent class from which all control blocks inherit. .. py:class:: BotaControlBlock(config) Abstract base class providing the interface for all control processing blocks. **Constructor** .. py:method:: __init__(config) Unified constructor that dispatches based on the type of ``config``. :param config: Configuration source — one of: - :py:class:`str` — path to a JSON configuration file (preferred) - :py:class:`dict` — a dictionary containing the configuration data - :py:class:`BotaControlConfig` — a configuration object populated elsewhere :raises TypeError: If ``config`` is not one of the accepted types. **Runtime Update Methods** .. note:: All update methods operate on **NumPy arrays** (``numpy.ndarray``, dtype ``float64``). Output arrays are written **in-place** — allocate them once before your control loop and reuse them every iteration to avoid unnecessary allocations. .. py:method:: update(input_signal, output_signal) Reads input data and performs processing, writing the result into ``output_signal`` in-place. :param input_signal: Input signal data. Shape ``(N,)``, dtype ``float64``. :type input_signal: numpy.ndarray :param output_signal: Pre-allocated output array. Overwritten in-place with the processed result. Shape ``(M,)``, dtype ``float64``. :type output_signal: numpy.ndarray :return: A return code indicating the status of the update operation. :rtype: BotaControlReturnCode .. py:method:: update_inplace(signal) Reads data and performs processing in place, overwriting ``signal`` with the result. :param signal: Signal data to be processed in place. Shape ``(N,)``, dtype ``float64``. :type signal: numpy.ndarray :return: A return code indicating the status of the update operation. :rtype: BotaControlReturnCode .. py:method:: update_observer(measurement, state_estimate) Observer update: reads measurement data and writes the state estimate in-place. :param measurement: Measurement data. Shape ``(N,)``, dtype ``float64``. :type measurement: numpy.ndarray :param state_estimate: Pre-allocated output array. Overwritten in-place with the state estimate. Shape ``(M,)``, dtype ``float64``. :type state_estimate: numpy.ndarray :return: A return code indicating the status of the update operation. :rtype: BotaControlReturnCode .. py:method:: update_controller(reference, state_estimate, control_action) Controller update: reads reference and state estimate, writes the control action in-place. :param reference: Reference / setpoint data. Shape ``(N,)``, dtype ``float64``. :type reference: numpy.ndarray :param state_estimate: Current state estimate from the observer. Shape ``(M,)``, dtype ``float64``. :type state_estimate: numpy.ndarray :param control_action: Pre-allocated output array. Overwritten in-place with the control action. Shape ``(P,)``, dtype ``float64``. :type control_action: numpy.ndarray :return: A return code indicating the status of the update operation. :rtype: BotaControlReturnCode **In-line Configuration Methods** Specific implementations of control blocks may include additional in-line configuration methods that allow users to modify block parameters at configuration or runtime without needing to re-instantiate the block. These methods are specific to each block and are not part of the base class interface. ---- BotaControlReturnCode ^^^^^^^^^^^^^^^^^^^^^ .. py:class:: BotaControlReturnCode Enum class representing the status of a control block update operation. .. py:attribute:: OK .. py:attribute:: Stale .. py:attribute:: Degraded .. py:attribute:: DeadlineMissed .. py:attribute:: NonFatalError .. py:attribute:: FatalError ---- Pipeline Wiring ----------------- Here's how to connect multiple blocks to create a processing pipeline: .. code-block:: python ############################################### # EXAMPLE OF A BUNDLED CONTROL PIPELINE USING THE BOTA CONTROL BLOCKS # ############################################### import time import numpy as np # Define signals as pre-allocated numpy arrays (reused every iteration) signal_1 = np.zeros(N, dtype=np.float64) signal_2 = np.zeros(M, dtype=np.float64) signal_3 = np.zeros(P, dtype=np.float64) # Path to the JSON configuration file for each block config_1 = "path/to/config_1.json" config_2 = "path/to/config_2.json" config_3 = "path/to/config_3.json" # Instantiation & configuration at initialisation time block_1 = Block1(config_1) block_2 = Block2(config_2) block_3 = Block3(config_3) # Block in-line static configuration block_1.set_parameter(value_1) block_2.set_parameter(value_2) block_3.set_parameter(value_3) # Set the desired processing rate rate_hz = 500.0 # 500 Hz dt = 1.0 / rate_hz running = True # Loop reactor function to handle return codes and control flow def loop_reactor(code: BotaControlReturnCode) -> None: global running match code: case BotaControlReturnCode.OK: pass # Normal operation, continue processing # ... other cases ... case BotaControlReturnCode.FatalError: # Handle fatal errors (e.g. log the error, shut down safely, etc.) running = False # Stop the loop on fatal error while running: start_time = time.perf_counter() # Update signal_1 with new data (e.g. from sensors, reference generators, etc.) # Write directly into the pre-allocated array to avoid re-allocation signal_1[:] = get_new_data_for_signal_1() # Process data through the pipeline loop_reactor(block_1.update(signal_1, signal_2)) # => signal_2 freshly updated by block_1 algorithm can be used HERE loop_reactor(block_2.update_inplace(signal_2)) # => signal_2 freshly updated by block_2 algorithm can be used HERE loop_reactor(block_3.update(signal_2, signal_3)) # => signal_3 freshly updated by block_3 algorithm can be used HERE ############# # Control logic here # ############# # Rate control — sleep to maintain the desired frequency elapsed = time.perf_counter() - start_time sleep_time = dt - elapsed if sleep_time > 0: time.sleep(sleep_time) ---- Distribution ------------ The **Bota Control Toolkit** definitions are distributed as a Python package. It can be installed using pip: .. code-block:: bash pip install bota-control .. important:: The ``bota-control`` package is a dependency of all the other Bota Control Toolkit blocks.