Skip to content

Commit f352a4d

Browse files
authored
Merge pull request #29 from PCrnjak/fix/settle-progress
Gate trajectory settling on progress, not elapsed ticks
2 parents 829c2c7 + e2de8be commit f352a4d

5 files changed

Lines changed: 34 additions & 14 deletions

File tree

‎parol6/motion/geometry.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,10 @@ def generate_spline(
192192

193193
pos_splines = []
194194
for i in range(3):
195-
bc: Any
195+
# Annotated assignment keeps bc as Any: scipy-stubs' bc_type rejects
196+
# the scalar derivative values scipy requires for 1-D y
196197
if velocity_start is not None and velocity_end is not None:
197-
bc = ((1, float(velocity_start[i])), (1, float(velocity_end[i])))
198+
bc: Any = ((1, float(velocity_start[i])), (1, float(velocity_end[i])))
198199
else:
199200
bc = "not-a-knot"
200201
spline = CubicSpline(timestamps_arr, waypoints_arr[:, i], bc_type=bc)

‎parol6/protocol/wire.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
def _enc_hook(obj: object) -> object:
4444
"""Custom encoder hook for numpy types."""
4545
if isinstance(obj, np.ndarray):
46-
return obj.tolist() # type: ignore[no-matching-overload, ty:no-matching-overload]
46+
return obj.tolist() # type: ignore[no-matching-overload]
4747
if isinstance(obj, (np.integer, np.floating)):
4848
return obj.item()
4949
raise NotImplementedError(f"Cannot encode {type(obj)}")

‎parol6/server/segment_player.py‎

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from typing import TYPE_CHECKING
1818

1919
import numpy as np
20-
from pinokin import arrays_equal_n
2120

2221
from parol6.commands._collision_guard import guard_joint_path
2322
from parol6.commands.base import CommandBase, ExecutionStatusCode
@@ -60,6 +59,7 @@ class SegmentPlayer:
6059
"_inline_activated",
6160
"_settling",
6261
"_settle_ticks",
62+
"_settle_err",
6363
"_last_shapes_version",
6464
)
6565

@@ -72,6 +72,7 @@ def __init__(self, planner: MotionPlanner) -> None:
7272
self._inline_activated: bool = False
7373
self._settling: bool = False
7474
self._settle_ticks: int = 0
75+
self._settle_err: int = -1
7576
self._last_shapes_version: int = 0
7677

7778
@property
@@ -127,16 +128,36 @@ def tick(self, state: ControllerState) -> bool:
127128
self._step += 1
128129
self._settling = False
129130
return True
130-
# All waypoints sent — hold MOVE at target until Position_in converges
131+
# All waypoints sent — hold MOVE at target until Position_in
132+
# converges. The tick cap gates on stall, not elapsed time:
133+
# while the firmware is still closing on the target (e.g. it
134+
# fell behind the waypoint stream under CPU starvation) the
135+
# segment stays active, so completion is never reported with
136+
# the robot still in motion.
131137
target = active.trajectory_steps[-1]
132138
if not self._settling:
133139
self._settling = True
134140
self._settle_ticks = 0
141+
self._settle_err = -1
142+
err = 0
143+
for i in range(6):
144+
d = int(state.Position_in[i]) - int(target[i])
145+
if d < 0:
146+
d = -d
147+
if d > err:
148+
err = d
149+
if self._settle_err < 0 or err < self._settle_err:
150+
self._settle_err = err
151+
self._settle_ticks = 0
135152
self._settle_ticks += 1
136-
if (
137-
arrays_equal_n(state.Position_in[:6], target[:6])
138-
or self._settle_ticks > SETTLE_MAX_TICKS
139-
):
153+
if err == 0 or self._settle_ticks > SETTLE_MAX_TICKS:
154+
if err != 0:
155+
logger.warning(
156+
"Segment completed %d steps short of target "
157+
"(no settle progress for %d ticks)",
158+
err,
159+
SETTLE_MAX_TICKS,
160+
)
140161
self._settling = False
141162
self._complete_segment(active, state)
142163
continue

‎parol6/server/transports/serial_transport.py‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import logging
99
import os
1010
import time
11-
from typing import cast
1211

1312
import numba
1413
import numpy as np
@@ -415,9 +414,7 @@ def get_latest_frame_view(self) -> tuple[memoryview | None, int, float]:
415414
Return a tuple of (memoryview|None, version:int, timestamp:float).
416415
The memoryview points to a stable 52-byte buffer which is updated by the reader.
417416
"""
418-
mv = cast(
419-
"memoryview | None", self._frame_mv if self._frame_version > 0 else None
420-
)
417+
mv = self._frame_mv if self._frame_version > 0 else None
421418
return (mv, self._frame_version, self._frame_ts)
422419

423420
def _update_hz_tracking(self) -> None:

‎pyproject.toml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ dev = [
6363
"trimesh",
6464
"fast-simplification",
6565
"rtree",
66-
"scipy-stubs",
66+
"scipy-stubs==1.17.1.5; python_version < '3.12'",
67+
"scipy-stubs==1.18.0.1; python_version >= '3.12'",
6768
"types-pyserial",
6869
]
6970

0 commit comments

Comments
 (0)