Drive a Rover with the Viam SDK
The Viam SDKs allow you to write code in Python, Go, or TypeScript to control a Viam-connected machine. In this tutorial you will learn how to use the Viam SDKS as you write code to make a robot drive in a square. You can follow this tutorial with a rented Viam Rover, your own Viam Rover, or another mobile robot.
Tip
You can also directly see the complete code for the tutorial.
Hardware requirements
You don’t need any hardware to complete this tutorial! You can rent a rover to drive remotely at no cost with Try Viam.
If you have your own rover on hand, whether it’s a Viam rover or not, this tutorial works for any wheeled robot that can be configured as a base component.
Important
If you are using your own robot for this tutorial instead of renting one, be sure to install viam-server
on it and configure its hardware before proceeding with this tutorial.
Install a Viam SDK
Install either the Viam Python SDK, the Viam Go SDK, or the TypeScript SDK on your local computer.
Tip
If you are renting your rover, we recommend that you get the Viam SDK set up before your reservation starts. This way, you can maximize the amount of time you have using the Viam Rover.
If you are running out of time during your rental, you can extend your rover rental as long as there are no other reservations.
Connect to your rover
The easiest way to get started writing an application with Viam is to navigate to your robot’s page on the Viam app, select the Code sample tab, then select Python and copy the boilerplate code.
API key and API key id
By default, the sample code does not include your robot API key and API key id. We strongly recommend that you add your API key and API key id as an environment variable and import this variable into your development environment as needed.
To show your robot’s API key and API key id in the sample code, toggle Include secret on the Code sample tab. You can also see your API key and API key id on your robot’s Security tab.
Caution
Do not share your API key or robot address publicly. Sharing this information could compromise your system security by allowing unauthorized access to your robot, or to the computer running your robot.
This code snippet imports all the necessary libraries and sets up a connection with the Viam app in the cloud.
Next, create a file named
Run the code to verify that the Viam SDK is properly installed and that the viam-server
instance on your robot is live.
You can run your code by typing the following into your terminal:
python3 square.py
The program prints an array of resources. These are the components and services that the robot is configured with in the Viam app.
python3 square.py
2023-05-12 11:33:21,045 INFO viam.rpc.dial (dial.py:211) Connecting to socket: /tmp/proxy-Dome34KJ.sock
Resources:
[namespace: "rdk"
type: "component"
subtype: "motor"
name: "left"
, namespace: "rdk"
type: "component"
subtype: "camera"
name: "cam"
, ...
]
The easiest way to get started writing an application with Viam is to navigate to the robot page on the Viam app, select the Code sample tab, then select Go and copy the boilerplate code.
API key and API key id
By default, the sample code does not include your robot API key and API key id. We strongly recommend that you add your API key and API key id as an environment variable and import this variable into your development environment as needed.
To show your robot’s API key and API key id in the sample code, toggle Include secret on the Code sample tab. You can also see your API key and API key id on your robot’s Security tab.
Caution
Do not share your API key or robot address publicly. Sharing this information could compromise your system security by allowing unauthorized access to your robot, or to the computer running your robot.
This code snippet imports all the necessary libraries and sets up a connection with the Viam app in the cloud.
Next, create a file named
Initialize your project, and install the necessary libraries, and then run the program to verify that the Viam SDK is properly installed and that the viam-server
instance on your robot is live:
go mod init square
go mod tidy
go run square.go
The program prints an array of resources. These are the components and services that the robot is configured with in the Viam app.
go run square.go
2023-05-12T11:28:00.383+0200 INFO client rover/square.go:40
Resources:
2023-05-12T11:28:00.383+0200 INFO client rover/square.go:41
[rdk:component:camera/fakeCam rdk:service:data_manager/overhead-cam:dm rdk:component:motor/left rdk:component:camera/cam rdk:component:encoder/Lenc rdk:component:encoder/Renc rdk:service:base_remote_control/base_rc rdk:service:sensors/builtin rdk:component:motor/right rdk:service:sensors/overhead-cam:builtin rdk:service:motion/overhead-cam:builtin rdk:component:input_controller/WebGamepad rdk:component:camera/overhead-cam:overheadcam rdk:service:data_manager/builtin rdk:service:motion/builtin rdk:component:board/local rdk:component:base/viam_base]
The easiest way to get started writing an application with Viam is to navigate to the robot page on the Viam app, select the Code sample tab, then select TypeScript and copy the boilerplate code.
API key and API key id
By default, the sample code does not include your robot API key and API key id. We strongly recommend that you add your API key and API key id as an environment variable and import this variable into your development environment as needed.
To show your robot’s API key and API key id in the sample code, toggle Include secret on the Code sample tab. You can also see your API key and API key id on your robot’s Security tab.
Caution
Do not share your API key or robot address publicly. Sharing this information could compromise your system security by allowing unauthorized access to your robot, or to the computer running your robot.
This code snippet imports all the necessary libraries and sets up a connection with the Viam app in the cloud.
Next, create a file named
Create another file named
{
"name": "test-rover",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "esbuild ./main.ts --bundle --outfile=static/main.js --servedir=static",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Viam Docs Team",
"license": "ISC",
"devDependencies": {
"esbuild": "0.16.12"
},
"dependencies": {
"@viamrobotics/sdk": "*"
}
}
Create a folder
<!doctype html>
<html>
<head>
<title>Drive a Rover</title>
<link rel="icon" href="favicon.ico" />
</head>
<body>
<div id="main">
<button id="main-button" disabled="true">Click me</button>
</div>
<script type="module" src="main.js"></script>
</body>
</html>
Run the following commands to install the necessary libraries, and then run the program to verify that the Viam SDK is properly installed and that the viam-server
instance on your robot is live:
npm install
npm start
Open a web browser and visit localhost:8000
.
You should see a disabled button that says Click me
.
If you successfully configured your robot and it is able to connect to the Viam app, the button will become enabled.
If you open the developer console, you should see some output including the names of your rover’s resources.
These are the components and services that the robot is configured with in the Viam app.
Tip
If you are renting your rover, and your reservation ends before you have completed this tutorial, change the connection information (the robot address and the payload) to connect to your new rover and continue.
Drive your rover in a square
Now that you have connected the rover to Viam with the SDK, you can start writing code to control the rover. The following code moves the rover in a square:
The first thing you need to do is import the base component. The base is responsible for controlling the motors attached to the base of the rover. Add the following line of code to your imports:
from viam.components.base import Base
Next, you need to initialize your rover base.
In the main function, after you connect, paste the code from line 5.
On the Try Viam rental rovers, the default base name is viam_base
.
If you have a different base name, update the name in your code.
Your main function should look like this:
async def main():
robot = await connect()
# Get the base component from the rover
roverBase = Base.from_robot(robot, 'viam_base')
await robot.close()
Now that your rover base is initialized, you can write code to drive it in a square.
Paste this snippet above your main()
function:
async def moveInSquare(base):
for _ in range(4):
# moves the rover forward 500mm at 500mm/s
await base.move_straight(velocity=500, distance=500)
print("move straight")
# spins the rover 90 degrees at 100 degrees per second
await base.spin(velocity=100, angle=90)
print("spin 90 degrees")
Invoke the moveInSquare()
function in your main function after initializing your base.
Your main function should now look like this:
async def main():
robot = await connect()
# Get the base component from the rover
roverBase = Base.from_robot(robot, 'viam_base')
# Move the rover in a square
await moveInSquare(roverBase)
await robot.close()
The first thing you need to do is import the base component. The base is responsible for controlling the motors attached to the base of the rover. Add the following line of code to your imports before:
import (
// Be sure to keep all of the other imported libraries
"go.viam.com/rdk/components/base"
)
Next, you need to initialize your rover base.
In the main function, after you connect, paste the code from lines 19-22.
On the Try Viam rental rovers, the default base name is viam_base
.
If you have a different base name, update the name in your code.
Your main function should look like this:
func main() {
logger := logging.NewLogger("client")
robot, err := client.New(
context.Background(),
"ADDRESS FROM THE VIAM APP",
logger,
client.WithDialOptions(rpc.WithEntityCredentials(
// Replace "<API-KEY-ID>" (including brackets) with your robot's api key id
"<API-KEY-ID>",
rpc.Credentials{
Type: rpc.CredentialsTypeAPIKey,
// Replace "<API-KEY>" (including brackets) with your robot's api key
Payload: "<API-KEY>",
})),
)
if err != nil {
logger.Fatal(err)
}
defer robot.Close(context.Background())
// Get the base from the rover
roverBase, err := base.FromRobot(robot, "viam_base")
if err != nil {
logger.Fatalf("cannot get base: %v", err)
}
}
Now that your rover base has been initialized, you can write code to drive it in a square.
Paste this snippet above your main()
function:
func moveInSquare(ctx context.Context, base base.Base, logger logging.Logger) {
for i := 0; i < 4; i++ {
// moves the rover forward 600mm at 500mm/s
base.MoveStraight(ctx, 600, 500.0, nil)
logger.Info("move straight")
// spins the rover 90 degrees at 100 degrees per second
base.Spin(ctx, 90, 100.0, nil)
logger.Info("spin 90 degrees")
}
}
Invoke the moveInSquare()
function in your main function after initializing your base.
Your main function should now look like this:
func main() {
// Connect rover to Viam...
// Get the base from the rover
roverBase, err := base.FromRobot(robot, "viam_base")
if err != nil {
logger.Fatalf("cannot get base: %v", err)
}
// Move the rover in a square
moveInSquare(context.Background(), roverBase, logger)
}
Your main function should look similar to this but only the first few lines that connect to your rover are important for us:
async function main() {
const host = "ADDRESS_FROM_VIAM_APP";
const robot = await VIAM.createRobotClient({
host,
credential: {
type: "api-key",
// Replace "<API-KEY>" (including brackets) with your robot's api key
payload: "<API-KEY>",
},
// Replace "<API-KEY-ID>" (including brackets) with your robot's api key id
authEntity: "<API-KEY-ID>",
signalingAddress: "https://app.viam.com:443",
});
// Note that the pin supplied is a placeholder. Please change this to a valid pin you are using.
// local
const localClient = new VIAM.BoardClient(robot, "local");
const localReturnValue = await localClient.getGPIO("16");
console.log("local getGPIO return value:", localReturnValue);
// right
const rightClient = new VIAM.MotorClient(robot, "right");
const rightReturnValue = await rightClient.isMoving();
console.log("right isMoving return value:", rightReturnValue);
// left
const leftClient = new VIAM.MotorClient(robot, "left");
const leftReturnValue = await leftClient.isMoving();
console.log("left isMoving return value:", leftReturnValue);
// viam_base
const viamBaseClient = new VIAM.BaseClient(robot, "viam_base");
const viamBaseReturnValue = await viamBaseClient.isMoving();
console.log("viam_base isMoving return value:", viamBaseReturnValue);
// cam
const camClient = new VIAM.CameraClient(robot, "cam");
const camReturnValue = await camClient.getImage();
console.log("cam getImage return value:", camReturnValue);
console.log("Resources:");
console.log(await robot.resourceNames());
}
Underneath the main
function, add the following function that initializes your rover base client and drives it in a square:
Important
On the Try Viam rental rovers, the default base name is viam_base
.
If you have a different base name, update the name in your code.
// This function moves a base component in a square.
async function moveInSquare(client: VIAM.RobotClient) {
// Replace with the name of a motor on your robot.
const name = "viam_base";
const baseClient = new VIAM.BaseClient(client, name);
try {
button().disabled = true;
for (let i = 0; i < 4; i++) {
console.log("move straight");
await baseClient.moveStraight(500, 500);
console.log("spin 90 degrees");
await baseClient.spin(90, 100);
}
} finally {
button().disabled = false;
}
}
Underneath the moveInSquare
function, add this button
function which gets the main-button
button from the page loaded from index.html
:
// This function gets the button element
function button() {
return <HTMLButtonElement>document.getElementById("main-button");
}
Next, register a listener on the button you obtain from the button
fuction and make it invoke the moveInSquare
function.
Place this code after the rover connection code:
You can keep or remove the additional boilerplate code as you wish.
Your main function should now look like this:
async function main() {
const host = "ADDRESS_FROM_VIAM_APP";
const robot = await VIAM.createRobotClient({
host,
credential: {
type: "api-key",
// Replace "<API-KEY>" (including brackets) with your robot's api key
payload: "<API-KEY>",
},
// Replace "<API-KEY-ID>" (including brackets) with your robot's api key id
authEntity: "<API-KEY-ID>",
signalingAddress: "https://app.viam.com:443",
});
button().onclick = async () => {
await moveInSquare(robot);
};
button().disabled = false;
}
Tip
If you are renting your rover, go to the Control tab, and make sure you can monitor the camera feed from your rover.
When you run your code, your robot moves in a square.
Complete Code
This is the complete code for the tutorial:
import asyncio
from viam.components.base import Base
from viam.robot.client import RobotClient
from viam.rpc.dial import Credentials, DialOptions
async def connect():
opts = RobotClient.Options.with_api_key(
# Replace "<API-KEY>" (including brackets) with your robot's api key
api_key='<API-KEY>',
# Replace "<API-KEY-ID>" (including brackets) with your robot's api key
# id
api_key_id='<API-KEY-ID>'
)
return await RobotClient.at_address('ADDRESS FROM THE VIAM APP', opts)
async def moveInSquare(base):
for _ in range(4):
# moves the rover forward 500mm at 500mm/s
await base.move_straight(velocity=500, distance=500)
print("move straight")
# spins the rover 90 degrees at 100 degrees per second
await base.spin(velocity=100, angle=90)
print("spin 90 degrees")
async def main():
robot = await connect()
print('Resources:')
print(robot.resource_names)
roverBase = Base.from_robot(robot, 'viam_base')
# Move the rover in a square
await moveInSquare(roverBase)
await robot.close()
if __name__ == '__main__':
asyncio.run(main())
package main
import (
"context"
"go.viam.com/rdk/components/base"
"go.viam.com/rdk/logging"
"go.viam.com/rdk/robot/client"
"go.viam.com/rdk/utils"
"go.viam.com/utils/rpc"
)
func moveInSquare(ctx context.Context, base base.Base, logger logging.Logger) {
for i := 0; i < 4; i++ {
// moves the rover forward 600mm at 500mm/s
base.MoveStraight(ctx, 600, 500.0, nil)
logger.Info("move straight")
// spins the rover 90 degrees at 100 degrees per second
base.Spin(ctx, 90, 100.0, nil)
logger.Info("spin 90 degrees")
}
}
func main() {
logger := logging.NewLogger("client")
robot, err := client.New(
context.Background(),
"ADDRESS FROM THE VIAM APP",
logger,
client.WithDialOptions(rpc.WithEntityCredentials(
// Replace "<API-KEY-ID>" (including brackets) with your robot's api key id
"<API-KEY-ID>",
rpc.Credentials{
Type: rpc.CredentialsTypeAPIKey,
// Replace "<API-KEY>" (including brackets) with your robot's api key
Payload: "<API-KEY>",
})),
)
if err != nil {
logger.Fatal(err)
}
defer robot.Close(context.Background())
// Get the base from the rover
roverBase, err := base.FromRobot(robot, "viam_base")
if err != nil {
logger.Fatalf("cannot get base: %v", err)
}
moveInSquare(context.Background(), roverBase, logger)
}
{
"name": "test-rover",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "esbuild ./main.ts --bundle --outfile=static/main.js --servedir=static",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Viam Docs Team",
"license": "ISC",
"devDependencies": {
"esbuild": "0.16.12"
},
"dependencies": {
"@viamrobotics/sdk": "^0.0.28"
}
}
<!doctype html>
<html>
<head>
<title>Drive a Rover</title>
<link rel="icon" href="favicon.ico" />
</head>
<body>
<div id="main">
<button id="main-button" disabled="true">Click me</button>
</div>
<script type="module" src="main.js"></script>
</body>
</html>
// This code must be run in a browser environment.
import * as VIAM from "@viamrobotics/sdk";
async function main() {
const host = "ADDRESS_FROM_VIAM_APP";
const robot = await VIAM.createRobotClient({
host,
credential: {
type: "api-key",
// Replace "<API-KEY>" (including brackets) with your robot's api key
payload: "<API-KEY>",
},
// Replace "<API-KEY-ID>" (including brackets) with your robot's api key id
authEntity: "<API-KEY-ID>",
signalingAddress: "https://app.viam.com:443",
});
button().onclick = async () => {
await moveInSquare(robot);
};
button().disabled = false;
}
// This function moves a base component in a square.
async function moveInSquare(client: VIAM.RobotClient) {
// Replace with the name of a motor on your robot.
const name = "viam_base";
const baseClient = new VIAM.BaseClient(client, name);
try {
button().disabled = true;
for (let i = 0; i < 4; i++) {
console.log("move straight");
await baseClient.moveStraight(500, 500);
console.log("spin 90 degrees");
await baseClient.spin(90, 100);
}
} finally {
button().disabled = false;
}
}
function button() {
return <HTMLButtonElement>document.getElementById("main-button");
}
main().catch((error) => {
console.error("encountered an error:", error);
});
Next Steps
If you’re ready for more, try making your rover move in different ways. Can you make it move in a circle? A figure-eight? You could also write some code to control the other components on the robot, like the camera, or the rover’s motors.
You could also control Viam’s services, by adding data management to collect data in real time or vision services to add color detection to your rover.
Have questions, or want to meet other people working on robots? Join our Community Discord.
If you notice any issues with the documentation, feel free to file an issue or edit this file.
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!