From e57cb6eebb717e81538421ed9c385ff6d034811b Mon Sep 17 00:00:00 2001
From: JessamyT
Date: Fri, 27 Dec 2024 15:33:40 -0800
Subject: [PATCH 01/10] Add services heading, add concepts to start a new
machine
---
docs/operate/get-started/setup.md | 30 ++++++++++++++++++-
.../operate/get-started/supported-hardware.md | 19 ++++++++++++
2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/docs/operate/get-started/setup.md b/docs/operate/get-started/setup.md
index 20474eb13a..45bb99ede6 100644
--- a/docs/operate/get-started/setup.md
+++ b/docs/operate/get-started/setup.md
@@ -8,11 +8,39 @@ no_list: true
description: "Install the software that drives hardware and connects your device to the cloud."
---
+Get started by installing the open-source software that drives your hardware and connects your device to the cloud.
+The easiest way to do this is through the Viam app, so that your machines are automatically connected to configuration and remote operation tools.
+
## Quickstart
1. Create a [Viam app](https://app.viam.com) account.
The Viam app is the online hub for configuring and managing devices as well as viewing data.
1. Add a new _{{< glossary_tooltip term_id="machine" text="machine" >}}_ in the app.
A machine represents your device.
-1. From the machine page, follow the setup instructions to install `viam-server` on your device and connect it to the cloud.
+1. From your machine's page in the Viam app, follow the setup instructions to install `viam-server` on your device and connect it to the cloud.
`viam-server` is the executable binary that runs on your device and manages hardware drivers, software, and data capture and sync.
+1. Use the **+** button on your machine's **CONFIGURE** tab to add [supported hardware components](/operate/get-started/supported-hardware/) so that `viam-server` can control your specific hardware.
+1. Use this same **+** button to configure software services such as [data capture and sync](/data-ai/capture-data/capture-sync/).
+
+As soon as you configure each component and save the configuration, you can use the **TEST** panel of the component's config card to, for example, view your camera's stream or turn your motor.
+
+## Concepts
+
+### What is a machine?
+
+A _machine_ is a computer (often a single-board computer like a Raspberry Pi or Jetson) or microcontroller and all the hardware attached to it, as well as the software running on it.
+You can think of one machine as representing one device, or one robot.
+
+When you create a new machine in the Viam app, Viam generates a unique set of credentials for that machine that connect the physical machine to its instance in the Viam app.
+
+### How the configuration works
+
+The machine setup steps displayed in the Viam app copy your machine's credentials to your machine.
+When you turn on your machine, `viam-server` starts up and uses the provided credentials to fetch its configuration from the Viam app.
+Once the machine has a configuration, it caches it locally and can use the config for up to 60 days.
+Since the configuration is cached locally, your machine does not need to stay connected to the Viam app after it has obtained its configuration file.
+
+If it is online, the machine checks for new configurations every 15 seconds and changes its config automatically when a new config is available.
+All communication happens securely over HTTPS using secret tokens that are in the machine's config.
+
+If your machine will never connect to the internet, you can also create a [local configuration file](/operate/reference/local-configuration-file/) on the machine itself.
diff --git a/docs/operate/get-started/supported-hardware.md b/docs/operate/get-started/supported-hardware.md
index 934094e115..f05fd4924a 100644
--- a/docs/operate/get-started/supported-hardware.md
+++ b/docs/operate/get-started/supported-hardware.md
@@ -85,6 +85,25 @@ After installing `viam-server` or `viam-micro-server` on your computer or microc
For details on configuring versioning and environment variables for modules, see [Modular Resource and Module Configuration Details](/operate/reference/module-configuration/).
+## Add software services to your machine
+
+In addition to hardware driver _{{< glossary_tooltip term_id="component" text="components" >}}_ and abstracted "virtual hardware" components, Viam offers _{{< glossary_tooltip term_id="service" text="services" >}}_ to provide higher-level software capabilities.
+You can read more about the Viam-maintained services and how to configure them in their respective documentation:
+
+- [Data capture and sync](/data-ai/capture-data/capture-sync/)
+- [ML model deployment](/data-ai/ai/deploy/)
+- [Computer vision](/data-ai/ai/run-inference/)
+- Motion planning for various components:
+ - [Robot arm motion](/operate/mobility/move-arm/)
+ - [Mobile robot navigation](/operate/mobility/move-base/)
+
+To add a service to your machine:
+
+1. Click the **+** button on your machine's **CONFIGURE** tab.
+1. Click **Service**, then select from available services.
+ The dropdown list includes services from the Viam Registry as well as the built-in services.
+1. Add required attributes according to the README or other documentation.
+
## How modules run
Modules run alongside [`viam-server`](/architecture/viam-server/) as separate processes, communicating with `viam-server` over UNIX sockets.
From 34f64b5a443a8113fd6a165c2d56bbdc10807bcf Mon Sep 17 00:00:00 2001
From: JessamyT
Date: Fri, 27 Dec 2024 17:34:47 -0800
Subject: [PATCH 02/10] frame system etc
---
docs/dev/reference/apis/services/motion.md | 2 +-
docs/operate/_index.md | 5 +-
.../{mobile-app/_index.md => mobile-app.md} | 4 +-
docs/operate/mobility/define-geometry.md | 176 ++++++++++++++++++
docs/operate/mobility/move-arm.md | 7 +
docs/operate/mobility/move-gantry.md | 2 +-
.../control}/drive-rover.md | 0
7 files changed, 191 insertions(+), 5 deletions(-)
rename docs/operate/control/{mobile-app/_index.md => mobile-app.md} (92%)
rename docs/{operate/control/mobile-app => tutorials/control}/drive-rover.md (100%)
diff --git a/docs/dev/reference/apis/services/motion.md b/docs/dev/reference/apis/services/motion.md
index ecb4f178bd..3f80671f62 100644
--- a/docs/dev/reference/apis/services/motion.md
+++ b/docs/dev/reference/apis/services/motion.md
@@ -3,7 +3,7 @@ title: "Motion Service API"
linkTitle: "Motion"
weight: 40
type: "docs"
-description: "Give commands to move a machine based on a SLAM map or GPS coordinates or to move a machine's components form one location to another."
+description: "Give commands to move a machine based on a SLAM map or GPS coordinates or to move a machine's components from one location to another."
icon: true
images: ["/icons/components/arm.svg"]
date: "2022-01-01"
diff --git a/docs/operate/_index.md b/docs/operate/_index.md
index f7a9ab6666..94e56ef268 100644
--- a/docs/operate/_index.md
+++ b/docs/operate/_index.md
@@ -41,7 +41,10 @@ overview: true
{{< cards >}}
{{% card link="/operate/mobility/define-geometry/" noimage="true" %}}
{{% card link="/operate/mobility/define-obstacles/" noimage="true" %}}
-{{% card link="/operate/mobility/define-dynamic-obstacles/" noimage="true" %}}
+
+
+
{{% card link="/operate/mobility/move-base/" noimage="true" %}}
{{% card link="/operate/mobility/move-arm/" noimage="true" %}}
{{% card link="/operate/mobility/move-gantry/" noimage="true" %}}
diff --git a/docs/operate/control/mobile-app/_index.md b/docs/operate/control/mobile-app.md
similarity index 92%
rename from docs/operate/control/mobile-app/_index.md
rename to docs/operate/control/mobile-app.md
index b9e9a7daf8..ab5a618155 100644
--- a/docs/operate/control/mobile-app/_index.md
+++ b/docs/operate/control/mobile-app.md
@@ -38,7 +38,7 @@ Refer to the [Viam Flutter SDK](https://flutter.viam.dev/) documentation for ava
### Example usage
-The following code, part of [Drive a rover in a square in 2 minutes](/how-tos/drive-rover/), shows how you could move a robotic rover base in a square using the base API's [`moveStraight`](https://flutter.viam.dev/viam_sdk/Base/moveStraight.html) and [`spin`](https://flutter.viam.dev/viam_sdk/Base/spin.html) methods:
+The following code, part of [Drive a rover in a square in 2 minutes](/tutorials/control/drive-rover/), shows how you could move a robotic rover base in a square using the base API's [`moveStraight`](https://flutter.viam.dev/viam_sdk/Base/moveStraight.html) and [`spin`](https://flutter.viam.dev/viam_sdk/Base/spin.html) methods:
```dart {class="line-numbers linkable-line-numbers"}
import 'package:flutter/material.dart';
@@ -78,7 +78,7 @@ class BaseScreen extends StatelessWidget {
See the guide for full code and instructions to get started by building a simple app to control a rented Viam rover:
{{< cards >}}
-{{% card link="/operate/control/mobile-app/drive-rover/" %}}
+{{% card link="/tutorials/control/mobile-app/drive-rover/" %}}
{{< /cards >}}
For a more in-depth guide with more screens, see the following guide:
diff --git a/docs/operate/mobility/define-geometry.md b/docs/operate/mobility/define-geometry.md
index 72eb770f58..af3243e455 100644
--- a/docs/operate/mobility/define-geometry.md
+++ b/docs/operate/mobility/define-geometry.md
@@ -7,3 +7,179 @@ type: "docs"
no_list: true
description: "Specify your robot's dimensions and how it is positioned in space."
---
+
+Before you can use the motion planning or navigation services, you need to create a description of your machine's dimensions and how it is positioned relative to its surroundings.
+The position and orientation readings returned by a component such as an accelerometer or a robot arm have no meaning without a reference frame.
+
+Use Viam's frame system to define a coordinate system for your machine, and configure the geometries of your machine's components.
+You can also [define static obstacles](/operate/mobility/define-obstacles/) for your machine to avoid.
+
+{{% alert title="Note" color="note" %}}
+For complex kinematic chain configuration, useful when creating a module to support an unsupported arm model, see [Configure Complex Kinematic Chains](/operate/reference/kinematic-chain-config/).
+{{% /alert %}}
+
+{{}}
+
+## Configure a reference frame
+
+Imagine you have a robotic [arm](/components/arm/) attached to a table.
+
+Consider one corner of the table the arm is attached to be the origin of the `world`, `(0, 0, 0)`.
+Measure from that point to the base of the arm to get the `translation` coordinates.
+
+- Suppose the arm is offset from the corner by 0.1 m in the positive X direction, and 0.25 m in the negative Y direction.
+- Supply this `translation` when configuring the arm component's `frame` information.
+- Leave `parent` and `orientation` at their default values.
+
+{{< tabs name="Example Frame Configuration of Component attached to Static Surface" >}}
+{{% tab name="Frame Editor" %}}
+
+To configure your machine following this example:
+
+- Navigate to the **CONFIGURE** tab of your machine's page in the [Viam app](https://app.viam.com).
+- Select **Builder** mode and [configure your arm](/components/arm/#configuration).
+- Select the **Frame** mode.
+- From the left-hand menu, select your arm:
+ {{}}
+- Keep the **Parent** frame as `world` and fill in the coordinates for **Translation** (meters) and **Orientation** (degrees) according to the position and orientation of the arm in relation to the `world` frame's origin:
+ {{}}
+
+{{< /tab >}}
+{{% tab name="JSON Example" %}}
+
+```json {class="line-numbers linkable-line-numbers"}
+{
+ "components": [
+ {
+ "depends_on": [],
+ "name": "myArm",
+ "model": "ur5e",
+ "type": "arm",
+ "namespace": "rdk",
+ "attributes": {
+ "host": "127.0.0.1"
+ },
+ "frame": {
+ "parent": "world",
+ "translation": {
+ "x": 100,
+ "y": -250,
+ "z": 0
+ },
+ "orientation": {
+ "type": "ov_degrees",
+ "value": {
+ "x": 0,
+ "y": 0,
+ "z": 1,
+ "th": 0
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+{{% /tab %}}
+{{< /tabs >}}
+
+
+| Parameter | Required? | Required |
+| --------- | ----------- | ----- |
+| `parent` | **Required** | Default: `world`. The name of the reference frame you want to act as the parent of this frame. |
+| `translation` | **Required** | Default: `(0, 0, 0)`. The coordinates that the origin of this component's reference frame has within its parent reference frame.
Units: m in Frame Editor, mm in JSON. |
+| `orientation` | **Required** | Default: `(0, 0, 1), 0`. The [orientation vector](/internals/orientation-vector/) that yields the axes of the component's reference frame when applied as a rotation to the axes of the parent reference frame.
**Types**: **Orientation Vector Degrees** (`ov_degrees`), **Orientation Vector Radians** (`ov_radians`), **Euler Angles** (`euler_angles`), and **Quaternion** (`quaternion`). |
+| `geometry` | Optional | Default: `none`. Collision geometries for defining bounds in the environment of the machine.
Units: m in Frame Editor, mm in JSON.
**Types**: **Sphere** (`sphere`), **Box** (`box`), and **Capsule** (`capsule`). |
+
+### Configure nested reference frames
+
+Imagine you have a robotic arm attached to the actuator (moving part) of a [gantry](/components/gantry/).
+
+Using a nested reference frame allows you to define the reference frame of the arm with respect to the end effector of the gantry.
+This allows `viam-server` to correctly calculate the position of the end of the arm, taking into account the combined motion of the gantry and the arm.
+
+1. Decide that the origin of the gantry and the origin of the `world` frame will be the same point.
+ Pick a point on the stationary portion of the gantry and define it as `(0,0,0)`.
+1. Pick a point on the gantry end effector where the arm is mounted, with the gantry end effector in what you consider to be the home position.
+ This is the origin `(0,0,0)` of the arm reference frame.
+1. Measure between these two points.
+ For example, if the arm origin is 10 centimeters above the gantry/world origin, the translation is (0.00, 0.00, 0.10).
+1. Configure the gantry reference frame and the arm reference frame as per the instructions in the section above, but set the `parent` of the arm as the gantry, and supply the translation you measured.
+
+{{< tabs >}}
+{{% tab name="Frame Editor" %}}
+
+- Since the gantry and world have the same origin, don't configure a translation between them:
+
+ {{}}
+
+- Next, select your arm from the left hand menu.
+- Select the **Parent** frame as the gantry, and fill in the coordinates for **Translation** (m) of the arm in relation to the gantry's origin:
+
+ {{}}
+
+{{< /tab >}}
+{{% tab name="JSON Example" %}}
+
+```json {class="line-numbers linkable-line-numbers"}
+{
+ "components": [
+ {
+ "name": "myGantry",
+ "model": "single-axis",
+ "type": "gantry",
+ "namespace": "rdk",
+ "attributes": {},
+ "depends_on": [],
+ "frame": {
+ "parent": "world",
+ "translation": {
+ "y": 0,
+ "z": 0,
+ "x": 0
+ },
+ "orientation": {
+ "type": "ov_degrees",
+ "value": {
+ "x": 0,
+ "y": 0,
+ "z": 1,
+ "th": 0
+ }
+ }
+ }
+ },
+ {
+ "depends_on": [],
+ "name": "myArm",
+ "model": "ur5e",
+ "type": "arm",
+ "namespace": "rdk",
+ "attributes": {
+ "host": "127.0.0.1"
+ },
+ "frame": {
+ "parent": "myGantry",
+ "translation": {
+ "x": 0,
+ "y": 0,
+ "z": 100
+ },
+ "orientation": {
+ "type": "ov_degrees",
+ "value": {
+ "x": 0,
+ "y": 0,
+ "z": 1,
+ "th": 0
+ }
+ }
+ }
+ }
+ ]
+}
+```
+
+{{% /tab %}}
+{{< /tabs >}}
diff --git a/docs/operate/mobility/move-arm.md b/docs/operate/mobility/move-arm.md
index c574b79b97..04570c97c8 100644
--- a/docs/operate/mobility/move-arm.md
+++ b/docs/operate/mobility/move-arm.md
@@ -7,3 +7,10 @@ type: "docs"
no_list: true
description: "Move an arm with joint positions or automated motion planning."
---
+
+You can move a robotic arm either with the direct joint position commands of the [arm API](/dev/reference/apis/components/arm/), or with automated complex motion planning using the [motion planning service API](/dev/reference/apis/services/motion/).
+
+{{< cards >}}
+{{% card link="/dev/reference/apis/components/arm/" %}}
+{{% card link="/dev/reference/apis/services/motion/" %}}
+{{< /cards >}}
diff --git a/docs/operate/mobility/move-gantry.md b/docs/operate/mobility/move-gantry.md
index ec9199efe6..365724169f 100644
--- a/docs/operate/mobility/move-gantry.md
+++ b/docs/operate/mobility/move-gantry.md
@@ -5,5 +5,5 @@ weight: 60
layout: "docs"
type: "docs"
no_list: true
-description: "TODO"
+description: "Move a gantry with linear actuator positions or automated motion planning."
---
diff --git a/docs/operate/control/mobile-app/drive-rover.md b/docs/tutorials/control/drive-rover.md
similarity index 100%
rename from docs/operate/control/mobile-app/drive-rover.md
rename to docs/tutorials/control/drive-rover.md
From 0a90ad68771eed1f3a6e12c63630c33726e1da16 Mon Sep 17 00:00:00 2001
From: JessamyT
Date: Mon, 30 Dec 2024 16:28:23 -0800
Subject: [PATCH 03/10] Module upload edits
---
.../get-started/other-hardware/_index.md | 71 ++++++++++++++++++-
.../other-hardware/manage-modules.md | 2 +-
2 files changed, 70 insertions(+), 3 deletions(-)
diff --git a/docs/operate/get-started/other-hardware/_index.md b/docs/operate/get-started/other-hardware/_index.md
index 2c310dff9a..25568dec85 100644
--- a/docs/operate/get-started/other-hardware/_index.md
+++ b/docs/operate/get-started/other-hardware/_index.md
@@ -100,7 +100,7 @@ Authenticate your CLI session with Viam using one of the following options:
| Namespace/Organization ID | In the [Viam app](https://app.viam.com), navigate to your organization settings through the menu in upper right corner of the page. Find the **Public namespace** and copy that string. |
| Resource to add to the module (API) | The [component API](/appendix/apis/#component-apis) your module will implement. |
| Model name | Name your component model based on what it supports, for example, if it supports a model of ultrasonic sensor called “XYZ Sensor 1234” you could call your model `xyz_1234` or similar. Must be all-lowercase and use only alphanumeric characters (`a-z` and `0-9`), hyphens (`-`), and underscores (`_`). |
-| Enable cloud build | You can select `No` if you will always build the module yourself before uploading it. If you select `Yes` and push the generated files (including the .github folder) and create a release of the format `vX.X.X`, the module will build and upload to the Viam registry and be available for all Viam-supported architectures without you needing to build for each architecture. |
+| Enable cloud build | If you select `Yes` (recommended) and push the generated files (including the .github folder) and create a release of the format `vX.X.X`, the module will build and upload to the Viam registry and be available for all Viam-supported architectures without you needing to build for each architecture. `Yes` also makes it easier to [upload](#upload-your-module) using PyInstaller by creating a build entrypoint script. You can select `No` if you will always build the module yourself before uploading it. |
| Register module | Select `Yes` unless you are creating a local-only module for testing purposes and do not intend to upload it. |
{{< /expand >}}
@@ -752,7 +752,74 @@ Do not change the module_id
.
To package (for Python) and upload your module and make it available to configure on machines in your organization (or in any organization, depending on how you set `visibility` in the meta.json file):
{{< tabs >}}
-{{% tab name="Python" %}}
+{{% tab name="Python: pyinstaller (recommended)" %}}
+
+The recommended approach for Python is to use [PyInstaller](https://pypi.org/project/pyinstaller/) to compile your module into a packaged executable: a standalone file containing your program, the Python interpreter, and all of its dependencies.
+When packaged in this fashion, you can run the resulting executable on your desired target platform or platforms without needing to install additional software or manage dependencies manually.
+
+{{% alert title="Note" color="note" %}}
+To follow these PyInstaller packaging steps, you must have enabled cloud build when moving through the module generator prompts.
+If you did not, you will need to manually create a build.sh entrypoint script.
+{{% /alert %}}
+
+To create a packaged executable:
+
+1. First, activate the [Python virtual environment](/sdks/python/python-venv/) the module generator created for you to ensure your module has access to any required libraries.
+ Run the following command from inside your module's directory to activate the virtual environment:
+
+ ```sh {id="terminal-prompt" class="command-line" data-prompt="$"}
+ source venv/bin/activate
+ ```
+
+ Be sure you are within your Python virtual environment for the rest of these steps.
+ Your terminal prompt should include the name of your virtual environment in parentheses.
+
+1. Edit the requirements.txt file, adding PyInstaller (`pyinstaller`), and the Google API Python client (`google-api-python-client`) to the list of dependencies:
+
+ ```sh { class="command-line" data-prompt="$"}
+ pyinstaller
+ google-api-python-client
+ ```
+
+1. Install the dependencies listed in your `requirements.txt` file within your Python virtual environment using the following command:
+
+ ```sh { class="command-line" data-prompt="$"}
+ python -m pip install -r requirements.txt -U
+ ```
+
+1. Then compile your module, adding the Google API Python client as a hidden import:
+
+ ```sh { class="command-line" data-prompt="$"}
+ python -m PyInstaller --onefile --hidden-import="googleapiclient" src/main.py
+ ```
+
+ If you need to include any additional data files to support your module, specify them using the `--add-data` flag:
+
+ ```sh { class="command-line" data-prompt="$"}
+ python -m PyInstaller --onefile --hidden-import="googleapiclient" --add-data src/arm/my_arm_kinematics.json:src/arm/ src/main.py
+ ```
+
+ By default, the output directory for the packaged executable is dist, and the name of the executable is derived from the name of the input script (in this case, main).
+
+We recommend you use PyInstaller with the [`build-action` GitHub action](https://github.com/viamrobotics/build-action) which provides a simple cross-platform build setup for multiple platforms: x86 and Arm Linux distributions, and MacOS.
+See [Update an existing module using a GitHub action](/how-tos/manage-modules/#update-an-existing-module-using-a-github-action) for more information.
+
+{{% alert title="Note" color="note" %}}
+
+PyInstaller does not support relative imports in entrypoints (imports starting with `.`).
+If you get `"ImportError: attempted relative import with no known parent package"`, set up a stub entrypoint as described on [GitHub](https://github.com/pyinstaller/pyinstaller/issues/2560).
+
+In addition, PyInstaller does not support cross-compiling: you must compile your module on the target architecture you wish to support.
+For example, you cannot run a module on a Linux `arm64` system if you compiled it using PyInstaller on a Linux `amd64` system.
+Viam makes this easy to manage by providing a build system for modules.
+Follow [these instructions](/cli/#using-the-build-subcommand) to automatically build for each system your module can support using Viam's [CLI](/cli/).
+
+{{% /alert %}}
+
+{{% /tab %}}
+{{% tab name="Python: venv" %}}
+
+You can use the following package and upload method if you opted not to enable cloud build when you ran `viam module generate`.
1. To package the module as an archive, run the following command from inside the module directory:
diff --git a/docs/operate/get-started/other-hardware/manage-modules.md b/docs/operate/get-started/other-hardware/manage-modules.md
index 8d85bd56d6..79a4b01c05 100644
--- a/docs/operate/get-started/other-hardware/manage-modules.md
+++ b/docs/operate/get-started/other-hardware/manage-modules.md
@@ -101,7 +101,7 @@ For more information, see the [`viam module` command](/cli/#module).
### Update an existing module using a GitHub action
To update an existing module in the [Viam Registry](https://app.viam.com/registry) using continuous integration (CI), you can use one of two GitHub actions.
-You can only use these GitHub actions if you have already created the module by running `viam module create` and `viam module update`.
+You can only use these GitHub actions if you have already created the module by running `viam module create` and `viam module upload` (or `viam module generate` and opting to register the module, and then `viam module upload`).
For most use cases, we recommend the [`build-action` GitHub action](https://github.com/viamrobotics/build-action) which provides a simple cross-platform build setup for multiple platforms: x86, ARM Linux, and MacOS.
However, if you already have your own CI with access to arm runners or only intend to build on `x86` or `mac`, you may also use the [`upload-module` GitHub action](https://github.com/viamrobotics/upload-module) instead which allows you to define the exact build steps.
From 8a2e840a910a0e413d547428f37ab974092a44ab Mon Sep 17 00:00:00 2001
From: JessamyT
Date: Mon, 30 Dec 2024 18:02:36 -0800
Subject: [PATCH 04/10] Base, arm examples and API description fixes
---
docs/dev/reference/apis/components/arm.md | 5 +++--
docs/dev/reference/apis/services/motion.md | 4 ++--
docs/operate/control/mobile-app.md | 2 +-
docs/operate/mobility/define-obstacles.md | 7 +++++++
docs/operate/mobility/move-arm.md | 14 +++++++++++++-
docs/operate/mobility/move-base.md | 21 ++++++++++++++++++++-
docs/operate/mobility/move-gantry.md | 10 ++++++++++
7 files changed, 56 insertions(+), 7 deletions(-)
diff --git a/docs/dev/reference/apis/components/arm.md b/docs/dev/reference/apis/components/arm.md
index 8448d550ef..13a8adc09a 100644
--- a/docs/dev/reference/apis/components/arm.md
+++ b/docs/dev/reference/apis/components/arm.md
@@ -3,14 +3,15 @@ title: "Arm API"
linkTitle: "Arm"
weight: 10
type: "docs"
-description: "Give commands to your arm components for linear motion planning, including self-collision prevention and obstacle avoidance."
+description: "Give commands to your arm components for linear motion planning."
icon: true
images: ["/icons/components/arm.svg"]
date: "2022-01-01"
# updated: "" # When the content was last entirely checked
---
-The arm API allows you to give commands to your [arm components](/components/arm/) for linear motion planning, including self-collision prevention and obstacle avoidance.
+The arm API allows you to give commands to your [arm components](/components/arm/) for linear motion planning.
+If you want self-collision prevention and obstacle avoidance, use the [motion API](/dev/reference/apis/services/motion/).
The arm component supports the following methods:
diff --git a/docs/dev/reference/apis/services/motion.md b/docs/dev/reference/apis/services/motion.md
index 3f80671f62..1ef07bfb5b 100644
--- a/docs/dev/reference/apis/services/motion.md
+++ b/docs/dev/reference/apis/services/motion.md
@@ -3,14 +3,14 @@ title: "Motion Service API"
linkTitle: "Motion"
weight: 40
type: "docs"
-description: "Give commands to move a machine based on a SLAM map or GPS coordinates or to move a machine's components from one location to another."
+description: "Give commands to move a machine's components from one location or pose to another."
icon: true
images: ["/icons/components/arm.svg"]
date: "2022-01-01"
# updated: "" # When the content was last entirely checked
---
-The motion service API allows you to give commands to your [motion service](/services/motion/) for moving a machine based on a SLAM map or GPS coordinates or for moving a machine's components from one location to another.
+The motion service API allows you to give commands to your [motion service](/services/motion/) for moving a mobile robot based on a SLAM map or GPS coordinates or for moving a machine's components from one pose to another.
The motion service supports the following methods:
diff --git a/docs/operate/control/mobile-app.md b/docs/operate/control/mobile-app.md
index ab5a618155..ae8964acfc 100644
--- a/docs/operate/control/mobile-app.md
+++ b/docs/operate/control/mobile-app.md
@@ -78,7 +78,7 @@ class BaseScreen extends StatelessWidget {
See the guide for full code and instructions to get started by building a simple app to control a rented Viam rover:
{{< cards >}}
-{{% card link="/tutorials/control/mobile-app/drive-rover/" %}}
+{{% card link="/tutorials/control/drive-rover/" %}}
{{< /cards >}}
For a more in-depth guide with more screens, see the following guide:
diff --git a/docs/operate/mobility/define-obstacles.md b/docs/operate/mobility/define-obstacles.md
index 9d2b2e8cfd..37d81f366d 100644
--- a/docs/operate/mobility/define-obstacles.md
+++ b/docs/operate/mobility/define-obstacles.md
@@ -7,3 +7,10 @@ type: "docs"
no_list: true
description: "Spatially describe your robot's working environment for collision avoidance."
---
+
+## Example usage
+
+{{< cards >}}
+{{% card link="/tutorials/services/constrain-motion/" %}}
+{{% card link="/tutorials/projects/claw-game/" %}}
+{{< /cards >}}
diff --git a/docs/operate/mobility/move-arm.md b/docs/operate/mobility/move-arm.md
index 04570c97c8..fa783d7262 100644
--- a/docs/operate/mobility/move-arm.md
+++ b/docs/operate/mobility/move-arm.md
@@ -8,9 +8,21 @@ no_list: true
description: "Move an arm with joint positions or automated motion planning."
---
-You can move a robotic arm either with the direct joint position commands of the [arm API](/dev/reference/apis/components/arm/), or with automated complex motion planning using the [motion planning service API](/dev/reference/apis/services/motion/).
+You have two options for moving a robotic arm:
+
+- Use direct joint position commands and simple linear commands with the [arm API](/dev/reference/apis/components/arm/)
+- Use automated complex motion planning with the [motion planning service API](/dev/reference/apis/services/motion/)
{{< cards >}}
{{% card link="/dev/reference/apis/components/arm/" %}}
{{% card link="/dev/reference/apis/services/motion/" %}}
{{< /cards >}}
+
+## Tutorials and example usage
+
+{{< cards >}}
+{{% card link="/how-tos/move-robot-arm/" %}}
+{{% card link="/tutorials/services/plan-motion-with-arm-gripper/" %}}
+{{% card link="/tutorials/services/constrain-motion/" %}}
+{{% card link="/tutorials/projects/claw-game/" %}}
+{{< /cards >}}
diff --git a/docs/operate/mobility/move-base.md b/docs/operate/mobility/move-base.md
index 1bc081df21..37d0b5cf50 100644
--- a/docs/operate/mobility/move-base.md
+++ b/docs/operate/mobility/move-base.md
@@ -1,9 +1,28 @@
---
linkTitle: "Move a base"
-title: "Move a base through space"
+title: "Move a wheeled robot base"
weight: 40
layout: "docs"
type: "docs"
no_list: true
description: "Move a mobile robot with manual or autonomous navigation."
---
+
+You have three options for moving a mobile robot base:
+
+- Give direct commands such as `Spin` and `MoveStraight` using the [base API](/dev/reference/apis/components/base/)
+- Send the base to a destination on a SLAM map or to a GPS coordinate using the [motion planning service API's](/dev/reference/apis/services/motion/) `MoveOnMap` or `MoveOnGlobe` commands, respectively
+- Define waypoints and move your base along those waypoints while avoiding obstacles, using the [navigation service API](/dev/reference/apis/services/navigation)
+
+{{< cards >}}
+{{% card link="/dev/reference/apis/components/base/" %}}
+{{% card link="/dev/reference/apis/services/motion/" %}}
+{{% card link="/dev/reference/apis/services/navigation/" %}}
+{{< /cards >}}
+
+## Example usage
+
+{{< cards >}}
+{{% card link="/tutorials/control/drive-rover/" %}}
+{{% card link="/tutorials/services/navigate-with-rover-base/" %}}
+{{< /cards >}}
diff --git a/docs/operate/mobility/move-gantry.md b/docs/operate/mobility/move-gantry.md
index 365724169f..508f47021e 100644
--- a/docs/operate/mobility/move-gantry.md
+++ b/docs/operate/mobility/move-gantry.md
@@ -7,3 +7,13 @@ type: "docs"
no_list: true
description: "Move a gantry with linear actuator positions or automated motion planning."
---
+
+You have two options for moving a gantry:
+
+- Move each axis of the gantry directly with the [gantry API](/dev/reference/apis/components/gantry/)
+- Use automated complex motion planning with the [motion planning service API](/dev/reference/apis/services/motion/)
+
+{{< cards >}}
+{{% card link="/dev/reference/apis/components/gantry/" %}}
+{{% card link="/dev/reference/apis/services/motion/" %}}
+{{< /cards >}}
From b2a7b6b27e4193ae803fd1479b8908b6febb8dbf Mon Sep 17 00:00:00 2001
From: JessamyT
Date: Tue, 31 Dec 2024 12:52:42 -0800
Subject: [PATCH 05/10] Put how-to and tutorials into move arm section, fix arm
API description
---
docs/dev/reference/apis/components/arm.md | 4 +-
docs/operate/mobility/move-arm/_index.md | 28 +++
.../mobility/move-arm}/constrain-motion.md | 6 +-
.../mobility/move-arm/move-robot-arm.md | 220 ++++++++++++++++++
.../move-arm}/plan-motion-with-arm-gripper.md | 12 +-
.../reference/components/arm/_index.md | 2 +-
6 files changed, 262 insertions(+), 10 deletions(-)
create mode 100644 docs/operate/mobility/move-arm/_index.md
rename docs/{tutorials/services => operate/mobility/move-arm}/constrain-motion.md (99%)
create mode 100644 docs/operate/mobility/move-arm/move-robot-arm.md
rename docs/{tutorials/services => operate/mobility/move-arm}/plan-motion-with-arm-gripper.md (98%)
diff --git a/docs/dev/reference/apis/components/arm.md b/docs/dev/reference/apis/components/arm.md
index 13a8adc09a..865d367838 100644
--- a/docs/dev/reference/apis/components/arm.md
+++ b/docs/dev/reference/apis/components/arm.md
@@ -10,8 +10,8 @@ date: "2022-01-01"
# updated: "" # When the content was last entirely checked
---
-The arm API allows you to give commands to your [arm components](/components/arm/) for linear motion planning.
-If you want self-collision prevention and obstacle avoidance, use the [motion API](/dev/reference/apis/services/motion/).
+The arm API allows you to give commands to your [arm components](/components/arm/) for linear motion planning with self-collision prevention.
+If you want the arm to avoid obstacles, or you want to plan complex motion in an automated way, use the [motion API](/dev/reference/apis/services/motion/).
The arm component supports the following methods:
diff --git a/docs/operate/mobility/move-arm/_index.md b/docs/operate/mobility/move-arm/_index.md
new file mode 100644
index 0000000000..f2310b53ae
--- /dev/null
+++ b/docs/operate/mobility/move-arm/_index.md
@@ -0,0 +1,28 @@
+---
+linkTitle: "Move an arm"
+title: "Move an arm"
+weight: 50
+layout: "docs"
+type: "docs"
+no_list: true
+description: "Move an arm with joint positions or automated motion planning."
+---
+
+You have two options for moving a robotic arm:
+
+- Use direct joint position commands and simple linear commands with the [arm API](/dev/reference/apis/components/arm/)
+- Use automated complex motion planning with the [motion planning service API](/dev/reference/apis/services/motion/)
+
+{{< cards >}}
+{{% card link="/dev/reference/apis/components/arm/" %}}
+{{% card link="/dev/reference/apis/services/motion/" %}}
+{{< /cards >}}
+
+## Tutorials and example usage
+
+{{< cards >}}
+{{% card link="/operate/mobility/move-arm/move-robot-arm/" %}}
+{{% card link="/operate/mobility/move-arm/plan-motion-with-arm-gripper/" %}}
+{{% card link="/operate/mobility/move-arm/constrain-motion/" %}}
+{{% card link="/tutorials/projects/claw-game/" %}}
+{{< /cards >}}
diff --git a/docs/tutorials/services/constrain-motion.md b/docs/operate/mobility/move-arm/constrain-motion.md
similarity index 99%
rename from docs/tutorials/services/constrain-motion.md
rename to docs/operate/mobility/move-arm/constrain-motion.md
index a401f511b7..dc94184bfa 100644
--- a/docs/tutorials/services/constrain-motion.md
+++ b/docs/operate/mobility/move-arm/constrain-motion.md
@@ -1,6 +1,6 @@
---
-title: "Add Constraints and Transforms to a Motion Plan"
-linkTitle: "Add Motion Constraints"
+title: "Add constraints and transforms to a motion plan"
+linkTitle: "Add motion constraints"
type: "docs"
description: "Use constraints and transforms with the motion service."
videos:
@@ -19,6 +19,8 @@ level: "Intermediate"
date: "2023-07-03"
# updated: ""
cost: 8400
+aliases:
+ - /tutorials/services/constrain-motion/
---
{{}}
diff --git a/docs/operate/mobility/move-arm/move-robot-arm.md b/docs/operate/mobility/move-arm/move-robot-arm.md
new file mode 100644
index 0000000000..5bc63ec8e5
--- /dev/null
+++ b/docs/operate/mobility/move-arm/move-robot-arm.md
@@ -0,0 +1,220 @@
+---
+title: "Move a robot arm with the arm API"
+linkTitle: "Use the arm API"
+weight: 70
+type: "docs"
+tags: ["arm", "components"]
+images: ["/how-tos/move_to_position.gif"]
+videos: ["/how-tos/move_to_position.webm", "/how-tos/move_to_position.mp4"]
+description: "Connect to a robotic arm and control it with the arm API."
+aliases:
+ - /tutorials/motion/accessing-and-moving-robot-arm
+ - /tutorials/motion/
+ - /how-tos/move-robot-arm/
+languages: ["Python", "Go"]
+viamresources: ["arm"]
+platformarea: ["mobility"]
+level: "Intermediate"
+date: "2024-10-31"
+# updated: "" # When the tutorial was last entirely checked
+cost: "8400"
+---
+
+In this guide you'll use Viam's Python or Go SDK to get the current position of your robot arm and issue movement commands.
+
+{{< alert title="In this page" color="tip" >}}
+
+1. [Access the arm](#access-the-arm)
+1. [Move the arm](#move-the-arm)
+
+{{< /alert >}}
+
+## Requirements
+
+You need the following hardware to complete this tutorial:
+
+- A single-board computer or other computer to run `viam-server`.
+- A Linux, macOS or Windows computer that can run SDK code.
+- A [robotic arm](/components/arm/).
+
+## Access the arm
+
+{{< table >}}
+{{% tablestep link="/configure/" %}}
+**1. Configure a machine with an arm**
+
+{{% snippet "setup.md" %}}
+
+Add an arm component.
+Select a model that supports your arm. Refer to the [documentation for the model](/components/arm/#configuration) for information about your arm's configuration attributes.
+
+{{}}
+Save your config.
+
+{{% /tablestep %}}
+{{% tablestep link="/appendix/apis/components/arm/#getendposition" %}}
+**2. Get the position of the end effector**
+
+One way to describe the state of a robot arm is with the position of the end effector, or the "tool" or "hand" of the arm.
+The code to get this information is a part of the autogenerated code sample the Viam app provides for your machine.
+Go to the **Code sample** page of the **CONNECT** tab and select either Python or Go.
+
+{{% snippet "show-secret.md" %}}
+
+Then, copy and paste the sample code into a file and run the resulting script to see the resources available on your screen and the position of the end effector output as a [`Pose`](/internals/orientation-vector/).
+For more information, see [`GetEndPosition`](/appendix/apis/components/arm/#getendposition).
+
+{{% /tablestep %}}
+{{% tablestep %}}
+**3. Get the joint positions**
+
+The state of a robot arm can also be described as the list of positions of each joint attached to the arm.
+To get that information, add the following code right after the code that gets the end effector pose from the prior code sample:
+
+{{< tabs >}}
+{{% tab name="Python" %}}
+
+```python
+# Joint Positions of arm1
+my_arm_joint_positions = await arm_1.get_joint_positions()
+print(f"myArm get_joint_positions return value: {my_arm_joint_positions}")
+```
+
+{{% /tab %}}
+{{% tab name="Go" %}}
+
+```go
+// Joint Positions of arm1
+myArmJointPositions, err := arm1.JointPositions(context.Background(), nil)
+if err != nil {
+ logger.Error(err)
+ return
+}
+logger.Infof("myArm JointPositions return value:", myArmJointPositions)
+```
+
+{{% /tab %}}
+{{< /tabs >}}
+
+Run your code again.
+Each individual value corresponds to the current position of a particular joint on your robot.
+You can also see these values reflected on the **CONTROL** tab in the Viam app for your robot arm.
+
+{{% /tablestep %}}
+{{< /table >}}
+
+## Move the arm
+
+The two main options for controlling arm movement are through **joint position commands** and through **pose commands**.
+Joint position commands allow for more detailed control and flexibility instead of commanding movement with the end effector position in a pose command.
+
+{{< alert title="Caution" color="caution" >}}
+Be careful when instructing robot arms to move.
+Before running any code, ensure your robotic arm has enough space and that there are no obstacles.
+Also pay attention to your surroundings, double-check your code for correctness, and make sure anyone nearby is aware and alert before issuing commands to your machine.
+{{< /alert >}}
+
+{{< table >}}
+{{% tablestep %}}
+**1. Initiate motion with a joint position command**
+
+{{< tabs >}}
+{{% tab name="Python" %}}
+Add the following line to your import list to be able to assign values to a `JointPositions` data structure:
+
+```python
+from viam.proto.component.arm import JointPositions
+```
+
+Add the following code to your script:
+
+```python
+# Command a joint position move: move the forearm of the arm slightly up
+cmd_joint_positions = JointPositions(values=[0, 0, -30.0, 0, 0, 0])
+await arm_1.move_to_joint_positions(positions=cmd_joint_positions)
+```
+
+{{% /tab %}}
+{{% tab name="Go" link="/appendix/apis/components/arm/#movetojointpositions" %}}
+Add `armapi "go.viam.com/api/component/arm/v1"` to your import list.
+Add the following code to your script:
+
+```go
+// Command a joint position move: move the forearm of the arm slightly up
+cmdJointPositions := &armapi.JointPositions{Values: []float64{0.0, 0.0, -30.0, 0.0, 0.0, 0.0}}
+err = arm1.MoveToJointPositions(context.Background(), cmdJointPositions, nil)
+if err != nil {
+ logger.Error(err)
+ return
+}
+```
+
+{{% /tab %}}
+{{< /tabs >}}
+
+{{}}
+
+Run the code.
+The third joint of your arm should move 30 degrees.
+For more information, see [`MoveToJointPositions`](/appendix/apis/components/arm/#movetojointpositions).
+
+{{% /tablestep %}}
+{{% tablestep link="/appendix/apis/components/arm/#movetoposition" %}}
+**2. Command to move to position**
+
+{{< tabs >}}
+{{% tab name="Python" %}}
+
+Add the following code to your script:
+
+```python
+# Generate a simple pose move +100mm in the +Z direction of the arm
+cmd_arm_pose = await arm_1.get_end_position()
+cmd_arm_pose.z += 100.0
+await arm_1.move_to_position(pose=cmd_arm_pose)
+```
+
+{{% /tab %}}
+{{% tab name="Go" %}}
+Add `"go.viam.com/rdk/spatialmath"` to your import list.
+
+Add the following code to your script:
+
+```go
+// Generate a simple pose move +100mm in the +Z direction of the arm
+currentArmPose, err := arm1.EndPosition(context.Background(), nil)
+if err != nil {
+ logger.Error(err)
+ return
+}
+adjustedArmPoint := currentArmPose.Point()
+adjustedArmPoint.Z += 100.0
+cmdArmPose := spatialmath.NewPose(adjustedArmPoint, currentArmPose.Orientation())
+
+err = arm1.MoveToPosition(context.Background(), cmdArmPose, nil)
+if err != nil {
+ logger.Error(err)
+ return
+}
+```
+
+{{% /tab %}}
+{{< /tabs >}}
+
+{{}}
+
+This code gets the arm's end position, makes a 100 millimeter adjustment in the +Z direction, and then uses that adjustment as a goal [`Pose`](/internals/orientation-vector/) when commanding arm motion.
+Run the code to see your arm move 100 mm upwards.
+For more information, see [`MoveToPosition`](/appendix/apis/components/arm/#movetoposition).
+
+{{% /tablestep %}}
+{{< /table >}}
+
+## Next steps
+
+{{< cards >}}
+{{% card link="/tutorials/services/plan-motion-with-arm-gripper" %}}
+{{% card link="/tutorials/projects/claw-game/" %}}
+{{< /cards >}}
+
+For more resources on robot kinematics, read through the Wikipedia pages for [Forward kinematics](https://en.wikipedia.org/wiki/Forward_kinematics) and [Inverse kinematics](https://en.wikipedia.org/wiki/Inverse_kinematics).
diff --git a/docs/tutorials/services/plan-motion-with-arm-gripper.md b/docs/operate/mobility/move-arm/plan-motion-with-arm-gripper.md
similarity index 98%
rename from docs/tutorials/services/plan-motion-with-arm-gripper.md
rename to docs/operate/mobility/move-arm/plan-motion-with-arm-gripper.md
index 8d0e77b4c0..73b894849b 100644
--- a/docs/tutorials/services/plan-motion-with-arm-gripper.md
+++ b/docs/operate/mobility/move-arm/plan-motion-with-arm-gripper.md
@@ -1,8 +1,8 @@
---
-title: "Plan Motion with an Arm and a Gripper"
-linkTitle: "Plan Motion with an Arm"
+title: "Plan motion with an arm and gripper"
+linkTitle: "Use the motion service"
type: "docs"
-description: "Use the motion service to move robot arms and other components."
+description: "Use the motion service to move a robot arm and gripper."
videos:
[
"/tutorials/videos/motion_armmoving.webm",
@@ -19,6 +19,8 @@ date: "2023-03-07"
# updated: ""
cost: 8400
no_list: true
+aliases:
+ - /tutorials/services/plan-motion-with-arm-gripper/
---
With Viam you can move individual components, like [arms](/components/arm/), by issuing commands like `MoveToPosition` or `MoveToJointPosition`.
@@ -55,8 +57,8 @@ This also helps simplify and shorten the code examples presented below.
## Configure a robot
-The [robot configuration from the prior tutorial](/how-tos/move-robot-arm/) should be used for this tutorial.
-We will revisit that robot configuration and add new components during specific sections below.
+Use the robot configuration from the [prerequisite guide](/how-tos/move-robot-arm/) for this tutorial as well.
+We will revisit that robot configuration and add new components.
The motion service is one of the "built-in" services, which means that no initial configuration is required to start planning and executing complex motion.
All you need is a robot with a component that can move, such as a robotic arm.
diff --git a/docs/operate/reference/components/arm/_index.md b/docs/operate/reference/components/arm/_index.md
index 8cf9d59df4..37f79db6fc 100644
--- a/docs/operate/reference/components/arm/_index.md
+++ b/docs/operate/reference/components/arm/_index.md
@@ -20,7 +20,7 @@ date: "2024-10-21"
# SME: Peter L
---
-The arm component provides an API for linear motion planning, including self-collision prevention and obstacle avoidance.
+The arm component provides an API for linear motion, including self-collision prevention.
If you have a physical robotic arm, consisting of a serial chain of joints and links, with a fixed end and an end effector end, use an arm component.
From eb993520bc45ef220087350e052bd8bc27aa054f Mon Sep 17 00:00:00 2001
From: JessamyT
Date: Tue, 31 Dec 2024 15:15:57 -0800
Subject: [PATCH 06/10] Delete old move an arm page
---
docs/operate/mobility/move-arm.md | 28 ----------------------------
1 file changed, 28 deletions(-)
delete mode 100644 docs/operate/mobility/move-arm.md
diff --git a/docs/operate/mobility/move-arm.md b/docs/operate/mobility/move-arm.md
deleted file mode 100644
index fa783d7262..0000000000
--- a/docs/operate/mobility/move-arm.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-linkTitle: "Move an arm"
-title: "Move an arm"
-weight: 50
-layout: "docs"
-type: "docs"
-no_list: true
-description: "Move an arm with joint positions or automated motion planning."
----
-
-You have two options for moving a robotic arm:
-
-- Use direct joint position commands and simple linear commands with the [arm API](/dev/reference/apis/components/arm/)
-- Use automated complex motion planning with the [motion planning service API](/dev/reference/apis/services/motion/)
-
-{{< cards >}}
-{{% card link="/dev/reference/apis/components/arm/" %}}
-{{% card link="/dev/reference/apis/services/motion/" %}}
-{{< /cards >}}
-
-## Tutorials and example usage
-
-{{< cards >}}
-{{% card link="/how-tos/move-robot-arm/" %}}
-{{% card link="/tutorials/services/plan-motion-with-arm-gripper/" %}}
-{{% card link="/tutorials/services/constrain-motion/" %}}
-{{% card link="/tutorials/projects/claw-game/" %}}
-{{< /cards >}}
From e8fdbd60ace40fabbd6185b484fb95a561987e2b Mon Sep 17 00:00:00 2001
From: JessamyT
Date: Tue, 31 Dec 2024 16:52:15 -0800
Subject: [PATCH 07/10] Gantry, arm changes, etc
---
docs/operate/control/headless-app.md | 20 +--
.../operate/get-started/supported-hardware.md | 1 +
docs/operate/mobility/define-obstacles.md | 30 +++-
docs/operate/mobility/move-arm/_index.md | 2 +-
.../mobility/move-arm/constrain-motion.md | 1 +
.../mobility/move-arm/move-robot-arm.md | 2 +-
.../move-arm/plan-motion-with-arm-gripper.md | 1 +
docs/operate/mobility/move-base.md | 4 +-
docs/operate/mobility/move-gantry.md | 165 +++++++++++++++++-
docs/operate/mobility/use-input-to-act.md | 2 +-
10 files changed, 210 insertions(+), 18 deletions(-)
diff --git a/docs/operate/control/headless-app.md b/docs/operate/control/headless-app.md
index 9a060b1bfe..540034cce3 100644
--- a/docs/operate/control/headless-app.md
+++ b/docs/operate/control/headless-app.md
@@ -25,7 +25,7 @@ You can run your code directly on the machine's single-board computer (SBC), or
### On a separate computer
-We recommend running your script on a laptop, desktop, or server if:
+We recommend running your code on a laptop, desktop, or server if:
- You are using computationally-intensive programs involving, for example, computer vision or motion planning, and
- You have a stable internet connection
@@ -34,7 +34,7 @@ The client code will establish a connection to the instance of `viam-server` on
### On the machine itself
-We recommend running your script on the SBC that directly controls your hardware if:
+We recommend running your code on the SBC that directly controls your hardware if:
- Your machines have intermittent or no network connectivity, or
- You want to reduce latency, for example for running [PID control loops](https://en.wikipedia.org/wiki/Proportional%E2%80%93integral%E2%80%93derivative_controller), or
@@ -93,7 +93,7 @@ You can use the toggle to include the machine API key and API key ID, though we
If your code will connect to multiple machines or use [Platform APIs](/dev/reference/apis/#platform-apis) you can create an API key with broader access.
-## Write your control script
+## Write your control code
For API reference including code snippets for each method, see [Viam's Client APIs](/dev/reference/apis/).
@@ -335,13 +335,13 @@ if __name__ == '__main__':
{{< /expand >}}
-## Run your script
+## Run your code
-You can run your script manually from your terminal, or you can automatically run it each time your machine starts.
+You can run your code manually from your terminal, or you can automatically run it each time your machine starts.
-### Run your script manually
+### Run your code manually
-To run your script on a laptop or desktop, or to run it on your machine's SBC manually for testing purposes, execute the following command in a terminal:
+To run your code on a laptop or desktop, or to run it on your machine's SBC manually for testing purposes, execute the following command in a terminal:
{{< tabs >}}
{{% tab name="Python" %}}
@@ -365,13 +365,13 @@ For information on running C++ code see [the instructions on GitHub](https://git
{{% /tab %}}
{{< /tabs >}}
-### Run your script as an automatic process
+### Run your code as an automatic process
-If you want your script to run each time your machine boots, configure the script as a _{{< glossary_tooltip term_id="process" text="process" >}}_ on your machine.
+If you want your code to run each time your machine boots, configure the code as a _{{< glossary_tooltip term_id="process" text="process" >}}_ on your machine.
Configured processes are managed by `viam-server` and are a way to run any specified command either once on boot or continuously over the lifecycle of `viam-server`.
{{% alert title="Tip" color="tip" %}}
-If you are running your code from a laptop or desktop, we do not recommend configuring your script to run as a process because doing so will cause the process to run whenever you boot your computer, even when you are using your computer for unrelated purposes.
+If you are running your code from a laptop or desktop, we do not recommend configuring your code to run as a process because doing so will cause the process to run whenever you boot your computer, even when you are using your computer for unrelated purposes.
{{% /alert %}}
To configure a process, click the **+** button on your machine's **CONFIGURE** tab and select **Process**.
diff --git a/docs/operate/get-started/supported-hardware.md b/docs/operate/get-started/supported-hardware.md
index f05fd4924a..cd7860fea1 100644
--- a/docs/operate/get-started/supported-hardware.md
+++ b/docs/operate/get-started/supported-hardware.md
@@ -96,6 +96,7 @@ You can read more about the Viam-maintained services and how to configure them i
- Motion planning for various components:
- [Robot arm motion](/operate/mobility/move-arm/)
- [Mobile robot navigation](/operate/mobility/move-base/)
+ - [Gantry motion planning](/operate/mobility/move-gantry/)
To add a service to your machine:
diff --git a/docs/operate/mobility/define-obstacles.md b/docs/operate/mobility/define-obstacles.md
index 37d81f366d..a27a2086fa 100644
--- a/docs/operate/mobility/define-obstacles.md
+++ b/docs/operate/mobility/define-obstacles.md
@@ -8,9 +8,35 @@ no_list: true
description: "Spatially describe your robot's working environment for collision avoidance."
---
-## Example usage
+To prevent your machine from colliding with objects or surfaces in its environment, you can define obstacles and include them in calls to the [motion service API](/dev/reference/apis/services/motion/).
+The motion service will take into account the obstacles as well as the geometry of the machine itself when planning motion.
+
+Start by [defining your machine's geometry](/operate/mobility/define-geometry/) so that you can define the obstacles with respect to the machine's reference frame.
+
+Next, define one or more obstacles.
+Here is a Python example from the [Add constraints and transforms to a motion plan guide](/operate/mobility/move-arm/constrain-motion/#modify-your-robots-working-environment):
+
+```python {class="line-numbers linkable-line-numbers"}
+box_origin = Pose(x=400, y=0, z=50+z_offset)
+box_dims = Vector3(x=120.0, y=80.0, z=100.0)
+box_object = Geometry(center=box_origin,
+ box=RectangularPrism(dims_mm=box_dims))
+
+obstacles_in_frame = GeometriesInFrame(reference_frame="world",
+ geometries=[table_object, box_object])
+```
+
+Finally, pass your obstacles to the motion planning API.
+See the API documentation for the following methods which can take into account obstacles:
+
+- [Move](/dev/reference/apis/services/motion/#move)
+ - Obstacles are passed as part of the `world_state` parameter
+- [MoveOnMap](/dev/reference/apis/services/motion/#moveonmap)
+- [MoveOnGlobe](/dev/reference/apis/services/motion/#moveonglobe)
+
+## Tutorials and example usage
{{< cards >}}
-{{% card link="/tutorials/services/constrain-motion/" %}}
+{{% card link="/operate/mobility/move-arm/constrain-motion/" %}}
{{% card link="/tutorials/projects/claw-game/" %}}
{{< /cards >}}
diff --git a/docs/operate/mobility/move-arm/_index.md b/docs/operate/mobility/move-arm/_index.md
index f2310b53ae..f6748f30a9 100644
--- a/docs/operate/mobility/move-arm/_index.md
+++ b/docs/operate/mobility/move-arm/_index.md
@@ -8,7 +8,7 @@ no_list: true
description: "Move an arm with joint positions or automated motion planning."
---
-You have two options for moving a robotic arm:
+You have two options for moving a robotic [arm](/operate/reference/components/arm/):
- Use direct joint position commands and simple linear commands with the [arm API](/dev/reference/apis/components/arm/)
- Use automated complex motion planning with the [motion planning service API](/dev/reference/apis/services/motion/)
diff --git a/docs/operate/mobility/move-arm/constrain-motion.md b/docs/operate/mobility/move-arm/constrain-motion.md
index dc94184bfa..1a753ba60e 100644
--- a/docs/operate/mobility/move-arm/constrain-motion.md
+++ b/docs/operate/mobility/move-arm/constrain-motion.md
@@ -2,6 +2,7 @@
title: "Add constraints and transforms to a motion plan"
linkTitle: "Add motion constraints"
type: "docs"
+weight: 60
description: "Use constraints and transforms with the motion service."
videos:
[
diff --git a/docs/operate/mobility/move-arm/move-robot-arm.md b/docs/operate/mobility/move-arm/move-robot-arm.md
index 5bc63ec8e5..0bf8b9b848 100644
--- a/docs/operate/mobility/move-arm/move-robot-arm.md
+++ b/docs/operate/mobility/move-arm/move-robot-arm.md
@@ -1,7 +1,7 @@
---
title: "Move a robot arm with the arm API"
linkTitle: "Use the arm API"
-weight: 70
+weight: 20
type: "docs"
tags: ["arm", "components"]
images: ["/how-tos/move_to_position.gif"]
diff --git a/docs/operate/mobility/move-arm/plan-motion-with-arm-gripper.md b/docs/operate/mobility/move-arm/plan-motion-with-arm-gripper.md
index 73b894849b..c8451f61b6 100644
--- a/docs/operate/mobility/move-arm/plan-motion-with-arm-gripper.md
+++ b/docs/operate/mobility/move-arm/plan-motion-with-arm-gripper.md
@@ -2,6 +2,7 @@
title: "Plan motion with an arm and gripper"
linkTitle: "Use the motion service"
type: "docs"
+weight: 40
description: "Use the motion service to move a robot arm and gripper."
videos:
[
diff --git a/docs/operate/mobility/move-base.md b/docs/operate/mobility/move-base.md
index 37d0b5cf50..70e0306918 100644
--- a/docs/operate/mobility/move-base.md
+++ b/docs/operate/mobility/move-base.md
@@ -8,7 +8,7 @@ no_list: true
description: "Move a mobile robot with manual or autonomous navigation."
---
-You have three options for moving a mobile robot base:
+You have three options for moving a mobile robot [base](/operate/reference/components/base/):
- Give direct commands such as `Spin` and `MoveStraight` using the [base API](/dev/reference/apis/components/base/)
- Send the base to a destination on a SLAM map or to a GPS coordinate using the [motion planning service API's](/dev/reference/apis/services/motion/) `MoveOnMap` or `MoveOnGlobe` commands, respectively
@@ -20,7 +20,7 @@ You have three options for moving a mobile robot base:
{{% card link="/dev/reference/apis/services/navigation/" %}}
{{< /cards >}}
-## Example usage
+## Tutorials and example usage
{{< cards >}}
{{% card link="/tutorials/control/drive-rover/" %}}
diff --git a/docs/operate/mobility/move-gantry.md b/docs/operate/mobility/move-gantry.md
index 508f47021e..d3196ce957 100644
--- a/docs/operate/mobility/move-gantry.md
+++ b/docs/operate/mobility/move-gantry.md
@@ -8,7 +8,7 @@ no_list: true
description: "Move a gantry with linear actuator positions or automated motion planning."
---
-You have two options for moving a gantry:
+You have two options for moving a [gantry](/operate/reference/components/gantry/):
- Move each axis of the gantry directly with the [gantry API](/dev/reference/apis/components/gantry/)
- Use automated complex motion planning with the [motion planning service API](/dev/reference/apis/services/motion/)
@@ -17,3 +17,166 @@ You have two options for moving a gantry:
{{% card link="/dev/reference/apis/components/gantry/" %}}
{{% card link="/dev/reference/apis/services/motion/" %}}
{{< /cards >}}
+
+## Usage
+
+### Prerequisites
+
+{{% expand "A running machine connected to the Viam app. Click to see instructions." %}}
+
+{{% snippet "setup.md" %}}
+
+{{% /expand%}}
+
+### Steps
+
+{{< table >}}
+{{% tablestep link="/operate/reference/components/motor/" %}}
+**1. Configure the gantry's motor components**
+
+First, connect the gantry's motors to your machine.
+
+Then, navigate to the **CONFIGURE** tab of your machine's page in the [Viam app](https://app.viam.com).
+Click the **+** icon next to your machine part in the left-hand menu and select **Component**.
+
+Complete the motor configuration and use the **TEST** panel in the configuration card to test that the motor is working.
+
+Repeat this for each motor of your gantry.
+
+{{% /tablestep %}}
+{{% tablestep link="/operate/reference/components/gantry/" %}}
+**2. Configure a gantry component**
+
+Use the **+** button again to add gantry components.
+
+If you have a multi-axis gantry, [configure a single-axis gantry](/operate/reference/components/gantry/single-axis/) for each axis, and then [configure a multi-axis gantry](/operate/reference/components/gantry/multi-axis/) to combine them all into one coordinated unit.
+
+{{% /tablestep %}}
+{{% tablestep link="/operate/control/headless-app/" %}}
+**3. Connect code to your gantry**
+
+Go to your machine's **CONNECT** tab in the Viam app.
+Select your preferred programming language and copy the code snippet.
+
+See [Create a web app](/operate/control/web-app/), [Create a mobile app](/operate/control/mobile-app/), or [Create a headless app](/operate/control/headless-app/) for more information, depending on your use case.
+
+{{% /tablestep %}}
+{{% tablestep %}}
+**4. Write code to control your gantry**
+
+{{< expand "Gantry API usage example" >}}
+
+The following is an example Python script.
+
+For more methods and code snippets in more languages, see [Gantry API](/dev/reference/apis/components/gantry/).
+
+```python {class="line-numbers linkable-line-numbers"}
+import asyncio
+
+from viam.robot.client import RobotClient
+from viam.rpc.dial import Credentials, DialOptions
+from viam.components.gantry import Gantry
+
+async def connect():
+ opts = RobotClient.Options.with_api_key(
+ # Replace "" (including brackets) with your machine's api key
+ api_key='',
+ # Replace "" (including brackets) with your machine's api key id
+ api_key_id=''
+ )
+ return await RobotClient.at_address('', opts)
+
+async def main():
+ machine = await connect()
+
+ print('Resources:')
+ print(machine.resource_names)
+
+ # get gantry-1
+ gantry_1 = Gantry.from_robot(machine, "gantry-1")
+ gantry_1_return_value = await gantry_1.get_lengths()
+ print(f"gantry-1 get_lengths return value: {gantry_1_return_value}")
+
+ # Home the gantry
+ await gantry_1.home()
+
+ # Move this three-axis gantry to a position 5mm in the positive Y direction from (0,0,0)
+ # and set the speed of each axis to 8 mm/sec
+ await gantry_1.move_to_position([0,5,0], [8,8,8])
+
+ # Don't forget to close the machine when you're done!
+ await machine.close()
+
+if __name__ == '__main__':
+ asyncio.run(main())
+
+```
+
+{{< /expand >}}
+
+{{< expand "Motion planning API usage example" >}}
+
+The following is an example Python script.
+
+{{% alert title="Important" color="note" %}}
+
+Before using the motion service with a gantry, you must [define your gantry's reference frame](/operate/mobility/define-geometry/).
+
+{{% /alert %}}
+
+For more methods and code snippets in more languages, see [Motion API](/dev/reference/apis/services/motion/).
+
+```python {class="line-numbers linkable-line-numbers"}
+import asyncio
+
+from viam.robot.client import RobotClient
+from viam.rpc.dial import Credentials, DialOptions
+from viam.components.gantry import Gantry
+from viam.services.motion import MotionClient
+from viam.proto.common import Pose, PoseInFrame
+
+async def connect():
+ opts = RobotClient.Options.with_api_key(
+ # Replace "" (including brackets) with your machine's api key
+ api_key='',
+ # Replace "" (including brackets) with your machine's api key id
+ api_key_id=''
+ )
+ return await RobotClient.at_address('', opts)
+
+async def main():
+ machine = await connect()
+
+ print('Resources:')
+ print(machine.resource_names)
+
+ # get gantry-1
+ gantry_1 = Gantry.from_robot(machine, "gantry-1")
+ gantry_1_return_value = await gantry_1.get_lengths()
+ print(f"gantry-1 get_lengths return value: {gantry_1_return_value}")
+
+ motion = MotionClient.from_robot(robot=machine, name="builtin")
+
+ goal_pose = Pose(x=0, y=0, z=300, o_x=0, o_y=0, o_z=1, theta=0)
+ # Move the gantry
+ await motion.move(component_name=gantry_1,
+ destination=PoseInFrame(reference_frame="myFrame", pose=goal_pose))
+
+ # Don't forget to close the machine when you're done!
+ await machine.close()
+
+if __name__ == '__main__':
+ asyncio.run(main())
+
+```
+
+{{< /expand >}}
+
+{{% /tablestep %}}
+{{% tablestep link="/operate/reference/components/motor/" %}}
+**5. Run your code**
+
+Make sure your gantry's path is unobstructed, and run your code.
+
+{{% /tablestep %}}
+{{< /table >}}
diff --git a/docs/operate/mobility/use-input-to-act.md b/docs/operate/mobility/use-input-to-act.md
index 0ff341e45e..bba4e2580c 100644
--- a/docs/operate/mobility/use-input-to-act.md
+++ b/docs/operate/mobility/use-input-to-act.md
@@ -5,5 +5,5 @@ weight: 70
layout: "docs"
type: "docs"
no_list: true
-description: "TODO"
+description: "Actuate your machine based on sensor readings or other inputs."
---
From da38ae0a737cb7f4fd0b3801ec4ac5901772a5e6 Mon Sep 17 00:00:00 2001
From: JessamyT
Date: Thu, 2 Jan 2025 12:41:48 -0800
Subject: [PATCH 08/10] Use input to act, and fix module typo
---
docs/operate/mobility/use-input-to-act.md | 100 ++++++++++++++++++
.../operate/reference/module-configuration.md | 2 +-
2 files changed, 101 insertions(+), 1 deletion(-)
diff --git a/docs/operate/mobility/use-input-to-act.md b/docs/operate/mobility/use-input-to-act.md
index bba4e2580c..9da33f2fad 100644
--- a/docs/operate/mobility/use-input-to-act.md
+++ b/docs/operate/mobility/use-input-to-act.md
@@ -7,3 +7,103 @@ type: "docs"
no_list: true
description: "Actuate your machine based on sensor readings or other inputs."
---
+
+You can program your machine to move based on sensor readings or other inputs.
+
+{{% alert title="Disambiguation" color="tip" %}}
+If you want to act or send alerts based on computer vision, see [Act based on inferences](/data-ai/ai/act/) or [Alert on inferences](/data-ai/ai/alert/).
+To alert based on data, see [Alert on data](/data-ai/data/advanced/alert-data/).
+{{% /alert %}}
+
+## Prerequisites
+
+{{% expand "A running machine connected to the Viam app. Click to see instructions." %}}
+
+{{% snippet "setup.md" %}}
+
+{{% /expand%}}
+
+{{% expand "Components configured on your machine." %}}
+
+[Configure your sensing and actuation hardware](/operate/get-started/supported-hardware/) as components of your machine.
+
+This may include sensors, cameras, motors, bases, arms, gantries, servos, grippers, or other components.
+
+{{% /expand%}}
+
+## Program your machine
+
+{{< table >}}
+{{% tablestep %}}
+**1. Start building your app**
+
+Depending on your use case, see [Create a web app](/operate/control/web-app/), [Create a mobile app](/operate/control/mobile-app/), or [Create a headless app](/operate/contro/headless-app/) for information on installing an SDK and connecting your code to your machine.
+
+{{% /tablestep %}}
+{{% tablestep %}}
+**2. Get an input**
+
+Use one of the input APIs, such as:
+
+- [Sensor](/dev/reference/apis/components/sensor/)
+
+ - Input methods include `GetReadings`.
+ Python example:
+
+ ```python {class="line-numbers linkable-line-numbers"}
+ my_sensor = Sensor.from_robot(robot=machine, name='my_sensor')
+
+ # Get the readings provided by the sensor.
+ readings = await my_sensor.get_readings()
+ ```
+
+- [Power sensor](/dev/reference/apis/components/power-sensor/)
+
+ - Input methods include `GetVoltage`, `GetCurrent`, `GetPower`, and `GetReadings`.
+
+- [Board](/dev/reference/apis/components/board/)
+ - Input methods include `GetGPIO`, `GetPWM`, `PWMFrequency`, `GetDigitalInterruptValue`, and `ReadAnalogReader`.
+
+If you are using camera input with computer vision, see [Act based on inferences](/data-ai/ai/act/) for relevant examples.
+
+{{% /tablestep %}}
+{{% tablestep %}}
+**3. Actuate based on the input**
+
+To move your actuator, use your actuator component's API, with logic based on your input:
+
+- [Motor](/dev/reference/apis/components/motor/)
+
+ - Actuation methods include `SetPower`, `SetRPM`, `GoFor`, `GoTo`, `Stop`.
+ Python example:
+
+ ```python {class="line-numbers linkable-line-numbers"}
+ my_motor = Motor.from_robot(robot=machine, name="my_motor")
+
+ # Assume your sensor returns a reading with a key called "level"
+ # If the sensor reads less than 50, spin the motor at 95 RPM.
+ current_level = readings.get('level')
+ if (current_level < 50):
+ await my_motor.set_rpm(rpm=95)
+ else:
+ await my_motor.stop()
+ ```
+
+- [Servo](/dev/reference/apis/components/servo/)
+ - Actuation methods include `Move`.
+- [Gripper](/dev/reference/apis/components/gripper/)
+ - Actuation methods include `Open`, `Grab`, `Stop`.
+- [Board](/dev/reference/apis/components/board/)
+ - Input methods include `SetGPIO`, `SetPWM`, `SetPWMFrequency`, and `WriteAnalog`.
+
+If your use case involves planning coordinated motion of multiple motors, see [Move a base](/operate/mobility/move-base/), [Move an arm](/operate/mobility/move-arm/), or [Move a gantry](/operate/mobility/move-gantry/) for more information on how to automate intelligent motion planning.
+Instead of actuating using the component API, you can use the [motion service API](/dev/reference/apis/services/motion/)'s `Move`, `MoveOnMap`, or `MoveOnGlobe` commands.
+
+{{% /tablestep %}}
+{{% /table %}}
+
+## Usage examples
+
+To water a plant based on moisture sensor readings using the board API, see [Plant watering robot with a Raspberry Pi](/tutorials/projects/make-a-plant-watering-robot/).
+
+To turn a fan on or off based on air quality sensor data, see [Automate air filtration with air quality sensors](https://codelabs.viam.com/guide/air-quality/index.html?index=..%2F..index#0).
diff --git a/docs/operate/reference/module-configuration.md b/docs/operate/reference/module-configuration.md
index a5e501825d..c0f1c0d455 100644
--- a/docs/operate/reference/module-configuration.md
+++ b/docs/operate/reference/module-configuration.md
@@ -199,7 +199,7 @@ The following properties are configurable for each module:
| `type` | string | **Required** | `registry` or `local`, depending on whether the module is in the [Viam Registry](https://app.viam.com/registry) or is only available [locally](/how-tos/create-module/#test-your-module-locally) on your computer. |
| `module_id` | string | **Required** | The module author's organization namespace or UUID, then a colon, then the name of the module. Identical to the first two pieces of the {{< glossary_tooltip term_id="model-namespace-triplet" text="model namespace triplet" >}}. `:`. |
| `name` | string | **Required** | A name for this instance of the module. |
-| `env` | object | Optional | Environment variables available to the module. For example `{ "API_KEY": "${environment.API_KEY}" }`. Some modules require that you set environment variables as part of configuration. Check the module's readme for more information. See [environments variables](#environment-variables). |
+| `env` | object | Optional | Environment variables available to the module. For example `{ "API_KEY": "${environment.API_KEY}" }`. Some modules require that you set environment variables as part of configuration. Check the module's readme for more information. See [environment variables](#environment-variables). |
#### Module versioning
From f2fa90a936b8d8640197a1e04f294a81574234c0 Mon Sep 17 00:00:00 2001
From: JessamyT
Date: Thu, 2 Jan 2025 18:05:10 -0800
Subject: [PATCH 09/10] Fix PyInstaller order of operations, and DOCS-3305 fix
local testing steps
---
assets/registry/restart-module.png | Bin 0 -> 10295 bytes
.../get-started/other-hardware/_index.md | 111 ++++++++++++------
2 files changed, 73 insertions(+), 38 deletions(-)
create mode 100644 assets/registry/restart-module.png
diff --git a/assets/registry/restart-module.png b/assets/registry/restart-module.png
new file mode 100644
index 0000000000000000000000000000000000000000..d256bb689daa370a54cb96039c3d396b04db3de0
GIT binary patch
literal 10295
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