Skip to content

fix(navigator): check loiter radius against geofences on mission upload - #28527

Open
masonzeng702550 wants to merge 1 commit into
PX4:mainfrom
masonzeng702550:fix-loiter-radius-geofence
Open

fix(navigator): check loiter radius against geofences on mission upload#28527
masonzeng702550 wants to merge 1 commit into
PX4:mainfrom
masonzeng702550:fix-loiter-radius-geofence

Conversation

@masonzeng702550

Copy link
Copy Markdown

Problem

MissionFeasibilityChecker::checkMissionAgainstGeofence() validates one point per mission item — the item's own coordinate. For a loiter item that is the centre of the circle, so the radius takes no part in the decision. A loiter whose centre sits outside an exclusion zone but whose perimeter crosses into it is accepted at upload.

Affected: src/modules/navigator/mission_feasibility_checker.cpp on v1.17.0 and on current main, which checks the same single point.

Demonstrated impact

On PX4 SITL with an exclusion polygon and a loiter centre placed outside it at R = 200 m:

  • the mission is accepted, with no geofence warning
  • flying it puts 41 of 2110 recorded track points inside the exclusion zone, at up to 28.9 m past the boundary

A control mission with a waypoint placed directly inside the zone is correctly rejected on the same build, so the fence is active and the acceptance above is a passing check rather than an absent one.

A reproducer exists. Following SECURITY.md I am not posting it; happy to share it privately.

Fix

Sample the loiter perimeter and check each sample with the existing checkPointAgainstAllGeofences(), in addition to the centre point. Sampling uses a 10 m maximum arc step, clamped to between 8 and 64 points, so the cost stays bounded for large radii.

Covered commands are NAV_CMD_LOITER_UNLIMITED, NAV_CMD_LOITER_TIME_LIMIT and NAV_CMD_LOITER_TO_ALT. The radius is read from item.loiter_radius, which mavlink_mission.cpp already populates from the correct parameter for each command.

Test

Stock build and patched build, same fence and same three missions:

Case Mission Stock Patched
control waypoint at the centre of the zone rejected rejected
loiter centre outside, R = 200 m, perimeter crosses in accepted rejected
regression centre outside, R = 10 m, perimeter clear accepted accepted

Only the middle case changes. New message: Geofence violation for loiter radius of waypoint 2.

Known limitation

Sampling is discrete, so a geofence smaller than the arc step between two samples can still pass. Making the step adaptive to the smallest fence feature, or testing perimeter segments rather than points, would close that; I kept the step fixed to keep upload-time cost predictable and am happy to change the approach.

Found with AI assistance (Claude); the fix was written and tested by me in SITL.

@github-actions github-actions Bot added kind:bug Something is broken or behaving incorrectly. scope:navigation Missions, RTL, geofence, takeoff, landing, or navigator behavior. labels Sep 3, 2026
if (!checkLoiterPerimeterAgainstGeofence(missionitem)) {
mavlink_log_critical(_navigator->get_mavlink_log_pub(), "Geofence violation for loiter radius of waypoint %zu\t",
i + 1);
events::send<int16_t>(events::ID("navigator_mis_geofence_violation_loiter"), {events::Log::Error, events::LogInternal::Info},

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.

[error] bugprone-chained-comparison [error]
chained comparison v0 < v1 > v2 may generate unintended results, use parentheses to specify order of evaluation or a logical operator to separate comparison expressions

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

🔎 FLASH Analysis

px4_fmu-v5x [Total VM Diff: 392 byte (0.02 %)]
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +392  +0.0%    +392    .text
    [NEW]    +232  [NEW]    +232    MissionFeasibilityChecker::checkLoiterPerimeterAgainstGeofence()
     +17%     +72   +17%     +72    MissionFeasibilityChecker::checkMissionAgainstGeofence()
    +0.0%     +60  +0.0%     +60    [section .text]
    +0.0%     +24  +0.0%     +24    g_cromfs_image
     +44%      +4   +44%      +4    g_nullstring
  +0.0%    +135  [ = ]       0    .debug_abbrev
  +0.0%      +8  [ = ]       0    .debug_aranges
  +0.0%     +60  [ = ]       0    .debug_frame
  +0.0% +1.01Ki  [ = ]       0    .debug_info
  +0.0%    +402  [ = ]       0    .debug_line
   -57.1%      -4  [ = ]       0    [Unmapped]
    +0.0%    +406  [ = ]       0    [section .debug_line]
  +0.0%    +310  [ = ]       0    .debug_loclists
  +0.0%     +39  [ = ]       0    .debug_rnglists
    +200%      +2  [ = ]       0    [Unmapped]
    +0.0%     +37  [ = ]       0    [section .debug_rnglists]
  +0.0%    +159  [ = ]       0    .debug_str
  -1.2%      -3  [ = ]       0    .shstrtab
  +0.0%     +87  [ = ]       0    .strtab
    [NEW]     +87  [ = ]       0    MissionFeasibilityChecker::checkLoiterPerimeterAgainstGeofence()
  +0.0%     +48  [ = ]       0    .symtab
    [NEW]     +48  [ = ]       0    MissionFeasibilityChecker::checkLoiterPerimeterAgainstGeofence()
  -3.7%    -392  [ = ]       0    [Unmapped]
  +0.0% +2.23Ki  +0.0%    +392    TOTAL

px4_fmu-v6x [Total VM Diff: 376 byte (0.02 %)]
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +376  +0.0%    +376    .text
    [NEW]    +232  [NEW]    +232    MissionFeasibilityChecker::checkLoiterPerimeterAgainstGeofence()
     +17%     +72   +17%     +72    MissionFeasibilityChecker::checkMissionAgainstGeofence()
    +0.0%     +52  +0.0%     +52    [section .text]
    +0.0%     +24  +0.0%     +24    g_cromfs_image
   -30.8%      -4 -30.8%      -4    g_nullstring
  +0.0%    +135  [ = ]       0    .debug_abbrev
  +0.0%      +8  [ = ]       0    .debug_aranges
  +0.0%     +60  [ = ]       0    .debug_frame
  +0.0% +1.01Ki  [ = ]       0    .debug_info
  +0.0%    +410  [ = ]       0    .debug_line
    +133%      +4  [ = ]       0    [Unmapped]
    +0.0%    +406  [ = ]       0    [section .debug_line]
  +0.0%    +310  [ = ]       0    .debug_loclists
  +0.0%     +39  [ = ]       0    .debug_rnglists
    +200%      +2  [ = ]       0    [Unmapped]
    +0.0%     +37  [ = ]       0    [section .debug_rnglists]
  +0.0%    +159  [ = ]       0    .debug_str
  -1.3%      -3  [ = ]       0    .shstrtab
  +0.0%     +87  [ = ]       0    .strtab
    [NEW]     +87  [ = ]       0    MissionFeasibilityChecker::checkLoiterPerimeterAgainstGeofence()
  +0.0%     +48  [ = ]       0    .symtab
    [NEW]     +48  [ = ]       0    MissionFeasibilityChecker::checkLoiterPerimeterAgainstGeofence()
  -5.7%    -376  [ = ]       0    [Unmapped]
  +0.0% +2.24Ki  +0.0%    +376    TOTAL

Updated: 2026-09-03T15:06:08

@masonzeng702550
masonzeng702550 force-pushed the fix-loiter-radius-geofence branch from dbb9702 to c21f154 Compare September 3, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind:bug Something is broken or behaving incorrectly. scope:navigation Missions, RTL, geofence, takeoff, landing, or navigator behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant