diff --git a/assets/how-tos/access-arm/config.png b/assets/how-tos/access-arm/config.png
new file mode 100644
index 0000000000..133298704b
Binary files /dev/null and b/assets/how-tos/access-arm/config.png differ
diff --git a/docs/_index.md b/docs/_index.md
index 1f91d5936c..f2a29202c7 100644
--- a/docs/_index.md
+++ b/docs/_index.md
@@ -352,7 +352,7 @@ err = myArmComponent.MoveToPosition(context.Background(), cmdArmPose, referencef
You can use any robotic arm with Viam.
Configure it as an arm component. Then you can move it using the arm API.
-[Move a robotic arm →](/tutorials/services/accessing-and-moving-robot-arm/)
+[Move a robotic arm →](/how-tos/move-robot-arm/)
diff --git a/docs/components/arm/_index.md b/docs/components/arm/_index.md
index 75e381a01c..f8e4a6b3c1 100644
--- a/docs/components/arm/_index.md
+++ b/docs/components/arm/_index.md
@@ -92,7 +92,7 @@ For general configuration, development, and usage info, see:
{{< cards >}}
{{% card link="/how-tos/configure/" noimage="true" %}}
{{% card link="/how-tos/develop-app/" noimage="true" %}}
-{{% card link="/tutorials/services/accessing-and-moving-robot-arm" noimage="true" %}}
+{{% card link="/how-tos/move-robot-arm/" noimage="true" %}}
{{< /cards >}}
You can also use the arm component with the following services:
diff --git a/docs/components/arm/eva.md b/docs/components/arm/eva.md
index 6db4c2e0d8..4d8b9ef7c9 100644
--- a/docs/components/arm/eva.md
+++ b/docs/components/arm/eva.md
@@ -46,5 +46,5 @@ For more configuration and usage info, see:
{{< cards >}}
{{% card link="/appendix/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
{{% card link="/how-tos/configure/" noimage="true" %}}
-{{% card link="/tutorials/services/accessing-and-moving-robot-arm/" noimage="true" %}}
+{{% card link="/how-tos/move-robot-arm/" noimage="true" %}}
{{< /cards >}}
diff --git a/docs/components/arm/fake.md b/docs/components/arm/fake.md
index 248de36e31..68cccb969f 100644
--- a/docs/components/arm/fake.md
+++ b/docs/components/arm/fake.md
@@ -91,5 +91,5 @@ For more configuration and usage info, see:
{{< cards >}}
{{% card link="/appendix/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
{{% card link="/how-tos/configure/" noimage="true" %}}
-{{% card link="/tutorials/services/accessing-and-moving-robot-arm/" noimage="true" %}}
+{{% card link="/how-tos/move-robot-arm/" noimage="true" %}}
{{< /cards >}}
diff --git a/docs/components/arm/lite6.md b/docs/components/arm/lite6.md
index a9cfe4867e..2cad21691b 100644
--- a/docs/components/arm/lite6.md
+++ b/docs/components/arm/lite6.md
@@ -176,5 +176,5 @@ For more configuration and usage info, see:
{{< cards >}}
{{% card link="/appendix/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
{{% card link="/how-tos/configure/" noimage="true" %}}
-{{% card link="/tutorials/services/accessing-and-moving-robot-arm/" noimage="true" %}}
+{{% card link="/how-tos/move-robot-arm/" noimage="true" %}}
{{< /cards >}}
diff --git a/docs/components/arm/ur5e.md b/docs/components/arm/ur5e.md
index 02b7133bed..a2a8e5ad37 100644
--- a/docs/components/arm/ur5e.md
+++ b/docs/components/arm/ur5e.md
@@ -94,5 +94,5 @@ For more configuration and usage info, see:
{{< cards >}}
{{% card link="/appendix/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
{{% card link="/how-tos/configure/" noimage="true" %}}
-{{% card link="/tutorials/services/accessing-and-moving-robot-arm/" noimage="true" %}}
+{{% card link="/how-tos/move-robot-arm/" noimage="true" %}}
{{< /cards >}}
diff --git a/docs/components/arm/xarm6.md b/docs/components/arm/xarm6.md
index 81a09c5eb8..55c4386fd2 100644
--- a/docs/components/arm/xarm6.md
+++ b/docs/components/arm/xarm6.md
@@ -177,5 +177,5 @@ For more configuration and usage info, see:
{{< cards >}}
{{% card link="/appendix/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
{{% card link="/how-tos/configure/" noimage="true" %}}
-{{% card link="/tutorials/services/accessing-and-moving-robot-arm/" noimage="true" %}}
+{{% card link="/how-tos/move-robot-arm/" noimage="true" %}}
{{< /cards >}}
diff --git a/docs/components/arm/xarm7.md b/docs/components/arm/xarm7.md
index 74f4ef768d..d8afc2bef4 100644
--- a/docs/components/arm/xarm7.md
+++ b/docs/components/arm/xarm7.md
@@ -177,5 +177,5 @@ For more configuration and usage info, see:
{{< cards >}}
{{% card link="/appendix/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
{{% card link="/how-tos/configure/" noimage="true" %}}
-{{% card link="/tutorials/services/accessing-and-moving-robot-arm/" noimage="true" %}}
+{{% card link="/how-tos/move-robot-arm/" noimage="true" %}}
{{< /cards >}}
diff --git a/docs/components/arm/yahboom-dofbot.md b/docs/components/arm/yahboom-dofbot.md
index 243c0ce28e..70d251ec19 100644
--- a/docs/components/arm/yahboom-dofbot.md
+++ b/docs/components/arm/yahboom-dofbot.md
@@ -84,5 +84,5 @@ For more configuration and usage info, see:
{{< cards >}}
{{% card link="/appendix/apis/components/arm/" customTitle="Arm API" noimage="true" %}}
{{% card link="/how-tos/configure/" noimage="true" %}}
-{{% card link="/tutorials/services/accessing-and-moving-robot-arm/" noimage="true" %}}
+{{% card link="/how-tos/move-robot-arm/" noimage="true" %}}
{{< /cards >}}
diff --git a/docs/how-tos/_index.md b/docs/how-tos/_index.md
index 47a6ca78d4..4c9e8c2cd7 100644
--- a/docs/how-tos/_index.md
+++ b/docs/how-tos/_index.md
@@ -34,12 +34,13 @@ The guides on this page provide solutions for common tasks and workflows. Browse
-{{< how-to-expand title="Get started with Viam basics" tasks="4" level="BEGINNER-FRIENDLY" >}}
+{{< how-to-expand title="Get started with Viam basics" tasks="5" level="BEGINNER-FRIENDLY" >}}
{{< cards >}}
{{% card link="/how-tos/drive-rover/" noimage="true" %}}
{{% card link="/how-tos/control-motor/" noimage="true" %}}
{{% card link="/how-tos/detect-people/" noimage="true" %}}
{{% card link="/how-tos/collect-data/" noimage="true" %}}
+{{% card link="/how-tos/move-robot-arm/" noimage="true" %}}
{{< /cards >}}
{{< /how-to-expand >}}
diff --git a/docs/how-tos/move-robot-arm.md b/docs/how-tos/move-robot-arm.md
new file mode 100644
index 0000000000..cf5ca5c792
--- /dev/null
+++ b/docs/how-tos/move-robot-arm.md
@@ -0,0 +1,219 @@
+---
+title: "Move a robot arm"
+linkTitle: "Move a robot arm"
+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/
+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/services/motion/_index.md b/docs/services/motion/_index.md
index 9e49fd7339..a4ade29ff6 100644
--- a/docs/services/motion/_index.md
+++ b/docs/services/motion/_index.md
@@ -60,6 +60,6 @@ The `plan_deviation_m` for `MoveOnMap()` on calls issued from the **CONTROL** ta
The following tutorials contain complete example code for interacting with a robot arm through the arm component API, and with the motion service API, respectively:
{{< cards >}}
-{{% card link="/tutorials/services/accessing-and-moving-robot-arm" %}}
+{{% card link="/how-tos/move-robot-arm/" %}}
{{% card link="/tutorials/services/plan-motion-with-arm-gripper" %}}
{{< /cards >}}
diff --git a/docs/tutorials/services/accessing-and-moving-robot-arm.md b/docs/tutorials/services/accessing-and-moving-robot-arm.md
deleted file mode 100644
index 1e6c2704fb..0000000000
--- a/docs/tutorials/services/accessing-and-moving-robot-arm.md
+++ /dev/null
@@ -1,486 +0,0 @@
----
-title: "Access and Move a Robot Arm"
-linkTitle: "Move a Robot Arm"
-type: "docs"
-description: "Access and control one of the most fundamental systems in robotics: A robotic arm."
-imageAlt: "A robotic arm"
-images: ["/tutorials/motion/preview.jpg"]
-tags: ["arm", "motion", "services"]
-aliases:
- - "/tutorials/motion/accessing-and-moving-robot-arm"
- - "/tutorials/motion/"
-authors: []
-languages: ["python", "go"]
-viamresources: ["arm"]
-platformarea: ["mobility"]
-level: "Intermediate"
-date: "2023-03-07"
-# updated: ""
-cost: 8400
-no_list: true
----
-
-
-
-{{< 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 >}}
-
-The following instructions show you how to interact with an [arm component](/components/arm/), help you understand how an arm describes its state, and assist you in issuing movement commands to your robotic arm.
-
-
-
-Code examples in this tutorial use a [UFACTORY xArm 6](https://www.ufactory.us/product/ufactory-xarm-6), but you can use any [arm model](/components/arm/).
-
-If you do not have a robotic arm of your own, the [fake arm component page](/components/arm/fake/) shows how you can set up a virtual robotic arm with the same kinematic model as a real robotic arm.
-Configure it with `"arm-model": "xArm6"` in its `attributes`.
-You can then continue through the code examples in this tutorial without making any changes (and without needing to buy or build an expensive robot arm)!
-
-The [full tutorial code](#full-tutorial-code) is available at the end of this page.
-
-## Prerequisites
-
-Before starting this tutorial, make sure you have the [Viam Python SDK](https://python.viam.dev/) or the [Viam Go SDK](https://pkg.go.dev/go.viam.com/rdk/robot/client#section-readme) installed.
-
-If you are connecting to a real robotic arm during this tutorial, make sure your computer can communicate with the controller before continuing.
-
-## Configure a machine
-
-{{% snippet "setup.md" %}}
-
-1. Once your machine is live, select the **CONFIGURE** tab.
-1. Click on the **+** symbol next to your machine in the **Builder** panel and select **Component** in the menu that opens:
-
- - Choose `arm` as the type.
- - Choose your desired model.
- - For example, if you're using an xArm 6, choose the `xArm6` model from the list.
- - Enter `myArm` as the **Name** for this component, then click **Create**.
-
-1. In the newly created `myArm` component panel, configure any attributes as needed.
- Refer to the documentation for the model for information about your arm's model.
-
-1. Switch to the **Frame** mode and select `myArm` in the left-hand menu to see the default values for your arm.
- You do not need to change the default values that populate the new frame card.
-
-
-
- {{}}
-
-1. Save your machine configuration.
-
-1. Go to the **Code sample** page of the **CONNECT** tab and select the programming language you are working in.
-
- {{% snippet "show-secret.md" %}}
-
- Then, copy and paste the sample code into a file and run the resulting script to verify you can connect to your machine.
- Throughout the rest of this tutorial, you will replace and amend this code.
- The [full tutorial code](#full-tutorial-code) is available at the bottom of this tutorial for reference.
-
-## Access the arm
-
-The `arm` component library has several methods to simplify accessing and working with robotic arms.
-In this step, you'll fetch data about the robotic arm's current position.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-Your script will resemble the following lines from the [full **Python** tutorial code](#full-tutorial-code) which enable you to use the `myArm` component you configured earlier.
-The code then calls the [`get_end_position`](/appendix/apis/components/arm/#getendposition) method to get the position of the **end of the robot arm with respect to the arm's base**.
-
-```python {class="line-numbers linkable-line-numbers"}
-# Access myArm
-my_arm = Arm.from_robot(machine, "myArm")
-
-# End Position of myArm
-my_arm_return_value = await my_arm.get_end_position()
-print(f"myArm get_end_position return value: {my_arm_return_value}")
-```
-
-You should see output that looks similar to the following:
-
-```sh {class="command-line" data-prompt="$" data-output="1-10"}
-myArm get_end_position return value: x: 200.73450755898915
-y: 0.0028507667654201754
-z: 108.63966593621173
-o_x: -0.019650946400315308
-o_y: -1.5485718223024914e-07
-o_z: -0.99980690150926033
-theta: -179.99979233107763
-```
-
-The `x`, `y`, and `z` values correspond to the `position` element of the pose, while the `o_x`, `o_y`, `o_z`, and `theta` values are for the `orientation` element of the pose (presented as an [Orientation Vector](/internals/orientation-vector/)).
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-Your script will resemble the following lines from the [full **Go** tutorial code](#full-tutorial-code) which enable you to use the `myArm` component you configured earlier.
-The code then calls the [`EndPosition`](/appendix/apis/components/arm/#getendposition) method to get the position of the **end of the robot arm with respect to the arm's base**.
-
-```go {class="line-numbers linkable-line-numbers"}
-// Access myArm
-myArmComponent, err := arm.FromRobot(machine, "myArm")
-if err!=nil {
- logger.Error(err)
- return
-}
-
-// End Position of myArm
-myArmReturnValue, err:= myArmComponent.EndPosition(context.Background(), map[string]interface{}{})
-if err!=nil {
- logger.Error(err)
- return
-}
-logger.Infof("myArm EndPosition return value: %+v", myArmReturnValue)
-
-```
-
-You should see output that looks similar to the following:
-
-```sh {class="command-line" data-prompt="$" data-output="1-10"}
-myArm EndPosition position return value: (200.734507558989150766137755, 0.002850766765420175395673, 108.639665936211727625959611)
-```
-
-The `Position` value is part of a `Pose`.
-
-{{% /tab %}}
-{{< /tabs >}}
-
-The state of a robot arm can also be described as the **combined positions of each joint** attached to the arm.
-You can access a robot arm's "joint states" (as they are sometimes referred to) by calling a different method on the arm component.
-Add the following code right after the code that gets the end effector pose from the prior code sample.
-When you run the code, you'll see that these two pieces of information are presented differently.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python {class="line-numbers linkable-line-numbers"}
-# Joint Positions of myArm
-my_arm_joint_positions = await my_arm.get_joint_positions()
-print(f"myArm get_joint_positions return value: {my_arm_joint_positions}")
-```
-
-You should see output that looks similar to the following:
-
-```sh {class="command-line" data-prompt="$" data-output="1-10"}
-myArm get_joint_positions return value: values: 0.00043945314765093886
-values: 0.46724854536551791
-values: 0.64500731344456741
-values: -0.00098876951707685271
-values: 0.013732909913080547
-values: 0.00076904296930648713
-```
-
-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.
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go {class="line-numbers linkable-line-numbers"}
-// Joint Positions of myArm
-myArmJointPositions, err := myArmComponent.JointPositions(context.Background(), nil)
-if err != nil {
- logger.Error(err)
- return
-}
-logger.Infof("myArm JointPositions return value:", myArmJointPositions)
-```
-
-You should see output that looks similar to the following:
-
-```sh {class="command-line" data-prompt="$" data-output="1-10"}
-myArm JointPositions return value: values:0.00043945314765093886 values:0.4672485453655179 values:0.6450073134445674 values:-0.0009887695170768527 values:0.013732909913080547 values:0.0007690429693064871
-```
-
-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.
-
-{{% /tab %}}
-{{< /tabs >}}
-
-Both representations of an arm's state are important.
-Sometimes you may wish to direct an arm in terms of joint positions.
-Other times you may need to describe the position of another object with respect to the end of the robot arm.
-There is a mathematical relationship that allows you to convert between these two representations, known as the **forward and inverse kinematics**, which is foundational to complex robotic motion.
-We will not cover forward and inverse kinematics in this tutorial, but resources for further reading on these topics are linked in the [**Next Steps**](#next-steps-and-references) section.
-
-## Move the arm
-
-The two main options for specifying arm movement are through **joint position commands** and through **pose commands**.
-Let's start with joint position commands, as their formulation is a little simpler.
-
-### Joint position commands
-
-First, you can initiate motion with a joint position command.
-A final note:
-
-{{< alert title="Caution" color="caution" >}}
-Executing code presented after this point _will_ induce motion in a connected robotic arm!
-{{< /alert >}}
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-Add the following line to your import list to be able to assign values to a `JointPositions` data structure:
-
-```python {class="line-numbers linkable-line-numbers"}
-from viam.proto.component.arm import JointPositions
-```
-
-See the [arm reference document](/appendix/apis/components/arm/#movetojointpositions) for further details on how to structure data that you pass to the `move_to_joint_positions` function.
-
-```python {class="line-numbers linkable-line-numbers"}
-# 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 my_arm.move_to_joint_positions(positions=cmd_joint_positions)
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-You must import an additional Go library to access the data structure that Viam uses to encode joint positions, which is shown next.
-
-Add `armapi "go.viam.com/api/component/arm/v1"` to your import list to be able to assign values to an `armapi.JointPositions` data structure.
-See the [arm reference document](/appendix/apis/components/arm/#movetojointpositions) for further details on how to structure data that you pass to the `MoveToJointPositions` function.
-
-```go {class="line-numbers linkable-line-numbers"}
-// 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 = myArmComponent.MoveToJointPositions(context.Background(), cmdJointPositions, nil)
-if err != nil {
- logger.Error(err)
- return
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-If you execute the sample joint move statement above, the third joint of your arm should move a small amount (30 degrees).
-Feel free to experiment further with joint position commands by changing the values for each joint and re-sending the commands.
-
-When you are ready to move on, the next section will show you how to use **pose commands**.
-
-### Pose commands
-
-When you [got the end position of the arm](#access-the-arm), this data was returned in the format of a `Pose`.
-The returned `Pose` is a combination of position and orientation data that indicates the end of the arm's full 6-dimensional configuration in space.
-
-The following code sample reuses the methods to get the pose of the end of the arm so that you can make small adjustments at will.
-You can then pass the adjusted pose back to the arm as a **goal** pose for the purposes of starting motion.
-For example, the following code gets the arm's end position, makes a 100 millimeter adjustment in the +Z direction, and then uses that adjustment as a goal when commanding arm motion.
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-Add the sample code below to your own client script to try using the arm component's [`move_to_position`](/appendix/apis/components/arm/#movetoposition) command.
-This example gets a `Pose` from `get_end_position()` so no additional imports are required.
-If you want to synthesize new poses directly, note that you must import an additional Python package by adding `from viam.proto.common import Pose` to your import list.
-
-```python {class="line-numbers linkable-line-numbers"}
-# Generate a simple pose move +100mm in the +Z direction of the arm
-cmd_arm_pose = await my_arm.get_end_position()
-cmd_arm_pose.z += 100.0
-await my_arm.move_to_position(pose=cmd_arm_pose)
-```
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-You must import some additional Go packages to synthesize new poses through the `spatialmath` library for the arms's [`MoveToPosition`](/appendix/apis/components/arm/#movetoposition) command.
-Add `"go.viam.com/rdk/referenceframe"` and `"go.viam.com/rdk/spatialmath"` to your import list and then add the sample code below to your own client script.
-
-```go {class="line-numbers linkable-line-numbers"}
-// Generate a simple pose move +100mm in the +Z direction of the arm
-currentArmPose, err := myArmComponent.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 = myArmComponent.MoveToPosition(context.Background(), cmdArmPose, nil)
-if err != nil {
- logger.Error(err)
- return
-}
-```
-
-{{% /tab %}}
-{{< /tabs >}}
-
-Using this code you can quickly adjust one or more elements of position AND orientation simultaneously, by modifying other elements of the original arm pose.
-
-For all motion actions taken in this tutorial, there may be joint positions or poses that are unreachable for particular reasons (potential collisions, a pose in space is unreachable because the arm is too short).
-Regularly check your client script's feedback and the `viam-server` logs for any issues that may arise.
-
-## Next steps and references
-
-If you would like to continue onto working with Viam's motion service, check out one of these tutorials:
-
-{{< cards >}}
-{{% card link="/tutorials/services/plan-motion-with-arm-gripper" %}}
-{{% card link="/tutorials/projects/claw-game/" %}}
-{{< /cards >}}
-
-{{< snippet "social.md" >}}
-
-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).
-
-## Full tutorial code
-
-{{< tabs >}}
-{{% tab name="Python" %}}
-
-```python {id="access-move-arm-python-ex" class="line-numbers linkable-line-numbers" data-line=""}
-import asyncio
-
-from viam.components.arm import Arm
-from viam.proto.component.arm import JointPositions
-from viam.robot.client import RobotClient
-from viam.rpc.dial import Credentials, DialOptions
-
-
-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('ADDRESS FROM THE VIAM APP', opts)
-
-
-async def main():
- machine = await connect()
-
- print('Resources:')
- print(machine.resource_names)
-
- # Access myArm
- my_arm = Arm.from_robot(machine, "myArm")
-
- # End Position of myArm
- my_arm_end_position = await my_arm.get_end_position()
- print(f"myArm get_end_position return value: {my_arm_end_position}")
-
- # Joint Positions of myArm
- my_arm_joint_positions = await my_arm.get_joint_positions()
- print(f"myArm get_joint_positions return value: {my_arm_joint_positions}")
-
- # Command a joint position move: small adjustment to the last joint
- cmd_joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 15.0])
- await my_arm.move_to_joint_positions(
- positions=cmd_joint_positions)
-
- # Generate a simple pose move +100mm in the +Z direction of the arm
- cmd_arm_pose = await my_arm.get_end_position()
- cmd_arm_pose.z += 100.0
- await my_arm.move_to_position(pose=cmd_arm_pose)
-
- # Don't forget to close the robot when you're done!
- await machine.close()
-
-if __name__ == '__main__':
- asyncio.run(main())
-```
-
-{{% snippet "show-secret.md" %}}
-
-{{% /tab %}}
-{{% tab name="Go" %}}
-
-```go {id="access-move-arm-go-ex" class="line-numbers linkable-line-numbers" data-line=""}
-package main
-
-import (
- "context"
-
- armapi "go.viam.com/api/component/arm/v1"
- "go.viam.com/rdk/logging"
- "go.viam.com/rdk/robot/client"
- "go.viam.com/rdk/components/arm"
- "go.viam.com/rdk/referenceframe"
- "go.viam.com/rdk/spatialmath"
- "go.viam.com/rdk/utils"
-)
-
-func main() {
- logger := logging.NewLogger("client")
- machine, err := client.New(
- context.Background(),
- "ADDRESS FROM THE VIAM APP",
- logger,
- client.WithDialOptions(utils.WithEntityCredentials(
- // Replace "" (including brackets) with your machine's API key ID
- "",
- utils.Credentials{
- Type: utils.CredentialsTypeAPIKey,
- // Replace "" (including brackets) with your machine's API key
- Payload: "",
- })),
- )
- if err != nil {
- logger.Fatal(err)
- }
- defer machine.Close(context.Background())
-
- logger.Info("Resources:")
- logger.Info(machine.ResourceNames())
-
- // Access myArm
- myArmComponent, err := arm.FromRobot(machine, "myArm")
- if err != nil {
- logger.Error(err)
- return
- }
-
- // End Position of myArm
- myArmReturnValue, err := myArmComponent.EndPosition(context.Background(), nil)
- if err != nil {
- logger.Error(err)
- return
- }
- logger.Infof("myArm EndPosition return value: %+v", myArmReturnValue)
-
- // Joint Positions of myArm
- myArmJointPositions, err := myArmComponent.JointPositions(context.Background(), nil)
- if err != nil {
- logger.Error(err)
- return
- }
- logger.Infof("myArm JointPositions return value:", myArmJointPositions)
-
- // Command a joint position move: small adjustment to the last joint
- cmdJointPositions := &armapi.JointPositions{Values: []float64{0.0, 0.0, 0.0, 0.0, 0.0, 15.0}}
- err = myArmComponent.MoveToJointPositions(context.Background(), cmdJointPositions, nil)
- if err != nil {
- logger.Error(err)
- return
- }
-
- // Generate a simple pose move +100mm in the +Z direction of the arm
- currentArmPose, err := myArmComponent.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 = myArmComponent.MoveToPosition(context.Background(), cmdArmPose, nil)
- if err != nil {
- logger.Error(err)
- return
- }
-}
-```
-
-{{% snippet "show-secret.md" %}}
-
-{{% /tab %}}
-{{< /tabs >}}
diff --git a/docs/tutorials/services/plan-motion-with-arm-gripper.md b/docs/tutorials/services/plan-motion-with-arm-gripper.md
index dbaa397b25..8d0e77b4c0 100644
--- a/docs/tutorials/services/plan-motion-with-arm-gripper.md
+++ b/docs/tutorials/services/plan-motion-with-arm-gripper.md
@@ -21,7 +21,7 @@ cost: 8400
no_list: true
---
-With Viam you can move individual components, like [arms](../accessing-and-moving-robot-arm/), by issuing commands like `MoveToPosition` or `MoveToJointPosition`.
+With Viam you can move individual components, like [arms](/components/arm/), by issuing commands like `MoveToPosition` or `MoveToJointPosition`.
The [motion service](/services/motion/) enables you to do much more sophisticated movement involving one or many components of your robot.
The service abstracts the lower-level commands away so that instead of passing in a series of joint positions, you can call the `Move()` command with the desired destination and any obstacles, and the service will move your machine to the desired location for you.
@@ -49,15 +49,13 @@ Before starting this tutorial, make sure you have the [Viam Python SDK](https://
If you are connecting to a real robotic arm during this tutorial, make sure your computer can communicate with the controller before continuing.
-Make sure you have mastery of the concepts outlined in the first motion tutorial, [Access and Move a Robot Arm](../accessing-and-moving-robot-arm/), before continuing.
+Make sure you have mastery of the concepts outlined in the first motion guide, [Access and Move a Robot Arm](/how-tos/move-robot-arm/), before continuing.
This tutorial picks up right where **Access and Move a Robot Arm** stops, so further examples depend on having a connected robot, client and service access, and other infrastructure in place.
This also helps simplify and shorten the code examples presented below.
-For a helpful recap of the code we previously added, look at [the full code sample from the prior tutorial](../accessing-and-moving-robot-arm/#full-tutorial-code).
-
## Configure a robot
-The [robot configuration from the prior tutorial](../accessing-and-moving-robot-arm/#configure-a-machine) should be used for this tutorial.
+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.
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.
diff --git a/layouts/docs/howto.html b/layouts/docs/howto.html
index cc16d8cad0..6aad18385e 100644
--- a/layouts/docs/howto.html
+++ b/layouts/docs/howto.html
@@ -76,6 +76,7 @@ Javascript
{{ partial "tutorialcard-no-js.html" (dict "link" "/how-tos/upload-module/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/how-tos/manage-modules/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/how-tos/navigate/") }}
+ {{ partial "tutorialcard-no-js.html" (dict "link" "/how-tos/move-robot-arm/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/how-tos/one-to-many/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/how-tos/provision-setup/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/how-tos/provision/") }}
diff --git a/layouts/docs/tutorials.html b/layouts/docs/tutorials.html
index ed2a973ced..b0d69c1c4e 100644
--- a/layouts/docs/tutorials.html
+++ b/layouts/docs/tutorials.html
@@ -89,7 +89,6 @@ Javascript
{{ partial "tutorialcard-no-js.html" (dict "link" "/tutorials/services/navigate-with-rover-base/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/tutorials/get-started/lazy-susan/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/tutorials/configure/build-a-mock-robot/") }}
- {{ partial "tutorialcard-no-js.html" (dict "link" "/tutorials/services/accessing-and-moving-robot-arm/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/tutorials/services/plan-motion-with-arm-gripper/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/tutorials/services/constrain-motion/") }}
{{ partial "tutorialcard-no-js.html" (dict "link" "/tutorials/services/color-detection-scuttle/") }}
diff --git a/static/how-tos/joint_positions.mp4 b/static/how-tos/joint_positions.mp4
new file mode 100644
index 0000000000..d4af9a6858
Binary files /dev/null and b/static/how-tos/joint_positions.mp4 differ
diff --git a/static/how-tos/joint_positions.webm b/static/how-tos/joint_positions.webm
new file mode 100644
index 0000000000..7cbbd898e5
Binary files /dev/null and b/static/how-tos/joint_positions.webm differ
diff --git a/static/how-tos/move_to_position.gif b/static/how-tos/move_to_position.gif
new file mode 100644
index 0000000000..5f1b29a8b0
Binary files /dev/null and b/static/how-tos/move_to_position.gif differ
diff --git a/static/how-tos/move_to_position.mp4 b/static/how-tos/move_to_position.mp4
new file mode 100644
index 0000000000..25d24674b6
Binary files /dev/null and b/static/how-tos/move_to_position.mp4 differ
diff --git a/static/how-tos/move_to_position.webm b/static/how-tos/move_to_position.webm
new file mode 100644
index 0000000000..a35f0aff39
Binary files /dev/null and b/static/how-tos/move_to_position.webm differ