The Navigation Service

The navigation service is the stateful definition of Viam’s motion service. It uses GPS to autonomously navigate a rover base to user-defined endpoints called waypoints. Configure your base with a navigation service, add waypoints, and set the mode of the service to Waypoint to move your rover along a defined path at your desired motion configuration.

Used with

* Required for use

Requirements

You must configure a base with movement sensors as part of your machine to configure a navigation service.

To use the navigation service, configure a stack of movement sensors that implement the following methods in their models' implementations of the movement sensor API:

The base should implement the following:

See navigation concepts for more info on how to implement and use movement sensors taking these measurements.

Configuration

Navigate to the CONFIGURE tab of your machine’s page in the Viam app. Click the + icon next to your machine part in the left-hand menu and select Service. Select the navigation type. Enter a name or use the suggested name for your service and click Create.

An example configuration for a navigation service in the Viam app Config Builder.

Edit the attributes as applicable to your machine, according to the table below.

"services": [
{
    "name": "your-navigation-service",
    "type": "navigation",
    "attributes": {
        "store": {
            "type": "<your-store-type>"
        },
        "movement_sensor": "<your-movement-sensor>",
        "base": "<your-base>",
        "obstacle_detectors": [
          {
          "vision_service": "<your-vision-service>",
          "camera": "<your-camera>"
          }
        ]
    }
}
    ... // Other services
]
{
  "name": "test_navigation",
  "type": "navigation",
  "attributes": {
    "store": {
      "type": "mongodb",
      // Remove "config": { ... } below if using "type": "memory"
      "config": {
        "uri": "mongodb://127.0.0.1:12345"
      }
    }
  },
  "movement_sensor": "your-movement-sensor",
  "obstacle_detectors": [
    {
      "vision_service": "your-vision-service",
      "camera": "your-camera"
    },
    {
      "vision_service": "your-vision-service-2",
      "camera": "your-camera-2"
    }
  ]
  "base": "your-base",
  "obstacles": [
    {
      "geometries": [
        {
          "label": "your-label-for-this-obstacle",
          "orientation": {
            "type": "ov_degrees",
            "value": {
              "x": 1,
              "y": 0,
              "z": 0,
              "th": 90
            }
          },
          "x": 10,
          "y": 10,
          "z": 10
        }
      ],
      "location": {
        "latitude": 1,
        "longitude": 1
      }
    }
  ]
}

Edit and fill in the attributes as applicable. The following attributes are available for Navigation services:

NameTypeRequired?Description
storeobjRequiredThe type and configuration of data storage to use. Either type "memory", where no additional configuration is needed and the waypoints are stored in local memory while the navigation process is running, or "mongodb", where data persists at the specified MongoDB URI of your MongoDB deployment.
Default: "memory"
basestringRequiredThe name you have configured for the base you are operating with this service.
movement_sensorstringRequiredThe name of the movement sensor you have configured for the base you are operating with this service.
motion_servicestringOptionalThe name of the motion service you have configured for the base you are operating with this service. If you have not added a motion service to your machine, the default motion service will be used. Reference this default service in your code with the name "builtin".
obstacle_detectorsarrayOptionalAn array containing objects with the name of each "camera" you have configured for the base you are navigating, along with the name of the "vision_service" you are using to detect obstacles. Note that any vision services on remote parts will only be able to access cameras on the same remote part.
position_polling_frequency_hzfloatOptionalThe frequency in Hz to poll for the position of the machine.
Default: 1
obstacle_polling_frequency_hzfloatOptionalThe frequency in Hz to poll each vision service for new obstacles.
Default: 1
plan_deviation_mfloatOptionalThe distance in meters that a machine is allowed to deviate from the motion plan.
Default: 2.6
degs_per_secfloatOptionalThe default angular velocity for the base in degrees per second.
Default: 20
meters_per_secfloatOptionalThe default linear velocity for the base in meters per second.
Default: 0.3
obstaclesobjOptionalAny obstacles you wish to add to the machine’s path. See the motion service for more information.

Configure and calibrate the frame system service for GPS navigation

To make sure your rover base’s autonomous GPS navigation with the navigation service is accurate, configure and calibrate the frame system service for the components of your machine.

Configure

Add a nested reference frame configuration to your rover base and movement sensor:

  • Navigate to the CONFIGURE tab of your machine’s page in the Viam app and select the Frame mode.

  • From the left-hand menu, select your base.

  • Since you haven’t adjusted any parameters yet, the default reference frame will be shown for your base:

    Frame card for a base with the default reference frame settings
  • Keep the parent frame as world. Select the Geometry dropdown menu.

  • Configure a Geometry for the base that reflects its physical dimensions. Measure the physical dimensions of your base and use them to configure the size of your geometry. Units are in mm.

    For example, you would configure a box-shaped base with dimensions of 100mm x 100mm x 100mm (l x h x w) as follows:

    The frame card for the base in the Viam app config builder.
  • Next, select your movement sensor from the left-hand menu. Click on the Parent menu and select your base component.

  • Give the movement sensor a Translation that reflects where it is mounted on your base, measuring the coordinates with respect to the origin of the base. In other words, designate the base origin as (0,0,0) and measure the distance between that and the origin of the sensor to obtain the coordinates.

    For example, you would configure a movement sensor mounted 200mm on top of your base as follows:

    The frame card for the movement sensor in the Viam app config builder.

You can also adjust the Orientation and Geometry of your movement sensor or base, if necessary. See the frame system service for instructions.

Calibrate

Then, to calibrate your frame system for the most accurate autonomous GPS navigation with the navigation service:

  • After configuring your machine, navigate to the CONTROL tab and select the card matching the name of your movement sensor.
  • Monitor the readings displayed on the card, and verify that the compass or orientation readings from the movement sensor report 0 when the base is facing north.
  • If you cannot verify this:
    • Navigate back to your machine’s CONFIGURE tab and Frame subtab. Scroll to the card with the name of your movement sensor. Adjust the Orientation of the frame to compensate for the mismatch.
    • Navigate back to the movement sensor card on your CONTROL page, and confirm that the compass or orientation readings from the movement sensor now report 0 when the base is facing north, confirming that you’ve successfully calibrated your machine to be oriented accurately within the frame system.
    • If you cannot verify this, repeat as necessary.

API

The navigation service supports the following methods:

Method NameDescription
ModeGet the mode the service is operating in.
SetModeSet the mode the service is operating in.
LocationGet the current location of the robot.
WaypointsGet the waypoints currently in the service’s data storage.
GetPropertiesGet information regarding the service.
AddWaypointAdd a waypoint to the service’s data storage.
RemoveWaypointRemove a waypoint from the service’s data storage.
PathsGet each path, the series of geo points the robot plans to travel through to get to a destination waypoint, in the robot’s motion planning.
ObstaclesGet the obstacles currently in the service’s data storage.
DoCommandExecute model-specific commands that are not otherwise defined by the service API.
CloseSafely shut down the resource and prevent further use.

Mode

Get the Mode the service is operating in.

There are two options for modes: MODE_MANUAL or MODE_WAYPOINT.

  • MODE_WAYPOINT: Start to look for added waypoints and begin autonomous navigation.
  • MODE_MANUAL: Stop autonomous navigation between waypoints and allow the base to be controlled manually.

Parameters:

  • timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:

For more information, see the Python SDK Docs.

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

# Get the Mode the service is operating in
await my_nav.get_mode()

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • (Mode): The Mode the service is operating in.
  • (error): An error, if one occurred.

For more information, see the Go SDK Docs.

// Get the Mode the service is operating in
mode, err := myNav.Mode(context.Background(), nil)

SetMode

Set the Mode the service is operating in.

There are two options for modes: MODE_MANUAL or MODE_WAYPOINT.

  • MODE_WAYPOINT: Start to look for added waypoints and begin autonomous navigation.
  • MODE_MANUAL: Stop autonomous navigation between waypoints and allow the base to be controlled manually.

Parameters:

Returns:

  • None

For more information, see the Python SDK Docs.

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

# Set the Mode the service is operating in to MODE_WAYPOINT and begin
# navigation
await my_nav.set_mode(Mode.ValueType.MODE_WAYPOINT)

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • mode (Mode): The Mode for the service to operate in.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • (error): An error, if one occurred.

For more information, see the Go SDK Docs.

// Set the Mode the service is operating in to MODE_WAYPOINT and begin navigation
err := myNav.SetMode(context.Background(), navigation.ModeWaypoint, nil)

Location

Get the current location of the robot in the navigation service.

Parameters:

  • timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:

  • (navigation.GeoPoint): The current location of the robot in the navigation service, represented in a GeoPoint with latitude and longitude values.

For more information, see the Python SDK Docs.

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

# Get the current location of the robot in the navigation service
location = await my_nav.get_location()

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • (*geo.Point): The current location of the robot in the navigation service, represented in a Point with latitude and longitude values.
  • (error): An error, if one occurred.

For more information, see the Go SDK Docs.

// Get the current location of the robot in the navigation service
location, err := myNav.Location(context.Background(), nil)

Waypoints

Get an array of waypoints currently in the service’s data storage. These are locations designated within a path for the robot to navigate to.

Parameters:

  • timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:

  • (List[navigation.Waypoint]): An array comprised of each Waypoint in the service’s data storage. These are locations designated within a path for the robot to navigate to.

For more information, see the Python SDK Docs.

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

# Get a list containing each waypoint stored by the navigation service
waypoints = await my_nav.get_waypoints()

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • ([]Waypoints): An array comprised of each Waypoint in the service’s data storage. These are locations designated within a path for the robot to navigate to.
  • (error): An error, if one occurred.

For more information, see the Go SDK Docs.

// Get an array containing each waypoint stored by the navigation service
waypoints, err := myNav.Waypoints(context.Background(), nil)

GetProperties

Get information about the navigation service.

Parameters:

  • timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

# Get the properties of the current navigation service.
nav_properties = await my_nav.get_properties()

For more information, see the Python SDK Docs.

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.

Returns:

  • Properties: Information about the current navigation service. This includes the map type being ingested and used by the navigation service.
  • (error): An error, if one occurred.

For more information, see the Go SDK Docs.

// Get the properties of the current navigation service
navProperties, err := myNav.Properties(context.Background())

AddWaypoint

Add a waypoint to the service’s data storage.

Parameters:

  • point(navigation.GeoPoint): The current location of the robot in the navigation service, represented in a GeoPoint with latitude and longitude values.
  • timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:

  • None

For more information, see the Python SDK Docs.

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

# Create a new waypoint with latitude and longitude values of 0 degrees
location = GeoPoint(latitude=0, longitude=0)


# Add your waypoint to the service's data storage
await my_nav.add_waypoint(point=location)

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • point (*geo.Point): The current location of the robot in the navigation service, represented in a Point with latitude (lat) and longitude (lng) values.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • (error): An error, if one occurred.

For more information, see the Go SDK Docs.

// Create a new waypoint with latitude and longitude values of 0 degrees
// Assumes you have imported "github.com/kellydunn/golang-geo" as `geo`
location := geo.NewPoint(0, 0)

// Add your waypoint to the service's data storage
err := myNav.AddWaypoint(context.Background(), location, nil)

RemoveWaypoint

Remove a waypoint from the service’s data storage. If the robot is currently navigating to this waypoint, the motion will be canceled, and the robot will proceed to the next waypoint.

Parameters:

  • id(str): The MongoDB ObjectID of the Waypoint to remove from the service’s data storage.
  • timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:

  • None

For more information, see the Python SDK Docs.

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

# Remove the waypoint matching that ObjectID from the service's data storage
await my_nav.remove_waypoint(waypoint_id)

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • id (primitive.ObjectID): The MongoDB ObjectID of the Waypoint to remove from the service’s data storage.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • (error): An error, if one occurred.

For more information, see the Go SDK Docs.

// Assumes you have already called AddWaypoint once and the waypoint has not yet been reached
waypoints, err := myNav.Waypoints(context.Background(), nil)
if (err != nil || len(waypoints) == 0) {
  return
}

// Remove the waypoint from the service's data storage
err = myNav.RemoveWaypoint(context.Background(), waypoints[0].ID, nil)

Obstacles

Get an array or list of the obstacles currently in the service’s data storage. These are objects designated for the robot to avoid when navigating. These include all transient obstacles which are discovered by the vision services configured for the navigation service, in addition to the obstacles that are configured as a part of the service. See the motion service for more information.

Parameters:

  • timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:

  • (List[navigation.GeoObstacle]): A list comprised of each GeoObstacle in the service’s data storage. These are objects designated for the robot to avoid when navigating.

For more information, see the Python SDK Docs.

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

# Get a list containing each obstacle stored by the navigation service
obstacles = await my_nav.get_obstacles()

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • ([]*spatialmath.GeoObstacle): An array comprised of each GeoObstacle in the service’s data storage. These are objects designated for the robot to avoid when navigating.
  • (error): An error, if one occurred.

For more information, see the Go SDK Docs.

// Get an array containing each obstacle stored by the navigation service
obstacles, err := myNav.Obstacles(context.Background(), nil)

Paths

Get each path, the series of geo points the robot plans to travel through to get to a destination waypoint, in the machine’s motion planning.

Parameters:

  • timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:

  • (List[navigation.Path]): An array comprised of Paths, each path being a user-provided destination, or, Waypoint and the set of geopoints that the robot expects to travel through to get there.

For more information, see the Python SDK Docs.

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

# Get a list containing each path stored by the navigation service
paths = await my_nav.get_paths()

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.
  • extra (map[string]interface{}): Extra options to pass to the underlying RPC call.

Returns:

  • ([]*Path: An array of paths, each path being a user-provided destination, or, Waypoint, and the set of geo Points the robot plans to travel through to get there.
  • (error): An error, if one occurred.

For more information, see the Go SDK Docs.

// Get an array containing each path stored by the navigation service
paths, err := myNav.Paths(context.Background(), nil)

DoCommand

Execute model-specific commands that are not otherwise defined by the service API. For built-in service models, any model-specific commands available are covered with each model’s documentation. If you are implementing your own navigation service and add features that have no built-in API method, you can access them with DoCommand.

Parameters:

Returns:

my_nav = NavigationClient.from_robot(robot=robot, name="my_nav_service")

my_command = {
  "command": "dosomething",
  "someparameter": 52
}

await my_nav.do_command(my_command)

For more information, see the Python SDK Docs.

Parameters:

Returns:

resp, err := myNav.DoCommand(context.Background(), map[string]interface{}{"command": "dosomething", "someparameter": 52})

For more information, see the Go SDK Docs.

Close

Safely shut down the resource and prevent further use.

Parameters:

  • None

Returns:

  • None
my_nav = NavigationClient.from_robot(robot, "my_nav_service")

await my_nav.close()

For more information, see the Python SDK Docs.

Parameters:

  • ctx (Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries.

Returns:

  • (error) : An error, if one occurred.
err := myNav.Close(context.Background())

For more information, see the Go SDK Docs.

Control tab usage

After configuring the navigation service for your machine, navigate to the CONTROL tab of the machine’s page in the Viam app and expand the card matching the name of your service to use an interface for rover navigation.

Here, you can toggle the mode of the service between Manual and Waypoint to start and stop navigation, add waypoints and obstacles, and view the position of your rover base on a map:

An example control interface for a navigation service in the Viam app Control Tab.

The following concepts are important to understand when utilizing the navigation service. Each concept is a type of relative or absolute measurement, taken by a movement sensor, which can then be utilized by your machine to navigate across space.

Here’s how to use the following types of measurements:

Compass heading

The following models of movement sensor take compass heading measurements:

An example of a CompassHeading reading:

// heading is a float64 between 0-360
heading, err := gps.CompassHeading(context.Background(), nil)

Use compass heading readings to determine the bearing of your machine, or, the cardinal direction that your machine is facing.

To read compass headings, configure a capable movement sensor on your machine. Then use the movement sensor API’s GetCompassHeading() method to get readings from the sensor.

Orientation

The following models of movement sensor take orientation measurements:

An example of an Orientation reading:

// orientation is a OrientationVector struct with OX, OY, OZ denoting the coordinates of the vector and rotation about z-axis, Theta
orientation, err := imuwit.Orientation(context.Background(), nil)

Use orientation readings to determine the orientation of an object in 3D space as an orientation vector. An orientation vector indicates how it is rotated relative to an origin coordinate system around the x, y, and z axes. You can choose the origin reference frame by configuring it using Viam’s frame system. The GetOrientation readings will report orientations relative to that initial frame.

To read orientation, first configure a capable movement sensor on your machine. Additionally, follow these instructions to configure the geometries of each component of your machine within the frame system. Then use the movement sensor API’s GetOrientation() method to get orientation readings.

Angular velocity

The following models of the movement sensor component take angular velocity measurements:

An example of an AngularVelocity reading:

// angularVelocity is an AngularVelocity r3 Vector with X, Y, and Z magnitudes
angularVelocity, err := imu.AngularVelocity(context.Background(), nil)

Use angular velocity readings to determine the speed and direction at which your machine is rotating.

To get an angular velocity reading, first configure a capable movement sensor on your machine. Then use the movement sensor API’s GetAngularVelocity() method to get angular velocity readings from the sensor.

Position

The following models of the movement sensor component take position measurements:

An example of a Position reading:

// position is a geo.Point consisting  of Lat and Long: -73.98 and an altitude in float64
position, altitude, err := imu.Position(context.Background(), nil)

Use position readings to determine the GPS coordinates of an object in 3D space or its position in the geographic coordinate system (GCS). These position readings reflect the absolute position of components.

To get a position, configure a capable movement sensor on your machine. Then use the movement sensor API’s GetPosition() method to get position readings from the sensor.

Linear velocity

The following models of movement sensor take linear velocity measurements:

An example of a LinearVelocity reading:

// linearVelocity is an r3.Vector with X, Y, and Z magnitudes
linearVelocity, err := imu.LinearVelocity(context.Background(), nil)

Use linear velocity readings to determine the speed at which your machine is moving through space.

To get linear velocity, configure a capable movement sensor on your machine. Then use the movement sensor API’s GetLinearVelocity() method to get linear velocity readings from the sensor.

Linear acceleration

The following models of movement sensor take linear acceleration measurements:

An example of a LinearAcceleration reading:

// linearAcceleration is an r3.Vector with X, Y, and Z magnitudes
linearAcceleration, err := imu.LinearAcceleration(context.Background(), nil)

You can use linear acceleration readings to determine the rate of change of the linear velocity of your machine, or, the acceleration at which your machine is moving through space.

To get linear acceleration, configure a capable movement sensor on your machine. Then use the movement sensor API’s GetLinearAcceleration() method to get linear acceleration readings from the sensor.

Next steps

If you would like to see the navigation service in action, check out this tutorial: