The navigation module provides autonomous navigation capabilities using the Nav2 stack. It supports two primary navigation modes: SLAM (Simultaneous Localization and Mapping) and AMCL (Adaptive Monte Carlo Localization).

Verbose Logging

Enable verbose logs to see detailed client and Nav2 activity in the console. This is useful while developing or debugging navigation flows.

JS
// Enable verbose logging for core ROS operations oloClient.core.setVerboseLogging(true); // Enable verbose logging for Nav2 (status and log broadcasts) oloClient.navigation.setVerboseLogging(true); // ... run navigation operations as normal // Disable later if desired oloClient.core.setVerboseLogging(false); oloClient.navigation.setVerboseLogging(false);

SLAM Mode - Real-time Mapping

  • Purpose: Build a map in real-time while navigating
  • Best for: New or unknown environments, first-time exploration
  • How it works: Robot simultaneously tracks its position and creates a map of the environment
  • Map persistence: Map exists only in memory until explicitly saved
  • Localization: Uses odometry and sensor data to estimate position relative to the growing map
  • Use cases: Exploration, mapping new areas, environments that change frequently

AMCL Mode - Map-based Localization

  • Purpose: Navigate using a pre-existing saved map
  • Best for: Known environments with saved maps, production navigation
  • How it works: Robot localizes itself within a static, pre-loaded map
  • Map persistence: Uses permanently saved maps from database
  • Localization: Particle filter algorithm compares sensor readings to known map
  • Use cases: Repeated navigation tasks, delivery routes, known warehouse/office environments

Choosing Between SLAM and AMCL

Use SLAM when:

  • ๐Ÿ—บ๏ธ Exploring new environments - First time in an unknown space
  • ๐Ÿ”„ Environment changes frequently - Furniture moves, doors open/close regularly
  • ๐Ÿ“ Creating accurate maps - Need to map the space for future use
  • ๐Ÿงช Prototyping and testing - Developing navigation algorithms
  • ๐Ÿš€ Quick setup - No existing maps available, want to start immediately

Use AMCL when:

  • ๐Ÿข Known environments - Have a saved map of the space
  • ๐Ÿšš Production deployments - Reliable, repeatable navigation tasks
  • โšก Better performance - Faster localization with existing map
  • ๐ŸŽฏ Precise navigation - More accurate positioning with good maps
  • ๐Ÿ”’ Stable environments - Space layout doesn't change much

For New Environments (SLAM):

  1. Start navigation in SLAM mode
  2. Drive robot around to build map
  3. Save the completed map to database
  4. Switch to AMCL mode for future navigation

For Known Environments (AMCL):

  1. Find and select a saved map from database
  2. Start navigation in AMCL mode with selected map
  3. Set initial pose to tell robot where it starts on the map
  4. Navigate using the saved map for precise localization

Hybrid Approach (Recommended):

  1. Phase 1 - Mapping: Use SLAM to explore and map new areas
  2. Phase 2 - Production: Switch to AMCL for daily operations
  3. Phase 3 - Updates: Periodically re-map areas that change
  4. Phase 4 - Optimization: Fine-tune maps and navigation parameters

Multi-Robot Support and Namespacing

The OLO navigation system supports multiple robots running simultaneously through ROS2 namespace isolation. Each robot operates independently with its own Nav2 stack, topics, and frames.

How It Works:

Specify a robotNamespace when starting navigation or sending goals. The system automatically:

  1. Isolates Nav2 processes and topics per namespace (e.g., /robot1/scan, /robot1/cmd_vel)
  2. Transforms frame IDs (e.g., robot1/base_link, robot1/map)
  3. Structures parameters for proper ROS2 namespace lookup

Usage Example:

JS
// Discover available robots const robots = await oloClient.core.discoverRobots(); // Start navigation for robot1 await oloClient.navigation.startNavigationEngine({ mode: 'slam', robotNamespace: robots[0].namespace // e.g., 'robot1' }); // Start navigation for robot2 await oloClient.navigation.startNavigationEngine({ mode: 'slam', robotNamespace: robots[1].namespace // e.g., 'robot2' }); // Send goals to specific robots await oloClient.navigation.navigateTo( { x: 2.0, y: 1.5, yaw: 0.0 }, { robotNamespace: 'robot1' } ); await oloClient.navigation.navigateTo( { x: -1.0, y: 2.0, yaw: 1.57 }, { robotNamespace: 'robot2' } );

Robot Requirements: Your robots must publish topics and frames with namespace prefixes (e.g., /robot1/scan, robot1/base_link). Use discoverRobots() to automatically detect properly configured multi-robot setups.

Named Configuration System

Navigation configurations are managed through the OLO Portal and stored in the database. Each configuration has three sections:

  • Nav2 Configuration - Path planning, costmaps, controllers, and recovery behaviors
  • AMCL Configuration - Localization parameters for map-based navigation
  • SLAM Configuration - Mapping parameters for SLAM Toolbox

Configuration Usage:

  • If no configName or configId is specified, the system uses the user's default configuration
  • Configurations can be created, edited, and managed through the OLO Portal
  • Configurations are user-specific and can be shared across robots
  • Namespace prefixing is applied automatically when robot namespace is detected

Configuration Best Practices:

  • Start with the default configuration to ensure basic functionality
  • Create named configurations for different robot behaviors (e.g., "fast", "precise", "conservative")
  • Test configuration changes incrementally
  • Keep YAML properly indented and valid
  • Do NOT add namespace prefixes to frame IDs or topics in your YAML - the system handles this automatically

startNavigationEngine(options)

Start the Nav2 navigation engine in specified mode using a named configuration from the OLO Portal.

Parameters:

  • options (object) - Navigation engine configuration
    • mode (string) - 'slam' or 'localization' (AMCL)
    • mapId (number) - Map ID for AMCL mode (obtained from listMaps())
    • initialPose (object, optional) - Starting pose for AMCL mode
      • x (number) - X coordinate in meters
      • y (number) - Y coordinate in meters
      • yaw (number) - Orientation in radians
    • configName (string, optional) - Name of configuration to use (leave empty for user's default)
    • configId (string, optional) - ID of configuration to use (alternative to configName)
    • robotNamespace (string, optional) - Robot namespace for multi-robot scenarios
    • lidar3d (boolean, optional) - Enable RTAB-Map for 3D SLAM with point cloud data (requires 3D LiDAR sensor)
    • lidar3dTopic (string, optional) - Topic name for 3D LiDAR point cloud data (e.g., '/velodyne_points', '/points')
    • gpsEnabled (boolean, optional) - Enable GPS sensor fusion for improved localization accuracy (requires GPS sensor)

Configuration Selection:

  • If neither configName nor configId is specified, the user's default configuration is used
  • Configurations are managed through the OLO Portal and contain Nav2, AMCL, and SLAM settings
  • Each configuration has three sections that are automatically applied based on the mode

Multi-Robot Support: Multiple Nav2 instances can run concurrently by specifying different robotNamespace values. Each namespace gets its own independent Nav2 stack with isolated topics and frames.

3D SLAM with RTAB-Map: When lidar3d: true is specified, the system uses RTAB-Map for 3D simultaneous localization and mapping with point cloud data from 3D LiDAR sensors (e.g., Velodyne, Ouster, Hesai). This creates 3D maps with richer environmental understanding compared to traditional 2D SLAM.

GPS Sensor Fusion: When gpsEnabled: true is specified, the system fuses GPS data with LiDAR, IMU, and odometry for improved localization accuracy, especially useful for outdoor navigation or large-scale environments.

Returns: Promise<boolean> - True if engine started successfully

JS
// SLAM Mode with default configuration (single robot) await oloClient.navigation.startNavigationEngine({ mode: 'slam' // No configName specified - uses user's default configuration }); // SLAM Mode with a specific named configuration await oloClient.navigation.startNavigationEngine({ mode: 'slam', configName: 'fast-navigation' // Use a configuration created in the portal }); // 3D SLAM Mode with RTAB-Map (requires 3D LiDAR) await oloClient.navigation.startNavigationEngine({ mode: 'slam', lidar3d: true, lidar3dTopic: '/velodyne_points', // Your 3D LiDAR point cloud topic configName: '3d-mapping' }); // SLAM with GPS sensor fusion (requires GPS sensor) await oloClient.navigation.startNavigationEngine({ mode: 'slam', gpsEnabled: true, configName: 'outdoor-navigation' }); // 3D SLAM with GPS fusion for outdoor mapping await oloClient.navigation.startNavigationEngine({ mode: 'slam', lidar3d: true, lidar3dTopic: '/ouster/points', gpsEnabled: true, configName: 'outdoor-3d-mapping' }); // SLAM Mode for specific robot in multi-robot setup const robots = await oloClient.core.discoverRobots(); await oloClient.navigation.startNavigationEngine({ mode: 'slam', robotNamespace: robots[0].namespace, // e.g., 'robot1' configName: 'precise-mapping' }); // Multi-robot: Start navigation for multiple robots concurrently await oloClient.navigation.startNavigationEngine({ mode: 'slam', robotNamespace: 'robot1', configName: 'fast-navigation' }); await oloClient.navigation.startNavigationEngine({ mode: 'slam', robotNamespace: 'robot2', configName: 'conservative' }); // Both robots now navigate independently with isolated Nav2 stacks // AMCL Mode with saved map const maps = await oloClient.navigation.listMaps({ limit: 1 }); const savedMap = maps.maps[0]; await oloClient.navigation.startNavigationEngine({ mode: 'localization', mapId: savedMap.id, initialPose: { x: 0.0, y: 0.0, yaw: 0.0 }, configName: 'precise-localization' }); // AMCL Mode with GPS fusion for outdoor localization await oloClient.navigation.startNavigationEngine({ mode: 'localization', mapId: savedMap.id, initialPose: { x: 0.0, y: 0.0, yaw: 0.0 }, gpsEnabled: true, configName: 'outdoor-localization' });

stopNavigationEngine()

Stop the Nav2 navigation engine.

Returns: Promise<boolean> - True if engine stopped successfully

JS
await oloClient.navigation.stopNavigationEngine();

sendNavigationGoal(goal, options)

Send a navigation goal to the robot.

Parameters:

  • goal (object) - Navigation goal with x, y coordinates and optional yaw
  • options (object, optional) - Navigation options
    • onResult (function) - Callback when navigation result is received (success/fail/cancel)
    • onError (function) - Callback for errors in sending/handling the goal
    • onFeedback (function) - Callback for navigation progress feedback
    • abortSignal (AbortSignal) - Optional signal to auto-cancel the goal (e.g., SDK Playground Stop button)
    • timeoutMs (number) - Optional timeout that auto-cancels the goal after N ms
    • robotNamespace (string) - Optional robot namespace for multi-robot scenarios

Returns: Promise<string> - Goal ID

JS
// Single robot or global navigation const goalId = await oloClient.navigation.sendNavigationGoal( { x: 2.0, y: 1.5, yaw: 0.0 }, { onResult: (message) => console.log('Navigation result:', message), onError: (err) => console.error('Navigation error:', err), onFeedback: (fb) => console.log('Progress:', fb), // In SDK Playground, abortSignal is provided globally; in standalone apps, use your own AbortController abortSignal, timeoutMs: 60000 } ); // Multi-robot navigation - send goal to specific robot const robots = await oloClient.core.discoverRobots(); const goalId = await oloClient.navigation.sendNavigationGoal( { x: 2.0, y: 1.5, yaw: 0.0 }, { robotNamespace: robots[0].namespace, // e.g., 'robot1' onResult: (message) => console.log('Navigation result:', message), abortSignal, timeoutMs: 60000 } );

High-level helper that resolves when the goal completes or is cancelled. Supports abortSignal and timeoutMs like sendNavigationGoal.

Parameters:

  • goal (object) - Navigation goal with x, y coordinates and optional yaw
  • options (object, optional)
    • onFeedback (function) - Callback for navigation progress feedback
    • abortSignal (AbortSignal) - Optional signal to auto-cancel (e.g., SDK Playground Stop button)
    • timeoutMs (number) - Optional timeout that auto-cancels the goal after N ms
    • robotNamespace (string) - Optional robot namespace for multi-robot scenarios

Returns: Promise<Object> - Resolves with { result, status, goalId } on completion or cancellation

JS
// SDK Playground: abortSignal is automatically provided try { const result = await oloClient.navigation.navigateTo( { x: 1.0, y: 0.5, yaw: 0.0 }, { abortSignal, timeoutMs: 60000, onFeedback: (fb) => console.log('Distance remaining โ‰ˆ', fb.distance_remaining) } ); console.log('Goal completed:', result); } catch (err) { if (abortSignal.aborted) { console.log('Navigation aborted by user'); } else { console.error('Navigation failed:', err); } } // Multi-robot navigation - navigate specific robot const robots = await oloClient.core.discoverRobots(); const result = await oloClient.navigation.navigateTo( { x: 1.0, y: 0.5, yaw: 0.0 }, { robotNamespace: robots[0].namespace, // Target specific robot abortSignal, timeoutMs: 60000, onFeedback: (fb) => console.log('Distance remaining โ‰ˆ', fb.distance_remaining) } );

Which API should I use?

  • Use navigateTo (recommended) when:

    • You want a simple, sequential flow that awaits goal completion/cancel.
    • Youโ€™re wiring the SDK Playground Stop button (pass abortSignal) or want a builtโ€‘in timeoutMs.
    • You donโ€™t need to juggle multiple concurrent goals or custom event routing.
  • Use sendNavigationGoal when you need advanced control:

    • Managing multiple concurrent goals and retaining each goalโ€™s ID.
    • Fineโ€‘grained handling of streaming updates (ack/status/feedback/result) to drive a custom UI.
    • Custom cancellation/retry strategies and orchestration with other subsystems.
    • Integrating goal lifecycle with your own state machine or telemetry pipeline.

cancelNavigationGoal(requestId, options)

Cancel current navigation goal.

Parameters:

  • requestId (string, optional) - Specific request ID to cancel
  • options (object, optional) - Cancel options
    • robotNamespace (string) - Optional robot namespace for multi-robot scenarios

Returns: Promise - Resolves when goal is cancelled

JS
// Cancel global navigation goal await oloClient.navigation.cancelNavigationGoal(); // Cancel navigation goal for specific robot await oloClient.navigation.cancelNavigationGoal(null, { robotNamespace: 'robot1' }); // Cancel specific goal by ID for specific robot await oloClient.navigation.cancelNavigationGoal('goal_id_123', { robotNamespace: 'robot1' });

getNavigationStatus()

Get current navigation system status.

Returns: Object - Navigation status information

JS
const status = oloClient.navigation.getNavigationStatus(); console.log('Nav2 available:', status.nav2_available); console.log('Running components:', status.running_components);

setInitialPose(pose)

Set initial pose for AMCL localization. This is critical for AMCL mode - the robot needs to know where it starts on the saved map.

When to use:

  • Required for AMCL mode: Robot must know its starting position on the saved map
  • Not needed for SLAM mode: SLAM builds the map relative to starting position
  • After map loading: Set pose after starting AMCL with a saved map
  • When robot is lost: Reset localization if robot becomes confused about its position

How to determine initial pose:

  1. Known position: If you know where robot starts (e.g., charging dock at coordinates 2.5, 1.0)
  2. Map center: Use center of saved map as fallback (automatically calculated)
  3. Previous session: Use last known position from previous navigation session
  4. Manual placement: Drive robot to known landmark, then set pose to that landmark's coordinates

Parameters:

  • pose (object) - Initial pose in map coordinates
  • x (number) - X coordinate in meters (map frame)
  • y (number) - Y coordinate in meters (map frame)
  • yaw (number) - Orientation in radians (0 = facing positive X direction)
  • options (object, optional) - Additional options
  • robotNamespace (string) - Optional robot namespace for multi-robot scenarios

Returns: Promise<boolean> - True if pose set successfully

JS
// Set pose at robot's known starting location await oloClient.navigation.setInitialPose({ x: 2.5, // 2.5 meters from map origin y: 1.0, // 1.0 meters from map origin yaw: 1.57 // Facing 90 degrees (ฯ€/2 radians) }); // Set pose at map center (safe fallback) const mapInfo = await oloClient.navigation.getCurrentMapInfo(); const centerX = mapInfo.origin.position.x + (mapInfo.width * mapInfo.resolution) / 2; const centerY = mapInfo.origin.position.y + (mapInfo.height * mapInfo.resolution) / 2; await oloClient.navigation.setInitialPose({ x: centerX, y: centerY, yaw: 0.0 }); // Use current odometry as seed (if robot hasn't moved much) const currentPose = await oloClient.navigation.getCurrentPose(); await oloClient.navigation.setInitialPose({ x: currentPose.x, y: currentPose.y, yaw: currentPose.yaw }); // Multi-robot: Set initial pose for specific robot await oloClient.navigation.setInitialPose({ x: 2.5, y: 1.0, yaw: 1.57 }, { robotNamespace: 'robot1' });

Important Notes:

  • Coordinate system: Pose coordinates are in the map frame, not robot frame
  • Accuracy matters: Inaccurate initial pose can cause navigation failures
  • AMCL will refine: Particle filter will improve pose estimate over time using sensor data
  • Multiple attempts: AMCL may need a few seconds to converge on correct localization
  • Multi-robot support: Use robotNamespace to set initial pose for specific robots in multi-robot scenarios

getCurrentPose()

Get robot's current pose.

Returns: Promise<Object> - Current robot pose

JS
const pose = await oloClient.navigation.getCurrentPose(); console.log('Robot position:', pose.x, pose.y, pose.yaw);

saveMap(mapName, options)

Save current map with given name.

Parameters:

  • mapName (string) - Name for the saved map
  • options (object, optional) - Save options
    • description (string) - Map description (optional)
    • onProgress (function) - Progress callback (optional)
    • onSuccess (function) - Success callback (optional)
    • onError (function) - Error callback (optional)
    • robotNamespace (string) - Robot namespace for multi-robot scenarios (optional)

Returns: Promise<Object> - Map save result with ID, name, and metadata

JS
// Save map for global/single robot await oloClient.navigation.saveMap('office_map', { description: 'Office floor map with cubicles' }); // Save map for specific robot in multi-robot setup await oloClient.navigation.saveMap('warehouse_map', { description: 'Warehouse navigation map', robotNamespace: 'robot1', onProgress: (step) => console.log('Save progress:', step), onSuccess: (result) => console.log('Map saved:', result.name), onError: (error) => console.error('Save failed:', error.message) }); // Get detailed save result const result = await oloClient.navigation.saveMap('detailed_map', { description: 'Map with full metadata', robotNamespace: 'burger' }); console.log('Map saved with ID:', result.id); console.log('File size:', (result.file_size / 1024).toFixed(1) + 'KB'); console.log('Dimensions:', result.metadata.width + 'x' + result.metadata.height);

loadMap(mapName)

Load a previously saved map.

Parameters:

  • mapName (string) - Name of map to load

Returns: Promise<boolean> - True if map loaded successfully

JS
await oloClient.navigation.loadMap('office_map');

clearCostmaps()

Clear navigation costmaps (useful when robot gets stuck).

Returns: Promise<boolean> - True if costmaps cleared successfully

JS
await oloClient.navigation.clearCostmaps();

clearSlamMap()

Clear/reset the SLAM map to start fresh mapping.

Returns: Promise - Resolves when map is cleared

JS
await oloClient.navigation.clearSlamMap();

Map Discovery and Selection

Before using AMCL mode, you need to find and select a saved map. The OLO platform provides comprehensive map management capabilities.

listMaps(options)

Get list of saved maps for current user.

Parameters:

  • options (object, optional) - List options
    • limit (number) - Maximum number of maps (default: 50)
    • offset (number) - Offset for pagination (default: 0)
    • search (string) - Search term to filter maps by name (optional)
    • mapType (string) - Filter by map type (optional)

Returns: Promise<Object> - Maps list with pagination info and metadata

Response Structure:

JS
{ maps: [ { id: 123, name: "office_map_2024-01-15", description: "Office floor map with cubicles", created_at: "2024-01-15T10:30:00Z", updated_at: "2024-01-15T10:30:00Z", size_bytes: 2048576, width: 2000, height: 1500, resolution: 0.05, area_meters: 7500.0, user_id: 456 } ], total: 15, offset: 0, limit: 50, has_more: false }
JS
// Get all available maps const maps = await oloClient.navigation.listMaps({ limit: 10 }); console.log(`Found ${maps.total} maps:`); maps.maps.forEach(map => { console.log(`- ${map.name} (ID: ${map.id})`); console.log(` Size: ${map.width}x${map.height}, Area: ${map.area_meters.toFixed(1)}mยฒ`); console.log(` Created: ${new Date(map.created_at).toLocaleDateString()}`); }); // Search for specific maps const officeMaps = await oloClient.navigation.listMaps({ search: 'office', limit: 5 }); // Get the most recent map const recentMaps = await oloClient.navigation.listMaps({ limit: 1 }); // Maps are returned newest first const latestMap = recentMaps.maps[0];

getMap(mapId, includeData)

Get detailed information about a specific map, optionally including the full map data.

Parameters:

  • mapId (number) - Map ID (from listMaps() results)
  • includeData (boolean) - Whether to include full map data (default: false)
    • false: Returns only metadata (fast, small response)
    • true: Includes full map data in base64 format (slower, large response)

Returns: Promise<Object> - Map information and optionally map data

JS
// Get map metadata only (fast) const mapInfo = await oloClient.navigation.getMap(123, false); console.log('Map info:', mapInfo.name, mapInfo.width + 'x' + mapInfo.height); // Get map with full data (for loading into AMCL) const mapWithData = await oloClient.navigation.getMap(123, true); console.log('Map data size:', mapWithData.map_data.length, 'bytes (base64)'); // Use map data for navigation await oloClient.navigation.startNavigationEngine({ mode: 'localization', mapId: mapWithData.id, initialPose: { x: 0.0, y: 0.0, yaw: 0.0 } });

Complete AMCL Workflow Example

JS
// Complete workflow: Find map, load it, and start AMCL navigation async function startAmclNavigation() { // Step 1: Find available maps const maps = await oloClient.navigation.listMaps({ limit: 10 }); if (maps.maps.length === 0) { console.error('No saved maps found. Create a map using SLAM mode first.'); return; } // Step 2: Select the most recent map (or let user choose) const selectedMap = maps.maps[0]; // Most recent console.log(`Using map: ${selectedMap.name} (${selectedMap.width}x${selectedMap.height})`); // Step 3: Start AMCL with the selected map await oloClient.navigation.startNavigationEngine({ mode: 'localization', mapId: selectedMap.id, initialPose: { x: 0.0, // Robot's actual starting X position y: 0.0, // Robot's actual starting Y position yaw: 0.0 // Robot's actual starting orientation } }); console.log('โœ… AMCL navigation started with saved map'); // Step 4: Navigate to a goal await oloClient.navigation.sendNavigationGoal({ x: 2.0, y: 1.5, yaw: 0.0 }); }

deleteMap(mapId)

Delete a saved map.

Parameters:

  • mapId (number) - Map ID to delete

Returns: Promise<Object> - Deletion result

JS
await oloClient.navigation.deleteMap(123);

subscribeToLocalization(callback)

Subscribe to robot localization updates.

Parameters:

  • callback (function) - Callback for pose updates

Returns: Promise<string> - Subscription ID

JS
await oloClient.navigation.subscribeToLocalization((pose) => { console.log('Robot pose:', pose.x, pose.y, pose.yaw); });

subscribeToMap(callback)

Subscribe to map updates.

Parameters:

  • callback (function) - Callback for map updates

Returns: Promise<string> - Subscription ID

JS
await oloClient.navigation.subscribeToMap((mapData) => { console.log('Map updated:', mapData.info.width, 'x', mapData.info.height); });

getCurrentMapInfo()

Get information about the current SLAM map.

Returns: Promise<Object> - Current map information

JS
const mapInfo = await oloClient.navigation.getCurrentMapInfo(); console.log('Map size:', mapInfo.width, 'x', mapInfo.height); console.log('Resolution:', mapInfo.resolution, 'm/cell');