Spaces:
Running on Zero
Running on Zero
| """Estimation, Kalman filtering, and system identification tools.""" | |
| from __future__ import annotations | |
| from typing import Any | |
| import numpy as np | |
| from controlai_agent.registry import registry | |
| def kalman_time_update( | |
| A: list[list[float]], | |
| P_plus: list[list[float]], | |
| Q: list[list[float]], | |
| x_plus: list[float], | |
| B: list[list[float]] | None = None, | |
| u: list[float] | None = None, | |
| ) -> dict[str, Any]: | |
| A_mat = np.array(A, dtype=float) | |
| P_mat = np.array(P_plus, dtype=float) | |
| Q_mat = np.array(Q, dtype=float) | |
| x_vec = np.array(x_plus, dtype=float) | |
| x_minus = A_mat @ x_vec | |
| if B is not None and u is not None: | |
| B_mat = np.array(B, dtype=float) | |
| u_vec = np.array(u, dtype=float) | |
| x_minus += B_mat @ u_vec | |
| P_minus = A_mat @ P_mat @ A_mat.T + Q_mat | |
| return { | |
| "x_minus_predicted": x_minus.tolist(), | |
| "P_minus_predicted": P_minus.tolist(), | |
| "is_symmetric": bool(np.max(np.abs(P_minus - P_minus.T)) < 1e-10), | |
| } | |
| def kalman_measurement_update( | |
| x_minus: list[float], | |
| P_minus: list[list[float]], | |
| H: list[list[float]], | |
| R: list[list[float]], | |
| z: list[float], | |
| ) -> dict[str, Any]: | |
| x_m = np.array(x_minus, dtype=float) | |
| P_m = np.array(P_minus, dtype=float) | |
| H_mat = np.array(H, dtype=float) | |
| R_mat = np.array(R, dtype=float) | |
| z_vec = np.array(z, dtype=float) | |
| innovation = z_vec - H_mat @ x_m | |
| S = H_mat @ P_m @ H_mat.T + R_mat | |
| K = P_m @ H_mat.T @ np.linalg.inv(S) | |
| x_plus = x_m + K @ innovation | |
| # Joseph stabilized form | |
| I = np.eye(len(x_m)) | |
| P_plus = (I - K @ H_mat) @ P_m @ (I - K @ H_mat).T + K @ R_mat @ K.T | |
| return { | |
| "innovation": innovation.tolist(), | |
| "innovation_covariance_S": S.tolist(), | |
| "kalman_gain_K": K.tolist(), | |
| "x_plus_updated": x_plus.tolist(), | |
| "P_plus_updated": P_plus.tolist(), | |
| } | |
| def least_squares_arx(Phi: list[list[float]], Y: list[list[float]]) -> dict[str, Any]: | |
| Phi_mat = np.array(Phi, dtype=float) | |
| Y_mat = np.array(Y, dtype=float) | |
| Gram = Phi_mat.T @ Phi_mat | |
| theta = np.linalg.solve(Gram, Phi_mat.T @ Y_mat) | |
| residual = Y_mat - Phi_mat @ theta | |
| res_norm = float(np.linalg.norm(residual)) | |
| cond = float(np.linalg.cond(Gram)) | |
| return { | |
| "theta_estimated": theta.tolist(), | |
| "residual_2norm": res_norm, | |
| "gram_matrix_condition_number": cond, | |
| "is_identifiable": cond < 1e12, | |
| } | |