Run actions at waypoints

The navigation service drives the robot between waypoints, but it doesn’t perform actions at each stop. To capture an image, take a sensor reading, or trigger an alert at each waypoint, write code that monitors the robot’s progress and acts when a waypoint is reached.

The pattern

  1. Add waypoints to the navigation service.
  2. Set the mode to Waypoint.
  3. Poll GetWaypoints to detect when a waypoint is visited (it disappears from the list).
  4. When a waypoint is visited, run your action.
  5. Repeat until all waypoints are visited.

Example: capture an image at each waypoint

This example navigates to a sequence of GPS coordinates and captures a camera image at each one.

import asyncio
from datetime import datetime
from viam.robot.client import RobotClient
from viam.services.navigation import NavigationClient
from viam.components.camera import Camera
from viam.proto.common import GeoPoint
from viam.proto.service.navigation import Mode


async def main():
    opts = RobotClient.Options.with_api_key(
        api_key="YOUR-API-KEY",
        api_key_id="YOUR-API-KEY-ID"
    )
    robot = await RobotClient.at_address("YOUR-MACHINE-ADDRESS", opts)

    nav = NavigationClient.from_robot(robot, "my-nav")
    cam = Camera.from_robot(robot, "my-cam")

    # Define inspection points
    inspection_route = [
        GeoPoint(latitude=40.6640, longitude=-73.9387),
        GeoPoint(latitude=40.6645, longitude=-73.9382),
        GeoPoint(latitude=40.6642, longitude=-73.9375),
    ]

    # Add all waypoints
    for point in inspection_route:
        await nav.add_waypoint(point)

    # Start navigating
    await nav.set_mode(Mode.MODE_WAYPOINT)

    # Track which waypoints have been visited
    previous_count = len(inspection_route)
    waypoint_index = 0

    while True:
        waypoints = await nav.get_waypoints()
        current_count = len(waypoints)

        # A waypoint was visited when the count decreases
        if current_count < previous_count:
            print(f"Reached waypoint {waypoint_index + 1}")

            # Pause navigation to hold position while capturing
            await nav.set_mode(Mode.MODE_MANUAL)

            # Capture image
            images, metadata = await cam.get_images()
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"waypoint_{waypoint_index + 1}_{timestamp}.jpg"

            # Save or process the image
            print(f"Captured image: {filename}")

            # Resume navigation
            await nav.set_mode(Mode.MODE_WAYPOINT)

            waypoint_index += 1
            previous_count = current_count

        if current_count == 0:
            print("Inspection complete")
            break

        await asyncio.sleep(2)

    await nav.set_mode(Mode.MODE_MANUAL)
    await robot.close()

if __name__ == "__main__":
    asyncio.run(main())

How it works

The navigation service marks waypoints as visited when the robot arrives at each one. GetWaypoints returns only unvisited waypoints. By tracking the count, you detect arrivals.

When a waypoint is visited:

  1. Switch to Manual mode to pause navigation and hold position.
  2. Perform your action (capture image, read sensor, send alert).
  3. Switch back to Waypoint mode to resume navigation to the next waypoint.

Switching to Manual mode stops the current motion plan but preserves the remaining waypoints. Switching back to Waypoint mode resumes navigation from the next unvisited waypoint.

Other actions you can run at waypoints

The pattern works with any Viam API call:

  • Sensor readings: await sensor.get_readings() to log environmental data at each location.
  • Vision detections: await vision.get_detections_from_camera("cam") to run object detection at each stop.
  • Alerts: send a webhook or log entry when the robot reaches a specific location.
  • Gripper operations: pick up or place objects at designated waypoints.

What’s next