feat(mavlink): Support MAV_CMD_DO_SET_GLOBAL_ORIGIN #24697
Conversation
f330c95 to
d330a1a
Compare
🔎 FLASH Analysispx4_fmu-v5x [Total VM Diff: 0 byte (0 %)]px4_fmu-v6x [Total VM Diff: -8 byte (-0 %)]Updated: 2025-07-30T08:06:56 |
|
@julianoes Many tests fail, but I think that is unrelated. I don't do any checking of the passed extra parameters. Given |
d330a1a to
cebf5bb
Compare
|
This PR was identified as stale and it will be closed in 30 days unless any activity is detected. |
|
@julianoes was there something preventing this to be merged? |
|
It's conflicting now, and I don't see the actual code that makes this work? Somehow it disappeared from the diff? |
|
That is odd. I'm going to have to do this again, and retest. Let's leave it open, but I will push back into draft. |
|
This pull request has been mentioned on Discussion Forum for PX4, Pixhawk, QGroundControl, MAVSDK, MAVLink. There might be relevant details there: https://discuss.px4.io/t/settings-for-topic-aux-global-position/48665/3 |
c43d3f3 to
d337dbe
Compare
| if (vehicle_command.command == vehicle_command_s::VEHICLE_CMD_SET_GPS_GLOBAL_ORIGIN | ||
| || vehicle_command.command == vehicle_command_s::VEHICLE_CMD_DO_SET_GLOBAL_ORIGIN) { |
There was a problem hiding this comment.
FYI. Note, separate handling for new command and the old message-handled as a command - but in the same place. Same update had to happen in BlockLocalPositionEstimator.cpp too.
🔎 FLASH Analysispx4_fmu-v5x [Total VM Diff: 24 byte (0 %)]px4_fmu-v6x [Total VM Diff: 24 byte (0 %)]Updated: 2026-07-01T21:41:57 |
julianoes
left a comment
There was a problem hiding this comment.
Looks right! And is backwards compatible.
d337dbe to
5af722f
Compare
MAV_CMD_DO_SET_GLOBAL_ORIGINwas added in mavlink/mavlink#2247 to replace the old message versionSET_GPS_GLOBAL_ORIGIN.This has the same behaviour - it sets the origin, emits an ACK if the command succeeded, emitting
GPS_GLOBAL_ORIGIN.Note that this ACKs if the origin is not changed and still emits the message.
I have tested this by sending the command with different and same values and noting that the message is emitted in both cases.
Note:
Claude deetail on all this is below in details. The short version though is that this adds a VehicleCommand.msg enum for the command, then handles it everywhere that the old message is handled.
Also see:
Details
PX4 already handles the legacy SET_GPS_GLOBAL_ORIGIN MAVLink message (ID 48) via
handle_message_set_gps_global_origin(), which converts it to the internal
VEHICLE_CMD_SET_GPS_GLOBAL_ORIGIN (100000) and publishes it for EKF2 to process.
MAV_CMD_DO_SET_GLOBAL_ORIGIN (command ID 611, development dialect) is the newer
COMMAND_INT-based replacement that supersedes the legacy message. It is currently
commented out in command_has_location() as "not supported". Adding support requires:
depending on MAVLink headers
with the correct command ID (611, not 100000)
MAV_CMD_DO_SET_GLOBAL_ORIGIN is only defined in the MAVLink development dialect
(MAVLINK_ENABLED_DEVELOPMENT). Boards that don't use the development dialect must not
fail to compile.
Files to Modify
Add a constant for 611 near the other standard MAVLink commands (before line 126 where
PX4-internal commands begin):
uint16 VEHICLE_CMD_DO_SET_GLOBAL_ORIGIN = 611 # Sets GNSS coordinates of the vehicle local origin. Supersedes
SET_GPS_GLOBAL_ORIGIN message. |Unused|Unused|Unused|Unused|Latitude (WGS-84)|Longitude (WGS-84)|[m] Altitude
(AMSL)|
In command_has_location() (line 537-553): Move MAV_CMD_DO_SET_GLOBAL_ORIGIN from
the commented-out "not supported" block into the supported return true section, guarded
so it only compiles when the development dialect is active:
case MAV_CMD_EXTERNAL_POSITION_ESTIMATE: // 43003
#ifdef MAVLINK_ENABLED_DEVELOPMENT
case MAV_CMD_DO_SET_GLOBAL_ORIGIN: // 611
#endif
return true;
No changes are needed to handle_message_command_both() — the command naturally falls
through to the default _cmd_pub.publish(vehicle_command) branch (line 856) with
send_ack = false, so EKF2 handles the ack.
In the command handler (line 523): Add the new vehicle command constant alongside
the legacy one:
if (vehicle_command.command == vehicle_command_s::VEHICLE_CMD_SET_GPS_GLOBAL_ORIGIN
|| vehicle_command.command == vehicle_command_s::VEHICLE_CMD_DO_SET_GLOBAL_ORIGIN) {
Because command_ack.command = vehicle_command.command (line 519), the ack will echo
back 611 to the GCS when the new command is used, and 100000 for the legacy path.
In the command handler (line 178): Same pattern as EKF2:
if (vehicle_command.command == vehicle_command_s::VEHICLE_CMD_SET_GPS_GLOBAL_ORIGIN
|| vehicle_command.command == vehicle_command_s::VEHICLE_CMD_DO_SET_GLOBAL_ORIGIN) {
Data Flow After Change
GCS sends COMMAND_INT (id=611, frame=GLOBAL, x=lat_1e7, y=lon_1e7, z=alt_m)
→ handle_message_command_int()
→ command_has_location(611) == true [new]
→ param5 = x / 1e7 (double degrees)
→ param6 = y / 1e7 (double degrees)
→ param7 = z (float meters)
→ vehicle_command.command = 611, published to uORB
→ EKF2 checks: command == 100000 || command == 611 [new: matches]
→ _ekf.setEkfGlobalOrigin(lat, lon, alt)
→ command_ack.command = 611, published
→ MAVLink streams COMMAND_ACK(611, ACCEPTED) back to GCS ✓
Legacy SET_GPS_GLOBAL_ORIGIN message path is unchanged (still uses 100000).
Frame note (MAVLink PR #2530)
Per mavlink/mavlink#2530, the correct
frame for MAV_CMD_DO_SET_GLOBAL_ORIGIN when sent as COMMAND_INT is MAV_FRAME_GLOBAL_INT
Because command_ack.command = vehicle_command.command (line 519), the ack will echo
back 611 to the GCS when the new command is used, and 100000 for the legacy path.
In the command handler (line 178): Same pattern as EKF2:
if (vehicle_command.command == vehicle_command_s::VEHICLE_CMD_SET_GPS_GLOBAL_ORIGIN
|| vehicle_command.command == vehicle_command_s::VEHICLE_CMD_DO_SET_GLOBAL_ORIGIN) {
Data Flow After Change
GCS sends COMMAND_INT (id=611, frame=GLOBAL, x=lat_1e7, y=lon_1e7, z=alt_m)
→ handle_message_command_int()
→ command_has_location(611) == true [new]
→ param5 = x / 1e7 (double degrees)
→ param6 = y / 1e7 (double degrees)
→ param7 = z (float meters)
→ vehicle_command.command = 611, published to uORB
→ EKF2 checks: command == 100000 || command == 611 [new: matches]
→ _ekf.setEkfGlobalOrigin(lat, lon, alt)
→ command_ack.command = 611, published
→ MAVLink streams COMMAND_ACK(611, ACCEPTED) back to GCS ✓
Legacy SET_GPS_GLOBAL_ORIGIN message path is unchanged (still uses 100000).
Frame note (MAVLink PR #2530)
Per mavlink/mavlink#2530, the correct
frame for MAV_CMD_DO_SET_GLOBAL_ORIGIN when sent as COMMAND_INT is MAV_FRAME_GLOBAL_INT
(5), not MAV_FRAME_GLOBAL (0). Both are handled identically in PX4 — handle_message_command_int()
divides x/y by 1e7 for any non-LOCAL frame (the else branch at line 637) — so no
special frame handling is needed. The test code below uses the correct frame.
Verification
description and fix in a follow-up.
This is by EKF design — there's even a // altitude is optional comment on line 95 of ekf_helper.cpp.
setAltOrigin() returns false for NaN (via checkAltitudeValidity()), but setEkfGlobalOrigin() ignores that
return value and returns true, so EKF2 sends ACCEPTED with the altitude origin unchanged. The MAVLink spec for
MAV_CMD_DO_SET_GLOBAL_ORIGIN says altitude is required, so this is a spec divergence. However, the same
behaviour applies to the legacy path. Don't fix here — it needs an EKF-level decision about whether altitude
remains optional.
PR #27541 is the right home for this — it builds a command lookup table for exactly this kind of param
validation. Once #27541 merges, add MAV_CMD_DO_SET_GLOBAL_ORIGIN to its table. Don't duplicate that mechanism
here.
Summary: Keep this PR focused on what it does — adding COMMAND_INT reception support. Document the three known
limitations in the PR description and link to #27541 for param1-4.