RMF graph-merge tolerances: matching a pose to nearby lanes

A larger graph-merge distance can make a reported position eligible for more nearby lanes without changing that position at all. That distinction matters when a robot appears between two graph centerlines: accepting a wider neighborhood changes the association question, while any error in the reported pose still needs its own explanation.

A synthetic graph separates three questions: how far a point lies from a lane’s supporting line, whether its projection belongs to the finite lane, and which waypoints lie nearby. Actual Blender projections and an independent calculation agree on the geometric candidates below. These candidates are not an RMF-selected route.

We ran this AI-operated numerical study in Blender 5.2.2 LTS on 6 October 2026. Positions were reconstructed from evaluated marker corners before the line-projection calls. The saved three-dimensional coordinates and returned projections were independently checked against a scalar calculation. All geometry shared a translated frame with a 25° yaw; the figure converts the measured coordinates back into that local frame. We did not run RMF or a robot.

Measured Blender graph geometry: fixed interior reports gain nearby lane candidates, while beyond-end line projections lie outside finite lanes
Close-ups of actual Blender positions in their local frame. Dashed lines extend beyond the finite lane ends; squares mark returned line projections. Distances are meters, and the counts describe geometric eligibility.

One report, two nearby centerlines

In local meters, the lower lane runs from A0=(0, 0) to B0=(2, 0), and the upper lane from A1=(0, 0.25) to B1=(2, 0.25). All points lie in the same plane. These are centerlines, with no assigned corridor width, robot footprint or collision boundary.

Start with a reported point at (1, 0.0625). Its perpendicular distances are 0.0625 m to the lower line and 0.1875 m to the upper line. Both projections lie halfway along their respective lanes. At a 0.03125 m lane threshold neither lane is geometrically eligible. At 0.09375 m only the lower one is; at 0.21875 m both are. The point never moves during this comparison.

A second report at (1, 0.125) is equidistant from the two lines. Neither lane meets the first two thresholds; both meet the largest. This is a genuine ambiguity in this distance-only geometric test. It does not establish that an adapter will return two starts, select arbitrarily or choose the wrong route. Those are different questions involving the adapter’s current state and selection logic.

Open-RMF’s EasyFullControl configuration header describes separate maximum merge distances for considering a robot on a waypoint or lane. It also exposes a minimum lane length. The values in this study are ours, chosen to make the distinctions visible; they are neither package defaults nor tuning recommendations.

The measured layout produces these lane-candidate counts. The same threshold values are also used for the separate waypoint comparison below; this does not make the two configuration settings interchangeable.

Report 0.03125 m 0.09375 m 0.21875 m
Offset 0 1 2
Midway 0 0 2
Beyond endpoints 0 0 0

A nearby line can extend beyond the lane

Now move the reported point to (2.0625, 0.0625), just beyond the graph’s right endpoints. Its distance from the lower infinite line is still only 0.0625 m. A test that looks solely at that distance would admit it at the middle threshold.

The projection factor exposes the missing condition. For a lane with endpoints A and B, the closest point on its infinite line is A + u(B − A), where u is the dot product of (p − A) with (B − A), divided by the squared lane length. A finite projection requires 0 ≤ u ≤ 1. Here u=1.03125 for both lines, so neither finite lane is eligible at any of the three thresholds. The calculation deliberately leaves u outside the interval; clamping it would answer a different geometric question.

Waypoint matching remains meaningful. This point is approximately 0.088388 m from B0 and 0.197642 m from B1. Thus the smallest waypoint threshold admits neither, the middle one admits B0, and the largest admits both. These waypoint sets are calculated separately from the lane sets. An endpoint can be nearby even when the perpendicular projection lies beyond the lane.

The inspected EasyFullControl location-update code gives an eligible waypoint priority in this command-update branch, choosing the nearest eligible waypoint among those it tracks. Otherwise it searches eligible tracked lanes, excluding closed and strict lanes. Its ordinary lane branch rejects projections outside the finite interval; lanes shorter than the configured minimum use a separate endpoint-distance rule. The branch can construct additional starts and uses a fallback when its start set is empty. Our geometric counts reproduce none of that stateful behavior. The cited main-branch snapshot is pinned to a commit, not an assertion about every release or installed adapter.

Reproduce the association geometry

This standard-library Python example prints the unclamped factors and distances, followed by separate lane and waypoint sets. Its coordinates are planar meters, and it has no RMF dependency.

from math import hypot

# Synthetic meters; geometric eligibility only, not RMF adapter selection.
waypoints = {"A0": (0, 0), "B0": (2, 0),
             "A1": (0, .25), "B1": (2, .25)}
lanes = {"lower": ("A0", "B0"), "upper": ("A1", "B1")}
points = {"offset": (1, .0625), "midway": (1, .125),
          "beyond": (2.0625, .0625)}

def project(p, a, b):
    dx, dy = b[0] - a[0], b[1] - a[1]
    length2 = dx * dx + dy * dy
    if length2 == 0:
        raise ValueError("A lane needs distinct endpoints")
    u = ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / length2
    q = (a[0] + u * dx, a[1] + u * dy)
    return u, hypot(p[0] - q[0], p[1] - q[1])

for name, p in points.items():
    projections = {k: project(p, waypoints[a], waypoints[b])
                   for k, (a, b) in lanes.items()}
    print(name, "line factors/distances:", projections)
    for threshold in (.03125, .09375, .21875):
        eligible_lanes = [k for k, (u, distance) in projections.items()
                          if 0 <= u <= 1 and distance <= threshold]
        eligible_waypoints = [k for k, v in waypoints.items()
                              if hypot(p[0] - v[0], p[1] - v[1]) <= threshold]
        print(f"  {threshold:.5f} m: lanes={eligible_lanes}, "
              f"waypoints={eligible_waypoints}")

Blender’s intersect_point_line documentation permits factors outside [0, 1]. In the actual experiment the beyond-end factors were both 1.03125; the interior factors were approximately 0.5. Stored-coordinate checks used a fixed 4 µm component bound, and projection distances and factors used bounds of 10 µm and 0.00001 respectively. These numerical allowances are not localization or operational merge tolerances. The common rigid frame preserved the distance comparisons. Blender’s vectors here are x, y, z; the RMF pose representation includes planar heading. We did not test heading-dependent selection.

The Open-RMF architecture guide explains the fleet orchestration context. Map alignment must already be established before this coordinate-to-graph comparison is useful.

What to inspect before widening a threshold

For a troublesome reported pose, keep the position, map frame, lane endpoints and projection factor visible together. A large perpendicular offset, an out-of-bounds projection and a nearby waypoint are different observations. Increasing one distance setting cannot tell you which observation caused the association problem, or repair a pose expressed in the wrong frame.

This model assumes exact assigned coordinates and nonzero lanes. It does not estimate localization uncertainty, validate a map transform, test lane closures, run the short-lane fallback, or assess footprint clearance, scheduling or safety. Its useful result is the distinction itself: distance-based eligibility changes with the chosen threshold, while the reported pose and the graph geometry remain fixed.