Files
TIR_PROJ/herding/control/sequential.py
T
Johnny Fernandes a01a5c9cef Checkpoint 7
2026-05-11 12:21:51 +01:00

75 lines
2.3 KiB
Python

"""Sequential "pin-and-push" shepherd-dog controller.
Single-target alternative to Strömbom: each step, target the sheep
closest to the pen, park behind it, drive it through; once it latches
penned the next-closest sheep becomes the target. Naturally queues
the flock through a narrow gate.
"""
import math
from herding.world.geometry import GATE_Y, PEN_ENTRY, in_pen
DELTA_DRIVE = 1.5 # standoff behind the target sheep
APPROACH_GAIN = 1.0 # action magnitude scale (1 = full speed)
def _unit(x, y):
d = math.hypot(x, y)
if d < 1e-6:
return 0.0, 0.0
return x / d, y / d
def _is_active(x, y) -> bool:
return (not in_pen(x, y)) and y > GATE_Y
def compute_action(dog_xy, sheep_positions, pen_target=PEN_ENTRY):
"""Return ``(vx, vy, mode)`` — same call signature as Strömbom."""
active = [(name, x, y) for name, (x, y) in sheep_positions.items()
if _is_active(x, y)]
if not active:
return 0.0, 0.0, "idle"
name, sx, sy = min(
active,
key=lambda s: math.hypot(s[1] - pen_target[0], s[2] - pen_target[1]),
)
ux, uy = _unit(sx - pen_target[0], sy - pen_target[1])
tx = sx + DELTA_DRIVE * ux
ty = sy + DELTA_DRIVE * uy
ax, ay = _unit(tx - dog_xy[0], ty - dog_xy[1])
return APPROACH_GAIN * ax, APPROACH_GAIN * ay, f"drive:{name}"
def compute_action_debug(dog_xy, sheep_positions, pen_target=PEN_ENTRY):
"""``compute_action`` plus a debug dict (target, drive point)."""
active = [(name, x, y) for name, (x, y) in sheep_positions.items()
if _is_active(x, y)]
if not active:
return 0.0, 0.0, "idle", {
"n_active": 0, "target_name": "",
"target_x": 0.0, "target_y": 0.0,
"drive_x": dog_xy[0], "drive_y": dog_xy[1],
}
name, sx, sy = min(
active,
key=lambda s: math.hypot(s[1] - pen_target[0], s[2] - pen_target[1]),
)
ux, uy = _unit(sx - pen_target[0], sy - pen_target[1])
tx = sx + DELTA_DRIVE * ux
ty = sy + DELTA_DRIVE * uy
ax, ay = _unit(tx - dog_xy[0], ty - dog_xy[1])
return APPROACH_GAIN * ax, APPROACH_GAIN * ay, f"drive:{name}", {
"n_active": len(active), "target_name": name,
"target_x": sx, "target_y": sy,
"drive_x": tx, "drive_y": ty,
}