ajuste na atuação do bico enquanto parado e antecipação da zona de atuação conforme velcodaide

This commit is contained in:
Diego Freitas 2026-08-13 21:23:28 -03:00
parent bfed826136
commit bb546da70f
1 changed files with 82 additions and 22 deletions

View File

@ -296,6 +296,8 @@ class WeedDetector:
cfg = self.config cfg = self.config
vel_mps = float(cfg.get("velocidade_robo", 0.0) or 0.0) vel_mps = float(cfg.get("velocidade_robo", 0.0) or 0.0)
# A geometria de atuação foi definida para o avanço. A ré não é
# reinterpretada como um percurso espacial inverso dos bicos.
if vel_mps < 0: if vel_mps < 0:
vel_mps = 0.0 vel_mps = 0.0
@ -463,9 +465,14 @@ class WeedDetector:
self._frac_grid_last = frac_grid self._frac_grid_last = frac_grid
# 3) Atualiza score espacial. # 3) Atualiza score espacial.
# Em movimento, acumula evidência e desloca a esteira normalmente.
# Parado, acompanha a evidência do frame atual por EMA: o mesmo ruído
# não pode ser somado indefinidamente, mas uma erva persistente continua
# podendo manter/ligar o bico.
score_update_info = self._atualizar_score_memoria( score_update_info = self._atualizar_score_memoria(
frac_grid=frac_grid, frac_grid=frac_grid,
cfg=cfg, cfg=cfg,
em_movimento=bool(shift_info["em_movimento"]),
) )
# 4) Decide bicos pela zona física de atuação, com compensação de latência. # 4) Decide bicos pela zona física de atuação, com compensação de latência.
@ -533,6 +540,7 @@ class WeedDetector:
return { return {
"dt_s": 0.0, "dt_s": 0.0,
"vel_mps": float(vel_mps), "vel_mps": float(vel_mps),
"em_movimento": False,
"delta_frac": 0.0, "delta_frac": 0.0,
"shift_cells": 0.0, "shift_cells": 0.0,
"distancia_deslocada_total_cm": float(self._distancia_deslocada_total_cm), "distancia_deslocada_total_cm": float(self._distancia_deslocada_total_cm),
@ -545,10 +553,17 @@ class WeedDetector:
if max_dt > 0: if max_dt > 0:
dt = min(dt, max_dt) dt = min(dt, max_dt)
if dt <= 0 or vel_mps <= 0: vel_min = max(
0.0,
float(cfg.get("vel_min_movimento_memoria_mps", 0.03) or 0.03),
)
em_movimento = bool(abs(vel_mps) >= vel_min)
if dt <= 0 or not em_movimento:
return { return {
"dt_s": float(max(0.0, dt)), "dt_s": float(max(0.0, dt)),
"vel_mps": float(vel_mps), "vel_mps": float(vel_mps),
"em_movimento": False,
"delta_frac": 0.0, "delta_frac": 0.0,
"shift_cells": 0.0, "shift_cells": 0.0,
"distancia_deslocada_total_cm": float(self._distancia_deslocada_total_cm), "distancia_deslocada_total_cm": float(self._distancia_deslocada_total_cm),
@ -582,6 +597,7 @@ class WeedDetector:
return { return {
"dt_s": float(dt), "dt_s": float(dt),
"vel_mps": float(vel_mps), "vel_mps": float(vel_mps),
"em_movimento": True,
"delta_frac": float(delta_frac), "delta_frac": float(delta_frac),
"shift_cells": float(shift_cells), "shift_cells": float(shift_cells),
"k_shift_frame_por_mps": float(k_shift), "k_shift_frame_por_mps": float(k_shift),
@ -723,6 +739,7 @@ class WeedDetector:
self, self,
frac_grid: np.ndarray, frac_grid: np.ndarray,
cfg: dict, cfg: dict,
em_movimento: bool = True,
): ):
min_frac_pred = float( min_frac_pred = float(
cfg.get( cfg.get(
@ -748,14 +765,35 @@ class WeedDetector:
evidencia = np.clip(frac_grid / percent_ref, 0.0, 1.0).astype(np.float32) evidencia = np.clip(frac_grid / percent_ref, 0.0, 1.0).astype(np.float32)
cond_pos = frac_grid >= min_frac_pred cond_pos = frac_grid >= min_frac_pred
# Decaimento geral, depois reforço positivo. if em_movimento:
score = np.maximum(0.0, self._score_grid - down_decay) # Em movimento, cada frame traz evidência de uma nova faixa física
# do terreno; mantém a estratégia acumulativa original.
score = np.maximum(0.0, self._score_grid - down_decay)
score = np.where( score = np.where(
cond_pos, cond_pos,
np.minimum(1.0, score + up_gain * evidencia), np.minimum(1.0, score + up_gain * evidencia),
score, score,
).astype(np.float32) ).astype(np.float32)
modo_atualizacao = "acumulativo_movimento"
else:
# Parado, vários frames observam essencialmente o mesmo terreno.
# Uma EMA faz o score convergir para a evidência atual, em vez de
# transformar repetição temporal em confiança espacial infinita.
alpha_parado = float(cfg.get("score_alpha_parado", 0.70) or 0.70)
alpha_parado = float(np.clip(alpha_parado, 0.0, 0.99))
evidencia_atual = np.where(
cond_pos,
evidencia,
0.0,
).astype(np.float32)
score = (
alpha_parado * self._score_grid
+ (1.0 - alpha_parado) * evidencia_atual
).astype(np.float32)
modo_atualizacao = "ema_parado"
self._score_grid = score self._score_grid = score
@ -764,6 +802,11 @@ class WeedDetector:
"percent_ref_evidencia": float(percent_ref), "percent_ref_evidencia": float(percent_ref),
"score_up_gain": float(up_gain), "score_up_gain": float(up_gain),
"score_down_decay": float(down_decay), "score_down_decay": float(down_decay),
"em_movimento": bool(em_movimento),
"modo_atualizacao": modo_atualizacao,
"score_alpha_parado": float(
cfg.get("score_alpha_parado", 0.70) or 0.70
),
"frac_grid_max": float(frac_grid.max()) if frac_grid.size else 0.0, "frac_grid_max": float(frac_grid.max()) if frac_grid.size else 0.0,
"evidencia_max": float(evidencia.max()) if evidencia.size else 0.0, "evidencia_max": float(evidencia.max()) if evidencia.size else 0.0,
} }
@ -788,20 +831,32 @@ class WeedDetector:
k_shift = max(0.0, k_shift) k_shift = max(0.0, k_shift)
# Antecipação em fração da imagem: # A zona configurada representa a posição física fixa dos bicos.
# Se demora para atuar, avalia células um pouco acima da zona real, # Com faixa=0,85, área=0,10 e inversão vertical, 85%..95% da imagem
# porque elas chegarão na zona quando o bico abrir de fato. # original vira 5%..15% na convenção interna. Essa zona NÃO muda de
antecipacao_frac = max(0.0, vel_mps * lat_s * k_shift) # parado até a velocidade de referência. Somente a parcela de
# velocidade ACIMA da referência antecipa a avaliação para compensar
# o tempo de resposta do sistema.
vel_referencia = max(
0.0,
float(cfg.get("velocidade_referencia_atuacao_mps", 0.65) or 0.65),
)
velocidade_abs = abs(float(vel_mps))
velocidade_excedente = max(0.0, velocidade_abs - vel_referencia)
antecipacao_extra_frac = velocidade_excedente * lat_s * k_shift
if getattr(self, "inverter_sentido_vertical_bicos", False): # A posição dos bicos é fixa. Não tentamos reinterpretar essa zona na
# Se a erva vem do pé para o topo, antecipar significa olhar # marcha a ré; a antecipação só indica uma região anterior no sentido
# um pouco abaixo da zona real, pois ela ainda vai subir até o bico. # visual configurado para a operação normal de avanço.
zona_eval_ini_frac = zona_ini_frac + antecipacao_frac sentido_vertical = (
zona_eval_fim_frac = zona_fim_frac + antecipacao_frac 1.0
else: if getattr(self, "inverter_sentido_vertical_bicos", False)
# Lógica atual: erva vem do topo para o pé. else -1.0
zona_eval_ini_frac = zona_ini_frac - antecipacao_frac )
zona_eval_fim_frac = zona_fim_frac - antecipacao_frac offset_assinado = antecipacao_extra_frac * sentido_vertical
zona_eval_ini_frac = zona_ini_frac + offset_assinado
zona_eval_fim_frac = zona_fim_frac + offset_assinado
zona_eval_ini_frac = max(0.0, min(1.0, zona_eval_ini_frac)) zona_eval_ini_frac = max(0.0, min(1.0, zona_eval_ini_frac))
zona_eval_fim_frac = max(0.0, min(1.0, zona_eval_fim_frac)) zona_eval_fim_frac = max(0.0, min(1.0, zona_eval_fim_frac))
@ -872,7 +927,12 @@ class WeedDetector:
# Zona avaliada com compensação de latência. # Zona avaliada com compensação de latência.
"latencia_total_atuacao_ms": float(lat_ms), "latencia_total_atuacao_ms": float(lat_ms),
"antecipacao_frac": float(antecipacao_frac), "antecipacao_frac": float(antecipacao_extra_frac),
"velocidade_referencia_atuacao_mps": float(vel_referencia),
"velocidade_excedente_atuacao_mps": float(velocidade_excedente),
"offset_base_referencia_frac": 0.0,
"antecipacao_extra_frac": float(antecipacao_extra_frac),
"offset_assinado_frac": float(offset_assinado),
"zona_eval_inicio_frac": float(zona_eval_ini_frac), "zona_eval_inicio_frac": float(zona_eval_ini_frac),
"zona_eval_fim_frac": float(zona_eval_fim_frac), "zona_eval_fim_frac": float(zona_eval_fim_frac),