Mission Nav2 (NavigateThroughPoses)

Description

The nav2 adapter of the Mission Measurement for nav2's NavigateThroughPoses action -- the action a deployment issues when a mission is a single job through several hard-constraint poses in one call, rather than a chain of separate NavigateToPose goals (that sibling adapter is mission_nav2, #387). Emits a mission_start Record when a goal is first observed and a mission_end Record once it reaches a terminal state, in the same Record schema #387 defines, with mission_type: "navigate_through_poses".

This Measurement is a passive observer of the action server's single active goal. It never sends a goal itself. Instead it subscribes to the action's own _action/status and _action/feedback topics and calls its _action/get_result service directly for whichever goal_id just reached a terminal status -- the same standard per-action topics/services every rclcpp_action::Server (nav2's bt_navigator included) exposes. Whatever in the deployment actually dispatches missions (a BT navigator, a fleet orchestrator, an operator command) keeps doing so exactly as before; this Measurement only watches.

Two Records per mission:

  • a mission_start Record, the first time a goal_id is observed on the action's status topic.
  • a mission_end Record, once that goal_id reaches a terminal status (SUCCEEDED, CANCELED, or ABORTED) and its result has been fetched.

outcome on the end Record is one of succeeded, failed, cancelled, aborted: CANCELED maps to cancelled, ABORTED to aborted (both carrying nav2's own error_msg/error_code as reason/error_code), and SUCCEEDED maps to succeeded unless NavigateThroughPoses::Result.error_code is non-zero, in which case it is failed -- an application-level failure nav2 reported without aborting the goal status itself.

recoveries (from NavigateThroughPoses::Feedback.number_of_recoveries, when feedback was seen before completion) is carried on the end Record when available.

No per-waypoint outcome is reported. The acceptance criteria this Measurement was built against ask for the final waypoint_statuses (which poses in the job succeeded versus failed) to be represented on mission_end, following an upstream nav2 NavigateThroughPoses.action result field of that name. That field does not exist on the nav2 release this repository actually builds against (nav2_msgs on the jazzy branch of ros-navigation/navigation2 -- verified directly, not assumed): its NavigateThroughPoses::Result carries only error_code/error_msg, the same shape as NavigateToPose::Result. It was present in an earlier implementation of this plugin (written against the upstream default branch without checking the pinned distro branch first) and removed once CI's real Jazzy build caught the mismatch (fatal error: nav2_msgs/msg/waypoint_status.hpp: No such file or directory). Adding it back is a follow-up for whenever nav2 backports per-waypoint result reporting to a distro this repository targets, not something this Measurement can honestly do today.

A mission still running when collection stops simply never gets a matching mission_end Record: nothing downstream can average an interval that was never closed as a zero, because there is no mission_end Record to average. sequence is a monotonically increasing counter across every Record this Measurement instance emits (start and end alike, not per mission_id), so a dropped Record shows up as a gap rather than silently corrupting a duration or a mission-success-rate denominator.

Parameters

ParameterDefaultDescription
action_namenavigate_through_posesThe NavigateThroughPoses action to watch. Its _action/status, _action/feedback, and _action/get_result endpoints are subscribed/called directly.

Schema

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "MissionNav2ThroughPoses",
  "properties": {
    "event": { "type": "string", "enum": ["mission_start", "mission_end"] },
    "mission_id": { "type": "string" },
    "mission_type": { "type": "string" },
    "sequence": { "type": "integer", "minimum": 1 },
    "outcome": { "type": "string", "enum": ["succeeded", "failed", "cancelled", "aborted"] },
    "reason": { "type": "string" },
    "error_code": { "type": "integer", "minimum": 0 },
    "duration_sec": { "type": "number", "minimum": 0 },
    "recoveries": { "type": "integer", "minimum": 0 }
  },
  "required": ["event", "mission_id", "sequence"],
  "type": "object"
}

Configuration

...
mission_nav2_through_poses:
  plugin: "dc_measurements/MissionNav2ThroughPoses"
  topic_output: "/dc/measurement/mission_nav2_through_poses"
  group_key: "mission"
  action_name: "navigate_through_poses"

Example output

Start:

{
  "event": "mission_start",
  "mission_id": "3f2504e04f8911d39a0c0305e82c3301",
  "mission_type": "navigate_through_poses",
  "sequence": 7
}

and end (succeeded):

{
  "event": "mission_end",
  "mission_id": "3f2504e04f8911d39a0c0305e82c3301",
  "mission_type": "navigate_through_poses",
  "sequence": 8,
  "outcome": "succeeded",
  "duration_sec": 96.4,
  "recoveries": 1
}

and end (aborted):

{
  "event": "mission_end",
  "mission_id": "9f86d081884c7d659a2feaa0c55ad015",
  "mission_type": "navigate_through_poses",
  "sequence": 10,
  "outcome": "aborted",
  "reason": "tf timeout",
  "error_code": 9102,
  "duration_sec": 12.1
}