From 4c38e974168a8f7336eab5b143715f93ea916de4 Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:02:16 +0200 Subject: [PATCH 1/2] Draft: Drive rover and celebrate --- docs/get-started/drive-rover.md | 1152 ++----------------------------- 1 file changed, 69 insertions(+), 1083 deletions(-) diff --git a/docs/get-started/drive-rover.md b/docs/get-started/drive-rover.md index 0cfde46f27..c0dedd551f 100644 --- a/docs/get-started/drive-rover.md +++ b/docs/get-started/drive-rover.md @@ -32,8 +32,6 @@ date: "2022-12-08" cost: "0" --- -If you haven't read through [Learn Viam](/get-started/), we recommend you do so, to get an overview of the Viam platform. - This is the first of a few quickstarts which will guide you through the concepts you need to know to get started with Viam. In this guide you'll write code that makes a rover drive in a square. @@ -41,6 +39,7 @@ In this guide you'll write code that makes a rover drive in a square. - How to run SDK code - How to use the base API to move a rover in a square +- How to use a vision service to find a color with your rover {{< /alert >}} @@ -60,8 +59,6 @@ You only need the following: Follow these steps to get your rover ready inside the Viam app and write code to control it: -{{< tabs >}} -{{% tab name="Borrowed Try Viam Rover" %}} {{< expand "Step 1: Borrow a Viam Rover" >}} Go to [Try Viam](https://app.viam.com/try) and borrow a rover. @@ -72,73 +69,7 @@ If you are running out of time during your session, you can [extend your rover s {{< /alert >}} {{< /expand >}} - -{{% /tab %}} -{{% tab name="Other Rover" %}} - -{{% alert title="Important" color="note" %}} -If you are using your own robot for this tutorial instead of [borrowing one](https://app.viam.com/try), be sure to [install `viam-server`](/installation/#install-viam-server) on it and [configure](/tutorials/configure/configure-rover/) its hardware before proceeding with this tutorial. -{{% /alert %}} - -{{% /tab %}} -{{< /tabs >}} - -{{< expand "Step 2: Install an SDK" >}} - -Navigate to your machine's **CONNECT** tab. -Click on any of the listed languages and follow the instructions to install the SDK. - -To install your preferred Viam SDK on your Linux or macOS development machine or [single-board computer](/components/board/), run one of the following commands in your terminal: - -{{< tabs >}} -{{% tab name="Python" %}} - -If you are using the Python SDK, [set up a virtual environment](/sdks/python/python-venv/) to package the SDK inside before running your code, avoiding conflicts with other projects or your system. - -For macOS (both Intel `x86_64` and Apple Silicon) or Linux (`x86`, `aarch64`, `armv6l`), run the following commands: - -```sh {class="command-line" data-prompt="$"} -python3 -m venv .venv -source .venv/bin/activate -pip install viam-sdk -``` - -Windows is not supported. -If you are using Windows, use the [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install) and install the Python SDK using the preceding instructions for Linux. -For other unsupported systems, see [Installing from source](https://python.viam.dev/#installing-from-source). - -{{% /tab %}} -{{% tab name="Go" %}} - -```sh {class="command-line" data-prompt="$"} -go get go.viam.com/rdk/robot/client -``` - -{{% /tab %}} -{{% tab name="TypeScript" %}} - -```sh {class="command-line" data-prompt="$"} -npm install @viamrobotics/sdk -``` - -{{% /tab %}} -{{% tab name="Flutter" %}} - -```sh {class="command-line" data-prompt="$"} -flutter pub add viam_sdk -``` - -{{% /tab %}} -{{% tab name="C++" %}} - -Follow the [instructions on the GitHub repository](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/BUILDING.md). - -{{% /tab %}} - -{{< /tabs >}} - -{{< /expand >}} -{{< expand "Step 3: Copy and run the sample code" >}} +{{< expand "Step 2: Copy and run the sample code" >}} The sample code on the **CONNECT** tab will show you how to authenticate and connect to a machine, as well as some of the methods you can use on your configured components and services. @@ -148,8 +79,6 @@ The sample code on the **CONNECT** tab will show you how to authenticate and con Go to the **CONNECT** tab and select **Python**. Save your API key and API key ID as environment variables or include them in the code: -{{% snippet "show-secret.md" %}} - Copy the code into a file called square.py and run the sample code to connect to your machine: ```sh {class="command-line" data-prompt="$"} @@ -167,173 +96,10 @@ Resources: ``` {{% /tab %}} -{{% tab name="Go" %}} - -Go to the **CONNECT** tab and select **Golang**. -Save your API key and API key ID as environment variables or include them in the code: - -{{% snippet "show-secret.md" %}} - -Copy the code into a file called square.go. -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 machine is live: - -```sh {class="command-line" data-prompt="$"} -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 machine is configured with in the Viam app. - -```sh {class="command-line" data-prompt="$" data-output="2-10"} -go run square.go -2024-08-09T11:27:05.034Z DEBUG client.webrtc rpc/wrtc_client.go:111 connecting to signaling server {"signaling_server":"app.viam.com:443","host":"muddy-snow-main.7kp7y4p393.viam.cloud"} -2024-08-09T11:27:05.314Z DEBUG client.webrtc rpc/wrtc_client.go:125 connected to signaling server {"signaling_server":"app.viam.com:443"} -... -2024-08-09T11:27:08.916Z INFO client test-rover/square.go:33 Resources: -2024-08-09T11:27:08.916Z INFO client test-rover/square.go:34 [rdk:component:board/local rdk:component:base/viam_base rdk:service:motion/builtin rdk:component:motor/right rdk:component:encoder/Renc rdk:component:encoder/Lenc rdk:service:sensors/builtin rdk:component:motor/left rdk:component:camera/cam] -``` - -{{% /tab %}} -{{< tab name="TypeScript" >}} - -{{< alert title="Fast track" color="note" >}} - -If you want to get the full sample code now, run the following command which will set up the files for you: - -```sh {class="command-line" data-prompt="$"} -npm create robot-app -``` - -Then, copy and paste your API key and API key ID, and machine address into the .env file. -If you have run this command and added your environment variables, skip ahead to running the program. - -{{< /alert >}} - -Go to the **CONNECT** tab and select **TypeScript**. -Save your API key and API key ID as environment variables or include them in the code: - -{{% snippet "show-secret.md" %}} - -Copy the code into a file called main.ts. - -Create another file named package.json with the following contents: - -```json {class="line-numbers linkable-line-numbers"} -{ - "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": "*" - }, - "dependencies": { - "@viamrobotics/sdk": "*" - } -} -``` - -Create a folder static and inside it another file named index.html. -Add the following markup: - -```html {class="line-numbers linkable-line-numbers"} - - - - Drive a Rover - - - -
-

Drive a rover in a square

-

- We recommend you open the developer tools in your browser to see logs. -

-

- Also open a second window with the - Viam app and navigate to your rover's - CONTROL tab, which allows you to interact with your rover's - resources. Click on one of the camera panels and toggle the camera - stream on so you can observe the rover's movements. -

- -
- - - -``` - -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 machine is live: - -```sh {class="command-line" data-prompt="$"} -npm install -npm start -``` - -Open a web browser and visit `localhost:8000`. -You should see a disabled button that says `Click me to drive rover in square`. -Open the developer console to see the console output. -If you successfully configured your machine and it is able to connect to the Viam app, you will see some output including the names of your rover's resources. -These are the components and services that the machine is configured with in the Viam app. - -{{< /tab >}} -{{% tab name="Flutter" %}} - -Flutter code must be launched from inside a running Flutter application. -To get started programming your rover with Flutter, follow the instructions to [Build a Flutter App that Integrates with Viam](/tutorials/control/flutter-app/). - -{{% /tab %}} -{{% tab name="C++" %}} - -Go to the **CONNECT** tab and select **C++**. -Save your API key and API key ID as environment variables or include them in the code: - -{{% snippet "show-secret.md" %}} - -Copy the code into a file called drive_in_square.cpp. - -Compile your code, and then run the program to verify that the Viam SDK is properly installed and that the `viam-server` instance on your machine is live: - -```sh {class="command-line" data-prompt="$"} -cmake . -G Ninja -ninja all -./drive_in_square -``` - -The program prints an array of resources. -These are the components and services that the machine is configured with in the Viam app. - -```sh {class="command-line" data-prompt="$" data-output="2-20"} -./src/viam/examples/camera/example_camera -Resources: - rdk:component:motor/right - rdk:component:encoder/Renc - rdk:component:encoder/Lenc - rdk:service:sensors/builtin - rdk:component:motor/left - rdk:component:camera/cam - rdk:component:board/local - rdk:component:base/viam_base - rdk:service:motion/builtin - ... -``` - -{{% /tab %}} - {{< /tabs >}} {{< /expand >}} -{{< expand "Step 4: Drive a rover in a square" >}} +{{< expand "Step 3: Drive a rover in a square" >}} Now that you have connected the rover to Viam with the SDK, you can write code to move the rover in a square: @@ -351,15 +117,20 @@ Then paste this snippet above your `main()` function, it will move any base pass ```python async def moveInSquare(base): - for _ in range(4): + for _ in range(5): # moves the rover forward 500mm at 500mm/s - await base.move_straight(velocity=500, distance=500) + await base.move_straight(velocity=500) print("move straight") - # spins the rover 90 degrees at 100 degrees per second + # spins the rover 90 degrees at 120 degrees per second await base.spin(velocity=100, angle=90) print("spin 90 degrees") ``` +{{% alert title="Tip" color="tip" %}} + +If you are interested to learn about what other commands you can give to a base, see the standardized [base API](/components/base/#api) for a full list of available API methods. +{{% /alert %}} + Next, remove all the code in the `main()` function between where the machine connection is established and closed and instead initialize your `base` and invoke the `moveInSquare()` function. On the Try Viam rovers, the default base name is `viam_base`. @@ -373,9 +144,9 @@ async def main(): roverBase = Base.from_robot(machine, 'viam_base') # Move the rover in a square - await moveInSquare(roverBase) + await moveInASquare("roverBase") - await machine.close() + # await machine.close() ``` If you have a borrowed Try Viam rover, navigate to your machine's **CONTROL** tab, which allows you to interact with your machine's {{< glossary_tooltip term_id="resource" text="resources" >}}. @@ -389,875 +160,90 @@ Then run your code and watch your rover move in a square. {{% /tab %}} -{{% tab name="Go" %}} - -The base is responsible for controlling the motors attached to the base of the rover. -Ensure that the `Base` class is being imported: - -```go {class="line-numbers linkable-line-numbers" data-line="3"} -import ( - // Be sure to keep all of the other imported libraries - "go.viam.com/rdk/components/base" -) -``` - -Then paste this snippet above your `main()` function, it will move any base passed to it in a square: - -```go -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") - } -} -``` - -Next, remove all the code in the `main()` function after the machine connection is established and instead initialize your `base` and invoke the `moveInSquare()` function. - -Also remove any unused imports. - -On the Try Viam rovers, the default base name is `viam_base`. -If you have a different base name, update the name in your code. - -```go {class="line-numbers linkable-line-numbers" data-line="22-29"} -func main() { - logger := logging.NewLogger("client") - machine, err := client.New( - context.Background(), - "ADDRESS FROM THE VIAM APP", - logger, - client.WithDialOptions(rpc.WithEntityCredentials( - /* Replace "" (including brackets) with your machine's api key id */ - "", - rpc.Credentials{ - Type: rpc.CredentialsTypeAPIKey, - /* Replace "" (including brackets) with your machine's api key */ - Payload: "", - })), - ) - if err != nil { - logger.Fatal(err) - } - - defer machine.Close(context.Background()) - - // Get the base from the rover - roverBase, err := base.FromRobot(machine, "viam_base") - if err != nil { - logger.Fatalf("cannot get base: %v", err) - } - - // Move the rover in a square - moveInSquare(context.Background(), roverBase, logger) -} -``` - -If you have a borrowed Try Viam rover, navigate to your machine's **CONTROL** tab, which allows you to interact with your machine's {{< glossary_tooltip term_id="resource" text="resources" >}}. - -Click on one of the camera panels and toggle the camera stream on so you can observe the rover's movements. - -Then run your code and watch your rover move in a square. - -
-{{}} -
- -{{% /tab %}} -{{% tab name="TypeScript" %}} - -Above the `main` function, add the following function that initializes your rover base client and drives it in a square. - -On the Try Viam rovers, the default base name is `viam_base`. -If you have a different base name, update the name in your code. - -```ts {class="line-numbers linkable-line-numbers"} -// This function moves a base component in a square. -async function moveInSquare(client: VIAM.RobotClient) { - // Replace with the name of the base on your machine. - 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`: - -```ts {class="line-numbers linkable-line-numbers"} -// This function gets the button element -function button() { - return document.getElementById("main-button"); -} -``` - -Next, register a listener on the button you obtain from the `button` function and make it invoke the `moveInSquare` function. -Place this code after the rover connection code: - -```ts {class="line-numbers linkable-line-numbers"} -button().onclick = async () => { - await moveInSquare(machine); -}; -button().disabled = false; -``` - -Your main function should now look like this: - -```ts {class="line-numbers linkable-line-numbers" data-line="16-19"} -async function main() { - const host = "ADDRESS_FROM_VIAM_APP"; - - const machine = await VIAM.createRobotClient({ - host, - credential: { - type: "api-key", - // Replace "" (including brackets) with your machine's API key - payload: "", - }, - // Replace "" (including brackets) with your machine's API key ID - authEntity: "", - signalingAddress: "https://app.viam.com:443", - }); - - button().onclick = async () => { - await moveInSquare(machine); - }; - button().disabled = false; -} -``` - -Save your code and refresh the browser page. -When the connection to the rover is established, the `Click me to drive rover in square` button will become enabled. - -If you have a borrowed Try Viam rover, open a tab and navigate to your machine's **CONTROL** tab, which allows you to interact with your rover's {{< glossary_tooltip term_id="resource" text="resources" >}}. - -Click on one of the camera panels and toggle the camera stream on so you can observe the rover's movements. - -Go back to the `Click me to drive rover in square` button and click the button to run your code and switch back to the **CONTROL** tab to watch your rover move in a square. - -
-{{}} -
- -{{% /tab %}} -{{% tab name="Flutter" %}} - -Add a new file to your application in /lib called base_screen.dart. -Paste this code into your file: - -```dart {class="line-numbers linkable-line-numbers"} -/// This is the BaseScreen, which allows us to control a Base. - -import 'package:flutter/material.dart'; -import 'package:viam_sdk/viam_sdk.dart'; -import 'package:viam_sdk/widgets.dart'; - -class BaseScreen extends StatelessWidget { - final Base base; - - const BaseScreen(this.base, {super.key}); - - Future moveSquare() async { - for (var i=0; i<4; i++) { - await base.moveStraight(500, 500); - await base.spin(90, 100); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: Text(base.name)), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: moveSquare, - child: const Text('Move Base in Square'), - ), - ])) - ,);}} -``` - -This code creates a screen with a singular centered button that, when pressed, calls on the `moveSquare()` method to drive the base in a square. - -Then, replace the contents of robot_screen.dart with the following file, or add the highlighted lines of code to your program in the locations indicated: - -```dart {class="line-numbers linkable-line-numbers" data-line="11, 73-85, 101-102"} -/// This is the screen that shows the resources available on a robot (or smart machine). -/// It takes in a Viam app client instance, as well as a robot client. -/// It then uses the Viam client instance to create a connection to that robot client. -/// Once the connection is established, you can view the resources available -/// and send commands to them. - -import 'package:flutter/material.dart'; -import 'package:viam_sdk/protos/app/app.dart'; -import 'package:viam_sdk/viam_sdk.dart'; - -import 'base_screen.dart'; - -class RobotScreen extends StatefulWidget { - final Viam _viam; - final Robot robot; - - const RobotScreen(this._viam, this.robot, {super.key}); - - @override - State createState() => _RobotScreenState(); -} - -class _RobotScreenState extends State { - /// Similar to previous screens, start with [_isLoading] to true. - bool _isLoading = true; - - /// This is the [RobotClient], which allows you to access - /// all the resources of a Viam Smart Machine. - /// This differs from the [Robot] provided to us in the widget constructor - /// in that the [RobotClient] contains a direct connection to the Smart Machine - /// and its resources. The [Robot] object simply contains information about - /// the Smart Machine, but is not actually connected to the machine itself. - /// - /// This is initialized late because it requires an asynchronous - /// network call to establish the connection. - late RobotClient client; - - @override - void initState() { - super.initState(); - // Call our own _initState method to initialize our state. - _initState(); - } - - @override - void dispose() { - // You should always close the [RobotClient] to free up resources. - // Calling [RobotClient.close] will clean up any tasks and - // resources created by Viam. - if (_isLoading == false) { - client.close(); - } - super.dispose(); - } - - /// This method will get called when the widget initializes its state. - /// It exists outside the overridden [initState] function since it's async. - Future _initState() async { - // Using the authenticated [Viam] the received as a parameter, - // the app can obtain a connection to the Robot. - // There is a helpful convenience method on the [Viam] instance for this. - final robotClient = await widget._viam.getRobotClient(widget.robot); - setState(() { - client = robotClient; - _isLoading = false; - }); - } - - /// A computed variable that returns the available [ResourceName]s of - /// this robot in an alphabetically sorted list. - List get _sortedResourceNames { - return client.resourceNames..sort((a, b) => a.name.compareTo(b.name)); - } - - bool _isNavigable(ResourceName rn) { - if (rn.subtype == Base.subtype.resourceSubtype) { - return true; - } - return false; - } - - void _navigate(ResourceName rn) { - if (rn.subtype == Base.subtype.resourceSubtype) { - final base = Base.fromRobot(client, rn.name); - Navigator.of(context).push(MaterialPageRoute(builder: (_) => BaseScreen(base))); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: Text(widget.robot.name)), - body: _isLoading - ? const Center(child: CircularProgressIndicator.adaptive()) - : ListView.builder( - itemCount: client.resourceNames.length, - itemBuilder: (_, index) { - final resourceName = _sortedResourceNames[index]; - return ListTile( - title: Text(resourceName.name), - subtitle: Text( - '${resourceName.namespace}:${resourceName.type}:${resourceName.subtype}'), - onTap: () => _navigate(resourceName), - trailing: _isNavigable(resourceName) ? Icon(Icons.chevron_right) : SizedBox.shrink(), - ); - })); - } -} -``` - -This imports the base_screen.dart file into the program and adds logic to check if a {{< glossary_tooltip term_id="resource" text="resource" >}} is "navigable", or, has a screen made for it. -Base is the only resource that is navigable. - -To navigate to the base screen, save your code and launch your simulator. -Navigate to the robot screen of a (live) machine with a base resource configured, and see the resources displayed like the following: - -{{}} - -Then, click on the base to display the base screen. -You may need to scroll to the bottom of the list of resources. +{{< /tabs >}} +{{< /expand>}} -{{}} +{{< expand "Step 4: Add a vision service" >}} -Click on the button to move your rover in a square: +As you have seen from the **CONTROL** tab, your rover has cameras. +You can use these cameras to adjust your rovers behaviour. -{{