ajuste no imu
This commit is contained in:
parent
1297a898d5
commit
afc91db881
|
|
@ -30,7 +30,7 @@ def main():
|
||||||
T_Code.Sen: ModuloSensoriamento(),
|
T_Code.Sen: ModuloSensoriamento(),
|
||||||
T_Code.Atu: ModuloAtuador(),
|
T_Code.Atu: ModuloAtuador(),
|
||||||
T_Code.Lra: ModuloLoRa(),
|
T_Code.Lra: ModuloLoRa(),
|
||||||
T_Code.Imu: CameraIMU(),
|
T_Code.Imu: CameraIMU(modulos_imu=[T_Code.Snr]),
|
||||||
T_Code.Npc: ModuloPC(),
|
T_Code.Npc: ModuloPC(),
|
||||||
T_Code.Lvx: ModuloLivox(),
|
T_Code.Lvx: ModuloLivox(),
|
||||||
T_Code.Ipb: ModuloIPBribge(),
|
T_Code.Ipb: ModuloIPBribge(),
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
pitch_rate_critical_dps=75.0,
|
pitch_rate_critical_dps=75.0,
|
||||||
filtro_alpha=0.35,
|
filtro_alpha=0.35,
|
||||||
historico_len=100,
|
historico_len=100,
|
||||||
|
modulos_imu=None,
|
||||||
):
|
):
|
||||||
self.freq = float(freq)
|
self.freq = float(freq)
|
||||||
self.publish_hz = float(publish_hz)
|
self.publish_hz = float(publish_hz)
|
||||||
|
|
@ -80,6 +81,17 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
|
|
||||||
self.filtro_alpha = float(filtro_alpha)
|
self.filtro_alpha = float(filtro_alpha)
|
||||||
|
|
||||||
|
# Quais módulos podem participar da IMU final.
|
||||||
|
# None mantém o comportamento histórico: frontal + traseira.
|
||||||
|
# Para usar somente a frontal:
|
||||||
|
# CameraIMU(modulos_imu=[T_Code.Snr])
|
||||||
|
self.modulos_imu = self._preparar_modulos_imu(modulos_imu)
|
||||||
|
self._modulos_imu_configurados = {
|
||||||
|
self._normalizar_identificador(modulo)
|
||||||
|
for modulo in self.modulos_imu
|
||||||
|
}
|
||||||
|
self.redundancia_configurada = len(self._modulos_imu_configurados) >= 2
|
||||||
|
|
||||||
self.ativo = True
|
self.ativo = True
|
||||||
self.last_data = None
|
self.last_data = None
|
||||||
self.ultima_saude = None
|
self.ultima_saude = None
|
||||||
|
|
@ -110,7 +122,11 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
)
|
)
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
self.mostrar_log("Task iniciada")
|
modulos_log = ", ".join(self._nome_codigo(m) for m in self.modulos_imu)
|
||||||
|
self.mostrar_log(
|
||||||
|
f"Task iniciada | módulos IMU considerados=[{modulos_log}] "
|
||||||
|
f"| redundância_exigida={self.redundancia_configurada}"
|
||||||
|
)
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Ciclo de vida
|
# Ciclo de vida
|
||||||
|
|
@ -163,26 +179,124 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
def _ema(self, anterior, atual, alpha):
|
def _ema(self, anterior, atual, alpha):
|
||||||
return (1.0 - alpha) * float(anterior) + alpha * float(atual)
|
return (1.0 - alpha) * float(anterior) + alpha * float(atual)
|
||||||
|
|
||||||
def _modelo_peso_base(self, cam_data, mx_id):
|
def _normalizar_identificador(self, valor):
|
||||||
|
if valor is None:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Para Enum/IntEnum, o nome é mais estável que o value.
|
||||||
|
nome = getattr(valor, "name", None)
|
||||||
|
if nome:
|
||||||
|
valor = nome
|
||||||
|
else:
|
||||||
|
valor_enum = getattr(valor, "value", None)
|
||||||
|
if valor_enum is not None:
|
||||||
|
valor = valor_enum
|
||||||
|
|
||||||
|
texto = str(valor).strip().lower()
|
||||||
|
if "." in texto:
|
||||||
|
texto = texto.rsplit(".", 1)[-1]
|
||||||
|
|
||||||
|
return "".join(ch for ch in texto if ch.isalnum())
|
||||||
|
|
||||||
|
def _aliases_codigo(self, codigo):
|
||||||
|
aliases = set()
|
||||||
|
for valor in (codigo, getattr(codigo, "name", None), getattr(codigo, "value", None)):
|
||||||
|
normalizado = self._normalizar_identificador(valor)
|
||||||
|
if normalizado:
|
||||||
|
aliases.add(normalizado)
|
||||||
|
return aliases
|
||||||
|
|
||||||
|
def _nome_codigo(self, codigo):
|
||||||
|
nome = getattr(codigo, "name", None)
|
||||||
|
if nome:
|
||||||
|
return str(nome)
|
||||||
|
|
||||||
|
valor = getattr(codigo, "value", codigo)
|
||||||
|
return str(valor)
|
||||||
|
|
||||||
|
def _preparar_modulos_imu(self, modulos_imu):
|
||||||
|
if modulos_imu is None:
|
||||||
|
modulos_imu = [T_Code.Snr, T_Code.Cam]
|
||||||
|
|
||||||
|
unicos = []
|
||||||
|
vistos = set()
|
||||||
|
|
||||||
|
for modulo in list(modulos_imu):
|
||||||
|
chave = self._normalizar_identificador(modulo)
|
||||||
|
if not chave or chave in vistos:
|
||||||
|
continue
|
||||||
|
|
||||||
|
vistos.add(chave)
|
||||||
|
unicos.append(modulo)
|
||||||
|
|
||||||
|
if not unicos:
|
||||||
|
raise ValueError(
|
||||||
|
"modulos_imu deve conter pelo menos um módulo, por exemplo [T_Code.Snr]"
|
||||||
|
)
|
||||||
|
|
||||||
|
return tuple(unicos)
|
||||||
|
|
||||||
|
def _identificar_modulo_camera(self, cam_data):
|
||||||
|
if not isinstance(cam_data, dict):
|
||||||
|
cam_data = {}
|
||||||
|
|
||||||
|
candidatos = set()
|
||||||
|
for campo in ("dispositivo", "t_code", "codigo", "tipo_modulo", "modulo"):
|
||||||
|
valor = cam_data.get(campo)
|
||||||
|
candidatos.update(self._aliases_codigo(valor))
|
||||||
|
|
||||||
|
conhecidos = (T_Code.Snr, T_Code.Cam)
|
||||||
|
for codigo in conhecidos:
|
||||||
|
if candidatos.intersection(self._aliases_codigo(codigo)):
|
||||||
|
return self._normalizar_identificador(codigo)
|
||||||
|
|
||||||
|
# Fallback por modelo para instalações antigas que não publicam dispositivo.
|
||||||
|
modelo = str(cam_data.get("modelo", "") or "").lower()
|
||||||
|
|
||||||
|
if "oak-d" in modelo or "oak d" in modelo:
|
||||||
|
return self._normalizar_identificador(T_Code.Snr)
|
||||||
|
|
||||||
|
if (
|
||||||
|
"ffc" in modelo
|
||||||
|
or "multiespectral" in modelo
|
||||||
|
or "multispectral" in modelo
|
||||||
|
):
|
||||||
|
return self._normalizar_identificador(T_Code.Cam)
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def _modulo_imu_habilitado(self, codigo_normalizado):
|
||||||
|
return codigo_normalizado in self._modulos_imu_configurados
|
||||||
|
|
||||||
|
def _modelo_peso_base(self, cam_data, mx_id, codigo_normalizado=""):
|
||||||
modelo = str(cam_data.get("modelo", "") or "").lower()
|
modelo = str(cam_data.get("modelo", "") or "").lower()
|
||||||
dispositivo = str(cam_data.get("dispositivo", "") or "").lower()
|
dispositivo = str(cam_data.get("dispositivo", "") or "").lower()
|
||||||
|
|
||||||
# OAK-D Lite presa no chassi/cabine deve ser a principal.
|
codigo_snr = self._normalizar_identificador(T_Code.Snr)
|
||||||
if "oak-d" in modelo or "snr" in dispositivo:
|
codigo_cam = self._normalizar_identificador(T_Code.Cam)
|
||||||
|
|
||||||
|
# OAK-D Lite presa ao chassi/cabine é a referência principal.
|
||||||
|
if codigo_normalizado == codigo_snr or "oak-d" in modelo or "snr" in dispositivo:
|
||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
# Multiespectral na haste entra como redundância.
|
# A traseira pode participar quando estiver habilitada, mas com menor influência.
|
||||||
if "ffc" in modelo or "multiespectral" in modelo or "multispectral" in modelo:
|
# Para removê-la completamente, retire T_Code.Cam de modulos_imu.
|
||||||
return 0.0
|
if (
|
||||||
|
codigo_normalizado == codigo_cam
|
||||||
|
or "ffc" in modelo
|
||||||
|
or "multiespectral" in modelo
|
||||||
|
or "multispectral" in modelo
|
||||||
|
):
|
||||||
|
return 0.35
|
||||||
|
|
||||||
# Fallback seguro.
|
# Fallback seguro para uma fonte habilitada ainda não catalogada.
|
||||||
return 0.50
|
return 0.50
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Leitura das IMUs das câmeras
|
# Leitura das IMUs das câmeras
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
def _buscar_camera_e_imu_individual(self, mx_id):
|
def _buscar_camera_e_imu_individual(self, mx_id, cam_detalhada=None):
|
||||||
"""
|
"""
|
||||||
Busca os dados detalhados da câmera e a IMU individual dela.
|
Busca os dados detalhados da câmera e a IMU individual dela.
|
||||||
|
|
||||||
|
|
@ -200,6 +314,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
dentro da chave detalhada da câmera.
|
dentro da chave detalhada da câmera.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if cam_detalhada is None:
|
||||||
cam_detalhada = ContextoGlobalRedis.get_camera(mx_id) or {}
|
cam_detalhada = ContextoGlobalRedis.get_camera(mx_id) or {}
|
||||||
|
|
||||||
imu_ctx = None
|
imu_ctx = None
|
||||||
|
|
@ -238,7 +353,14 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
|
|
||||||
return cam_detalhada, imu_dados
|
return cam_detalhada, imu_dados
|
||||||
|
|
||||||
def _extrair_imu_da_camera(self, mx_id, cam_data, imu, agora):
|
def _extrair_imu_da_camera(
|
||||||
|
self,
|
||||||
|
mx_id,
|
||||||
|
cam_data,
|
||||||
|
imu,
|
||||||
|
agora,
|
||||||
|
codigo_normalizado,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Extrai uma fonte de IMU individual.
|
Extrai uma fonte de IMU individual.
|
||||||
|
|
||||||
|
|
@ -295,7 +417,11 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
except Exception:
|
except Exception:
|
||||||
imu_saude = None
|
imu_saude = None
|
||||||
|
|
||||||
peso_base = self._modelo_peso_base(cam_data, mx_id)
|
peso_base = self._modelo_peso_base(
|
||||||
|
cam_data,
|
||||||
|
mx_id,
|
||||||
|
codigo_normalizado=codigo_normalizado,
|
||||||
|
)
|
||||||
|
|
||||||
motivos = []
|
motivos = []
|
||||||
confiavel = True
|
confiavel = True
|
||||||
|
|
@ -345,6 +471,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
"mx_id": str(mx_id),
|
"mx_id": str(mx_id),
|
||||||
"modelo": cam_data.get("modelo"),
|
"modelo": cam_data.get("modelo"),
|
||||||
"dispositivo": cam_data.get("dispositivo"),
|
"dispositivo": cam_data.get("dispositivo"),
|
||||||
|
"t_code": codigo_normalizado,
|
||||||
|
|
||||||
"valido": valido,
|
"valido": valido,
|
||||||
"calibrado": calibrado,
|
"calibrado": calibrado,
|
||||||
|
|
@ -375,33 +502,50 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
|
|
||||||
def _ler_fontes_imu(self):
|
def _ler_fontes_imu(self):
|
||||||
"""
|
"""
|
||||||
Lê as IMUs individuais das câmeras.
|
Lê apenas as IMUs configuradas em ``modulos_imu``.
|
||||||
|
|
||||||
get_cameras():
|
Câmeras fora da seleção são classificadas como desabilitadas e não
|
||||||
usado apenas como inventário/lista de câmeras conhecidas.
|
participam da fusão, da divergência, da saúde ou da exigência de
|
||||||
|
redundância.
|
||||||
get_camera(mx_id) + CamImuKey(mx_id):
|
|
||||||
usados para buscar os dados reais de cada câmera.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
agora = time.time()
|
agora = time.time()
|
||||||
|
|
||||||
cameras_mapeadas = ContextoGlobalRedis.get_cameras() or {}
|
cameras_mapeadas = ContextoGlobalRedis.get_cameras() or {}
|
||||||
|
|
||||||
fontes = []
|
fontes = []
|
||||||
ignoradas = []
|
ignoradas = []
|
||||||
|
desabilitadas = []
|
||||||
|
modulos_detectados = set()
|
||||||
|
|
||||||
if not isinstance(cameras_mapeadas, dict):
|
if not isinstance(cameras_mapeadas, dict):
|
||||||
return [], []
|
cameras_mapeadas = {}
|
||||||
|
|
||||||
for mx_id in list(cameras_mapeadas.keys()):
|
for mx_id in list(cameras_mapeadas.keys()):
|
||||||
cam_detalhada, imu_dados = self._buscar_camera_e_imu_individual(mx_id)
|
cam_detalhada = ContextoGlobalRedis.get_camera(mx_id) or {}
|
||||||
|
codigo_normalizado = self._identificar_modulo_camera(cam_detalhada)
|
||||||
|
|
||||||
|
if not self._modulo_imu_habilitado(codigo_normalizado):
|
||||||
|
desabilitadas.append({
|
||||||
|
"mx_id": str(mx_id),
|
||||||
|
"modelo": cam_detalhada.get("modelo") if isinstance(cam_detalhada, dict) else None,
|
||||||
|
"dispositivo": cam_detalhada.get("dispositivo") if isinstance(cam_detalhada, dict) else None,
|
||||||
|
"t_code": codigo_normalizado or None,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
|
modulos_detectados.add(codigo_normalizado)
|
||||||
|
|
||||||
|
cam_detalhada, imu_dados = self._buscar_camera_e_imu_individual(
|
||||||
|
mx_id,
|
||||||
|
cam_detalhada=cam_detalhada,
|
||||||
|
)
|
||||||
|
|
||||||
fonte = self._extrair_imu_da_camera(
|
fonte = self._extrair_imu_da_camera(
|
||||||
mx_id=mx_id,
|
mx_id=mx_id,
|
||||||
cam_data=cam_detalhada,
|
cam_data=cam_detalhada,
|
||||||
imu=imu_dados,
|
imu=imu_dados,
|
||||||
agora=agora,
|
agora=agora,
|
||||||
|
codigo_normalizado=codigo_normalizado,
|
||||||
)
|
)
|
||||||
|
|
||||||
if fonte is None:
|
if fonte is None:
|
||||||
|
|
@ -409,6 +553,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
"mx_id": str(mx_id),
|
"mx_id": str(mx_id),
|
||||||
"modelo": cam_detalhada.get("modelo") if isinstance(cam_detalhada, dict) else None,
|
"modelo": cam_detalhada.get("modelo") if isinstance(cam_detalhada, dict) else None,
|
||||||
"dispositivo": cam_detalhada.get("dispositivo") if isinstance(cam_detalhada, dict) else None,
|
"dispositivo": cam_detalhada.get("dispositivo") if isinstance(cam_detalhada, dict) else None,
|
||||||
|
"t_code": codigo_normalizado,
|
||||||
"confiavel": False,
|
"confiavel": False,
|
||||||
"idade_s": 999.0,
|
"idade_s": 999.0,
|
||||||
"confidence": 0.0,
|
"confidence": 0.0,
|
||||||
|
|
@ -427,7 +572,29 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
else:
|
else:
|
||||||
ignoradas.append(fonte)
|
ignoradas.append(fonte)
|
||||||
|
|
||||||
return fontes, ignoradas
|
# Também representa módulos configurados que nem apareceram no inventário.
|
||||||
|
for codigo in sorted(self._modulos_imu_configurados):
|
||||||
|
if codigo in modulos_detectados:
|
||||||
|
continue
|
||||||
|
|
||||||
|
ignoradas.append({
|
||||||
|
"mx_id": None,
|
||||||
|
"modelo": None,
|
||||||
|
"dispositivo": None,
|
||||||
|
"t_code": codigo,
|
||||||
|
"confiavel": False,
|
||||||
|
"idade_s": 999.0,
|
||||||
|
"confidence": 0.0,
|
||||||
|
"motivos": ["modulo_imu_configurado_nao_detectado"],
|
||||||
|
"roll": 0.0,
|
||||||
|
"pitch": 0.0,
|
||||||
|
"yaw": 0.0,
|
||||||
|
"roll_rate_dps": 0.0,
|
||||||
|
"pitch_rate_dps": 0.0,
|
||||||
|
"yaw_rate_dps": 0.0,
|
||||||
|
})
|
||||||
|
|
||||||
|
return fontes, ignoradas, desabilitadas
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Fusão
|
# Fusão
|
||||||
|
|
@ -575,7 +742,15 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
|
|
||||||
return nivel, motivos
|
return nivel, motivos
|
||||||
|
|
||||||
def _avaliar_risco(self, roll, pitch, roll_rate, pitch_rate, sensores_agree, n_fontes):
|
def _avaliar_risco(
|
||||||
|
self,
|
||||||
|
roll,
|
||||||
|
pitch,
|
||||||
|
roll_rate,
|
||||||
|
pitch_rate,
|
||||||
|
sensores_agree,
|
||||||
|
redundancia_disponivel,
|
||||||
|
):
|
||||||
abs_roll = abs(float(roll))
|
abs_roll = abs(float(roll))
|
||||||
abs_pitch = abs(float(pitch))
|
abs_pitch = abs(float(pitch))
|
||||||
abs_roll_rate = abs(float(roll_rate))
|
abs_roll_rate = abs(float(roll_rate))
|
||||||
|
|
@ -623,7 +798,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
nivel = max(nivel, 2)
|
nivel = max(nivel, 2)
|
||||||
motivos.append("imus_divergentes")
|
motivos.append("imus_divergentes")
|
||||||
|
|
||||||
if n_fontes <= 1:
|
if self.redundancia_configurada and not redundancia_disponivel:
|
||||||
motivos.append("sem_redundancia")
|
motivos.append("sem_redundancia")
|
||||||
|
|
||||||
risk_score = 0.0
|
risk_score = 0.0
|
||||||
|
|
@ -686,7 +861,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
**data
|
**data
|
||||||
)
|
)
|
||||||
|
|
||||||
def _montar_saida_sem_imu(self, fontes_ignoradas, agora):
|
def _montar_saida_sem_imu(self, fontes_ignoradas, fontes_desabilitadas, agora):
|
||||||
return {
|
return {
|
||||||
"valido": False,
|
"valido": False,
|
||||||
"modo": "sem_imu",
|
"modo": "sem_imu",
|
||||||
|
|
@ -704,15 +879,23 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
"risk_level": "emergency",
|
"risk_level": "emergency",
|
||||||
"risk_level_num": 4,
|
"risk_level_num": 4,
|
||||||
"risk_score": 100.0,
|
"risk_score": 100.0,
|
||||||
|
"motivos_risco": ["sem_imu_valida"],
|
||||||
"acao_sugerida": "emergency_stop",
|
"acao_sugerida": "emergency_stop",
|
||||||
"velocidade_factor": 0.0,
|
"velocidade_factor": 0.0,
|
||||||
"bloquear_avanco": True,
|
"bloquear_avanco": True,
|
||||||
"stop": True,
|
"stop": True,
|
||||||
"emergency_stop": True,
|
"emergency_stop": True,
|
||||||
"confidence": 0.0,
|
"confidence": 0.0,
|
||||||
"sensors_count": 0,
|
"modulos_imu_configurados": [
|
||||||
|
self._nome_codigo(modulo) for modulo in self.modulos_imu
|
||||||
|
],
|
||||||
|
"redundancia_configurada": self.redundancia_configurada,
|
||||||
|
"redundancia_disponivel": False,
|
||||||
|
"sensors_expected_count": len(self._modulos_imu_configurados),
|
||||||
|
"sensors_count": len(fontes_ignoradas),
|
||||||
"sensors_valid_count": 0,
|
"sensors_valid_count": 0,
|
||||||
"sensors_ignored_count": len(fontes_ignoradas),
|
"sensors_ignored_count": len(fontes_ignoradas),
|
||||||
|
"sensors_disabled_count": len(fontes_desabilitadas),
|
||||||
"sensors_agree": False,
|
"sensors_agree": False,
|
||||||
"roll_disagreement_deg": 0.0,
|
"roll_disagreement_deg": 0.0,
|
||||||
"pitch_disagreement_deg": 0.0,
|
"pitch_disagreement_deg": 0.0,
|
||||||
|
|
@ -720,6 +903,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
"fonte_principal": None,
|
"fonte_principal": None,
|
||||||
"fontes": [],
|
"fontes": [],
|
||||||
"fontes_ignoradas": fontes_ignoradas,
|
"fontes_ignoradas": fontes_ignoradas,
|
||||||
|
"fontes_desabilitadas": fontes_desabilitadas,
|
||||||
"timestamp": agora * 1000.0,
|
"timestamp": agora * 1000.0,
|
||||||
"last_packet_ts": agora,
|
"last_packet_ts": agora,
|
||||||
"last_publish_ts": agora,
|
"last_publish_ts": agora,
|
||||||
|
|
@ -728,7 +912,17 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
"frequencia": self.frequencia,
|
"frequencia": self.frequencia,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _montar_saida(self, fontes, ignoradas, fusao, divergencia, risco, agora):
|
def _montar_saida(
|
||||||
|
self,
|
||||||
|
fontes,
|
||||||
|
ignoradas,
|
||||||
|
desabilitadas,
|
||||||
|
fusao,
|
||||||
|
divergencia,
|
||||||
|
risco,
|
||||||
|
redundancia_disponivel,
|
||||||
|
agora,
|
||||||
|
):
|
||||||
roll = float(fusao["roll"])
|
roll = float(fusao["roll"])
|
||||||
pitch = float(fusao["pitch"])
|
pitch = float(fusao["pitch"])
|
||||||
yaw = float(fusao["yaw"])
|
yaw = float(fusao["yaw"])
|
||||||
|
|
@ -769,8 +963,8 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
estabilidade_score = 100.0 - self.norm_to_100(oscilacao_total, 0.5, 8.0)
|
estabilidade_score = 100.0 - self.norm_to_100(oscilacao_total, 0.5, 8.0)
|
||||||
estabilidade_score = self.clamp(estabilidade_score, 0.0, 100.0)
|
estabilidade_score = self.clamp(estabilidade_score, 0.0, 100.0)
|
||||||
|
|
||||||
# Penaliza estabilidade se a redundância caiu.
|
# Penaliza apenas se a redundância foi solicitada e realmente caiu.
|
||||||
if len(fontes) <= 1:
|
if self.redundancia_configurada and not redundancia_disponivel:
|
||||||
estabilidade_score = max(0.0, estabilidade_score - 10.0)
|
estabilidade_score = max(0.0, estabilidade_score - 10.0)
|
||||||
|
|
||||||
# Penaliza se sensores divergem.
|
# Penaliza se sensores divergem.
|
||||||
|
|
@ -779,9 +973,14 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
|
|
||||||
fonte_principal = fusao.get("fonte_principal")
|
fonte_principal = fusao.get("fonte_principal")
|
||||||
|
|
||||||
|
if self.redundancia_configurada:
|
||||||
|
modo = "redundante" if redundancia_disponivel else "redundancia_degradada"
|
||||||
|
else:
|
||||||
|
modo = "single_source_configurada"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"valido": True,
|
"valido": True,
|
||||||
"modo": "redundante" if len(fontes) >= 2 else "single_source",
|
"modo": modo,
|
||||||
|
|
||||||
# Dado rápido.
|
# Dado rápido.
|
||||||
"roll": round(roll, 3),
|
"roll": round(roll, 3),
|
||||||
|
|
@ -822,9 +1021,16 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
"fonte_principal": fonte_principal,
|
"fonte_principal": fonte_principal,
|
||||||
"single_source": bool(fusao.get("single_source", False)),
|
"single_source": bool(fusao.get("single_source", False)),
|
||||||
|
|
||||||
|
"modulos_imu_configurados": [
|
||||||
|
self._nome_codigo(modulo) for modulo in self.modulos_imu
|
||||||
|
],
|
||||||
|
"redundancia_configurada": self.redundancia_configurada,
|
||||||
|
"redundancia_disponivel": redundancia_disponivel,
|
||||||
|
"sensors_expected_count": len(self._modulos_imu_configurados),
|
||||||
"sensors_count": len(fontes) + len(ignoradas),
|
"sensors_count": len(fontes) + len(ignoradas),
|
||||||
"sensors_valid_count": len(fontes),
|
"sensors_valid_count": len(fontes),
|
||||||
"sensors_ignored_count": len(ignoradas),
|
"sensors_ignored_count": len(ignoradas),
|
||||||
|
"sensors_disabled_count": len(desabilitadas),
|
||||||
|
|
||||||
"sensors_agree": divergencia["sensors_agree"],
|
"sensors_agree": divergencia["sensors_agree"],
|
||||||
"roll_disagreement_deg": round(divergencia["roll_disagreement_deg"], 3),
|
"roll_disagreement_deg": round(divergencia["roll_disagreement_deg"], 3),
|
||||||
|
|
@ -836,6 +1042,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
{
|
{
|
||||||
"mx_id": f["mx_id"],
|
"mx_id": f["mx_id"],
|
||||||
"modelo": f.get("modelo"),
|
"modelo": f.get("modelo"),
|
||||||
|
"t_code": f.get("t_code"),
|
||||||
"roll": round(f["roll"], 3),
|
"roll": round(f["roll"], 3),
|
||||||
"pitch": round(f["pitch"], 3),
|
"pitch": round(f["pitch"], 3),
|
||||||
"yaw": round(f["yaw"], 3),
|
"yaw": round(f["yaw"], 3),
|
||||||
|
|
@ -849,13 +1056,23 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
],
|
],
|
||||||
"fontes_ignoradas": [
|
"fontes_ignoradas": [
|
||||||
{
|
{
|
||||||
"mx_id": f["mx_id"],
|
"mx_id": f.get("mx_id"),
|
||||||
"modelo": f.get("modelo"),
|
"modelo": f.get("modelo"),
|
||||||
|
"t_code": f.get("t_code"),
|
||||||
"idade_s": round(f["idade_s"], 3),
|
"idade_s": round(f["idade_s"], 3),
|
||||||
"motivos": f.get("motivos", []),
|
"motivos": f.get("motivos", []),
|
||||||
}
|
}
|
||||||
for f in ignoradas
|
for f in ignoradas
|
||||||
],
|
],
|
||||||
|
"fontes_desabilitadas": [
|
||||||
|
{
|
||||||
|
"mx_id": f.get("mx_id"),
|
||||||
|
"modelo": f.get("modelo"),
|
||||||
|
"dispositivo": f.get("dispositivo"),
|
||||||
|
"t_code": f.get("t_code"),
|
||||||
|
}
|
||||||
|
for f in desabilitadas
|
||||||
|
],
|
||||||
|
|
||||||
# Índices úteis para painel/manager.
|
# Índices úteis para painel/manager.
|
||||||
"estabilidade_idx": round(estabilidade_score, 1),
|
"estabilidade_idx": round(estabilidade_score, 1),
|
||||||
|
|
@ -885,10 +1102,24 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
try:
|
try:
|
||||||
agora = time.time()
|
agora = time.time()
|
||||||
|
|
||||||
fontes, ignoradas = self._ler_fontes_imu()
|
fontes, ignoradas, desabilitadas = self._ler_fontes_imu()
|
||||||
|
|
||||||
|
modulos_validos = {
|
||||||
|
f.get("t_code")
|
||||||
|
for f in fontes
|
||||||
|
if f.get("t_code")
|
||||||
|
}
|
||||||
|
redundancia_disponivel = bool(
|
||||||
|
self.redundancia_configurada
|
||||||
|
and self._modulos_imu_configurados.issubset(modulos_validos)
|
||||||
|
)
|
||||||
|
|
||||||
if len(fontes) <= 0:
|
if len(fontes) <= 0:
|
||||||
data = self._montar_saida_sem_imu(ignoradas, agora)
|
data = self._montar_saida_sem_imu(
|
||||||
|
ignoradas,
|
||||||
|
desabilitadas,
|
||||||
|
agora,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
fusao = self._fundir_fontes(fontes)
|
fusao = self._fundir_fontes(fontes)
|
||||||
divergencia = self._calcular_divergencia(fontes)
|
divergencia = self._calcular_divergencia(fontes)
|
||||||
|
|
@ -899,15 +1130,17 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
roll_rate=fusao["roll_rate_dps"],
|
roll_rate=fusao["roll_rate_dps"],
|
||||||
pitch_rate=fusao["pitch_rate_dps"],
|
pitch_rate=fusao["pitch_rate_dps"],
|
||||||
sensores_agree=divergencia["sensors_agree"],
|
sensores_agree=divergencia["sensors_agree"],
|
||||||
n_fontes=len(fontes),
|
redundancia_disponivel=redundancia_disponivel,
|
||||||
)
|
)
|
||||||
|
|
||||||
data = self._montar_saida(
|
data = self._montar_saida(
|
||||||
fontes=fontes,
|
fontes=fontes,
|
||||||
ignoradas=ignoradas,
|
ignoradas=ignoradas,
|
||||||
|
desabilitadas=desabilitadas,
|
||||||
fusao=fusao,
|
fusao=fusao,
|
||||||
divergencia=divergencia,
|
divergencia=divergencia,
|
||||||
risco=risco,
|
risco=risco,
|
||||||
|
redundancia_disponivel=redundancia_disponivel,
|
||||||
agora=agora,
|
agora=agora,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -955,6 +1188,13 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
valido = bool(data.get("valido", False))
|
valido = bool(data.get("valido", False))
|
||||||
sensors_valid_count = int(data.get("sensors_valid_count", 0) or 0)
|
sensors_valid_count = int(data.get("sensors_valid_count", 0) or 0)
|
||||||
sensors_count = int(data.get("sensors_count", 0) or 0)
|
sensors_count = int(data.get("sensors_count", 0) or 0)
|
||||||
|
sensors_expected_count = int(
|
||||||
|
data.get("sensors_expected_count", len(self._modulos_imu_configurados)) or 0
|
||||||
|
)
|
||||||
|
redundancia_configurada = bool(
|
||||||
|
data.get("redundancia_configurada", self.redundancia_configurada)
|
||||||
|
)
|
||||||
|
redundancia_disponivel = bool(data.get("redundancia_disponivel", False))
|
||||||
sensors_agree = bool(data.get("sensors_agree", False))
|
sensors_agree = bool(data.get("sensors_agree", False))
|
||||||
risk_level_num = int(data.get("risk_level_num", 4) or 0)
|
risk_level_num = int(data.get("risk_level_num", 4) or 0)
|
||||||
|
|
||||||
|
|
@ -984,7 +1224,9 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
sensors_ignored_count = int(data.get("sensors_ignored_count", 0) or 0)
|
sensors_ignored_count = int(data.get("sensors_ignored_count", 0) or 0)
|
||||||
|
|
||||||
if sensors_count <= 0:
|
if sensors_count <= 0:
|
||||||
motivos.append("Nenhuma câmera com IMU detectada")
|
motivos.append(
|
||||||
|
"Nenhum dos módulos IMU configurados foi detectado"
|
||||||
|
)
|
||||||
elif sensors_valid_count <= 0 and sensors_ignored_count > 0:
|
elif sensors_valid_count <= 0 and sensors_ignored_count > 0:
|
||||||
motivos.append("Câmeras detectadas, mas nenhuma IMU válida")
|
motivos.append("Câmeras detectadas, mas nenhuma IMU válida")
|
||||||
else:
|
else:
|
||||||
|
|
@ -995,11 +1237,14 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
saude = 0
|
saude = 0
|
||||||
motivos.append("Sem sensores IMU válidos")
|
motivos.append("Sem sensores IMU válidos")
|
||||||
|
|
||||||
elif sensors_valid_count == 1:
|
elif redundancia_configurada and not redundancia_disponivel:
|
||||||
saude -= 15
|
saude -= 15
|
||||||
motivos.append("Operando sem redundância de IMU")
|
motivos.append(
|
||||||
|
"Redundância de IMU configurada indisponível "
|
||||||
|
f"({sensors_valid_count}/{sensors_expected_count})"
|
||||||
|
)
|
||||||
|
|
||||||
if sensors_count >= 2 and not sensors_agree:
|
if sensors_valid_count >= 2 and not sensors_agree:
|
||||||
saude -= 30
|
saude -= 30
|
||||||
motivos.append("IMUs divergentes")
|
motivos.append("IMUs divergentes")
|
||||||
|
|
||||||
|
|
@ -1061,6 +1306,11 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
"status": status.value,
|
"status": status.value,
|
||||||
"saude": saude,
|
"saude": saude,
|
||||||
"motivos": motivos,
|
"motivos": motivos,
|
||||||
|
"modulos_imu_configurados": [
|
||||||
|
self._nome_codigo(modulo) for modulo in self.modulos_imu
|
||||||
|
],
|
||||||
|
"redundancia_configurada": redundancia_configurada,
|
||||||
|
"redundancia_disponivel": redundancia_disponivel,
|
||||||
"saude_individual": [],
|
"saude_individual": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1117,9 +1367,16 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
"stop": True,
|
"stop": True,
|
||||||
"emergency_stop": True,
|
"emergency_stop": True,
|
||||||
"confidence": 0.0,
|
"confidence": 0.0,
|
||||||
|
"modulos_imu_configurados": [
|
||||||
|
self._nome_codigo(modulo) for modulo in self.modulos_imu
|
||||||
|
],
|
||||||
|
"redundancia_configurada": self.redundancia_configurada,
|
||||||
|
"redundancia_disponivel": False,
|
||||||
|
"sensors_expected_count": len(self._modulos_imu_configurados),
|
||||||
"sensors_count": 0,
|
"sensors_count": 0,
|
||||||
"sensors_valid_count": 0,
|
"sensors_valid_count": 0,
|
||||||
"sensors_ignored_count": 0,
|
"sensors_ignored_count": 0,
|
||||||
|
"sensors_disabled_count": 0,
|
||||||
"sensors_agree": False,
|
"sensors_agree": False,
|
||||||
"estabilidade_idx": 0.0,
|
"estabilidade_idx": 0.0,
|
||||||
"timestamp": 0.0,
|
"timestamp": 0.0,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue