Skip to content

Add sequential workshop tutorial format (pick-and-place seed)#5104

Open
Nick Hehr (HipsterBrown) wants to merge 44 commits into
mainfrom
workshop-format-pick-and-place
Open

Add sequential workshop tutorial format (pick-and-place seed)#5104
Nick Hehr (HipsterBrown) wants to merge 44 commits into
mainfrom
workshop-format-pick-and-place

Conversation

@HipsterBrown

@HipsterBrown Nick Hehr (HipsterBrown) commented Jun 24, 2026

Copy link
Copy Markdown
Member

Summary

Introduces a reusable, multi-page sequential workshop format in the existing docs/tutorials/ section, seeded by a vision-guided pick-and-place workshop. Everything ships as draft: true.

  • New shortcodes: checkpoint (verify-before-continuing callout) and workshop-nav (renders "Phase N of M" + prev/next from frontmatter). Both inline their CSS once per page via the existing notice-style Scratch guard.
  • Landing page: layouts/docs/tutorials.html now renders one card per workshop (via workshop_overview) and excludes the individual phase pages from the card grid.
  • Pick-and-place content: fully authored overview (_index.md) and a fully authored exemplar phase (03-static-positions.md); phases 01/02/04/05 are structured stubs; plus a reusable headless _phase-template.md.
  • .htmltest.yml ignores the not-yet-created companion repo so link-checking stays green.

Scope (v1)

The reusable format end to end + overview + one fully-written exemplar phase + structured stubs. Intentionally out of scope (each flagged inline as a TODO):

  • companion repo viam-devrel/pick-and-place (machine fragment, starter/reference scripts)
  • hardware-setup how-to guide
  • full authoring of phases 01/02/04/05
  • reconciling the Phase 3 obstacle JSON with the real motion-service schema
  • hardware overview photo
  • excluding phase pages from the Typesense (production) card index

Verification

  • prettier --check, markdownlint, and vale all clean (0 vale errors/warnings/suggestions across 7 files).
  • Hugo dev build renders the overview + all 5 phases; the tutorials landing shows exactly 1 workshop card and 0 phase cards; checkpoint + workshop-nav render; the prev/next chain (01 → 02 → 03 → 04 → 05) resolves end to end with clean slug URLs.
  • make build-prod was not run locally (production PostCSS pipeline could not run in the authoring environment) — relying on CI for the production build.

Test plan

  • CI / Netlify production build passes
  • /tutorials/ shows a single "Pick-and-Place Workshop" card (note: staging/prod card grid is Typesense-driven; phase-page exclusion there is a tracked follow-up)
  • /tutorials/pick-and-place/ overview and Phase 3 render; checkpoints styled; prev/next work; sidebar lists phases 1→5
  • _phase-template.md does not render as a page

Review the tutorial pages here: https://deploy-preview-5104--viam-docs.netlify.app/tutorials/pick-and-place/

Preview:
image

image image

🤖 Generated with Claude Code

@netlify

netlify Bot commented Jun 24, 2026

Copy link
Copy Markdown

Deploy Preview for viam-docs ready!

Name Link
🔨 Latest commit 2a45b04
🔍 Latest deploy log https://app.netlify.com/projects/viam-docs/deploys/6a46ccad7249e61497924d3f
😎 Deploy Preview https://deploy-preview-5104--viam-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 37 (🔴 down 8 from production)
Accessibility: 100 (no change from production)
Best Practices: 100 (no change from production)
SEO: 92 (no change from production)
PWA: 60 (🔴 down 10 from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@viambot viambot added the safe to build This pull request is marked safe to build from a trusted zone label Jun 24, 2026
@HipsterBrown Nick Hehr (HipsterBrown) force-pushed the workshop-format-pick-and-place branch from f9c42ec to c324bc2 Compare June 26, 2026 13:51
Nick Hehr (HipsterBrown) and others added 27 commits July 2, 2026 15:53
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138T5LK4cgYNgn3X54V7iZq
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138T5LK4cgYNgn3X54V7iZq
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138T5LK4cgYNgn3X54V7iZq
…gation

The tutorials sidebar is a minimal, per-section-cached skeleton that does not
render a section tree, so workshop phases were not navigable from phase pages.
Add a workshop-phases shortcode (full phase list, current highlighted) at the
top of each phase page and the template.
Take the overview and five phase pages out of Hugo draft mode so they render in
the Netlify deploy preview (which builds without --buildDrafts). The headless
_phase-template.md stays draft. Note: phase-page exclusion from the production
Typesense card grid remains a tracked follow-up.
The overview Phases list and the Prerequisites Phase 1 references were plain
text, so there was no way to start the workshop from the overview. Link each
phase to its page.
Populate the previously-empty Site.Menus.main by adding a menu.main entry to
the tutorials section front matter. Hugo associates the entry with the page,
so the navbar resolves the href to /tutorials/ and applies the active class on
every /tutorials/... page. The sidebar (toc_hide) and footer are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138T5LK4cgYNgn3X54V7iZq
Creates docs/tutorials/all/_index.md and layouts/docs/tutorials-all.html
so the Typesense filter UI and dev archive fallback are available at the
new /tutorials/all/ URL, without touching the existing /tutorials/ landing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZUbnKMDUmYy1iRkxkpjyz
Resolve the Tutorials top-nav item rendering white-on-white by raising the
project rule specificity above Bootstrap's .navbar-dark nav-link color (covers
base, active, hover, focus). Align Tutorials next to the brand with a separator
matched to the brand divider height, absolutely center the search input in the
navbar, and remove the nav-link hover state to match the static brand link.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZUbnKMDUmYy1iRkxkpjyz
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZUbnKMDUmYy1iRkxkpjyz
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZUbnKMDUmYy1iRkxkpjyz
…hive link

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZUbnKMDUmYy1iRkxkpjyz
…ghter workshop scope

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZUbnKMDUmYy1iRkxkpjyz
Create layouts/partials/sidebar.html as a project override that branches
on .Params.workshop: workshop pages get a targeted sidebar-workshop.html
partial listing all phases with the current one highlighted; all other
pages fall through to a verbatim copy of the Docsy default sidebar logic,
preserving existing behaviour site-wide.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZUbnKMDUmYy1iRkxkpjyz
…ase numbers

Restore the else-branch jQuery in sidebar.html to match Docsy's default
byte-for-byte (active-path walks from #{{ $mid }}, not #{{ $mid }}-li),
and remove the redundant {{ add $i 1 }}. prefix in sidebar-workshop.html
since phase linkTitles already include their numbers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZUbnKMDUmYy1iRkxkpjyz
…version note

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZUbnKMDUmYy1iRkxkpjyz

- **The Viam cloud app** is the source of truth for configuration. When you add a component, change an attribute, or wire up a service, you are editing a JSON document stored in the cloud. The app never runs your robot directly; it describes what should run.
- **viam-agent** runs on the machine itself. It manages the install: it installs, updates, and keeps `viam-server` running, restarts it if it crashes, and provides the initial bootstrap credentials viam-server needs to reach the cloud. Think of viam-agent as the process supervisor, not as the source of your resource config, and not as something you interact with directly during this workshop.
- **viam-server** is the process that does the actual work. It pulls its resource config from the cloud app, starts every component and service that config describes, and exposes them over an API. This is the layer that drives the arm, reads the camera, and runs the vision pipeline.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- **viam-server** is the process that does the actual work. It pulls its resource config from the cloud app, starts every component and service that config describes, and exposes them over an API. This is the layer that drives the arm, reads the camera, and runs the vision pipeline.
- **viam-server** pulls the machine resource configuration from the cloud app, starts every component and service that configuration describes, and exposes the components and services over an API. This is the layer that handles the modules that drive the arm, reads the camera, and runs the vision pipeline.


## How your SDK connects

In Phase 4 you write a Python script that imports the Viam SDK and connects to your machine. That connection goes to `viam-server`, not to the cloud app. The cloud app helps your script locate the machine and authenticate, but once the connection is established, every control API call goes directly to `viam-server` on the robot: moving the arm, reading the camera, calling the vision service.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better not to reference phase 4 here, and instead just focus on the api?


This is why the workshop asks you to make changes in the app rather than by editing a file on the robot directly. The app's CONFIGURE tab is the only place you need to look to know what a machine will do.

Open the **CONFIGURE** tab now and find the JSON view toggle near the top of the panel. Switching to JSON shows you the exact document that `viam-server` receives, the same resources you see as cards in the builder view, expressed as the config it consumes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Open the **CONFIGURE** tab now and find the JSON view toggle near the top of the panel. Switching to JSON shows you the exact document that `viam-server` receives, the same resources you see as cards in the builder view, expressed as the config it consumes.
Open the **CONFIGURE** tab now and find the JSON view toggle near the top of the panel. Switching to JSON shows you the exact document that `viam-server` receives, the same resources you see as cards in the builder view.


Everything a Viam machine does, hardware and software alike, is modeled as a **resource**. Each resource has a name you choose (like `arm-1`), an API that describes what kind of thing it is (an arm, a camera, a vision service), and a model that identifies the specific implementation.

Open the **CONFIGURE** tab and find `arm-1`. Next to its name you will see a triplet in the form `namespace:family:model`, for example `viam:ufactory:xArm6`. That triplet tells `viam-server` exactly which code to run for this resource: who published it (`namespace`), what family of hardware it belongs to (`family`), and the specific model (`model`).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use module instead of family?

- **Python 3.10 or newer.** Install it with [uv](https://docs.astral.sh/uv/getting-started/installation/) (recommended) or from [python.org](https://www.python.org/downloads/).
- **The Viam Python SDK.** The companion `scripts/` project already declares `viam-sdk`, so `uv run` installs it for you in Phase 4. See the [Python SDK docs](https://python.viam.dev/) for reference. Pip works too if you prefer it.
- **A working terminal** on the machine you will run the Phase 4 and Phase 5 scripts from, typically your laptop rather than the robot's Meerkat.
- **A Viam account with an accessible machine.** Log in at [app.viam.com](https://app.viam.com), open your machine, and confirm the green **Live** indicator before you begin.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add something about accessing the machine with the resources in the machine config?

Resources split into two kinds:

- **Components** represent physical hardware. `arm-1`, `gripper-1`, and `cam-1` are all components: each one wraps a piece of hardware and exposes a standard API for it (an arm API with move commands, a camera API that returns images, and so on).
- **Services** represent software capabilities that consume other resources rather than hardware directly. A motion service plans collision-free paths for an arm. Later, in Phase 5, you configure a `shape-detector` vision service that reads frames from `cam-1` and finds blocks by shape, and a `vision-segment` service (model `detections-to-segments`) that takes those detections and turns them into point cloud segments the motion planner can grasp. Neither service is a physical thing; each one composes other resources into a new capability.

@btshrewsbury-viam btshrewsbury-viam Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- **Services** represent software capabilities that consume other resources rather than hardware directly. A motion service plans collision-free paths for an arm. Later, in Phase 5, you configure a `shape-detector` vision service that reads frames from `cam-1` and finds blocks by shape, and a `vision-segment` service (model `detections-to-segments`) that takes those detections and turns them into point cloud segments the motion planner can grasp. Neither service is a physical thing; each one composes other resources into a new capability.
- **Services** represent software tasks or capabilities. A motion service plans collision-free paths for an arm. Later, in Phase 5, you configure a `shape-detector` vision service that reads frames from `cam-1` and finds blocks by shape, and a `vision-segment` service (model `detections-to-segments`) that takes those detections and turns them into point cloud segments the motion planner can grasp. Neither service is a physical thing; each one composes other resources into a new capability.

- **Components** represent physical hardware. `arm-1`, `gripper-1`, and `cam-1` are all components: each one wraps a piece of hardware and exposes a standard API for it (an arm API with move commands, a camera API that returns images, and so on).
- **Services** represent software capabilities that consume other resources rather than hardware directly. A motion service plans collision-free paths for an arm. Later, in Phase 5, you configure a `shape-detector` vision service that reads frames from `cam-1` and finds blocks by shape, and a `vision-segment` service (model `detections-to-segments`) that takes those detections and turns them into point cloud segments the motion planner can grasp. Neither service is a physical thing; each one composes other resources into a new capability.

Open the **CONTROL** tab and look at the cards laid out for your machine. Each card corresponds to one resource. The arm, gripper, and camera cards let you jog hardware directly; any service card lets you exercise a capability that is built on top of that hardware.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Open the **CONTROL** tab and look at the cards laid out for your machine. Each card corresponds to one resource. The arm, gripper, and camera cards let you jog hardware directly; any service card lets you exercise a capability that is built on top of that hardware.
Open the **CONTROL** tab and look at the cards laid out for your machine. Each card corresponds to one resource. The arm, gripper, and camera cards let you interact with hardware directly; any service card lets you exercise a capability that is built on top of that hardware.

Open the **CONTROL** tab and look at the cards laid out for your machine. Each card corresponds to one resource. The arm, gripper, and camera cards let you jog hardware directly; any service card lets you exercise a capability that is built on top of that hardware.

{{< alert title="Foreshadowing" color="note" >}}
You will not configure `shape-detector` or `vision-segment` until Phase 5. For now, just notice the pattern: a service is defined by what other resources it depends on, not by hardware it owns.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
You will not configure `shape-detector` or `vision-segment` until Phase 5. For now, just notice the pattern: a service is defined by what other resources it depends on, not by hardware it owns.
You will not configure `shape-detector` or `vision-segment` until Phase 5. For now, just notice the pattern: a service is defined by what resources it depends on or features it provides, not by hardware it owns.


The `viam:ufactory:xArm6` arm you saw in the CONFIGURE tab is module-provided. In Phase 2, the moment you add that arm to your config, `viam-server` recognizes it does not have `viam:ufactory:xArm6` built in, downloads the module that provides it from the Viam registry, and starts it. You will be able to watch that download and start happen live in the LOGS tab.

Open the **CONFIGURE** tab again and compare `arm-1`'s namespace to the namespace on any resource marked `rdk` (if you see one). A namespace of `rdk` means the model ships inside `viam-server` itself; any other namespace, like `viam` or `erh`, means the model arrives as a module.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if its not in the premade machine config, consider removing


<!-- ASSET P0 diagram-dependency-graph (DIAGRAM): cam-1 -> shape-detector -> vision-segment; arm-1 -> gripper-1 + five pose switches; motion service -->

Resources can depend on each other. A gripper attaches to an arm, so `gripper-1`'s config points at `arm-1`. A vision service reads from a camera, so it depends on `cam-1`. `viam-server` reads these dependencies out of your config and builds a graph, then starts resources in an order that respects it: a resource cannot start until everything it depends on has started.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Resources can depend on each other. A gripper attaches to an arm, so `gripper-1`'s config points at `arm-1`. A vision service reads from a camera, so it depends on `cam-1`. `viam-server` reads these dependencies out of your config and builds a graph, then starts resources in an order that respects it: a resource cannot start until everything it depends on has started.
Resources can depend on each other. A gripper attaches to an arm, so `gripper-1`'s config points at `arm-1`. A vision service reads from a camera, so it depends on `cam-1`. `viam-server` reads these dependencies out of your config and builds a dependency graph, then starts resources in an order that respects it: a resource cannot start until everything it depends on has started.

Open the **LOGS** tab and look at the startup sequence the next time the machine restarts (you do not need to trigger one now). The order resources come online in the log follows the dependency graph, not the order they appear in the config file.

{{< alert title="Check yourself" color="note" >}}
Before moving to Phase 2, make sure you can answer the three questions from the top of this page: name the three layers and which one your code talks to, what separates a component from a service, and why adding the arm triggers a module download. If any answer feels shaky, re-skim the relevant section above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably should swap out the third question

Before moving to Phase 2, make sure you can answer the three questions from the top of this page: name the three layers and which one your code talks to, what separates a component from a service, and why adding the arm triggers a module download. If any answer feels shaky, re-skim the relevant section above.
{{< /alert >}}

{{< workshop-nav >}}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might look prettier to have "You have completed The Mental Model, lets move on to Configuring Resources! instead of the generic next

- **Components** represent physical hardware. `arm-1`, `gripper-1`, and `cam-1` are all components: each one wraps a piece of hardware and exposes a standard API for it (an arm API with move commands, a camera API that returns images, and so on).
- **Services** represent software capabilities that consume other resources rather than hardware directly. A motion service plans collision-free paths for an arm. Later, in Phase 5, you configure a `shape-detector` vision service that reads frames from `cam-1` and finds blocks by shape, and a `vision-segment` service (model `detections-to-segments`) that takes those detections and turns them into point cloud segments the motion planner can grasp. Neither service is a physical thing; each one composes other resources into a new capability.

Open the **CONTROL** tab and look at the cards laid out for your machine. Each card corresponds to one resource. The arm, gripper, and camera cards let you jog hardware directly; any service card lets you exercise a capability that is built on top of that hardware.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Section 2 says the machine config should be empty, I think this implies students would have something to play with, should this move to section 2?

| `gripper-1` | `rdk:component:gripper` | `viam:ufactory:gripper` |
| `cam-1` | `rdk:component:camera` | `viam:camera:realsense` |

You will not touch the vision service in this phase. The `shape-detector` and `vision-segment` services that let the machine find blocks by shape come later, in Phase 5, right before you write the code that calls them. For now, focus on the hardware.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
You will not touch the vision service in this phase. The `shape-detector` and `vision-segment` services that let the machine find blocks by shape come later, in Phase 5, right before you write the code that calls them. For now, focus on the hardware.

<!-- ASSET P0 logs-xarm-module-start (UI+): LOGS showing the viam:ufactory module download + start (the module-download moment) -->
<!-- ASSET P1 configure-arm-attributes (UI): arm-1 card with host / port 502 -->

On the **CONFIGURE** tab, add a new component. In the add-component dialog, search for `xArm6` and select the `viam:ufactory:xArm6` result. Name the component `arm-1`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they renamed them "blocks" recently, + > Blocks -> search

<!-- ASSET P0 logs-xarm-module-start (UI+): LOGS showing the viam:ufactory module download + start (the module-download moment) -->
<!-- ASSET P1 configure-arm-attributes (UI): arm-1 card with host / port 502 -->

On the **CONFIGURE** tab, add a new component. In the add-component dialog, search for `xArm6` and select the `viam:ufactory:xArm6` result. Name the component `arm-1`.

@btshrewsbury-viam btshrewsbury-viam Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
On the **CONFIGURE** tab, add a new component. In the add-component dialog, search for `xArm6` and select the `viam:ufactory:xArm6` result. Name the component `arm-1`.
On the **CONFIGURE** tab, add a new component. In the add-component dialog, search for `xArm6` and select the `ufactory/xArm6` result and press Add to machine. Name the component `arm-1`.


You can leave `speed_degs_per_sec` and `acceleration_degs_per_sec_per_sec` unset for now; both are optional and default to safe values.

Save the config. This is the moment from Phase 1 made concrete: `viam-server` does not have `viam:ufactory:xArm6` built in, so it fetches the `viam:ufactory` module from the Viam registry and starts it. Open the **LOGS** tab and watch it happen: a log line for the module download, then one for the module starting, then `arm-1` itself coming online. The whole sequence usually takes well under a minute.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the "made concrete" sounds funny, but better phrasing is escaping me

Save the config. This is the moment from Phase 1 made concrete: `viam-server` does not have `viam:ufactory:xArm6` built in, so it fetches the `viam:ufactory` module from the Viam registry and starts it. Open the **LOGS** tab and watch it happen: a log line for the module download, then one for the module starting, then `arm-1` itself coming online. The whole sequence usually takes well under a minute.

{{< alert title="Two components, one module" color="note" >}}
The `viam:ufactory` module provides both the arm model and the gripper model you configure next. You only pay the download cost once.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `viam:ufactory` module provides both the arm model and the gripper model you configure next. You only pay the download cost once.
The `viam:ufactory` module provides both the arm model and the gripper model you configure next. Viam Server only has to download the module once to access both models.


<!-- ASSET P2 configure-gripper (UI): gripper-1 config with arm: "arm-1" -->

Add another component. In the add-component dialog, search for the `viam:ufactory:gripper` model and select it. This model comes from the same `viam:ufactory` module as the arm. Name it `gripper-1`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Add another component. In the add-component dialog, search for the `viam:ufactory:gripper` model and select it. This model comes from the same `viam:ufactory` module as the arm. Name it `gripper-1`.
Add another component. In the add-component dialog, search for the `ufactory/gripper` model and select it. This model comes from the same `viam:ufactory` module as the arm. Name it `gripper-1`.


<!-- ASSET P2 configure-camera (UI): cam-1 realsense config (sensors color+depth, 640x480) -->

Add a third component. In the add-component dialog, search for `realsense` and select the `viam:camera:realsense` result. Name it `cam-1`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Add a third component. In the add-component dialog, search for `realsense` and select the `viam:camera:realsense` result. Name it `cam-1`.
Add a third component. In the add-component dialog, search for `realsense` and select the `realsense/realsesne` result. Name it `cam-1`.

Add a third component. In the add-component dialog, search for `realsense` and select the `viam:camera:realsense` result. Name it `cam-1`.

Set the following attributes:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need the SN? or does it just grab the first one it finds if non exist?

On the camera card, confirm you get a live feed:

{{< checkpoint >}}
The camera card shows a live color stream from `cam-1`. If depth is available as a separate view, confirm that stream updates too. If the card is blank, check the LOGS tab for a camera error before moving on.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the depth should be there? can we word it differently?

The camera card shows a live color stream from `cam-1`. If depth is available as a separate view, confirm that stream updates too. If the card is blank, check the LOGS tab for a camera error before moving on.
{{< /checkpoint >}}

On the arm card, jog a joint with the sliders and confirm the arm moves. Then select **Get end position** and confirm it returns x, y, and z coordinates.

@btshrewsbury-viam btshrewsbury-viam Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs a warning that the arm is about to move and we need to check the estop, and i think joint sliders can cause collisions

With a block between the fingers, **Grab** closes the fingers and the gripper holds the block without dropping it. **Open** releases the block. This grab-and-release is the same action your Python code performs later in the workshop when it picks a block and drops it in a bin. If your gripper card also shows a holding status indicator, it now reads true while the block is held and false once the gripper is open and empty.
{{< /checkpoint >}}

## The 3D scene tab

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we skipped the frame part for these to connect to eachother

Set one attribute:

- `arm`: `"arm-1"`

@btshrewsbury-viam btshrewsbury-viam Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we need to add a frame connecting the gripper to the arm

- `width_px`: `640`
- `height_px`: `480`

Save the config and confirm in the **LOGS** tab that `viam-server` downloads the `viam:camera-realsense` module and starts `cam-1`. This is a separate module from `viam:ufactory`, since it comes from a different publisher and family.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we need to add a frame connecting the camera to the arm

Jog joint 1 with the arm card's sliders and watch the 3D scene as the arm turns. The `cam-1` frame moves with the arm, because the camera is mounted on the wrist rather than fixed in the workspace.

{{< alert title="Why this matters later" color="note" >}}
Because the camera is wrist-mounted, every detection it makes is relative to wherever the arm happens to be pointed at that moment. In Phase 5 you will detect from a single known pose so that "camera space" always means the same thing. Notice that pose here; you will meet it again as the "detect from home" rule.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think detection and camera space feel weird and might need to replaced with something slightly more clear for the reader?

Because the camera is wrist-mounted, every detection it makes is relative to wherever the arm happens to be pointed at that moment. In Phase 5 you will detect from a single known pose so that "camera space" always means the same thing. Notice that pose here; you will meet it again as the "detect from home" rule.
{{< /alert >}}

The exact camera mounting offset used to transform its frame into the arm's frame comes from your hardware setup guide or the pre-provisioned config, not from anything you configure in this phase. For now, just confirm the relationship: the frame exists, and it tracks the arm.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe consider moving this to later in perception guided picking
?

The exact camera mounting offset used to transform its frame into the arm's frame comes from your hardware setup guide or the pre-provisioned config, not from anything you configure in this phase. For now, just confirm the relationship: the frame exists, and it tracks the arm.

## Check your work

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe list what should be in the config and what should be seen in the 3d view before referencing the json. might be able to just render it here but pull from github so it always stays up to date


If you want to compare your configuration against a known-good version, the companion repo has a reference copy at [machine-fragment.json](https://github.com/viam-devrel/pick-and-place/blob/main/config/machine-fragment.json). Use it to check your work, not to import over what you just built by hand.

With `arm-1`, `gripper-1`, and `cam-1` all live and verified, you are ready for Phase 3, where you save a set of fixed arm poses and prove the full hardware sequence works before perception enters the picture.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
With `arm-1`, `gripper-1`, and `cam-1` all live and verified, you are ready for Phase 3, where you save a set of fixed arm poses and prove the full hardware sequence works before perception enters the picture.
With `arm-1`, `gripper-1`, and `cam-1` components all live and verified, you are ready for [Phase 3, where you move the arm through a set of arm poses](03-static-positions.md).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to build This pull request is marked safe to build from a trusted zone

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants