Rename multi-segment functions to two-concept names; polish docstrings

Naming pass: rename functions whose third+ segment is redundant or
implementation-detail, sticking to the codebase's preferred
``noun_verb`` / ``verb_noun`` two-concept idiom. Renames are atomic
across definitions, callers, and tests.

  is_penned_position        →  is_penned
  modulate_speed_near_sheep →  modulate_speed
  mecanum_kinematics_step   →  mecanum_step
  policy_forward_mean       →  forward_mean

Two-concept patterns like ``velocity_to_wheels`` / ``detections_from_scan``
/ ``make_strombom_predictor`` are left alone — they're idiomatic
converters / factories that read as a single concept, and the longer
form aids grep-ability.

Docstring polish:
* ``herding/config.py`` header drops the "previously lived as a
  module-level literal" historical framing — we ship as a single
  thing, so the refactor anecdote no longer earns its keep. The
  usage examples now mention both ``HERDING_WEBOTS`` and
  ``HERDING_MEC_WEBOTS`` presets.

126 pytest cases still pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Johnny Fernandes
2026-05-17 01:58:15 +00:00
parent 10c01a938e
commit 7ab69ab0f3
14 changed files with 75 additions and 77 deletions
+9 -9
View File
@@ -8,7 +8,7 @@ from herding.control.active_scan import (
EMPTY_DEBOUNCE_STEPS, INITIAL_SCAN_STEPS, ActiveScanTeacher,
)
from herding.control.modulation import (
MIN_SPEED, SLOW_NEAR_SHEEP, modulate_speed_near_sheep,
MIN_SPEED, SLOW_NEAR_SHEEP, modulate_speed,
)
from herding.control.sequential import compute_action as sequential_action
from herding.control.strombom import (
@@ -23,23 +23,23 @@ from herding.world.geometry import PEN_ENTRY
# ---------------------------------------------------------------------------
def test_modulation_empty_input_passthrough():
assert modulate_speed_near_sheep(1.0, 0.0, (0.0, 0.0), []) == (1.0, 0.0)
assert modulate_speed_near_sheep(1.0, 0.0, (0.0, 0.0), {}) == (1.0, 0.0)
assert modulate_speed(1.0, 0.0, (0.0, 0.0), []) == (1.0, 0.0)
assert modulate_speed(1.0, 0.0, (0.0, 0.0), {}) == (1.0, 0.0)
def test_modulation_far_sheep_passthrough():
vx, vy = modulate_speed_near_sheep(1.0, 0.0, (0.0, 0.0), [(100.0, 0.0)])
vx, vy = modulate_speed(1.0, 0.0, (0.0, 0.0), [(100.0, 0.0)])
assert (vx, vy) == (1.0, 0.0)
def test_modulation_close_sheep_min_speed():
vx, vy = modulate_speed_near_sheep(1.0, 0.0, (0.0, 0.0), [(0.0, 0.0)])
vx, vy = modulate_speed(1.0, 0.0, (0.0, 0.0), [(0.0, 0.0)])
assert math.isclose(vx, MIN_SPEED)
assert vy == 0.0
def test_modulation_preserves_direction():
vx, vy = modulate_speed_near_sheep(0.6, 0.8, (0.0, 0.0), [(1.0, 0.0)])
vx, vy = modulate_speed(0.6, 0.8, (0.0, 0.0), [(1.0, 0.0)])
ratio = math.hypot(vx, vy)
# Direction preserved.
assert math.isclose(vx / ratio, 0.6, abs_tol=1e-6)
@@ -47,16 +47,16 @@ def test_modulation_preserves_direction():
def test_modulation_linear_ramp_midpoint():
vx, _ = modulate_speed_near_sheep(1.0, 0.0, (0.0, 0.0),
vx, _ = modulate_speed(1.0, 0.0, (0.0, 0.0),
[(SLOW_NEAR_SHEEP / 2, 0.0)])
expected = MIN_SPEED + (1.0 - MIN_SPEED) * 0.5
assert math.isclose(vx, expected, abs_tol=1e-6)
def test_modulation_accepts_dict_input():
vx_list, _ = modulate_speed_near_sheep(1.0, 0.0, (0.0, 0.0),
vx_list, _ = modulate_speed(1.0, 0.0, (0.0, 0.0),
[(1.0, 0.0)])
vx_dict, _ = modulate_speed_near_sheep(1.0, 0.0, (0.0, 0.0),
vx_dict, _ = modulate_speed(1.0, 0.0, (0.0, 0.0),
{"t0": (1.0, 0.0)})
assert math.isclose(vx_list, vx_dict)
+8 -8
View File
@@ -6,7 +6,7 @@ import pytest
from herding.world.diffdrive import (
heading_speed_to_wheels, kinematics_step,
mecanum_inverse, mecanum_kinematics_step,
mecanum_inverse, mecanum_step,
velocity_to_mecanum_wheels, velocity_to_wheels,
)
@@ -95,7 +95,7 @@ LY = 0.14 # half wheel_base_y
def test_mecanum_kinematics_zero_is_identity():
x, y, h = mecanum_kinematics_step(
x, y, h = mecanum_step(
1.0, 2.0, 0.5, 0.0, 0.0, 0.0, 0.0, WHEEL_R, LX, LY, DT,
)
assert (x, y, h) == (1.0, 2.0, 0.5)
@@ -104,7 +104,7 @@ def test_mecanum_kinematics_zero_is_identity():
def test_mecanum_kinematics_pure_forward():
# All 4 wheels equal → pure forward (vx_body > 0, vy_body = 0).
w = 10.0
x, y, h = mecanum_kinematics_step(
x, y, h = mecanum_step(
0.0, 0.0, 0.0, w, w, w, w, WHEEL_R, LX, LY, DT,
)
assert h == pytest.approx(0.0, abs=1e-9)
@@ -118,7 +118,7 @@ def test_mecanum_kinematics_pure_strafe():
# vy_body = (-w_fl+w_fr+w_rl-w_rr)*r/4 > 0
# Use w_fl=-10, w_fr=10, w_rl=10, w_rr=-10.
w_fl, w_fr, w_rl, w_rr = -10.0, 10.0, 10.0, -10.0
x, y, h = mecanum_kinematics_step(
x, y, h = mecanum_step(
0.0, 0.0, 0.0, w_fl, w_fr, w_rl, w_rr, WHEEL_R, LX, LY, DT,
)
assert h == pytest.approx(0.0, abs=1e-9)
@@ -130,7 +130,7 @@ def test_mecanum_kinematics_pure_strafe():
def test_mecanum_kinematics_strafe_efficiency_scales_y():
# With strafe_efficiency=0.4, realised strafe should be 40% of ideal.
w_fl, w_fr, w_rl, w_rr = -10.0, 10.0, 10.0, -10.0
x, y, _ = mecanum_kinematics_step(
x, y, _ = mecanum_step(
0.0, 0.0, 0.0, w_fl, w_fr, w_rl, w_rr, WHEEL_R, LX, LY, DT,
strafe_efficiency=0.4,
)
@@ -142,7 +142,7 @@ def test_mecanum_kinematics_strafe_efficiency_scales_y():
def test_mecanum_kinematics_strafe_bleed_pushes_backward():
# Negative bleed means strafe commands also push the body backward.
w_fl, w_fr, w_rl, w_rr = -10.0, 10.0, 10.0, -10.0
x, y, _ = mecanum_kinematics_step(
x, y, _ = mecanum_step(
0.0, 0.0, 0.0, w_fl, w_fr, w_rl, w_rr, WHEEL_R, LX, LY, DT,
strafe_efficiency=1.0,
strafe_to_forward_bleed=-0.28,
@@ -156,7 +156,7 @@ def test_mecanum_kinematics_strafe_bleed_pushes_backward():
def test_mecanum_kinematics_forward_unaffected_by_strafe_params():
# Forward command should be untouched by strafe_efficiency / bleed.
w_fl = w_fr = w_rl = w_rr = 10.0
x, y, _ = mecanum_kinematics_step(
x, y, _ = mecanum_step(
0.0, 0.0, 0.0, w_fl, w_fr, w_rl, w_rr, WHEEL_R, LX, LY, DT,
strafe_efficiency=0.4,
strafe_to_forward_bleed=-0.28,
@@ -170,7 +170,7 @@ def test_mecanum_kinematics_pure_rotation():
# Pure rotation: vx_body=0, vy_body=0, omega>0.
# w_fl=-10, w_fr=10, w_rl=-10, w_rr=10 → all sums cancel except omega.
w_fl, w_fr, w_rl, w_rr = -10.0, 10.0, -10.0, 10.0
x, y, h = mecanum_kinematics_step(
x, y, h = mecanum_step(
0.0, 0.0, 0.0, w_fl, w_fr, w_rl, w_rr, WHEEL_R, LX, LY, DT,
)
assert x == pytest.approx(0.0, abs=1e-9)
+12 -12
View File
@@ -5,7 +5,7 @@ import math
from herding.world.geometry import (
FIELD_X, FIELD_Y, GATE_X, GATE_Y, MAX_SHEEP, PEN_ENTRY, PEN_X, PEN_Y,
distance_to_pen_entry, in_field, in_gate_corridor, in_pen,
is_penned_position,
is_penned,
)
@@ -44,23 +44,23 @@ def test_in_gate_corridor():
assert not in_gate_corridor(5.0, -18.0)
def test_is_penned_position_latches_below_gate():
def test_is_penned_latches_below_gate():
# In the gate column and south of the gate plane → penned.
assert is_penned_position(11.5, -15.0)
assert is_penned_position(10.5, -18.0)
assert is_penned_position(12.5, -22.0)
assert is_penned(11.5, -15.0)
assert is_penned(10.5, -18.0)
assert is_penned(12.5, -22.0)
# Above the gate plane → not yet.
assert not is_penned_position(11.5, -14.9)
assert not is_penned(11.5, -14.9)
# Outside the gate column → not penned even if south.
assert not is_penned_position(0.0, -16.0)
assert not is_penned_position(14.0, -16.0)
assert not is_penned(0.0, -16.0)
assert not is_penned(14.0, -16.0)
def test_is_penned_position_latch_margin():
def test_is_penned_latch_margin():
# Slight tolerance on the gate column.
assert is_penned_position(9.9, -15.5)
assert is_penned_position(13.1, -15.5)
assert not is_penned_position(9.7, -15.5)
assert is_penned(9.9, -15.5)
assert is_penned(13.1, -15.5)
assert not is_penned(9.7, -15.5)
def test_distance_to_pen_entry():
+1 -1
View File
@@ -136,7 +136,7 @@ def test_tracker_forgets_stale_tracks():
def test_tracker_penned_position_promotes_track():
t = SheepTracker()
t.update([(11.5, -16.0)]) # spawn inside the pen column
# is_penned_position is True for this point.
# is_penned is True for this point.
assert t.n_penned() == 1
assert t.n_active() == 0