Skip to content

Graph based topological navigation#242

Open
ibrahimhroob wants to merge 88 commits intoaocfrom
aoc_refactor
Open

Graph based topological navigation#242
ibrahimhroob wants to merge 88 commits intoaocfrom
aoc_refactor

Conversation

@ibrahimhroob
Copy link
Copy Markdown
Contributor

@ibrahimhroob ibrahimhroob commented Feb 3, 2026

V5.0.0

Refactored the entire codebase by removing all ROS1 legacy components. Added spatial search capabilities and integrated NetworkX for graph-based search. Embedded behavior trees and actions directly within the map YAML file and reviewed the map configuration to remove unused parameters. Implemented a visualization package enabling RViz interaction with nodes, including direct drag-and-drop manipulation. Added a simulation package for testing navigation with Nav2.

- Introduced a comprehensive flow report detailing the system interaction diagram, node responsibilities, function-call flow, suggested refactor plan, and short-term fixes for the topological navigation system in ROS 2.
- Included mermaid diagram for visual representation of system interactions.
- Documented responsibilities and key files for each node involved in the navigation process.
- Outlined a phased approach for refactoring to improve code structure and maintainability.
- Suggested immediate fixes to enhance system reliability and consistency.
- Moved CustomSafeLoader to map_types.py for reuse across multiple scripts.
- Introduced type-safe data classes (TopologicalPose, TopologicalEdge, TopologicalNode, TopologicalMap) to improve map handling.
- Updated scripts to utilize the new CustomSafeLoader and data classes, enhancing code clarity and maintainability.
@ibrahimhroob ibrahimhroob changed the title WIP-Refactor Graph based topological navigation Mar 9, 2026
@ibrahimhroob ibrahimhroob requested a review from cooperj March 9, 2026 02:11
@ibrahimhroob
Copy link
Copy Markdown
Contributor Author

Topological Navigation

A ROS 2 framework for graph-based topological navigation of autonomous mobile robots. Unlike traditional metric navigation that operates in continuous coordinate space, topological navigation represents the environment as a graph of discrete nodes (waypoints) connected by edges (traversable paths), enabling high-level route planning, named-location navigation, and pluggable edge actions (e.g., Nav2 goals, row traversal, door opening).

Originally developed for the STRANDS long-term autonomy project, this framework is actively used in agricultural robotics for autonomous navigation in vineyards, orchards, and crop rows.

ROS 2 Only — version 4.0.0+ is ROS 2 exclusive (Humble / Iron). For ROS 1 support use version 3.x or earlier.


Table of Contents


Architecture Overview

┌──────────────────────────────────────────────────────────────────────┐
│                        Topological Navigation Stack                  │
│                                                                      │
│  ┌────────────────┐   ┌──────────────────┐   ┌───────────────────┐  │
│  │  Map Manager   │──▶│  Localisation     │──▶│  Navigation       │  │
│  │  (map_manager2)│   │  (localisation2)  │   │  (navigation2)    │  │
│  │                │   │                   │   │                   │  │
│  │ Loads YAML map │   │ Determines        │   │ A* route planning │  │
│  │ Publishes to   │   │ current/closest   │   │ Edge action exec  │  │
│  │ /topological_  │   │ node via KD-tree  │   │ GotoNode action   │  │
│  │ map_2 topic    │   │ + influence zones │   │ server            │  │
│  └────────────────┘   └──────────────────┘   └────────┬──────────┘  │
│                                                        │             │
│                                                        ▼             │
│                                              ┌───────────────────┐  │
│                                              │  Nav2 Stack       │  │
│                                              │  (or Simulator)   │  │
│                                              │  NavigateToPose   │  │
│                                              │  NavigateThrough  │  │
│                                              │  FollowWaypoints  │  │
│                                              └───────────────────┘  │
│                                                                      │
│  ┌────────────────────┐   ┌──────────────────────────────────────┐  │
│  │  Map Visualiser    │   │  Fake Nav2 Simulator                 │  │
│  │  (visual package)  │   │  (simulator package)                 │  │
│  │  RViz markers +    │   │  Virtual robot + fake action servers │  │
│  │  interactive edit  │   │  for testing without real hardware   │  │
│  └────────────────────┘   └──────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────────┘

Data Flow:

  1. Map Manager loads a .tmap2.yaml file and publishes it as a JSON string on /topological_map_2
  2. Localisation subscribes to the map, builds a NetworkX graph with KD-tree spatial indexing, and continuously publishes which node the robot is at (/current_node, /closest_node)
  3. Navigation receives GotoNode action goals, computes A* routes, and executes edge actions by calling Nav2 action servers
  4. Visualiser renders the topological map as interactive RViz markers for viewing and editing

Packages

topological_navigation (Core)

Type ament_python
Version 5.0.0
Purpose Core navigation stack: map management, localisation, route planning, navigation execution

Key modules:

Module Description
scripts/navigation2.py Main navigation action server — receives GotoNode goals, plans routes (A*), and executes edge actions via Nav2
scripts/localisation2.py Topological localisation — determines current/closest node using KD-tree + influence zone checks
scripts/map_manager2.py Loads .tmap2.yaml maps, validates them against JSON schema, publishes to ROS topics
scripts/manual_topomapping.py Joystick-driven waypoint recording for field mapping
networkx_utils.py NetworkX graph construction, KD-tree spatial indexing, point-in-polygon, edge distance calculations
navigation_graph.py Navigation state machine, route planning, segment merging
tmap_utils.py YAML map loading utilities
validate_map.py CLI tool for topological map validation against schema
convert_tmap.py Convert between topological map format versions

topological_navigation_msgs

Type ament_cmake
Version 3.0.5
Purpose ROS 2 message, service, and action interface definitions

Defines 16 message types, 9 service types, and 3 action types used across the navigation stack. See Custom Message, Service & Action Definitions below for full details.

topological_navigation_visual

Type ament_python
Version 1.0.0
Purpose RViz visualisation and interactive map editing

Key executables:

Executable Description
topological_map_visualiser.py Unified visualiser + interactive drag-and-drop editor for topological maps in RViz
topological_visual.py Route and occupied-node visualisation
policy_marker.py Policy arrow visualisation for MDP planning

topological_nav_simulator

Type ament_python
Version 1.0.0
Purpose Fake Nav2 action servers with a virtual robot for testing without real hardware

Provides NavigateToPose, NavigateThroughPoses, and FollowWaypoints action servers that simulate robot movement, publishing TF transforms, odometry, and RViz markers. The simulated robot smoothly interpolates toward goal poses at a configurable speed.


Installation

Prerequisites

  • ROS 2 Humble or Iron
  • Python 3.8+
  • Nav2 (for real robot deployment; the simulator replaces it for testing)

Build from Source

# Create workspace
mkdir -p ~/ws/src && cd ~/ws/src
git clone https://github.com/LCAS/topological_navigation.git

# Install dependencies
cd ~/ws
rosdep install --from-paths src --ignore-src -r -y

# Build
colcon build --symlink-install

# Source
source install/setup.bash

Python Dependencies

Package Min Version Purpose
networkx 2.5 Graph data structures and algorithms
scipy 1.5 KD-tree spatial indexing
numpy 1.19 Numerical operations
jsonschema Map schema validation

Launching the System

Full Stack (with Simulator)

Launch the complete topological navigation system with a virtual robot (no real hardware needed):

ros2 launch topological_navigation topological_navigation.launch.py

This starts (in order with delays for dependency readiness):

Delay Node Description
0 s topological_map_manager_2 Loads and publishes the topological map
2 s topological_localisation Starts localisation
2 s fake_nav2_server Virtual robot + fake Nav2 action servers
3 s topological_navigation Navigation action server
4 s topological_map_visualiser Interactive RViz map editor
5 s rviz2 RViz with pre-configured display layout

Launch arguments:

Argument Default Description
map_path <share>/config/mixed_actions_map.yaml Absolute path to a .tmap2.yaml topological map
rviz_config <share>/rviz/topological_navigation.rviz Path to the RViz config file
initial_x 0.0 Initial robot X position (metres)
initial_y 0.0 Initial robot Y position (metres)
initial_yaw 0.0 Initial robot yaw (radians)

Example with a custom map:

ros2 launch topological_navigation topological_navigation.launch.py \
    map_path:=/path/to/my_map.tmap2.yaml \
    initial_x:=5.0 initial_y:=3.0

Standalone Simulator

Launch only the fake Nav2 server (useful when developing against a simulated robot):

ros2 launch topological_nav_simulator fake_nav2.launch.py

Launch arguments:

Argument Default Description
robot_speed 0.8 Simulated linear speed (m/s)
angular_speed 1.5 Simulated angular speed (rad/s)
goal_tolerance 0.15 Goal position tolerance (m)
update_rate 30.0 TF/marker publish rate (Hz)
initial_x 0.0 Initial robot X position
initial_y 0.0 Initial robot Y position
initial_yaw 0.0 Initial robot yaw (rad)
map_frame map Map TF frame
odom_frame odom Odometry TF frame
base_frame base_link Robot base TF frame

Visualiser Only

Launch the topological map visualiser standalone:

ros2 launch topological_navigation_visual topological_map_visualiser.launch.py \
    map_file:=/path/to/map.tmap2.yaml edit_mode:=true

Launch arguments:

Argument Default Description
map_file '' Path to .tmap2.yaml file (empty = subscribe to topic)
auto_save false Auto-save changes every 30 seconds
marker_scale 0.5 Scale factor for RViz markers
edit_mode true Enable interactive drag-and-drop editing
nav_action_name /topological_navigation GotoNode action name for click-to-navigate

On a Real Robot

When using a real robot with Nav2, launch the navigation stack without the simulator:

# 1. Start Nav2 (robot-specific)
ros2 launch <your_robot_pkg> navigation.launch.py

# 2. Start the map manager
ros2 run topological_navigation map_manager2.py /path/to/map.tmap2.yaml

# 3. Start localisation
ros2 run topological_navigation localisation2.py

# 4. Start topological navigation
ros2 run topological_navigation navigation2.py

# 5. (Optional) Start visualiser
ros2 run topological_navigation_visual topological_map_visualiser.py

Sending Navigation Goals

Once the system is running, send a robot to a named node:

# Navigate to a node called "node3"
ros2 action send_goal /topological_navigation \
    topological_navigation_msgs/action/GotoNode \
    "{target: 'node3', no_orientation: false}"

ROS 2 Node Parameters

topological_navigation (navigation2.py)

Parameter Type Default Description
max_dist_to_closest_edge double 1.0 Maximum distance (m) to consider an edge as valid origin for navigation
default_boundary_left double 0.5 Default left boundary offset for row corridor polygons
default_boundary_right double 0.5 Default right boundary offset for row corridor polygons
route_algorithm string "astar" Path planning algorithm: "astar" or "dijkstra"
route_weight_attr string "weight" Edge attribute used as cost for path planning

topological_localisation (localisation2.py)

Parameter Type Default Description
LocalisationThrottle int 1 Process every Nth pose callback (throttle rate)
OnlyLatched bool true Only publish when values actually change
base_frame string "base_link" Robot base TF frame for localisation lookups

topological_map_manager_2 (map_manager2.py)

Parameter Type Default Description
cache_topological_maps bool false Cache maps locally to ~/.ros/topological_maps
auto_write_topological_maps bool false Automatically save map on any modification
topological_map2_name string "" Current map name identifier
topological_map2_path string "" Filesystem path where maps are stored

manual_tmapping_node (manual_topomapping.py)

Parameter Type Default Description
tmap string "" Map YAML filename
tmap_dir string "" Directory to save map files
site_name string "" Site or farm name
node_thresh double 0.5 Minimum distance (m) between nodes when recording
lock_btn int 6 Joystick lock button index
add_btn int 1 Joystick add-node button index
remove_btn int 2 Joystick remove-node button index
gen_map_btn int 3 Joystick generate-map button index
topic_joy string "/joy" Joystick input topic
topic_pose string "/gps_base/odometry" Robot pose input topic
topic_imu string "/gps_base/yaw" IMU yaw input topic

topological_map_visualiser (topological_map_visualiser.py)

Parameter Type Default Description
map_file string "" Path to .tmap2.yaml file for editing (empty = subscribe to topic)
auto_save bool false Automatically save map every 30 seconds
marker_scale double 0.5 Scale factor for RViz node/edge markers
edit_mode bool true Enable interactive drag-and-drop node editing
nav_action_name string "/topological_navigation" GotoNode action name for click-to-navigate context menu

route_visualiser / occupancy_visualiser (topological_visual.py)

Parameter Type Default Description
tmap string (required) Path to a .tmap2.yaml topological map file

fake_nav2_server (fake_nav2_server.py — simulator)

Parameter Type Default Description
robot_speed double 0.8 Simulated linear speed (m/s)
angular_speed double 1.5 Simulated angular speed (rad/s)
goal_tolerance double 0.15 Position tolerance for goal reached (m)
update_rate double 30.0 TF and marker publish rate (Hz)
initial_x double 0.0 Initial robot X position
initial_y double 0.0 Initial robot Y position
initial_yaw double 0.0 Initial robot yaw (radians)
map_frame string "map" Map TF frame
odom_frame string "odom" Odometry TF frame
base_frame string "base_link" Robot base TF frame

ROS 2 Topics

Published Topics

Topic Type Publisher Node Description
/topological_map_2 std_msgs/String Map Manager Full topological map as a JSON string (latched)
/topological_map_schema std_msgs/String Map Manager JSON schema for map validation (latched)
/current_node std_msgs/String Localisation Name of the node the robot is currently inside (influence zone). "none" if outside all zones
/closest_node std_msgs/String Localisation Name of the nearest node by Euclidean distance
/closest_node_distance std_msgs/Float32 Localisation Distance (m) to the closest node
/closest_edges topological_navigation_msgs/ClosestEdges Localisation Sorted list of closest edge IDs with distances
/current_node/tag std_msgs/String Localisation Semantic tag of the current node (from properties)
/current_edge std_msgs/String Navigation Edge currently being traversed
topological_navigation/Statistics topological_navigation_msgs/NavStatistics Navigation Navigation statistics for completed edges
topological_navigation/Route topological_navigation_msgs/TopologicalRoute Navigation Currently planned route (list of node names)
topological_navigation/move_action_status std_msgs/String Navigation Status of the current Nav2 move action
/boundary_checker geometry_msgs/PolygonStamped Navigation Row corridor boundary polygon for agricultural operations
/robot_operation_current_status std_msgs/String Navigation Current robot operation status
/virtual_robot/markers visualization_msgs/MarkerArray Fake Nav2 Virtual robot body visualisation in RViz
/odometry/global nav_msgs/Odometry Fake Nav2 Simulated global odometry
/virtual_robot/pose geometry_msgs/PoseStamped Fake Nav2 Simulated robot pose
topological_map_visualisation visualization_msgs/MarkerArray Map Visualiser Node and edge markers for RViz
topological_route_visualisation visualization_msgs/MarkerArray Route Visualiser Active route highlight markers
/topological_navigation/visual/occupied_node visualization_msgs/MarkerArray Occupancy Visualiser Occupied node indicators
/tmapping_nodes visualization_msgs/MarkerArray Manual Mapping Recorded waypoint markers

Subscribed Topics

Topic Type Subscriber Node Description
/topological_map_2 std_msgs/String Localisation, Navigation, Visualiser Topological map data
/current_node std_msgs/String Navigation, Visualiser Current node for navigation state
/closest_node std_msgs/String Navigation, Visualiser Closest node for route origin
/closest_edges topological_navigation_msgs/ClosestEdges Navigation Edge proximity for navigation decisions
/initialpose geometry_msgs/PoseStamped Fake Nav2 Set the virtual robot's pose (e.g., from RViz 2D Pose Estimate)
topological_navigation/Route topological_navigation_msgs/TopologicalRoute Route Visualiser Route to highlight
/topological_navigation/occupied_node topological_navigation_msgs/TopologicalOccupiedNode Occupancy Visualiser Nodes occupied by other robots
/joy sensor_msgs/Joy Manual Mapping Joystick input for waypoint recording
mdp_plan_exec/current_policy_mode topological_navigation_msgs/NavRoute Policy Marker Policy route for arrow rendering

TF Broadcasts

From To Publisher Type
map odom Fake Nav2 Dynamic TF
odom base_link Fake Nav2 Dynamic TF
parent topo_frame_id Map Manager Static TF

ROS 2 Services

Service Type Node Description
/topological_map_manager2/get_topological_map std_srvs/Trigger Map Manager Returns the current map as a JSON string
/topological_map_manager2/write_topological_map WriteTopologicalMap Map Manager Saves the current map to a YAML file
/topological_map_manager2/switch_topological_map WriteTopologicalMap Map Manager Switches to a different map file
/topological_localisation/localise_pose LocalisePose Localisation Localise an arbitrary pose (returns current + closest node)
/tmapping_robot/save_waypoints std_srvs/Trigger Manual Mapping Save raw waypoints to file
/tmapping_robot/save_map std_srvs/Trigger Manual Mapping Generate and save topological map from waypoints
<visualiser>/save_map std_srvs/Trigger Map Visualiser Save the edited map to YAML

ROS 2 Actions

Action Servers

Action Type Node Description
/topological_navigation GotoNode Navigation Navigate the robot to a target topological node
/topological_navigation/execute_policy_mode ExecutePolicyMode Navigation Execute a pre-computed policy route
/navigate_to_pose nav2_msgs/NavigateToPose Fake Nav2 Simulated single-goal metric navigation
/navigate_through_poses nav2_msgs/NavigateThroughPoses Fake Nav2 Simulated multi-waypoint metric navigation
/follow_waypoints nav2_msgs/FollowWaypoints Fake Nav2 Simulated waypoint-following navigation

Action Clients

Action Type Node Description
(dynamic from map actions section) (e.g., nav2_msgs/NavigateToPose) Navigation Created for each unique (action_type, action_server) pair defined in the map
/topological_navigation GotoNode Map Visualiser Click-to-navigate from RViz context menu

TF Frames

map (global frame)
 ├── topo_frame_id (static TF, published by map_manager2)
 │    └── Topological node poses are defined in this frame
 │
 └── odom (dynamic TF, published by simulator or real robot)
      └── base_link (robot body frame)

The localisation node resolves the robot pose by looking up topo_frame_id → base_frame via tf2_ros.


Custom Message, Service & Action Definitions

All definitions reside in the topological_navigation_msgs package.

Messages (.msg)

Message Fields Description
NavRoute string[] source, string[] edge_id Ordered route: source nodes and edge IDs to traverse
TopologicalRoute string[] nodes Ordered list of node names forming a route
ClosestEdges string[] edge_ids, float32[] distances Sorted edges by proximity with distances
NavStatistics string edge_id, string status, string origin, string target, string final_node, string date_started, string date_at_node, string date_finished, float64 time_to_waypoint, float64 operation_time, string topological_map Detailed statistics for one edge traversal
CurrentEdge Header header, string edge_id, bool active, bool result Currently active edge status
GotoNodeFeedback string route Navigation progress feedback (remaining route)
ExecutePolicyModeFeedback string current_wp, uint8 status Policy execution progress
ExecutePolicyModeGoal Header header, NavRoute route Policy execution goal with header
TopologicalMap string name, string map, string pointset, string last_updated, TopologicalNode[] nodes Full topological map structure
TopologicalNode string name, string map, string pointset, Pose pose, float64 yaw_goal_tolerance, float64 xy_goal_tolerance, Vertex[] verts, Edge[] edges, string localise_by_topic Single topological node with edges and influence zone
Edge string edge_id, string node, string action, float64 top_vel, string map_2d, float64 inflation_radius, string recovery_behaviours_config Edge connecting two nodes
Vertex float32 x, float32 y 2D vertex of an influence zone polygon
AddNodeReq string name, Pose pose, bool add_close_nodes Request to add a node to the map
AddEdgeReq string origin, string destination, string action, string action_type, string edge_id Request to add an edge between nodes
SetInfluenceZoneReq string name, float64[] vertices_x, float64[] vertices_y Request to set a node's influence zone polygon
UpdateEdgeConfigReq string edge_id, string namespace_name, string name, string value, bool value_is_string, bool not_reset Request to update edge configuration parameters
TopologicalOccupiedNode string[] nodes List of currently occupied node names

Services (.srv)

Service Request → Response Description
LocalisePose Pose posestring current_node, string closest_node Determine which node a pose belongs to
GetRouteTo string goalNavRoute route Get route from current position to goal node
GetRouteBetween string origin, string goalNavRoute route Get route between two named nodes
WriteTopologicalMap string filename, bool no_aliasbool success, string message Write / save a topological map to disk
EvaluateEdge string state, string edge, bool runtimebool success, bool evaluation Evaluate restrictions on an edge
EvaluateNode string state, string node, bool runtimebool success, bool evaluation Evaluate restrictions on a node
ReconfAtEdges string edge_idbool success Reconfigure parameters at edge traversal
LoadTopoNavTestScenario string pointset(empty) Load a test scenario
RunTopoNavTestScenario (empty)bool nav_success, bool nav_timeout, bool graceful_fail, bool human_success, float64 min_distance_to_human, float64 mean_speed, float64 distance_travelled, float64 travel_time Run a test scenario and return metrics

Actions (.action)

Action Goal Result Feedback
GotoNode string target, bool no_orientation bool success string route
ExecutePolicyMode NavRoute route bool success string current_wp, uint8 status
BuildTopPrediction Time start_range, Time end_range bool success string result

Topological Map Format

Topological maps are YAML files with a .tmap2.yaml extension. Here is an abbreviated example:

meta:
  last_updated: 08-03-2026_12-00-00
  origin:
    latitude: 53.268
    longitude: -0.524
    altitude: 0

metric_map: my_farm
name: my_farm
pointset: my_farm

transformation:
  topo_frame_id: my_farm      # Child frame for node poses
  parent: map                  # Parent TF frame
  rotation: {w: 1.0, x: 0.0, y: 0.0, z: 0.0}
  translation: {x: 0.0, y: 0.0, z: 0.0}

definitions:
  default_bt: |
    <root main_tree_to_execute="MainTree">
      <BehaviorTree ID="MainTree">
        <PipelineSequence name="NavigateWithReplanning">
          <ComputePathToPose goal="{goal}" path="{path}" planner_id="GridBased"/>
          <FollowPath path="{path}" controller_id="FollowPath"/>
        </PipelineSequence>
      </BehaviorTree>
    </root>

actions:
  navigate_to_pose:
    composable: false
    action_type: nav2_msgs.action.NavigateToPose
    action_server: /navigate_to_pose
    action_goal_template:
      pose:
        header:
          frame_id: '${node.nav_frame}'
        pose: '${node.pose}'
      behavior_tree: '${definitions.default_bt}'

nodes:
  - meta:
      map: my_farm
      node: RowEntry_A1
      pointset: my_farm
    node:
      name: RowEntry_A1
      nav_frame: '${transformation.topo_frame_id}'
      pose:
        position: {x: 10.5, y: 5.2, z: 0.0}
        orientation: {w: 0.707, x: 0.0, y: 0.0, z: 0.707}
      properties:                    # Flexible key-value metadata
        xy_goal_tolerance: 0.3
        yaw_goal_tolerance: 0.1
      verts:                         # Influence zone polygon
        - {x: -0.5, y: -0.5}
        - {x:  0.5, y: -0.5}
        - {x:  0.5, y:  0.5}
        - {x: -0.5, y:  0.5}
      edges:
        - edge_id: RowEntry_A1_RowEnd_A1
          node: RowEnd_A1            # Target node name
          action: navigate_to_pose   # Action key from 'actions' section
          properties:
            max_speed: 0.5

Key sections:

  • meta: Map metadata (timestamps, origin coordinates)
  • transformation: TF frame for all node poses (published as a static transform)
  • definitions: Reusable templates (e.g., Nav2 behavior trees)
  • actions: Named action configurations — each maps to a ROS 2 action server with a goal template supporting variable substitution (${node.pose}, ${definitions.default_bt}, etc.)
  • nodes: List of topological nodes, each with pose, properties, influence zone vertices, and outgoing edges

Flexible Properties System

Both nodes and edges support an optional properties dictionary. Properties are freeform key-value pairs — no schema changes needed:

properties:
  xy_goal_tolerance: 0.3          # Navigation tolerance
  semantics: "row_entry"          # Semantic label
  roboflow:                       # Namespaced properties
    enabled: true
    confidence: 0.7

Always access properties defensively in code:

props = node["node"].get("properties", {})
tolerance = props.get("xy_goal_tolerance", 0.5)

See doc/PROPERTIES.md for full documentation.

Map Validation

ros2 run topological_navigation validate_map.py /path/to/map.tmap2.yaml -v

Command-Line Utilities

Command Description
ros2 run topological_navigation validate_map.py <map.yaml> -v Validate a map against the JSON schema
ros2 run topological_navigation convert_tmap.py <args> Convert between topological map format versions
ros2 action send_goal /topological_navigation topological_navigation_msgs/action/GotoNode "{target: 'node_name'}" Send a navigation goal
ros2 topic echo /current_node Monitor which node the robot is at
ros2 topic echo /closest_node Monitor the nearest node
ros2 service call /topological_localisation/localise_pose topological_navigation_msgs/srv/LocalisePose "{pose: {position: {x: 1.0, y: 2.0, z: 0.0}}}" Localise an arbitrary pose

Testing

# Build the workspace
colcon build

# Run all tests for the core package
colcon test --packages-select topological_navigation --event-handlers console_direct+

# Run specific tests
cd topological_navigation
pytest test/test_networkx_utils.py -v
pytest test/test_localisation2.py -v

# Python linting
colcon test --packages-select topological_navigation \
    --event-handlers console_direct+ --pytest-args -k "test_flake8 or test_pep257"

Contributing

  1. Follow PEP 8 / PEP 257 for Python code
  2. Use ament_flake8 and ament_pep257 for validation
  3. Add tests for new functionality
  4. Validate maps with validate_map.py
  5. Update documentation when changing behavior

License

Apache License 2.0 — see LICENSE for details.

The publishers (map_manager2, localisation2) use TRANSIENT_LOCAL
durability. The test subscribers were using the default VOLATILE QoS,
which caused a race condition in CI: if the subscriber connected after
the one-time publish, it would never receive the message. Matching
the QoS durability ensures late-joining subscribers get the last
published value.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 94 out of 280 changed files in this pull request and generated 14 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

setup(
name=package_name,
version='3.0.5',
version='5.0.0', # Major version bump - ROS1 code removed
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The version comment and value indicate v5.0.0, while package.xml is v4.0.0 and the description text says ROS1 support was removed in v4.0.0. Pick a single versioning story (either bump package.xml to 5.0.0 or set setup.py back to 4.0.0) and ensure release notes/descriptions match the chosen version.

Suggested change
version='5.0.0', # Major version bump - ROS1 code removed
version='4.0.0', # Version aligned with package.xml (ROS1 code removed in v4.0.0)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should have this consistent throughout, i'm not against being at v5... but we should have it matching all the way through. Verify what copilot picked up and see if it's valid.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

agree, it was mismatched in the original code. it is quite confusing as we do have the version number in the xml file and in setup.py file. I will make it consistent across both to v5

Comment on lines +10 to +12
- **[Interactive Map Editor](doc/INTERACTIVE_MAP_EDITOR.md)** - Edit maps visually in RViz2
- **[Properties System](doc/PROPERTIES.md)** - Flexible node/edge metadata
- **[Quick Start](QUICK_START_INTERACTIVE_EDITOR.md)** - Get started with the interactive editor
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

doc/PROPERTIES.md is linked here but the PR deletes topological_navigation/doc/PROPERTIES.md, so this link will be broken. Either restore the doc (possibly updated for the new YAML-embedded definitions approach), or update the README to point to the new/renamed documentation location.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Has a point

Comment on lines 15 to +17
('share/' + package_name + '/config/', glob('config/*', recursive=True)),
('share/' + package_name + '/launch/', glob('launch/*', recursive=True))
('share/' + package_name + '/config/',
glob('test/fixtures/mixed_actions_map.yaml')),
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

There are two data_files entries targeting the same destination directory (share/.../config/). While it can work, it’s easy to miss and can cause confusion during packaging/release reviews. Consider moving mixed_actions_map.yaml into config/ (so it’s covered by the existing glob), or merging all config-bound files into a single tuple for clarity.

Copilot uses AI. Check for mistakes.

## Validation

All fixtures should validate against `topological_navigation/config/schema2.json`.
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

This README references schema2.json, but the repo is using a YAML schema file (config/tmap-schema.yaml) in this PR, and it also includes absolute local paths under /home/... which won’t exist for other developers/CI. Update the schema references to the current schema file and replace/remove local absolute-path references (or link to repo-relative docs).

Copilot uses AI. Check for mistakes.
Comment on lines +161 to +163
- Requirements: `/home/ibrahim/.kiro/specs/localisation-networkx-refactor/requirements.md`
- Design: `/home/ibrahim/.kiro/specs/localisation-networkx-refactor/design.md`
- Schema: `topological_navigation/config/schema2.json`
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

This README references schema2.json, but the repo is using a YAML schema file (config/tmap-schema.yaml) in this PR, and it also includes absolute local paths under /home/... which won’t exist for other developers/CI. Update the schema references to the current schema file and replace/remove local absolute-path references (or link to repo-relative docs).

Copilot uses AI. Check for mistakes.

import math

import rclpy
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

rclpy is imported but not used anywhere in this module. Removing unused imports reduces lint noise and makes dependencies clearer.

Suggested change
import rclpy

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +12
from geometry_msgs.msg import Pose, Quaternion # noqa: E402
from topological_nav_simulator.virtual_robot import (
_euler_from_quaternion,
_lerp,
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

Pose and Quaternion are imported but not used in this test file. Consider removing them to keep the tests clean (and reduce the need for noqa exceptions).

Suggested change
from geometry_msgs.msg import Pose, Quaternion # noqa: E402
from topological_nav_simulator.virtual_robot import (
_euler_from_quaternion,
_lerp,
from topological_nav_simulator.virtual_robot import (
_euler_from_quaternion,
_lerp,
_lerp,

Copilot uses AI. Check for mistakes.
@ibrahimhroob ibrahimhroob requested a review from Iranaphor March 9, 2026 09:15
@ibrahimhroob
Copy link
Copy Markdown
Contributor Author

@Iranaphor can you please test if the execute policy works in the new implementations?

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@ibrahimhroob ibrahimhroob requested a review from arsh09 March 9, 2026 10:19
Copy link
Copy Markdown
Member

@cooperj cooperj left a comment

Choose a reason for hiding this comment

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

I think this needs further polishing before it can replace the state that the 'old' topo nav system was.

I'm not able to launch the specific toponav tools (i.e. adding points in rviz) Do we want to implement this alongside moving nodes (right click menu option to add node?)

Do we also want to be able to lock the nodes in place (ie for deployment)

I've left some comments throughout.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need this separate from copilot instructions? can they be symlinked?

Comment on lines +19 to +35
┌──────────────────────────────────────────────┐
│ fake_nav2_server (node) │
│ │
│ Action Servers: │
│ /navigate_to_pose (NavigateToPose) │
│ /navigate_through_poses(NavigateThroughPoses)│
│ /follow_waypoints (FollowWaypoints) │
│ │
│ Publishers: │
│ TF: map → odom → base_link │
│ /odometry/global (Odometry) │
│ /virtual_robot/markers (MarkerArray) │
│ /virtual_robot/pose (PoseStamped) │
│ │
│ Subscribers: │
│ /initialpose (teleport from RViz) │
└──────────────────────────────────────────────┘
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this instead be a mermaid diagram so it is easier to read?

Comment on lines +1 to +2
# Copyright (c) 2026, topological_navigation contributors
# Licensed under the MIT License.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The overall repository is licensed as Apache-2.0 and Not MIT. Is this intentional?

Comment on lines +10 to +12
- **[Interactive Map Editor](doc/INTERACTIVE_MAP_EDITOR.md)** - Edit maps visually in RViz2
- **[Properties System](doc/PROPERTIES.md)** - Flexible node/edge metadata
- **[Quick Start](QUICK_START_INTERACTIVE_EDITOR.md)** - Get started with the interactive editor
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Has a point

last_updated:
type: string
pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}$
pattern: ^[0-9]{2}-[0-9]{2}-[0-9]{4}_[0-9]{2}-[0-9]{2}-[0-9]{2}$
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think having both is a good option, but we should be following ISO8601 and historical versions and using yyyy-mm-dd

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this readme isn't as clear as it should be.

The TOC should be removed, as it just taking up vheight.

Is this something that should be crafted into a full documentation site (ala github wiki or read the docs etc)? Each TOC item should instead be a doc page?

TMap format should be a separate example file and or doc page.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's also unfinished sections (i.e. MongoDB) these should be completed or removed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why are we using JSON Schemas instead of native ros2 types?


setup(
name=package_name,
version='1.0.0',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this match the overarching packages version?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

same as below

<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>topological_nav_simulator</name>
<version>1.0.0</version>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Version 5?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this simulator is a new pkg that I added, so I think v1 is ok for it

runs-on: ubuntu-latest
strategy:
matrix:
ros_distribution:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we also add Jazzy as a testing target?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think I have added it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@cooperj, can you please add it and use aoc base images please

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Okay can do, working on this issue LCAS/aoc_container_base#5 before I can provide you a working version. Do you want me to add it to this PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes please to this pr as this one is already huge

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we need to review where this sits in terms of integration of the aoc_container_base. This means we would base this on lcas.lincoln.ac.uk/ros.

This would remove the need to create the lcas user here.

To then match other containers we should then have another target of devcontainer and then final.

The devcontainer then gives you a dev workspace and then final target gives you a finished deployable container.

This is assuming that our AOC use case is the most effective way of doing this for people outside of AOC.

Thoughts?

ibrahimhroob and others added 9 commits March 12, 2026 21:34
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants