Drive the Viam Rover with the Viam SDK
The Viam SDKs allow you to write code in Python, Go, or TypeScript to control a Viam-connected robot like the Viam Rover. You can follow this tutorial with a rented Viam Rover or with your own Viam Rover.
Tip
You can also directly see the complete code for the 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 Viam Rover
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 Python and copy the boilerplate code.
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.
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:cam 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.
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 Viam 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 Viam Rover. The following code moves the Viam 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 Viam Rover base.
In the main function, after you connect, paste the code from line 5.
By default, the base name is viam_base
.
If you have changed the 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 Viam Rover
roverBase = Base.from_robot(robot, 'viam_base')
await robot.close()
Now that your Viam 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 Viam Rover forward 500mm at 500mm/s
await base.move_straight(velocity=500, distance=500)
print("move straight")
# spins the Viam 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 Viam Rover
roverBase = Base.from_robot(robot, 'viam_base')
# Move the Viam 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 Viam Rover base.
In the main function, after you connect, paste the code from lines 19-22.
By default, the base name is viam_base
.
If you have changed the base name, update the name in your code.
Your main function should look like this:
func main() {
logger := golog.NewDevelopmentLogger("client")
robot, err := client.New(
context.Background(),
"ADDRESS_FROM_VIAM_APP",
logger,
client.WithDialOptions(rpc.WithCredentials(rpc.Credentials{
Type: utils.CredentialsTypeRobotLocationSecret,
Payload: "SECRET_FROM_VIAM_APP",
})),
)
if err != nil {
logger.Fatal(err)
}
defer robot.Close(context.Background())
// Get the base from the Viam Rover
roverBase, err := base.FromRobot(robot, "viam_base")
if err != nil {
logger.Fatalf("cannot get base: %v", err)
}
}
Now that your Viam 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 golog.Logger) {
for i := 0; i < 4; i++ {
// moves the Viam Rover forward 600mm at 500mm/s
base.MoveStraight(ctx, 600, 500.0, nil)
logger.Info("move straight")
// spins the Viam 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 Viam Rover
roverBase, err := base.FromRobot(robot, "viam_base")
if err != nil {
logger.Fatalf("cannot get base: %v", err)
}
// Move the Viam 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: 'robot-location-secret',
payload: 'SECRET_FROM_VIAM_APP',
},
authEntity: host,
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 Viam Rover base client and drives it in a square:
Note
By default, the base name is viam_base
.
If you have changed the 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: 'robot-location-secret',
payload: 'SECRET_FROM_VIAM_APP',
},
authEntity: host,
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():
creds = Credentials(
type='robot-location-secret',
payload='SECRET_FROM_VIAM_APP')
opts = RobotClient.Options(
refresh_interval=0,
dial_options=DialOptions(credentials=creds)
)
return await RobotClient.at_address('ADDRESS_FROM_VIAM_APP', opts)
async def moveInSquare(base):
for _ in range(4):
# moves the Viam Rover forward 500mm at 500mm/s
await base.move_straight(velocity=500, distance=500)
print("move straight")
# spins the Viam 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 Viam Rover in a square
await moveInSquare(roverBase)
await robot.close()
if __name__ == '__main__':
asyncio.run(main())
package main
import (
"context"
"github.com/edaniels/golog"
"go.viam.com/rdk/components/base"
"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 golog.Logger) {
for i := 0; i < 4; i++ {
// moves the Viam Rover forward 600mm at 500mm/s
base.MoveStraight(ctx, 600, 500.0, nil)
logger.Info("move straight")
// spins the Viam Rover 90 degrees at 100 degrees per second
base.Spin(ctx, 90, 100.0, nil)
logger.Info("spin 90 degrees")
}
}
func main() {
logger := golog.NewDevelopmentLogger("client")
robot, err := client.New(
context.Background(),
"ADDRESS_FROM_VIAM_APP",
logger,
client.WithDialOptions(rpc.WithCredentials(rpc.Credentials{
Type: utils.CredentialsTypeRobotLocationSecret,
Payload: "SECRET_FROM_VIAM_APP",
})),
)
if err != nil {
logger.Fatal(err)
}
defer robot.Close(context.Background())
// Get the base from the Viam 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 Viam 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: 'robot-location-secret',
payload: 'SECRET_FROM_VIAM_APP',
},
authEntity: host,
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 Viam Rover, 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.
You can also ask questions in the Community Discord and we will be happy to help.
Have questions, or want to meet other people working on robots? Join our Community Discord.
Was this page helpful?
Glad to hear it! If there is anything we could be doing better, please create an issue.
We're sorry about that. If you'd like to talk to us for help, please join the Community Discord. To ensure we know what's wrong with this page, you can also open an issue.