swarmflow op• Data kinds: table → table
• Call: import fullseye as fs; fs.ledger.obstacle_fit_doublet(field, speed=None, upstream_only: 'bool' = False, exclude_margin: 'float' = 1.25, min_explained: 'float' = 0.5, min_radius=None) -> 'dict' (to call the implementation directly, import swarmflow; swarmflow.obstacle_fit_doublet(field, speed=None, upstream_only: 'bool' = False, exclude_margin: 'float' = 1.25, min_explained: 'float' = 0.5, min_radius=None) -> 'dict'; from the registry, opsdrive.get("obstacle_fit_doublet"))
Fits the potential flow around a cylinder u − i v = U (1 − R²/(z − z_c)²) to the velocity field (implementation 2, 2-D nonlinear least squares).
Unknowns are (x_c, y_c, R², U). Two starting points: stagnation_from_centerline, and a coarse search using the grid nodes as candidate centres (with U fixed, the model is linear in R², so it is solved in closed form). The one with the larger explained fraction is taken. Grid points inside exclude_margin times the circle are not used (because the kernel that transfers onto the grid straddles the wall and averages out the speed) —— re-estimate and iterate 3 times. With upstream_only, the fit uses only the grid points upstream of the impact point (x < x_c − R) (can it be known "before hitting it"?).
Decision: relative to the residual RSS₀ of a uniform-flow-only model (u, v constant), the fraction reduced by the cylinder model 1 − RSS₁/RSS₀ (the fraction of the disturbance explained by the cylinder) is at least min_explained, the radius is at least min_radius (default = 2 grid spacings), and the centre lies within the field —— these 3 give detected. A negative verdict (no obstacle) comes from this fraction being small. ★The lower bound on the radius is the resolution limit: the grid of swarm_field_from_tracks is spaced at the individual spacing s, so by default obstacles with R < 2s are not declared "present". That the refusal is justified was confirmed by measurement (uniformly scattered particles, R = 0.6 m: at R/s ≈ 5 both centre and radius within 2 %, at R/s ≈ 2.5 within 5 %, at R/s ≈ 1.2 the radius comes out 48 % too small).
Returns:
dict: center, radius, speed, stagnation, explained, detected, n_used, rms_residual
(m/s), rms_null (m/s).
• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).
• Operator provenance and references — the sources of the research/methods this op family came from.
• The canonical algorithm (author, year) and its uses are named in the family usage guide above.
• poc_swarm_obstacle_from_flow — py -3.11 examples/poc_swarm_obstacle_from_flow.py
table as input)course_layout · course_occupancy · course_contains · world_build · world_camera · world_move · lidar_scan · rss_longitudinal_same
swarmflow)sph_kernel · sph_density_pressure · swarm_simulate · swarm_render_overhead · swarm_field_from_tracks · swarm_field_from_piv · potential_flow_cylinder · velocity_deficit_map
*Provenance: swarmflow.py — DRIVE operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.