Skip to content

Commit 1f98a2b

Browse files
authored
Merge pull request #3007 from annietllnd/gtc-strands
GTC strands final edits
2 parents ccc864f + 8fee23e commit 1f98a2b

9 files changed

Lines changed: 76 additions & 80 deletions

File tree

assets/contributors.csv

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,5 @@ Asier Arranz,NVIDIA,,asierarranz,,asierarranz.com
118118
Prince Agyeman,Arm,,,,
119119
Parichay Das,,parichaydas,parichaydas,,
120120
Johnny Nunez,NVIDIA,johnnynunez,johnnycano,,
121-
Raymond Lo,NVIDIA,raymondlo84,raymondlo84,,
121+
Raymond Lo,NVIDIA,raymondlo84,raymondlo84,,
122+
Kavya Sri Chennoju,Arm,kavya-chennoju,kavya-sri-chennoju,,

content/learning-paths/embedded-and-microcontrollers/device-connect-strands/_index.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ learning_objectives:
1616
- Discover and invoke the robot using the Device Connect agent tools and the robot_mesh Strands tool
1717

1818
prerequisites:
19-
- A development machine with Python 3.12 installed
20-
- Git installed
21-
- Basic familiarity with Python virtual environments and command-line tools
19+
- A development machine with git installed
20+
- Basic familiarity with command-line tools
2221
- (Optional) A Raspberry Pi for testing a full device-to-device (D2D) setup
2322

2423
author:
@@ -48,11 +47,15 @@ further_reading:
4847
type: website
4948
- resource:
5049
title: Strands robots repository
51-
link: https://github.com/strands-labs/robots/tree/dev
50+
link: https://github.com/strands-labs/robots
5251
type: website
5352
- resource:
54-
title: Device Connect integration guide
55-
link: https://github.com/atsyplikhin/robots/blob/feat/device-connect-integration-draft/strands_robots/device_connect/GUIDE.md
53+
title: device-connect-agent-tools on PyPI
54+
link: https://pypi.org/project/device-connect-agent-tools
55+
type: website
56+
- resource:
57+
title: device-connect-sdk on PyPI
58+
link: https://pypi.org/project/device-connect-sdk
5659
type: website
5760

5861
### FIXED, DO NOT MODIFY

content/learning-paths/embedded-and-microcontrollers/device-connect-strands/background.md

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,57 +6,39 @@ weight: 2
66
layout: learningpathall
77
---
88

9-
## Why connect AI agents to edge devices?
9+
## Physical AI starts with connectivity
1010

11-
Arm processors are at the heart of a remarkable range of systems - from Cortex-M microcontrollers in industrial sensors to Neoverse servers running in the cloud. That breadth of hardware is one of Arm's greatest strengths, but it raises a practical question for AI developers: how do you give an agent structured, safe access to devices that are physically distributed and built on different software stacks?
11+
Arm processors are at the heart of a remarkable range of systems, from Cortex-M microcontrollers in industrial sensors to Neoverse servers running in the cloud. That breadth of hardware is one of Arms greatest strengths, but it raises a practical question for AI developers: how do you give an agent structured, safe access to devices that are physically distributed and built on different software stacks?
1212

13-
Device Connect is Arm's answer to that question. It's a platform layer that handles device registration, discovery, and remote procedure calls across a network of devices, with no bespoke networking code required. Strands is an open-source agent SDK from AWS that takes a model-driven approach to building AI agents - an LLM calls Python tools in a structured reasoning loop, and the SDK handles the rest. When you combine them, an agent can ask "which devices are online and what can they do?" and then invoke a function on a specific device, turning natural language intent into physical action.
13+
Natural language is becoming more than a software interface. Physical AI systems can now sense, decide, and act based on instructions from an LLM agent. But for that to work, the devices need to be reachable. They need a shared infrastructure for discovery, communication, and coordination.
1414

15-
This Learning Path puts both tools through their paces. It starts with a single machine, for example a laptop, where a simulated robot and an agent discover each other automatically, then extends to a two-machine setup where a Raspberry Pi joins the same device mesh over the network.
15+
[Device Connect](https://github.com/arm/device-connect) is Arm's device-aware framework for exactly that. Once devices register through a shared mesh, agents can discover and command any of them without caring where they run. A fleet of robot arms, a network of sensors, or a mix of physical and simulated devices all become equally reachable.
1616

17-
## Device Connect architecture layers
17+
[Strands Robots](https://github.com/strands-labs/robots) is a robot SDK that integrates Device Connect with the [AWS Strands Agents SDK](https://strandsagents.com/). Using Strands, an LLM can query the device mesh ("who's available?"), understand what each device can do, and dynamically invoke actions - turning natural language intent into real-world outcomes.
1818

19-
**Device layer**
19+
This Learning Path starts on a single machine, where a simulated robot and an agent discover each other automatically, then optionally extends to a Raspberry Pi joining the same device mesh over the network.
2020

21-
A device is any process that registers itself on the mesh and exposes callable functions. In this Learning Path you'll create a simulated robot arm, namely the simulated robotic arm SO-100 from Hugging Face, from the `strands-robots` SDK. The moment this object is created, it registers on the local network under a unique device ID (for example, `so100_sim-abc23`) and begins publishing a presence heartbeat. No explicit registration call is required. Device Connect uses Zenoh as its underlying messaging transport, which handles low-level connectivity and routing automatically.
21+
## How the pieces fit together
2222

23-
**Agent layer**
23+
Two packages make this work.
2424

25-
Two interfaces sit at this layer. The `device-connect-agent-tools` package exposes `discover_devices()` and `invoke_device()` as plain Python functions you can call directly from a script or REPL, with no LLM involved. The `robot_mesh` tool from `robots` wraps the same capabilities as a Strands agent tool, which means an LLM can also call them during a reasoning loop. Both share the same underlying Device Connect transport, so anything you can do with one you can do with the other.
25+
**`device-connect-sdk`** is the device-side runtime. Any process that wraps a robot in a `DeviceDriver` and starts a `DeviceRuntime` joins the mesh: it registers itself, announces what it can do, and starts publishing state events. Zenoh handles low-level connectivity; in device-to-device mode no broker or environment configuration is needed.
2626

27-
The diagram below shows how these layers communicate at runtime:
27+
**`device-connect-agent-tools`** is the agent-side runtime. It exposes `discover_devices()` and `invoke_device()` as plain Python functions. The `robot_mesh` tool in Strands Robots wraps the same interface as a Strands tool, so an LLM can call it too. Both use the same underlying transport, so the same calls work whether the caller is a script or an agent.
2828

29-
```
30-
┌──────────────────────────────────────┐
31-
│ Agent layer │
32-
│ discover_devices · invoke_device │
33-
│ robot_mesh Strands tool │
34-
└──────────────┬───────────────────────┘
35-
│ Device Connect
36-
│ (device-to-device discovery & RPC)
37-
┌──────────────▼───────────────────────┐
38-
│ Device layer │
39-
│ Simulated SO-100 arm |
40-
| - so100-abc123 │
41-
│ heartbeat · execute · getStatus │
42-
└──────────────────────────────────────┘
43-
```
29+
![Architecture diagram showing two tiers: the agent layer containing discover_devices, invoke_device, and the robot_mesh Strands tool, connected via a Device Connect Zenoh D2D arrow to the device layer containing the SO-100 robot process with its peer ID and RPC functions#center](./images/visual1.png "Device Connect architecture: agent layer and device layer connected over Zenoh D2D")
4430

45-
## How device discovery works
31+
## What a device exposes
4632

47-
When the `SO-100 arm` instance starts, Device Connect automatically announces the device on the local network. Any process running `discover_devices()` or `robot_mesh(action='peers')` on the same network will hear the announcement and add the device to its live table of available hardware.
33+
This Learning Path uses the SO-100 arm, an open-source robot arm. When `Robot('so100').run()` starts, it registers on the mesh and exposes three callable functions. These are what `invoke_device()` on the agent side targets — calling `invoke_device("so100-abc123", "execute", {...})` routes a request over Zenoh to the robot process and executes the function there, returning the result back to the caller:
4834

49-
## What the simulated robot provides
35+
- `execute` — send a natural language instruction and a policy provider to the robot
36+
- `getStatus` — query what the robot is currently doing
37+
- `stop` — halt the current task, or `emergency_stop` to halt every device on the mesh at once
5038

51-
When you run `Robot('so100')`, the SDK downloads the MuJoCo physics model for the SO-100 arm (this happens once on first run) and starts a local simulation. The robot exposes three functions that any agent can call via RPC:
39+
A motion policy is the component that translates a high-level instruction like "pick up the cube" into a sequence of joint movements. Different policy providers connect to different backends — from local model inference to remote policy servers. For this Learning Path, `policy_provider='mock'` is used, so `execute` accepts the task and returns immediately without running real motion. Replacing `'mock'` with a real provider like `'lerobot_local'` or `'groot'` is a one-line change once you have the connectivity working.
5240

53-
- `execute` - start a task with a given instruction and policy provider
54-
- `getStatus` - query the current task state
55-
- `stop` - halt the current task
56-
57-
For this Learning Path, the `policy_provider='mock'` argument is used, which means `execute` accepts the call and returns `{'status': 'accepted'}` without actually running a motion policy. This keeps the focus on the connectivity and invocation patterns rather than robotics.
58-
59-
Once you have the flow working end to end, replacing `'mock'` with a real policy is a one-line change.
41+
Beyond discrete RPC calls, devices can also publish a continuous stream of sensor data over the same mesh. A camera publishes image frames, a depth sensor publishes point clouds, and an IMU reports pose updates — all as Device Connect events that any subscriber on the network receives in real time. The simulated robot in this Learning Path publishes joint state and observation updates at 10 Hz.
6042

6143
## What you'll learn in this Learning Path
6244

41.4 KB
Loading
36.9 KB
Loading
63 KB
Loading

content/learning-paths/embedded-and-microcontrollers/device-connect-strands/run-example.md

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This section runs Device Connect's device-to-device discovery. There are two way
1212

1313
### Option 1: run on a single machine
1414

15-
For a proof-of-concept, follow the steps using two terminal windows on your machine with the virtual environment set up.
15+
For a conceptual implementation, follow the steps using two terminal windows on your machine with the virtual environment set up.
1616

1717
### Option 2: run with real hardware
1818

@@ -45,12 +45,24 @@ python <<'PY'
4545
import logging
4646
logging.basicConfig(level=logging.INFO)
4747
from strands_robots import Robot
48-
r = Robot('so100')
48+
r = Robot('so100', peer_id='so100-abc123')
4949
r.run()
5050
PY
5151
```
5252

53-
When the `Robot('so100')` object is created, the SDK downloads the MuJoCo physics model for the SO-100 arm. This download happens only on the first run and takes a minute or two. After that, it starts the simulation and registers the robot on the Device Connect device mesh. The robot publishes a presence heartbeat every 0.5 seconds under a unique device ID, for example `so100-abc123`.
53+
Two things happen when this script runs.
54+
55+
**`Robot('so100')`** calls the factory function, which checks for USB servo hardware and, finding none, creates a MuJoCo `Simulation` instance. On the first run it downloads the SO-100 MJCF physics model from Hugging Face — this can take up to 20 minutes depending on your connection. Subsequent runs use the local cache and start immediately.
56+
57+
**`r.run()`** calls `init_device_connect_sync()`, which does the following:
58+
59+
1. Creates a `SimulationDeviceDriver` — a Device Connect `DeviceDriver` adapter that wraps the simulation and maps its methods to structured RPCs.
60+
2. Starts a `DeviceRuntime` with the Zenoh D2D backend. No broker or environment variables are needed; devices discover each other on the LAN via Zenoh multicast scouting.
61+
3. Subscribes the device to its command topic: `device-connect.default.<PEER_ID>.cmd`.
62+
4. Registers RPC handlers: `execute`, `getStatus`, `getFeatures`, `step`, `reset`, and `stop`.
63+
5. Starts a 10Hz background loop that emits `stateUpdate` and `observationUpdate` events to any listener on the mesh.
64+
65+
![Sequence diagram showing the call flow when r.run() executes: the script calls Robot() and DeviceRuntime, which announces presence to the Zenoh mesh, subscribes to the command topic, registers RPC handlers, and starts emitting stateUpdate events at 10 Hz#center](./images/visual2.png "Call flow inside r.run(): device registration and event publishing over Zenoh")
5466

5567
You should see INFO-level log output similar to:
5668

@@ -64,6 +76,17 @@ device_connect_sdk.device.so100-abc123 - INFO - Subscribed to commands on device
6476

6577
Leave this process running. The simulated robot is only discoverable as long as this process is alive.
6678

79+
## Open a second terminal
80+
81+
Leave terminal 1 running with the robot process. Open a new terminal window, navigate to the repository, and activate the virtual environment:
82+
83+
```bash
84+
cd ~/strands-device-connect/robots
85+
source .venv/bin/activate
86+
```
87+
88+
Run all remaining commands in this section from this second terminal.
89+
6790
## Control the robot using the robot_mesh Strands tool
6891

6992
The `robot_mesh` tool wraps the same discovery and invocation primitives as a Strands agent tool. You can call it directly from a Python script or attach it to an LLM agent; the API is identical either way.
@@ -79,6 +102,8 @@ print(robot_mesh(action='peers'))
79102
PY
80103
```
81104

105+
When this runs, `robot_mesh` calls `_ensure_connected()`, which sets `MESSAGING_BACKEND=zenoh` (if the environment variable is not already set) and opens the agent-side Zenoh connection via `device_connect_agent_tools`. It then calls `conn.list_devices()`, which queries the Zenoh network for all registered Device Connect devices. Each device registers its `device_id`, `device_type`, availability from its `DeviceStatus`, and the list of RPC functions it exposes. The tool formats this into the human-readable summary below.
106+
82107
The output is similar to:
83108

84109
```output
@@ -105,6 +130,8 @@ print(robot_mesh(
105130
PY
106131
```
107132

133+
Under the hood, `robot_mesh` calls `conn.invoke(target, "execute", params)`, which serializes the arguments and routes them over Zenoh to the device's command topic: `device-connect.default.<PEER_ID>.cmd`. The `SimulationDeviceDriver.execute()` RPC handler on the robot side receives the call, resolves the robot name inside the simulation world, and calls `sim.start_policy(instruction=..., policy_provider='mock', ...)`. With the mock policy provider, the handler returns immediately with a success result without running real motion — the call is a connectivity and RPC round-trip test. The `stateUpdate` events you'll see in terminal 1 are published by the separate 10Hz background loop that was started by `r.run()`.
134+
108135
You will see the following output:
109136

110137
```output
@@ -135,6 +162,8 @@ print(robot_mesh(action='emergency_stop'))
135162
PY
136163
```
137164

165+
This doesn't send a single broadcast message. Instead, `robot_mesh` first calls `conn.list_devices()` to enumerate every device currently on the mesh, then calls `conn.invoke(device_id, "stop", timeout=3.0)` on each one in sequence. On the device side, `SimulationDeviceDriver.stop()` sets `policy_running = False` for every robot in the simulation world and returns immediately. Failures per device are swallowed so that a single unresponsive device doesn't block the rest from stopping.
166+
138167
The output is similar to:
139168

140169
```output

0 commit comments

Comments
 (0)