/** * Depth-only follow-me loop (no neural net). * * Subscribes only to a depth image topic (sensor_msgs/Image, 16UC1 mm or 32FC1 m), * picks the closest "person-like" blob in front of the robot, and drives toward * it using the same FollowerController as the YOLO loop. * * Why this exists: the YOLO-based local loop needs a yolov8n.onnx file and * onnxruntime-node, both of which can be unavailable on a freshly-flashed robot * (no ONNX hosted publicly anymore, no working torch on some Jetson images). * Depth-only follow needs only the depth stream — perfect for unblocking the * skill when the camera publishes depth but no person detector is wired up. * * Accuracy note: this picks the closest contiguous depth region in front of the * camera. It works well in uncluttered indoor space with a single person, and * deliberately ignores anything outside the [minPersonDepth, maxPersonDepth] band * so floor/ceiling/back wall don't capture the controller. It does *not* * recognise people semantically — if you walk behind a chair, it may follow the * chair. Use mode='local' (YOLO) once yolov8n.onnx is available for real * person re-identification. */ import type { AgenticROSConfig, ResolvedRobot, RosTransport } from "@agenticros/core"; import { type Twist } from "./controller.js"; export interface FollowMeDepthStatus { enabled: boolean; tracking: boolean; targetDistance: number; /** Recorded but not used (depth mode has no semantic recognition). */ targetDescription: string | null; lastTarget: { x: number; z: number; cellsInBlob: number; } | null; lastTwist: Twist; lastError: string | null; framesProcessed: number; detectionsSinceStart: number; } export interface StartOptions { targetDescription?: string; } interface LatestDepth { width: number; height: number; step: number; encoding: string; isBigEndian: boolean; data: Uint8Array; receivedAt: number; } export declare class FollowMeDepth { private readonly robot; private readonly config; private readonly transport; private readonly controller; private enabled; private running; private targetDescription; private depthSub; private latestDepth; private tickHandle; private lastError; private framesProcessed; private detectionsSinceStart; private lastTarget; private tracking; constructor(robot: ResolvedRobot, config: AgenticROSConfig, transport: RosTransport); /** Stable id of the robot this loop is bound to. Used by the registry below. */ get robotId(): string; start(opts?: StartOptions): Promise; stop(): Promise; setTargetDistance(d: number): void; setTargetDescription(description: string): void; status(): FollowMeDepthStatus; private subscribeDepth; private unsubscribeDepth; private tickSafe; private tick; private publishTwist; private publishStop; } /** * Coarse grid blob finder. * * Splits the central ROI of the depth image into a small grid, computes a * median depth per cell from pixels in the person-band [MIN_PERSON_DEPTH_M, * MAX_PERSON_DEPTH_M], then chooses the connected component (4-neighbour) with * the closest median that meets a minimum size threshold. Returns the centroid * in pixel space and the blob's median depth in metres. * * Trade-offs: * - Cheap O(W·H) median per cell with a fixed-size sampling sort. * - Coarse grid (16×12 ≈ 192 cells) ⇒ very robust to noise; sub-cell * precision is not needed for a P-controller that drives at ≤0.5 m/s. * - 4-connectivity gives natural "find one person blob" behaviour without a * full image-level flood fill. */ export declare function findClosestPersonBlob(d: LatestDepth): { cx: number; cy: number; z: number; cellsInBlob: number; } | null; export declare function getFollowMeDepth(robot: ResolvedRobot, config: AgenticROSConfig, transport: RosTransport): FollowMeDepth; /** Stop the in-process depth follow loop if one was started for this robot. */ export declare function stopFollowMeDepthIfPresent(robotId: string): Promise; /** Test-only: clear the registry so suites can run in isolation. */ export declare function _resetFollowMeDepthRegistry(): void; export {}; //# sourceMappingURL=depth-loop.d.ts.map