Skip to content

feat(px4): Px4DroneConnection, flight supervisor and SITL gate (PX4 stack 3/5) - #4291

Open
Ez4ezka wants to merge 3 commits into
dimensionalOS:mainfrom
Ez4ezka:ezen/feat/px4-3-flight
Open

Ez4ezka wants to merge 3 commits into
dimensionalOS:mainfrom
Ez4ezka:ezen/feat/px4-3-flight

Conversation

@Ez4ezka

@Ez4ezka Ez4ezka commented Sep 25, 2026 •

Copy link
Copy Markdown

What is this feature?

Fly and command a PX4 multicopter from dimOS.

  • Px4DroneConnection (dimos/robot/px4/connection.py): the only module that talks to PX4. One MAVLink socket (pymavlink, one setpoint writer), vehicle streams (odometry, tf, imu, gps, battery, gimbal attitude, vehicle status), and the flight supervisor. Mirrors R1ProConnection.
  • supervisor_core.py: 20 Hz flight state machine, pure logic, no I/O. IDLE > PREFLIGHT > STREAMING > OFFBOARD_REQ > ARMING > TAKEOFF > HOVER > LANDING, with TELEOP and GOTO from HOVER. Preflight checks, RC enable switch, fence and ceiling, abort reasons, E-STOP.
  • mavlink.py: socket, vehicle state, PX4 modes, NED/FLU frames.
  • RPCs: takeoff(altitude_m), go_to(...), land, hold, set_guidance_mode, estop. No arm, mode or raw-setpoint RPC. Goals outside the fence or ceiling are refused. TELEOP holds altitude, and holds position when keys stop.
  • Safety: the RC pilot always wins (PX4 leaves Offboard, the supervisor stops sending). The enable switch low refuses takeoff and aborts to Hold in flight. If the tick thread stops, PX4's Offboard-loss failsafe takes over.
  • Blueprints: px4-basic, px4-drone, px4-sitl, px4-teleop, px4-sitl-teleop. Gate: tool_sitl_gate.py.
  • pymavlink over MAVSDK: mavsdk_server heartbeats as a ground station with no off switch (PX4 arms with no GCS and cannot detect GCS loss), opens an unauthenticated gRPC port, and in SITL had setpoint timing p99 ~10 ms vs ~1 ms.

Why do we need this?

dimOS has no aerial platform. This is the base the skills (#4292) and target following (#4293) build on.

How to Test

uv sync --extra px4
uv run pytest dimos/robot/px4
# PX4 SITL running (HEADLESS=1 make px4_sitl gz_x500), QGC closed:
uv run python dimos/robot/px4/tool_sitl_gate.py --fly

The gate (PX4 v1.16.2): takeoff cancelled by the enable switch never arms; takeoff to 2 m; go-to 2 m south at 3 m; go-to past the fence refused; a held key moves 2 m at a locked 3 m and holds on release; land to IDLE. dimos/robot/px4/README.md has the vehicle setup.

Hardware, props off (Pixhawk 6C, PX4 1.17, Jetson Orin Nano): takeoff refused with the enable switch low; with it high the chain runs to TAKEOFF; switch low aborts to Hold; a stick move hands control to the pilot (PILOT_OVERRIDE). Not flown yet.

Stack

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

  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 (this PR)
  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

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.
@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 0/5

[High risk] Adds PX4 drone stack with flight supervisor and SITL simulation.

Not safe to merge for aircraft use until the flight-control, video, gimbal, calibration, and credential-exposure blockers are resolved. The three remaining concerns are non-blocking.

Findings

  1. P1 Stale heartbeat masks takeover ▶
  2. P1 Unknown accuracy passes preflight ▶
  3. P1 Unknown battery passes preflight ▶
  4. P1 Safety handoffs lack confirmation ▶
  5. P1 Video loss breaks decoding ▶
  6. P1 Security RTSP passwords enter logs ▶
  7. P1 Aim requests cannot actuate ▶
  8. P1 Aircraft zoom gate disabled ▶
  9. P2 SITL calibration mismatches images ▶
  10. P2 Cached zoom never expires ▶
  11. P2 One packet passes camera gate ▶

Summary

The PR adds PX4 flight supervision, A8 camera and gimbal modules, aircraft and simulator blueprints, and a SITL gate. Flight-readiness and safety-handoff failures, video loss, an unconnected gimbal aim path, aircraft zoom calibration, and RTSP credential logging need correction before merging. The SITL calibration, cached-zoom, and camera-gate concerns are non-blocking.

Reviews (1) · Last reviewed commit: "feat(px4): Px4DroneConnection, flight su..."

# HEARTBEAT is 1 Hz and px4_stale_s is 1.0 s, so heartbeat age alone sits on the
# threshold and a few ms of jitter would abort a flight (seen in SITL). Any message
# from 1/1 proves the link.
return min(st.heartbeat_age, st.px4_msg_age)

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 Stale heartbeat masks takeover

If HEARTBEAT stops while other PX4 telemetry continues, this check treats the cached flight mode as current. The supervisor can continue Offboard setpoints after a pilot mode change, or send Hold during an unrelated abort instead of yielding to the pilot. Check heartbeat freshness separately before relying on its mode.

Artifacts

Mocked heartbeat freshness check source

  • The authored Python script feeds mocked messages through VehicleState and executes SupervisorCore.step and stream in both conditions, showing exactly what was tested.

Heartbeat-only comparator output

  • The executed comparator used heartbeat age for freshness and recorded an abort with no setpoint, establishing the same-scope comparison.

Current generic-freshness output

  • The executed unchanged code continued a setpoint with a stale heartbeat and recorded the distinct RC-loss and fresh-pilot-mode outcomes, confirming the defect.

View artifacts

T-Rex Ran code and verified through T-Rex

f.append("local position stale")
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:

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 accuracy passes preflight

A zero or absent GPS horizontal-accuracy reading becomes NaN, which this condition exempts from the 1.5 m limit. With an otherwise valid 3D fix, takeoff preflight advances without confirming the required accuracy. Treat unknown accuracy as a preflight failure.

Artifacts

Executed GPS accuracy preflight check source

  • The authored Python command feeds GPS messages through the real state and supervisor code and asserts both control and unknown-accuracy outcomes.

Preflight with measured GPS accuracy

  • The executed control run shows that 0.8 m passes while 3.0 m is rejected, establishing that the configured limit works for measured values.

Preflight with zero or absent GPS accuracy

  • The executed candidate run shows both inputs becoming NaN and advancing to STREAMING with no preflight failures.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +266 to +268
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

Without SYS_STATUS, battery percentage is -1 and bypasses the minimum; a previously received high reading is also accepted without a freshness check. Takeoff preflight can advance without confirming the required 40% battery level. Require a present, recent reading.

Artifacts

Battery preflight reproduction script

  • The executed script feeds MAVLink-like messages into the real vehicle-state and supervisor code for fresh, missing, and retained battery readings; it provides the reproducible check.

Preflight run with low battery readings

  • The command ran the reproduction with fresh and retained 20% readings and exited successfully; both kept takeoff in PREFLIGHT.

Preflight run with missing and stale battery telemetry

  • The command ran the same reproduction path without SYS_STATUS and with a 100-second-old 80% reading and exited successfully; both advanced takeoff to STREAMING.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +413 to +421
def hold_cmd(
self, st: VehicleSnapshot, m: Px4Actuator, now: float | None = None
) -> Rejection | None:
if self._holds_px4(st):
m.set_mode(MAIN_AUTO, SUB_AUTO_LOITER)
self.sp = None
self.teleop = None
self.entered_offboard = False
self.goto("IDLE", "operator hold", now)

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 Safety handoffs lack confirmation

If PX4 rejects or misses a Hold or Land command, Hold, E-STOP, Land, and abort still clear setpoints and progress as though the handoff succeeded. The operator can be told the aircraft is holding or landing while PX4 remains armed in Offboard. Confirm the resulting mode before reporting completion.

Artifacts

Mock PX4 mode-change reproduction script

  • The authored harness invokes the real connection and supervisor paths against synthetic PX4 feedback for accepted, rejected, and lost commands, making the comparison reproducible.

Accepted mode-change run

  • Ran all four actions with PX4 accepting their mode commands; reported vehicle mode changed to Hold or Land.

Rejected mode-change run

  • Ran the same actions with PX4 rejecting commands; ACK result 2 and OFFBOARD feedback coexist with accepted RPCs and completed supervisor transitions.

Lost mode-change run

  • Ran the same actions without delivering commands or ACKs to PX4; OFFBOARD feedback persists while the supervisor reports Hold, E-STOP, Landing, or IDLE after abort.

View artifacts

T-Rex Ran code and verified through T-Rex

# 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 loss breaks decoding

This transport permits encoded video packets to be dropped under congestion, and the default viewer bridge can discard intermediate packets when it falls behind. With a slowed bridge, only 21 of 50 H.265 packets arrived and those packets decoded to one frame. Provide delivery or recovery that preserves usable video without blocking flight control.

Artifacts

Source for the camera-to-bridge H.265 reproduction

  • This authored script was run in both modes against synthetic camera replay and the PX4 video transport, with a slow callback in the bridge run; it defines the comparison.

Source-packet decode control

  • The executed before command decoded all 50 camera-produced packets into 50 frames and exited 0, establishing that the generated stream is decodable.

Congested default-bridge packet delivery

  • The executed after command observed 21 of 50 packets at the default bridge and decoded one frame before exiting 0, demonstrating loss and broken downstream decode.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +212 to +217
RtspCamera.blueprint(
url=SYNTHETIC_URL,
frame_id="a8_optical",
capture_latency_s=0.08,
),
SiyiA8Gimbal.blueprint(mount_xyz=GIMBAL_MOUNT_XYZ_UNMEASURED),

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 SITL calibration mismatches images

The synthetic camera produces 320×180 images, while its paired gimbal publishes intrinsics for 1280×720. Geometry consumers project simulated pixels in the wrong directions, reducing the usefulness of SITL vision checks. This is non-blocking for aircraft operation; match the calibration to the synthetic images.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Artifacts

SITL camera and gimbal reproduction script

  • Runs the modules selected by the SITL blueprint and compares actual calibration with an isolated corrected calibration, without changing tracked source.

SITL camera output with published intrinsics

  • The executed camera produced a 320×180 image while the gimbal published 1280×720 intrinsics, reversing the sample pixel’s projected angle.

SITL camera output with matching intrinsics

  • The same flow with an isolated 320×180 intrinsics override produced matching dimensions and the expected projected angle.

View artifacts

T-Rex Ran code and verified through T-Rex

if now - self._last_aim_mono < 1.0 / self.config.aim_hz:
return False
self._last_aim_mono = now
self.gimbal_target.publish(

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 Aim requests cannot actuate

The public aim() request reports success after publishing gimbal_target, but the PX4 aircraft blueprint has no consumer for that output and the connection has no gimbal-target input. An accepted request therefore issues no A8 actuator command. Connect the target to a command sender or reject requests that cannot be acted on.

Artifacts

Executable gimbal aim contract scenario

  • The authored Python scenario invokes the public RPC, observes its output, and inspects the PX4 blueprint contract.

Gimbal aim contract before the PX4 change

  • The parent-revision run returned true and published a target; that revision had no PX4 blueprint.

Gimbal aim contract with the PX4 blueprint

  • The current-revision run returned true and published a target while finding no blueprint consumer or PX4 connection input.

View artifacts

T-Rex Ran code and verified through T-Rex

px4_visualization(),
px4_control(),
RtspCamera.blueprint(url=A8_RTSP_URL, frame_id="a8_optical", capture_latency_s=0.08),
SiyiA8Gimbal.blueprint(mount_xyz=GIMBAL_MOUNT_XYZ_UNMEASURED),

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 Aircraft zoom gate disabled

If the aircraft’s A8 is zoomed away from 1×, this blueprint supplies no SDK address, so the gimbal never polls zoom and still publishes fixed 1× intrinsics. Vision consumers then receive calibration for the wrong focal length. Configure zoom polling and withhold calibration when zoom is unknown or unsupported.

Artifacts

Aircraft blueprint and mocked zoom-camera reproduction script

  • The authored script starts the gimbal using real aircraft blueprint arguments and compares them with an SDK-IP-configured control.

Control run with SDK address configured

  • The executed control queried a mocked 2.5x camera twice and published no CameraInfo, showing the zoom gate working.

Run with the unmodified aircraft blueprint

  • The executed blueprint setup made zero zoom queries and published 1x CameraInfo, confirming the missing-address path.

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 Cached zoom never expires

A missing zoom reply retains the last 1× value indefinitely. If the camera reconnects at another zoom before replies resume, incorrect 1× intrinsics continue to be published. This is a non-blocking calibration concern during outages; expire the cached reading so publication pauses until zoom is known.

Artifacts

Clock and mock zoom reproduction script

  • Run this script in before or after mode to exercise the source zoom loop and camera-info gate without hardware.

Original zoom loop after lost replies and reconnect

  • The executed source loop retained cached 1x and published four camera-info messages after the simulated 2.5x reconnect, confirming the defect.

In-memory cache invalidation after lost replies

  • The same executed scenario with only the loop patched in memory withheld camera info after replies stopped, preventing publication at 2.5x.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +216 to +218
# 2. Camera.
print(f"video: {frames} frames in {args.seconds:.0f}s")
ok &= frames > 0

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 One packet passes camera gate

One video packet at the start of the listening window passes this check even if the stream then stalls. The gate can report a healthy camera without detecting a stopped stream, weakening its video check. This is non-blocking; check recent or sustained packet arrivals.

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 Fresh telemetry masks stale flight-mode heartbeat ▶

    • Bug
      • With HEARTBEAT stopped but other PX4 traffic current, the supervisor treats cached OFFBOARD mode as current and keeps streaming setpoints. A separate safety abort can issue Hold based on that cached mode; a pilot mode change cannot be recognized until another HEARTBEAT arrives.
    • Cause
      • supervisor_core.py:254 returns min(st.heartbeat_age, st.px4_msg_age). Both abort_reason and preflight use that value, while VehicleSnapshot.in_offboard reads the last heartbeat without an age check.
    • Fix
      • Check heartbeat freshness separately before relying on its armed or mode fields; keep generic telemetry freshness as a distinct link check, with an appropriate heartbeat-specific tolerance.
  • P1 Unknown GPS horizontal accuracy bypasses the preflight limit ▶

    • Bug
      • With a 3D fix and otherwise valid preflight inputs, zero or absent GPS_RAW_INT.h_acc allows takeoff preflight to advance to STREAMING without a known horizontal accuracy measurement.
    • Cause
      • VehicleState converts zero or missing h_acc to NaN, while supervisor_core.py:264 checks the accuracy limit only when eph is not NaN.
    • Fix
      • Treat non-finite or unknown horizontal accuracy as a preflight failure when the accuracy limit is required.
  • P1 Takeoff preflight accepts missing or stale battery telemetry ▶

    • Bug
      • A missing SYS_STATUS yields battery percentage -1, which preflight accepts. A previously received high battery reading remains usable indefinitely, so fresh non-battery messages can accompany a stale battery value through preflight.
    • Cause
      • VehicleSnapshot.batt_pct returns -1 without SYS_STATUS (dimos/robot/px4/mavlink.py:328-329); snapshots retain the latest SYS_STATUS (:470-474), and preflight rejects only nonnegative percentages below the threshold (dimos/robot/px4/supervisor_core.py:266-268).
    • Fix
      • Require a present, recent, valid battery percentage before advancing takeoff preflight; reject unknown or stale battery telemetry.
  • P1 Safety mode changes are treated as complete without PX4 confirmation ▶

    • Bug
      • Hold and E-STOP report IDLE, E-STOP Land reports LANDING, and a safety abort progresses to IDLE even when PX4 rejects or never receives the mode change. Feedback still shows an armed aircraft in OFFBOARD with no supervisor setpoint.
    • Cause
      • The mode command is fire-and-forget; these transitions clear setpoints and update supervisor state without checking the command ACK or confirming the requested mode in subsequent heartbeats.
    • Fix
      • Keep an explicit pending safety transition, verify PX4 heartbeat mode and command outcome, and report failure or retry/escalate when Hold or Land is not confirmed. Do not report the handoff as complete while feedback remains OFFBOARD.
  • P1 Default PX4 video delivery can break H.265 decoding under congestion ▶

    • Bug
      • With a 95 ms bridge callback against a 25 fps source, the default bridge received 21 of 50 encoded packets; decoding those packets yielded only one frame.
    • Cause
      • The video transport uses best-effort, drop-on-congestion QoS at dimos/robot/px4/blueprints.py:97, while the default bridge uses subscribe_all, whose newest-per-topic buffer can discard intermediate packets at dimos/protocol/pubsub/impl/zenohpubsub.py:233–260. An uncapped Rerun video entity does not prevent either loss.
    • Fix
      • Provide ordered, non-shedding delivery for encoded video through both transport and bridge, or use a video-specific strategy that recovers at keyframes; test it under backpressure without blocking flight-critical work.
  • P1 Camera warnings log RTSP credentials ▶

    • Bug
      • When a live stream disconnects or ends, the warnings include the configured RTSP URL, exposing its username and password in console and JSON logs.
    • Cause
      • dimos/hardware/sensors/camera/rtsp/camera.py lines 177 and 185 pass self._source directly as the structured url field.
    • Fix
      • Redact URL userinfo before logging either warning; also sanitize exception text, which may contain the URL.
  • P1 Accepted gimbal aim requests have no PX4 actuator path ▶

    • Bug
      • In px4-drone, SiyiA8Gimbal.aim() returns true and increments aim_sent after publishing gimbal_target, but no module consumes that output. An accepted request therefore does not command the A8.
    • Cause
      • blueprints.py includes the gimbal producer without a gimbal_target consumer or routing; Px4DroneConnection has no target input or gimbal command sender.
    • Fix
      • Connect gimbal_target to the MAVLink-owning connection and implement the A8 command sender, then make the RPC's acceptance result reflect whether an actuator command can be issued.
  • P1 Aircraft blueprint leaves zoom polling off while publishing 1x intrinsics ▶

    • Bug
      • When the aircraft's A8 is physically zoomed away from 1x, consumers can receive CameraInfo calibrated for 1x. The mock reproduced this at 2.5x; actual hardware zoom was not observed.
    • Cause
      • px4_drone passes only mount_xyz to the gimbal (dimos/robot/px4/blueprints.py:194-203). The SDK IP therefore defaults to None (dimos/hardware/gimbal/siyi/gimbal.py:100-104), so start() creates neither an SDK client nor a zoom-poll thread (lines 133-147). With zoom unknown, publish_camera_info() permits publication specifically when IP is None (lines 285-296). The environmental conditional is an aircraft A8 actually zoomed away from 1x while no SDK IP is configured; at 1x the fixed intrinsics are appropriate.
    • Fix
      • Configure the aircraft A8's reachable SDK IP in the aircraft blueprint so zoom polling runs and CameraInfo is withheld whenever polled zoom is unknown or not 1x.
  • P1 Stale 1x zoom permits incorrect camera intrinsics after reconnect ▶

    • Bug
      • When zoom queries stop replying after a 1x reading, camera_info continues to publish even if the camera reconnects at a different zoom. Publication stops only when a 2.5x reply arrives.
    • Cause
      • gimbal.py:239-242 updates _zoom only for non-None replies, leaving the last 1x value indefinitely eligible at the gate in lines 285-296.
    • Fix
      • Invalidate the cached zoom when a query fails, withholding camera info until a fresh 1x reply is received.
  • P2 SITL camera publishes intrinsics for the wrong image size ▶

    • Bug
      • Decoded synthetic images are 320×180, but the gimbal publishes CameraInfo for 1280×720 under the same a8_optical frame ID. A pixel to the right of image center projects left, misdirecting geometry consumers.
    • Cause
      • The SITL blueprint selects the synthetic camera, which generates 320×180 frames, while SiyiA8Gimbal initializes its CameraInfo using the default A8 dimensions of 1280×720.
    • Fix
      • Make SITL CameraInfo dimensions and intrinsics match the synthetic frames, or generate synthetic frames at the published calibration size.
  • P2 SITL gate accepts a stalled video stream ▶

    • Bug
      • A single early video packet is sufficient for a pass even if the camera produces no packets for the remainder of the window.
    • Cause
      • dimos/robot/px4/tool_sitl_gate.py:216-218 checks only the cumulative condition frames > 0; it does not check recent arrivals or sustained frame rate.
    • Fix
      • Check video freshness or a minimum frame rate across the window, including its final interval.

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