50 u_relaxed: np.ndarray,
51 state: ControllerState,
52 cfg: ControllerConfig,
53 dt: Optional[float] =
None,
54 backend: ExecutionBackend = ExecutionBackend.PWM,
55 u_prev_relaxed: Optional[np.ndarray] =
None,
56 sigma_delta_state: Optional[dict[str, float]] =
None,
59 Convert relaxed controls to actuation commands.
63 u_relaxed : np.ndarray, shape (N_CONTROL,)
64 Relaxed control vector [u_F, u_O] in [0, 1]
65 state : ControllerState
66 Controller state (for dwell timers)
67 cfg : ControllerConfig
68 Controller configuration
70 Time step [s] (defaults to cfg.dt)
71 backend : ExecutionBackend
72 Execution backend (PWM or BINARY)
73 u_prev_relaxed : np.ndarray, optional, shape (N_CONTROL,)
74 Previous relaxed control (for dwell enforcement)
75 If None, uses state.u_prev
79 cmd : ActuationCommand
80 Actuation command with duty cycles and on/off states
86 if u_relaxed.shape != (N_CONTROL,):
87 raise ValueError(f
"u_relaxed must have shape ({N_CONTROL},), got {u_relaxed.shape}")
89 u_F = float(np.clip(u_relaxed[IDX_U_F], 0.0, 1.0))
90 u_O = float(np.clip(u_relaxed[IDX_U_O], 0.0, 1.0))
93 if u_prev_relaxed
is not None:
94 u_F_prev = float(np.clip(u_prev_relaxed[IDX_U_F], 0.0, 1.0))
95 u_O_prev = float(np.clip(u_prev_relaxed[IDX_U_O], 0.0, 1.0))
99 u_F_prev = state.u_prev.get(
"P_u_fuel", 0.0)
100 u_O_prev = state.u_prev.get(
"P_u_ox", 0.0)
102 u_F_prev = float(np.clip(u_F_prev, 0.0, 1.0))
103 u_O_prev = float(np.clip(u_O_prev, 0.0, 1.0))
106 dwell_timer_F = state.dwell_timers.get(
"P_u_fuel", 0.0)
107 dwell_timer_O = state.dwell_timers.get(
"P_u_ox", 0.0)
115 u_F_quantized, u_F_prev, dwell_timer_F, cfg.dwell_time, dt
118 u_O_quantized, u_O_prev, dwell_timer_O, cfg.dwell_time, dt
122 if backend == ExecutionBackend.PWM:
125 u_F_onoff = duty_F > 0.0
126 u_O_onoff = duty_O > 0.0
127 elif backend == ExecutionBackend.BINARY:
129 sd_state_F = sigma_delta_state.get(
"F",
None)
if sigma_delta_state
else None
130 sd_state_O = sigma_delta_state.get(
"O",
None)
if sigma_delta_state
else None
134 raise ValueError(f
"Unknown backend: {backend}")
141 u_F_quantized=u_F_quantized,
142 u_O_quantized=u_O_quantized,
144 dwell_timer_F=dwell_timer_F_new,
145 dwell_timer_O=dwell_timer_O_new,
ActuationCommand compute_actuation(np.ndarray u_relaxed, ControllerState state, ControllerConfig cfg, Optional[float] dt=None, ExecutionBackend backend=ExecutionBackend.PWM, Optional[np.ndarray] u_prev_relaxed=None, Optional[dict[str, float]] sigma_delta_state=None)