Build a Mock Robot

In this tutorial you will build a mock robot to learn how to configure smart machines with Viam. You do not need any hardware to do this tutorial.

Follow this tutorial to set up and control a robot with a fake arm, board, and motor, and an additional mock sub-part with a motor. These fake components interact with Viam like real hardware but do not physically exist.

Set up a mock robot

You’ll need the following hardware and software for this tutorial:

If you don’t already have a Viam account, sign up for one on the Viam app. Create a new machine in the organization and location of your choice. Go to this machine’s Setup tab.

Install and start viam-server on your computer

Before you proceed with configuring and controlling your machine, install viam-server. Follow the steps outlined for your computer’s architecture on the Setup tab of the Viam app to install viam-server on your computer as a system service.

Configure your mock robot

Configure your mock robot to represent a physical machine with robotic board, arm, and motor hardware.

If you were using physical hardware, this process would provide viam-server with information about what hardware is attached to it and how to communicate with it. For this robot, you configure viam-server to use fake components that emulate physical hardware.

  1. Navigate to the Config tab of your mock machine’s page in the Viam app.

  2. Configure a fake board component:

    • Click on the Components subtab and click Create component.
    • Select the board type, then select the fake model.
    • Enter the name myBoard for your board and click Create.
  3. Configure a fake arm component:

    • Click Create component.
    • Select the arm type, then select the fake model.
    • Enter the name myArm for your board and click Create.
    • Make your fake arm act like a UR5e by adding the following attribute:
    {
      "arm-model": "ur5e"
    }
    

    The config panel should look like this:

    A fake arm being configured in Builder mode in the Viam app config tab.

    • Click Save config.
  4. Configure a fake motor component:

    • Click Create component.
    • Select the motor type, then select the fake model.
    • Enter the name myMotor for your board and click Create.
    • Most motors are wired to a board which sends them signals. Even though your motor is fake, make it more realistic by assigning it a board. Select myBoard from the board dropdown.
  5. Click Save config.

You will need to reference the component names later when you connect to your mock robot with code.

Control your mock robot using the Viam app

When you add components to your machine, the Viam app automatically generates a UI for them under the Control tab:

The Control tab with the fake arm, and motor components.

You can use the Control tab UI to send commands to your machine.

For example, you can control the direction and speed of your motor, or change the joint positions of your machineic arm. You can also see the machine’s reported positions and speeds change. With real physical components, you would not only be able to control and see your machine’s readings on this tab, but you would also see your machine move in the physical world.

Control your mock robot using a Viam SDK

Install a Viam SDK

Install a Viam SDK (software development kit) so you can write custom logic to control the mock machine. Use the programming language you are most comfortable with.

Refer to the appropriate SDK documentation for SDK installation instructions:

Connect to your mock robot with your code

The easiest way to get started writing an application with Viam’s SDKs is to use the boilerplate code on the Code sample tab.

Navigate to your machine’s page on the Viam app, select the Code sample tab, select your SDK language (Python or Golang), and copy the boilerplate code.

This code snippet imports all the necessary libraries, is pre-populated with your machine credentials, and sets up a connection with the Viam app in the cloud. Next, paste that boilerplate code into a file named index.py or index.go in your code editor, and save your file locally.

You can now run the code. Doing so verifies that the Viam SDK is properly installed, that the viam-server instance on your machine is live, and that the computer running the program is able to connect to that instance.

Run your code by entering the following in a new terminal on your computer:

python3 index.py
go run index.go

If you successfully configured your machine and it is able to connect to the Viam app you should see the program print a list of the various resources that have been configured on your machine in the Viam app:

Command line output from running python3 index.py when your Raspberry Pi has correctly connected and initialized with the Viam app. The output is an array of resources that have been pulled from the Viam app. The list includes the motion service, arm component, data manager, board component and motor component. There is also a list of arm position and orientation values.

Control your mock robot

Now, write a program that moves the mock robotic arm to a new random position every second.

First, import the arm component from the Viam Python SDK, and the random and async.io libraries.

At the top of your index.py file, paste the following:

from viam.components.arm import ArmClient, JointPositions
import random
import asyncio

At the top of your index.go file, paste the following:

First, import the arm component from the Viam Go SDK, and the random and time libraries.

import (
  "fmt"
  "math/rand"
  "time"
  componentpb "go.viam.com/api/component/arm/v1"
  "go.viam.com/rdk/components/arm"
)

Next, you need to initialize your fake robotic arm. In the main function, paste the following. Make sure that the name of your fake arm matches the arm named in your config file.

arm = ArmClient.from_robot(robot=robot, name='myArm')
myArm, err := arm.FromRobot(robot, "myArm")
if err != nil {
    logger.Fatalf("cannot get arm: %v", err)
}

Now that your mock arm has been initialized, you can write some code to control it.

# Gets a random position for each servo on the arm that is within the safe
# range of motion of the arm. Returns a new array of safe joint positions.
def getRandoms():
    return [random.randint(-90, 90),
            random.randint(-120, -45),
            random.randint(-45, 45),
            random.randint(-45, 45),
            random.randint(-45, 45)]


# Moves the arm to a new random position every second
async def randomMovement(arm: ArmClient):
    while (True):
        randomPositions = getRandoms()
        newRandomArmJointPositions = JointPositions(values=randomPositions)
        await arm.move_to_joint_positions(newRandomArmJointPositions)
        print(await arm.get_joint_positions())
        await asyncio.sleep(1)
    return
// Returns an array of random floats between two numbers
func getRandoms(min, max float64) []float64 {
    res := make([]float64, 5)
    for i := range res {
        res[i] = min + rand.Float64() * (max - min)
    }
    return res
}

// Moves the arm to a new random position every second
func randomMovement (ctx context.Context, a arm.Arm ) {
  for {
    randomPositions := getRandoms(-90, 90)
    newRandomArmJointPositions := &componentpb.JointPositions{Values: randomPositions}
    a.MoveToJointPositions(ctx, newRandomArmJointPositions, nil)
    fmt.Println(a.JointPositions(ctx, nil))
    time.Sleep(1 * time.Second)
  }
}

You can run this code by invoking this function below your arm initialization in main. Your main function should look like this:

async def main():
    robot = await connect()

    print('Resources:')
    print(robot.resource_names)

    arm = ArmClient.from_robot(robot=robot, name='myArm')
    await randomMovement(arm)

    await robot.close()
func main() {
  // Connect to the machine...
  myArm, err := arm.FromRobot(robot, "myArm")
  if err != nil {
    logger.Fatalf("cannot get arm: %v", err)
  }
  randomMovement(context.Background(), myArm)
}

Now when you run this code, you should see the new mock arm positions listed in the command line.

Verify that your mock robotic arm is working in the Control tab of the Viam app. Watch the robotic arm’s JointPositions() changing in real-time along with the code on your development machine.

Configure your machine’s mock sub-part

Now that you have your fake robotic arm, board, and motor working, add a fake motor sub-part to your machine. Imagine for the purpose of this tutorial that the fake motor we are adding controls a conveyor belt in front of your mock arm on an assembly line.

What is a sub-part?

Usually, when building a machine, you pick out a single-board computer like the Jetson Nano or Raspberry Pi. You follow the instructions in the Setup tab to install viam-server on your board, and you start operating your machine with that computer, adding the components and services you want to use to that viam-server instance.

By utilizing parts, you can expand upon this, chaining multiple computers together to build a complex robot with Viam:

  • Each individual computer-controlled unit of a machine is called a “part” in Viam.
  • Typically, simple robots have just one part, but you can have as many parts as your project requires.
  • Parts are organized in a tree, with one of them being the main part, and the others being sub-parts.
  • You can access any sub-part either directly, or through any part above it in the tree.
  • Each part runs a single viam-server instance.

Add a new sub-part in the Viam app

On your machine’s page on the Viam app, click on the dropdown next to the main part, name your part and click Add new.

Screenshot of the Viam app with a dropdown below the main part. ‘sub-part’ is written in the textbox.

Navigate to your new sub-part’s Config tab and create a new motor:

Click Create component in the lower-left corner of the page. Select type motor and model fake. Enter motor2 as the name and click Create.

The config tab of the sub-part. A new fake motor component called motor2 is being created.

Click Save config.

Start a new instance of viam-server for your mock sub-part

Every sub-part of a machine needs to run an instance of viam-server. Since you are using only one computer, you need to bind the sub-part to a new port so you can run two servers on your machine at the same time.

The following instructions use port 8081, but you can use any open port you want.

  1. Go to the Config tab and then go to the Auth/Network subtab.
  2. Under Network click Add bind address.
  3. In the Host field type localhost.
  4. In the Port field type 8081.
  5. Click Save config.

Run a second instance of viam-server for your sub-part

In the upper right corner of the Setup tab, click Copy viam-server config.

The Setup tab of the sub-part’s machine page showing the ‘Copy viam-server config’ button highlighted by a red box.

On your local machine, create a new file called viam-sub-part.json, then paste the contents of your server config into that file and save. From a new terminal window, navigate to the directory where you saved the config file, and run the following command to create a new instance of viam-server using this configuration.

viam-server -config viam-sub-part.json

Now that you have two instances of viam-server running on your local machine, you should be able to see both your main robot arm and your new mock sub motor listed on your main machine’s Control tab.

Screenshot of the Viam app’s Control tab for the main part that lists the main arm, and the sub part motor component.

To test that your motor sub-part has been added to your machine, run your Python or Go script again. Review the output of your program that prints the machine’s resources to see your sub-part’s motor’s name listed.

Control a sub-part using the Viam SDK

Now that you have your mock sub-part connected as a remote to your main mock robot, you can control all of your sub-part’s components and services with Viam’s SDKs.

In your main function, you need to instantiate your mock sub motor. Make sure your motor’s name matches the one that you configured for it.

motor = Motor.from_robot(robot=robot, name='SubPart:motor2')
myMotor, err := motor.FromRobot(robot, "motor2")
if err != nil {
  logger.Fatalf("cannot get motor: %v", err)
}

Write a function that toggles your sub-part’s motor on and off every second:

# Toggles the motor on and off every second
async def toggleMotor(motor: MotorClient):
    while (True):
        await motor.set_power(1)
        print("go")
        await asyncio.sleep(1)
        await motor.stop()
        print("stop")
        await asyncio.sleep(1)
    return
// Toggles the motor on and off every second
func toggleMotor (ctx context.Context, m motor.Motor) {
  for {
    m.SetPower(ctx, 1, nil)
    fmt.Println("go")
    time.Sleep(1 * time.Second)
    m.Stop(ctx, nil)
    fmt.Println("stop")
    time.Sleep(1 * time.Second)
  }
}

Now, invoke your new function in main(). Your main function should look similar to this snippet:

async def main():
    robot = await connect()
    print('Resources:')
    print(robot.resource_names)
    arm = Arm.from_robot(robot=robot, name='myArm')
    motor = Motor.from_robot(robot=robot, name='SubPart:motor2')
    await toggleMotor(motor)
    # await randomMovement(arm)
    await robot.close()
func main() {
  // Connect to the machine...
  myMotor, err := motor.FromRobot(robot, "motor2")
    if err != nil {
    logger.Fatalf("cannot get motor: %v", err)
  }
  toggleMotor(context.Background(), myMotor)

  myArm, err := arm.FromRobot(robot, "myArm")
  if err != nil {
    logger.Fatalf("cannot get arm: %v", err)
  }
  randomMovement(context.Background(), myArm)
}

Run this code to see your mock sub-part’s motor toggling between running and idle in real time from the Viam app!

Next steps

In this tutorial, we showed you how to set up a mock robot with a sub-part so that you can learn more about using fake components, setting up a local development environment, and writing code using a Viam SDK.

If you’re ready to get started with building robots with real hardware components, pick up a board and try following another tutorial.



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.