Skip to content

feat(px4): target following on dimos/perception detections (PX4 stack 5/5) - #4293

Open
Ez4ezka wants to merge 5 commits into
dimensionalOS:mainfrom
Ez4ezka:ezen/feat/px4-5-follow
Open

Ez4ezka wants to merge 5 commits into
dimensionalOS:mainfrom
Ez4ezka:ezen/feat/px4-5-follow

Conversation

@Ez4ezka

@Ez4ezka Ez4ezka commented Sep 25, 2026 •

Copy link
Copy Markdown

What is this feature?

Follow a selected target, on the existing dimos/perception/detection stack.

  • dimos/perception/geolocation/: pixel to line of sight to ground intersection, and the target filter. No PX4 or MAVLink in it.
  • PerceptionBridge (dimos/robot/px4/perception_bridge.py): consumes Detection2DArray from Detection2DModule (track ids from the detector), publishes target_state, target_valid, target_los. Selection by RPC (select_track, clear_selection).
  • Flight side: follow.py (YAW_TRACK, FOLLOW, target-loss ladder), the connection's target_* inputs, the vehicle-clock timebase (frames and vehicle data on one clock), the gimbal aim path.
  • Blueprints px4-follow, px4-sitl-follow. SITL runs the real Detection2DModule with a blob detector, so no GPU or model is needed. Gate: tool_follow_gate.py.

Why do we need this?

Target tracking is the drone's main task. This reuses dimOS detection instead of a separate detector.

How to Test

uv run pytest dimos/perception/geolocation dimos/robot/px4
uv run python dimos/robot/px4/tool_follow_gate.py --offline   # no simulator
# PX4 SITL running, QGC closed:
uv run python dimos/robot/px4/tool_follow_gate.py --fly

SITL: line-of-sight error 0.00 deg; YAW_TRACK turns toward the target at the law's rate; FOLLOW flies along the target bearing at the 2 m/s cap; target loss holds position and returns to HOVER after 5 s. Geometry is ported from code flown on the aircraft; detection with dimOS tracker ids has not flown.

Seen while wiring it: Detection2DBBox.to_ros_detection2d never sets results_length (class id and score are lost on the wire).

Stack

Five PRs, all against main; each contains the ones above it. Merge in order. Review only this PR's own commit: 6f630cd

  1. feat(hardware): SIYI A8 gimbal and RTSP H.265 camera (PX4 stack 1/5) #4289 SIYI A8 gimbal and RTSP camera
  2. feat(msgs): NavSatFix, BatteryState and PX4 VehicleStatus (PX4 stack 2/5) #4290 NavSatFix, BatteryState, PX4 VehicleStatus
  3. feat(px4): Px4DroneConnection, flight supervisor and SITL gate (PX4 stack 3/5) #4291 PX4 connection, flight supervisor, SITL gate
  4. feat(px4): agent skills and agentic blueprints (PX4 stack 4/5) #4292 agent skills
  5. feat(px4): target following on dimos/perception detections (PX4 stack 5/5) #4293 target following on dimos/perception detections (this PR)

Which issue(s) does this PR close?

None. New platform: PX4 multicopters with a SIYI A8 gimbal camera.

Checklist

  • I have read and approved the CLA.

🤖 Generated with Claude Code

RtspCamera (dimos/hardware/sensors/camera/rtsp): RTSP URL, file or a
generated clip in; video (encoded, untouched), color_image and color_jpeg
out. PyAV only, url is required, set_video_enabled() and set_jpeg_rate()
are RPCs. Blueprint rtsp-camera-vis.

SiyiA8Gimbal (dimos/hardware/gimbal/siyi): gimbal tf chain, camera_info
(a8_camera_info() is the one source of the A8 intrinsics), aim requests
on gimbal_target. ip is a config field with no default; with it set the
SIYI SDK client polls the zoom and camera_info is withheld off 1x.

New px4 extra: av.

Tests: pytest dimos/hardware/gimbal/siyi dimos/hardware/sensors/camera/rtsp
-> 37 passed (gimbal 22, camera 15).
sensor_msgs.NavSatFix and sensor_msgs.BatteryState wrap the dimos_lcm
types and take their enum values from them. px4_msgs.VehicleStatus is a
hand-written LCM type with the wire layout of sensor_msgs/ImuInfo
(fingerprint, Header, big-endian fields); its base hash is an arbitrary
constant, there is no .lcm schema. The wire helpers are in
dimos/msgs/lcm_wire.py.

No registry, pyproject or lock change. The producer is Px4DroneConnection
in the next commit: its gps, battery and vehicle_status outputs.

Tests: pytest dimos/msgs/px4_msgs dimos/msgs/sensor_msgs/test_NavSatFix.py
dimos/msgs/sensor_msgs/test_BatteryState.py -> 6 passed (43 with the
payload tests below).
dimos/robot/px4:
- connection.py: Px4DroneConnection, the one MAVLink link. The vehicle as
  streams (odometry, imu, gps, battery, gimbal_attitude, vehicle_status)
  and the operator RPCs takeoff, go_to, land, hold, set_guidance_mode,
  estop*. No arm, mode or raw-setpoint RPC.
- supervisor_core.py: 20 Hz state machine, no I/O. Preflight, RC enable
  switch, fence and ceiling, pilot override, E-STOP latch, go-to, TELEOP.
  When the tick stops the setpoints stop and PX4's Offboard-loss failsafe
  takes over.
- mavlink.py: pymavlink socket, vehicle state, PX4 modes, NED/FLU frames.
- blueprints.py: px4-basic, px4-drone, px4-sitl, px4-teleop,
  px4-sitl-teleop. tool_sitl_gate.py runs px4-sitl against PX4 SITL.

px4 extra += pymavlink; stubs/pymavlink/mavutil.pyi is extended for mypy.

Tests: pytest dimos/robot/px4 -> 103 passed (connection 10, mavlink 21,
supervisor_core 4, supervisor_operator 68); 146 with the payload and
message tests below.
Px4SkillContainer: takeoff, go_to, land, set_guidance_mode, flight_status.
Each skill is the connection RPC plus a wait for the outcome, so the
supervisor refuses a skill exactly as it refuses the RPC; a takeover while
the hover settles is reported, not read as an arrival.

connection_spec.py holds the RPCs the skills call. px4-agentic and
px4-sitl-agentic are in blueprints_agentic.py because they need the agents
extra. supervisor_core.py gains TAKEOFF_STATES, which the takeoff skill
waits on.

Tests: pytest dimos/robot/px4/test_skill_container.py -> 13 passed, against
a scripted connection plus one contract test against the real
Px4DroneConnection and SupervisorCore.status(); pytest dimos/robot/px4 ->
116 passed; 159 with the payload and message tests below.
- dimos/perception/geolocation: pixel to line of sight to ground point,
  target estimators.
- perception_bridge.py: PerceptionBridge turns the selected detection of
  the upstream Detection2DModule into target_state, target_valid and
  target_los.
- follow.py: YAW_TRACK and FOLLOW guidance. The connection gains the
  target_* inputs, the gimbal aim path over MAVLink and global_pose.
- timebase.py: vehicle clock; PX4's own streams are stamped at the vehicle
  instant (gimbal_attitude at receipt) and odometry carries the attitude
  interpolated to the position sample.
- sitl.py: FakeA8 and BrightBlobDetector for the simulator twin.
- blueprints_follow.py: px4-follow, px4-sitl-follow. tool_follow_gate.py,
  with an --offline mode that needs no simulator.

Tests: pytest dimos/robot/px4 dimos/perception/geolocation -> 156 passed
(new: follow 4, timebase 8, perception_bridge 8, geolocation 10,
connection +8, mavlink +2); 199 with the payload and message tests below.
@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 0/5

[High risk] Adds PX4 drone control and perception integration modules.

Not safe to merge until the flight-readiness, active-guidance, target-timing, and video-delivery failures are addressed. The logging and zoom concerns are non-blocking.

Findings

  1. P1 Video loses dependent units ▶
  2. P1 Unknown battery passes preflight ▶
  3. P1 Unverified GPS passes preflight ▶
  4. P1 Estimator status need not be current ▶
  5. P1 Invalid position does not abort ▶
  6. P1 Target uses mismatched positions ▶
  7. P1 Invalid target still drives FOLLOW ▶
  8. P2 Security RTSP credentials enter logs ▶
  9. P2 Zoom reading never expires ▶

Summary

The PR adds PX4 flight control, target following, camera and gimbal modules, SITL support, and operator blueprints. It should not merge yet: preflight can proceed without verified battery, GPS, or estimator data; active guidance can continue after position or target validity is lost; delayed detections displace FOLLOW targets; and congested video delivery loses H.265 units. RTSP credential logging and stale zoom calibration are additional, non-blocking concerns.

Reviews (1) · Last reviewed commit: "feat(px4): target following on dimos/per..."

# Out of the connection: the supervisor.
("supervisor_state", String): _zenoh_transport("supervisor_state", String),
# Camera and gimbal.
("video", CompressedVideo): _zenoh_transport("video", CompressedVideo, latest_wins=True),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Video loses dependent units

The H.265 stream sends interdependent units through a latest-wins transport. When the viewer falls behind, discarded units leave it unable to decode the complete stream until a usable keyframe arrives. A stalled-viewer check delivered only units 0 and 24 of 25 and decoded one frame. Video delivery must preserve a decodable sequence before merging.

Artifacts

Offline H.265 transport probe script

  • The authored script generated a synthetic clip, published its units over local Zenoh, and decoded received units to compare unstalled and stalled viewer callbacks; it exercises the reported dependency loss.

Unstalled viewer: all video units decode

  • The recorded command delivered all 25 H.265 units through local Zenoh and decoded 25 matching frames; the stream remained complete.

Stalled viewer: dependent video units missing

  • The recorded command stalled the viewer callback while publishing the same clip and delivered only units 0 and 24, yielding one decoded frame; the stream became incomplete.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +270 to +272
batt = st.batt_pct
if batt >= 0 and batt < c.min_batt_pct:
f.append(f"battery {batt}%")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unknown battery passes preflight

When battery status is missing or reports −1, this condition skips the 40% minimum. Both cases passed preflight and issued an arm request after a simulated Offboard acknowledgement, so the supervisor can attempt takeoff without establishing sufficient charge. PX4 arm acceptance was not established. Require a valid battery reading before merging.

Artifacts

Isolated battery preflight test script

  • The executed script drives real supervisor methods with recorded actuator calls, separating preflight from later Offboard and arming acknowledgements.

Preflight output with a known 30% battery

  • The control run held a 30% reading in PREFLIGHT until timeout and recorded no arm request.

Preflight output with missing and unknown battery readings

  • The unknown-reading runs passed preflight and requested arming only after a simulated Offboard acknowledgement, confirming the gate bypass but not PX4 arm acceptance.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +266 to +269
if st.gps is None or st.gps.fix < c.min_fix_type:
f.append("GPS fix")
elif not math.isnan(st.gps.eph) and st.gps.eph > c.max_eph_m:
f.append(f"eph {st.gps.eph:.1f}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unverified GPS passes preflight

A 3D fix with unknown horizontal accuracy (NaN EPH) passes this comparison, and the check does not consider when the fix arrived. A fix last received 120 seconds earlier passed preflight while other inputs remained fresh. Takeoff can therefore proceed without establishing the required current, accurate GPS fix. Enforce both requirements before merging.

Artifacts

Isolated GPS preflight reproduction script

  • The authored script feeds a GPS message into VehicleState and runs supervisor preflight with fresh or stale GPS.

Preflight run with fresh GPS and unknown accuracy

  • The captured command and output show a fresh 3D fix with NaN accuracy passing preflight and streaming a setpoint.

Preflight run with 120-second-old GPS and unknown accuracy

  • The captured command and output show the same preflight and streaming result despite a GPS sample timestamp 120 seconds old.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +281 to +284
# PX4 refuses to arm for Offboard without an absolute position estimate; saying so
# here beats "arming refused" three seconds later.
if st.estimator is not None and not st.estimator.position_valid:
f.append("position estimate not valid")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Estimator status need not be current

This check rejects invalid flags only when estimator status exists; it neither requires a status nor checks its age. Missing status and an hour-old valid status both passed preflight and produced Offboard and arm requests. The supervisor thus cannot establish that absolute position is currently valid before attempting takeoff. PX4 arm acceptance was not established. Require current valid status before merging.

Artifacts

Offline estimator preflight check script

  • The authored script exercises the snapshot and supervisor transitions with a recording actuator, without connecting to an aircraft.

Preflight output with a fresh invalid estimate

  • Running the control case left the supervisor in PREFLIGHT with no mode or arm calls, showing that invalid flags are rejected.

Preflight output with missing estimator status

  • Running without estimator status reached ARMING and recorded Offboard and arm calls, showing that absence passes the gate.

Preflight output with an hour-old estimator status

  • Running with hour-old valid flags reached ARMING and recorded Offboard and arm calls, showing that age is not checked.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +294 to +313
def abort_reason(self, st: VehicleSnapshot) -> tuple[str, Rejection] | None:
c = self.cfg
if self._px4_age(st) > c.px4_stale_s:
return "PX4 heartbeat lost", Rejection.STALE_INPUT
if not self.enable_switch(st):
return "enable switch off / RC lost", Rejection.ENABLE_SWITCH_OFF
# The guidance laws and the fence below fly on this sample; a frozen one blinds both.
if st.local is None or st.local_age > c.px4_stale_s:
return "local position lost", Rejection.STALE_INPUT
batt = st.batt_pct
if 0 <= batt < c.min_batt_pct - 10:
return f"battery {batt}%", Rejection.BATTERY
if st.local and self.takeoff:
dist = math.hypot(st.local.n - self.takeoff.n, st.local.e - self.takeoff.e)
alt = self.takeoff.d0 - st.local.d
if dist > c.geofence_radius_m:
return f"geofence {dist:.0f} m", Rejection.FENCE
if alt > c.max_alt_m:
return f"altitude {alt:.1f} m", Rejection.CEILING
return None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Invalid position does not abort

If the estimator reports absolute position invalid while local-position messages remain fresh, this abort check does not react. FOLLOW and GOTO both continued issuing velocity setpoints, so guidance and fence decisions rely on a position estimate PX4 has declared invalid. Stop guidance on invalid estimator status before merging.

Artifacts

Offline supervisor reproduction script

  • The authored script feeds estimator flags through the MAVLink state parser and exercises both guidance modes, providing a repeatable isolated check.

Unmodified supervisor with valid and invalid estimator flags

  • The command ran against the unmodified product and shows FOLLOW and GOTO still sending velocity setpoints when absolute position is invalid.

Supervisor with a runtime-only estimator guard

  • The command applied a guard only to the test instance and shows invalid-estimator cases aborting and requesting Hold while valid controls continue.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +202 to +209
heading = self._heading.at(capture_time)
pos = self._position
veh = VehicleGeo(
yaw_deg=heading["yaw"] if heading else None,
attitude_age_s=_gap_s(self._heading, capture_time) if heading else math.inf,
n=pos[0] if pos else None,
e=pos[1] if pos else None,
d=pos[2] if pos else None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Target uses mismatched positions

Heading is looked up at image capture time, but position comes from the latest odometry. When an image is processed after the aircraft moves, this shifts the projected ground target and changes FOLLOW's bearing. With 400 ms of latency and 4 m of movement, the calculated target shifted 4 m east. Use a capture-time position before merging.

Artifacts

Delayed-image moving-aircraft reproduction script

  • The authored script sends timestamped odometry and a delayed wire-encoded detection through the bridge, then calculates FOLLOW commands; it also provides a script-only capture-position control.

Shipped behavior with delayed detection

  • The executed shipped path used the latest 4 m-east position with capture-time heading and published an east target of 19.894 m.

Capture-position control with delayed detection

  • The same executed path with only a script-side capture-position substitution published an east target of 15.894 m and a different FOLLOW bearing.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +399 to +400
def _on_target_valid(self, msg: Bool) -> None:
self._target_valid = bool(msg.data)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Invalid target still drives FOLLOW

Receiving target_valid=False changes this input flag without forwarding the change to guidance. If no subsequent position or line-of-sight message arrives, FOLLOW continues treating the previous target as valid until its position times out. The next supervisor tick still sent a 2.0 m/s pursuit setpoint after invalidation. Forward validity changes immediately before merging.

Artifacts

Offline target-validity contract test

  • The authored executable exercises the connection handler, supervisor tick, and recorded actuator setpoints without an aircraft.

FOLLOW setpoint before target invalidation

  • The executed baseline records a fresh target and a 2.0 m/s north velocity setpoint.

FOLLOW setpoint after target invalidation

  • The executed invalidation run shows velocity continuing despite a false input flag, then changing to position hold after a position update.

View artifacts

T-Rex Ran code and verified through T-Rex

self.relay_once()
except (av.FFmpegError, OSError, IndexError, ValueError) as exc:
self._count("video", errors=1)
logger.warning("camera stream unavailable", url=self._source, error=str(exc))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 security RTSP credentials enter logs

If the configured RTSP URL contains user:password@host, stream errors and stream-end warnings write that userinfo into application logs; error text can expose it too. This non-blocking concern creates avoidable credential exposure wherever those logs are retained or read.

How this was verified: Both warning paths recorded dummy URL credentials in console and structured log fields.

Artifacts

Dummy-credential RTSP logging test script

  • The authored script drives both warning paths and compares current logging with a test-only redaction wrapper, without contacting a camera.

Current logging exposes dummy RTSP credentials

  • Running the unchanged warning paths captured the dummy username and password in console output and JSON records.

Test-only redaction removes dummy RTSP credentials

  • Running the same paths with a test-only logger wrapper captured warnings without userinfo, demonstrating a comparison rather than a product change.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +239 to +242
zoom = self._sdk.query_zoom()
if zoom is not None: # a lost reply keeps the last known zoom
with self._lock:
self._zoom = zoom

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Zoom reading never expires

Failed zoom polls retain the last successful reading indefinitely. If the camera zoom changes while replies are unavailable, CameraInfo continues publishing newly stamped 1× intrinsics. This non-blocking concern can cause consumers to geolocate detections with the wrong calibration.

Artifacts

Scripted zoom poll and CameraInfo publish probe

  • The authored script runs the real poll and publish methods with scripted SDK replies and a simulated zoom change, alongside a fail-closed comparison.

Unmodified gimbal publishes after failed zoom polls

  • The executed unmodified path retains zoom 1.0 and publishes on both failed polls despite the simulated physical zoom reaching 2.5×.

Fail-closed comparison withholds CameraInfo

  • The executed comparison clears zoom on failed polls and withholds both subsequent CameraInfo messages.

View artifacts

T-Rex Ran code and verified through T-Rex

@greptile-apps

greptile-apps Bot commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

Comments Outside Diff

These findings sit on lines the diff does not cover, so they could not be posted inline. Each one leaves this list once its file changes.

  • P1 PX4 video transport permits dependent H.265 units to be dropped ▶

    • Bug
      • The new video mapping at dimos/robot/px4/blueprints.py:106 uses latest-wins even though the camera publishes each encoded access unit separately (camera.py:254–261). In the offline local-Zenoh check, a stalled viewer callback received units 0 and 24 rather than all 25, and only one frame decoded. The blueprint itself notes at lines 153–155 that dropping H.265 units breaks viewer decoding.
    • Cause
      • latest_wins=True selects QOS_LATEST_WINS (best_effort reliability and drop congestion control); the viewer's Zenoh.subscribe_all independently retains only the newest pending message per topic (zenohpubsub.py:233–258). Neither mechanism preserves a decodable sequence under backlog.
    • Fix
      • Use a delivery path that preserves ordered H.265 access units end to end, or implement codec-aware dropping that resumes at a keyframe; changing publisher QoS alone will not remove the viewer's latest-only coalescing.
  • P1 Unknown battery reading passes the preflight battery gate ▶

    • Bug
      • A missing SYS_STATUS or battery value of −1 lets takeoff progress toward arming despite the configured 40% minimum.
    • Cause
      • VehicleSnapshot.batt_pct returns −1 when status is missing (dimos/robot/px4/mavlink.py:339-341), while preflight_failures only rejects nonnegative values below the minimum (dimos/robot/px4/supervisor_core.py:260-272). The later battery abort check also excludes negative values (supervisor_core.py:303-305).
    • Fix
      • Reject unknown battery readings in preflight, or explicitly require a valid battery measurement before takeoff.
  • P1 Preflight accepts unknown accuracy and stale GPS data ▶

    • Bug
      • A 3D fix with NaN eph passes the accuracy check. The same fix still passes after 120 seconds without a new GPS message, provided other link and position data remain fresh.
    • Cause
      • The math.isnan exception at supervisor_core.py:268 deliberately skips the accuracy limit for unknown accuracy; preflight checks neither GpsFix.t nor a GPS age. The snapshot retains the last GPS fix without exposing a computed GPS-age field.
    • Fix
      • Define whether unknown receiver accuracy should block preflight, and enforce that policy explicitly. Compute GPS age from GpsFix.t at snapshot time and reject fixes older than a configured threshold.
  • P1 Invalid absolute-position estimate does not abort FOLLOW or GOTO ▶

    • Bug
      • While local-position samples remain fresh, both guidance modes continue issuing velocity setpoints after the estimator reports absolute position invalid.
    • Cause
      • SupervisorCore.abort_reason checks local-position freshness but not st.estimator.position_valid, although preflight checks that flag.
    • Fix
      • Add an invalid-estimator check to the active-flight abort path, with coverage for FOLLOW and GOTO.
  • P1 Delayed detections shift the FOLLOW target by aircraft movement ▶

    • Bug
      • A valid delayed frame produces a ground target 4 m east of the capture-position result in the reproduced scenario, changing FOLLOW's commanded bearing.
    • Cause
      • In dimos/robot/px4/perception_bridge.py:202-209, _geo interpolates heading at capture_time but reads _position, which _on_odometry overwrites with the latest position at lines 140-145.
    • Fix
      • Buffer odometry position with its timestamp and interpolate it at capture_time alongside heading; use that synchronized pose for ground intersection.
  • P1 Target invalidation does not reach FOLLOW guidance immediately ▶

    • Bug
      • Receiving target_valid=False while FOLLOW has a fresh position does not stop its velocity command on the next supervisor tick. The offline consumer test recorded another 2.0 m/s north setpoint; a later position update changed it to position hold.
    • Cause
      • dimos/robot/px4/connection.py:399-400 updates only _target_valid. It does not call _push_target; lines 402-424 push on LOS or position events instead. follow.py:151-158,208-227 therefore continues to treat the previously forwarded target as fresh and generate velocity setpoints.
    • Fix
      • On a validity change, acquire _core_lock and forward the updated target validity to guidance immediately, preserving the original position timestamp.
  • P1 RTSP credentials are written to stream warning logs ▶

    • Bug
      • When a configured RTSP URL contains userinfo, stream errors and normal live-stream endings write those credentials to console and structured JSON logs. Reconnection can repeat the exposure.
    • Cause
      • _relay_loop passes the unredacted source URL to both warnings and the unredacted exception text to the error warning.
    • Fix
      • Redact URL userinfo before logging and sanitize or omit exception text that can contain the URL.
  • P1 Failed zoom polls allow stale 1× intrinsics to keep publishing ▶

    • Bug
      • After a successful 1× poll, two failed polls while the simulated physical zoom is 2.5× still produce CameraInfo messages with newly assigned timestamps and 1× focal length.
    • Cause
      • _zoom_loop updates _zoom only for non-None replies at gimbal.py:239-242. publish_camera_info then accepts the retained 1× value and timestamps the cached intrinsics at gimbal.py:285-296.
    • Fix
      • Invalidate the cached zoom on a failed poll, or expire it after a bounded age, so CameraInfo is withheld until 1× is confirmed again.
  • P2 Preflight accepts missing or stale estimator status ▶

    • Bug
      • With otherwise passing inputs, either condition allows an Offboard mode request followed by an arm request. The check therefore does not provide the early rejection described in its comment.
    • Cause
      • dimos/robot/px4/supervisor_core.py:281-284 rejects only a present estimator whose flags are invalid. dimos/robot/px4/mavlink.py:481-499 passes the stored estimator into snapshots without checking its age. The resulting empty failure list advances through supervisor_core.py:537-578.
    • Fix
      • Require a present, sufficiently recent estimator status with valid position flags before leaving PREFLIGHT.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

first-time-contributor PR opened by an author who had not previously committed to this repository

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant