Add a movement sensor
Add a movement sensor to your machine’s configuration so you can track position, velocity, and orientation from the Viam app and from code.
Concepts
Unlike a generic sensor (which returns arbitrary readings), a movement sensor has a structured API with specific methods for spatial data:
GetPosition: latitude, longitude, altitude.GetLinearVelocity: speed in X, Y, Z.GetAngularVelocity: rotation rate around each axis.GetCompassHeading: heading in degrees.GetOrientation: orientation as Euler angles or quaternion.
Not every movement sensor supports every method. A GPS provides position and
compass heading but not angular velocity. An IMU provides orientation and
angular velocity but not GPS position. Use GetProperties to check which
methods a particular sensor supports.
Built-in models
| Model | Use case |
|---|---|
wheeled-odometry | Estimates position and velocity from motor encoders on a wheeled base. No additional hardware needed. |
merged | Combines data from multiple movement sensors into one. For example, GPS position + IMU orientation. |
Hardware-specific models (GPS modules, IMUs, RTK receivers) are available as modules in the Viam registry.
Steps
Option A: Wheeled odometry (no extra hardware)
If you have a wheeled base with encoders on the motors, you can get position and velocity estimates without any additional sensors.
1. Prerequisites
- A wheeled base with motors that have encoders configured.
2. Add the movement sensor
- Click the + button.
- Select Configuration block.
- Search for wheeled-odometry. This is the built-in model that computes position from wheel encoder data.
- Name it (for example,
odometry) and click Create.
3. Configure attributes
{
"base": "my-base",
"left_motors": ["left-motor"],
"right_motors": ["right-motor"]
}
| Attribute | Type | Required | Description |
|---|---|---|---|
base | string | Yes | Name of the wheeled base component. |
left_motors | list of strings | Yes | Left motor names (must have encoders). |
right_motors | list of strings | Yes | Right motor names (must have encoders). |
time_interval_msec | float | No | How often to recalculate. Default: 500. |
Option B: Hardware sensor (GPS, IMU)
1. Add the component
- Click the + button.
- Select Configuration block.
- Search for the model that matches your sensor hardware. Search by sensor name or chip (for example, NMEA GPS, BNO055, MPU6050).
- Name it and click Create.
- Configure attributes per the model’s documentation (typically I2C address or serial port).
Option C: Merged sensor
Combine data from multiple movement sensors into one.
{
"position": "gps-sensor",
"orientation": "imu-sensor",
"compass_heading": "gps-sensor",
"angular_velocity": "imu-sensor",
"linear_velocity": "odometry"
}
Each field names the movement sensor to use for that type of data. This lets you build a complete spatial picture from multiple hardware sources.
Save and test
Click Save, then expand the Test section.
- The test panel shows all available data: position, velocity, orientation, and heading.
- Methods the sensor doesn’t support will show as unavailable.
Try it
Read position and velocity data from the movement sensor.
To get the credentials for the code below, go to your machine’s page in the Viam app, click the CONNECT tab, and select API keys. Copy the API key and API key ID. Copy the machine address from the same tab. When you run the code below, you’ll see which methods your sensor supports and the current readings for each.
pip install viam-sdk
Save this as movement_sensor_test.py:
import asyncio
from viam.robot.client import RobotClient
from viam.components.movement_sensor import MovementSensor
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)
sensor = MovementSensor.from_robot(robot, "odometry")
# Check which methods this sensor supports
properties = await sensor.get_properties()
print(f"Supports position: {properties.position_supported}")
print(f"Supports linear velocity: {properties.linear_velocity_supported}")
print(f"Supports angular velocity: {properties.angular_velocity_supported}")
print(f"Supports compass heading: {properties.compass_heading_supported}")
if properties.position_supported:
position = await sensor.get_position()
print(f"Position: {position}")
if properties.linear_velocity_supported:
velocity = await sensor.get_linear_velocity()
print(f"Linear velocity: {velocity}")
await robot.close()
if __name__ == "__main__":
asyncio.run(main())
Run it:
python movement_sensor_test.py
mkdir movement-sensor-test && cd movement-sensor-test
go mod init movement-sensor-test
go get go.viam.com/rdk
Save this as main.go:
package main
import (
"context"
"fmt"
"go.viam.com/rdk/components/movementsensor"
"go.viam.com/rdk/logging"
"go.viam.com/rdk/robot/client"
"go.viam.com/rdk/utils"
)
func main() {
ctx := context.Background()
logger := logging.NewLogger("movement-sensor-test")
robot, err := client.New(ctx, "YOUR-MACHINE-ADDRESS", logger,
client.WithCredentials(utils.Credentials{
Type: utils.CredentialsTypeAPIKey,
Payload: "YOUR-API-KEY",
}),
client.WithAPIKeyID("YOUR-API-KEY-ID"),
)
if err != nil {
logger.Fatal(err)
}
defer robot.Close(ctx)
sensor, err := movementsensor.FromProvider(robot, "odometry")
if err != nil {
logger.Fatal(err)
}
// Check which methods this sensor supports
properties, err := sensor.Properties(ctx, nil)
if err != nil {
logger.Fatal(err)
}
fmt.Printf("Supports position: %v\n", properties.PositionSupported)
fmt.Printf("Supports linear velocity: %v\n", properties.LinearVelocitySupported)
fmt.Printf("Supports angular velocity: %v\n", properties.AngularVelocitySupported)
fmt.Printf("Supports compass heading: %v\n", properties.CompassHeadingSupported)
if properties.PositionSupported {
pos, alt, err := sensor.Position(ctx, nil)
if err != nil {
logger.Fatal(err)
}
fmt.Printf("Position: %v, altitude: %.2f\n", pos, alt)
}
if properties.LinearVelocitySupported {
vel, err := sensor.LinearVelocity(ctx, nil)
if err != nil {
logger.Fatal(err)
}
fmt.Printf("Linear velocity: %v\n", vel)
}
}
Run it:
go run main.go
Troubleshooting
What’s next
- Movement sensor API reference: full method documentation.
- Add a Base: the movement sensor typically pairs with a mobile base.
- Capture and Sync Data: capture position and motion data over time.
Was this page helpful?
Glad to hear it! If you have any other feedback please let us know:
We're sorry about that. To help us improve, please tell us what we can do better:
Thank you!