Control System

Overview

The control system in daolite (Durham Adaptive Optics Latency Inspection and Timing Estimator) governs how the adaptive optics system responds to measured wavefront errors. This component is responsible for converting reconstructed wavefront shapes into appropriate commands for deformable mirrors (DMs), applying control laws to ensure stability and optimal performance.

Key Control Features

  • Multiple Control Laws: Implementation of various control strategies from simple integrators to advanced predictive controllers

  • Performance Modeling: Accurate timing estimates for control algorithms based on system complexity and hardware

  • Modal Control: Support for modal gain optimization and modal filtering

  • Predictive Control: Advanced techniques for reducing temporal error through prediction

  • Real-Time Implementation Models: Realistic modeling of controller implementation on various hardware platforms

Using Control Components

Adding control operations to your AO pipeline:

from daolite import Pipeline, PipelineComponent, ComponentType
from daolite.pipeline.control import FullFrameControl
from daolite.compute import hardware

# Create a pipeline
pipeline = Pipeline()

# Define a CPU resource for control
cpu = hardware.intel_xeon_gold_6342()

# Add controller component
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CONTROL,
    name="DM Controller",
    compute=cpu,
    function=FullFrameControl,
    params={
        "n_acts": 81*81,  # 81×81 actuator grid
        "operations": ["integration", "offset", "saturation", "dm_power"],
        "flop_scale": 1.0,
        "mem_scale": 1.0
    },
    dependencies=["Reconstructor"]
))

Control Operations

daolite provides timing models for several control operations:

Integration

Models the timing for integrator control operations:

from daolite.pipeline.control import Integrator

# Add integrator to pipeline
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CONTROL,
    name="Integrator",
    compute=cpu,
    function=Integrator,
    params={
        "n_acts": 81*81,
        "flop_scale": 1.0,
        "mem_scale": 1.0
    },
    dependencies=["Reconstructor"]
))

Full-Frame Control

For convenience, daolite provides a combined control function that performs multiple operations:

from daolite.pipeline.control import FullFrameControl

# Add full-frame control
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CONTROL,
    name="Full Control",
    compute=cpu,
    function=FullFrameControl,
    params={
        "n_acts": 81*81,
        "operations": ["integration", "offset", "saturation", "dm_power"],
        "flop_scale": 1.0,
        "mem_scale": 1.0,
        "debug": False
    },
    dependencies=["Reconstructor"]
))

Practical Examples

Example: Complete Pipeline with Control

from daolite import Pipeline, PipelineComponent, ComponentType
from daolite.pipeline.camera import PCOCamLink
from daolite.pipeline.calibration import PixelCalibration
from daolite.pipeline.centroider import Centroider
from daolite.pipeline.reconstruction import Reconstruction
from daolite.pipeline.control import FullFrameControl
from daolite.compute import hardware
import numpy as np

# Create pipeline
pipeline = Pipeline()

# Define compute resources
cpu = hardware.intel_xeon_gold_6342()
gpu = hardware.nvidia_rtx_4090()

# System parameters
n_subaps = 80 * 80
n_acts = 81 * 81
n_pixels = 1024 * 1024
n_groups = 50

# Define agendas
centroid_agenda = np.ones(n_groups, dtype=int) * (n_subaps // n_groups)
pixel_agenda = np.ones(n_groups, dtype=int) * (n_pixels // n_groups)

# Add camera
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CAMERA,
    name="Camera",
    compute=cpu,
    function=PCOCamLink,
    params={"n_pixels": n_pixels, "group": n_groups, "readout": "rolling"}
))

# Add calibration
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CALIBRATION,
    name="Pixel Calibration",
    compute=cpu,
    function=PixelCalibration,
    params={"pixel_agenda": pixel_agenda, "bit_depth": 16},
    dependencies=["Camera"]
))

# Add centroider
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CENTROIDER,
    name="Centroider",
    compute=gpu,
    function=Centroider,
    params={"centroid_agenda": centroid_agenda, "n_pix_per_subap": 16*16},
    dependencies=["Pixel Calibration"]
))

# Add reconstructor
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.RECONSTRUCTOR,
    name="Reconstructor",
    compute=gpu,
    function=Reconstruction,
    params={"centroid_agenda": centroid_agenda, "n_acts": n_acts},
    dependencies=["Centroider"]
))

# Add controller
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CONTROL,
    name="Controller",
    compute=cpu,
    function=FullFrameControl,
    params={
        "n_acts": n_acts,
        "operations": ["integration", "offset", "saturation", "dm_power"]
    },
    dependencies=["Reconstructor"]
))

# Run pipeline
results = pipeline.run()
print(f"Control time: {results['Controller'].duration:.2f} µs")

Performance Considerations

Several factors affect controller performance:

System Size

The computational requirements generally scale linearly with the number of actuators for basic control operations like integration.

Algorithm Computational Complexity

Different control operations have different computational costs:

  • Integration: O(n) - Vector addition and scaling

  • Offset: O(n) - Vector addition

  • Saturation: O(n) - Conditional operations on actuator commands

  • DM Power: O(n) - Power calculation for actuator commands

API Reference

For complete API details, see the Control API Reference section.