Skip to content

Commit 08c9473

Browse files
Merge pull request #87 from restackio/bostonSpot
add boston dynamics repo
2 parents 2998b97 + b163e35 commit 08c9473

File tree

15 files changed

+456
-0
lines changed

15 files changed

+456
-0
lines changed

bostondynamics_spot/.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
ROBOT_IP=
3+
SPOT_USERNAME=
4+
SPOT_PASSWORD=
5+
6+
RESTACK_ENGINE_ID=
7+
RESTACK_ENGINE_ADDRESS=
8+
RESTACK_ENGINE_API_KEY=

bostondynamics_spot/README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Restack AI SDK - Boston Dynamics Spot Example
2+
3+
This repository contains a simple example project to help you get started with the Restack AI SDK.
4+
5+
It demonstrates how to control Boston Dynamics Spot robot through a basic workflow and functions using the SDK.
6+
7+
## Prerequisites
8+
9+
- Python 3.8 or higher
10+
- Poetry (for dependency management)
11+
- Docker (for running the Restack services)
12+
13+
## Usage
14+
15+
1. Run Restack local engine with Docker:
16+
17+
```bash
18+
docker run -d --pull always --name restack -p 5233:5233 -p 6233:6233 -p 7233:7233 ghcr.io/restackio/restack:main
19+
```
20+
21+
2. Open the web UI to see the workflows:
22+
23+
```bash
24+
http://localhost:5233
25+
```
26+
27+
3. Clone this repository:
28+
29+
```bash
30+
git clone https://github.com/restackio/examples-python
31+
cd examples-python/examples/bostondynamics_spot
32+
```
33+
34+
4. Install dependencies using Poetry:
35+
36+
```bash
37+
poetry env use 3.12
38+
```
39+
40+
```bash
41+
poetry shell
42+
```
43+
44+
```bash
45+
poetry install
46+
```
47+
48+
```bash
49+
poetry env info # Optional: copy the interpreter path to use in your IDE (e.g. Cursor, VSCode, etc.)
50+
```
51+
52+
5. Run the services:
53+
54+
```bash
55+
poetry run services
56+
```
57+
58+
This will start the Restack service with the defined workflows and functions.
59+
60+
6. In a new terminal, schedule the workflow:
61+
62+
```bash
63+
poetry shell
64+
```
65+
66+
```bash
67+
poetry run schedule
68+
```
69+
70+
This will schedule the `GreetingWorkflow` and print the result.
71+
72+
7. Optionally, schedule the workflow to run on a specific calendar or interval:
73+
74+
```bash
75+
poetry run calendar
76+
```
77+
78+
```bash
79+
poetry run interval
80+
```
81+
82+
## Project Structure
83+
84+
- `src/`: Main source code directory
85+
- `client.py`: Initializes the Restack client
86+
- `functions/`: Contains function definitions
87+
- `workflows/`: Contains workflow definitions
88+
- `services.py`: Sets up and runs the Restack services
89+
- `schedule_workflow.py`: Example script to schedule and run a workflow

bostondynamics_spot/pyproject.toml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Project metadata
2+
[tool.poetry]
3+
name = "get_started"
4+
version = "0.0.1"
5+
description = "A simple example to get started with the restack-ai SDK"
6+
authors = [
7+
"Restack Team <[email protected]>",
8+
]
9+
readme = "README.md"
10+
packages = [{include = "src"}]
11+
12+
[tool.poetry.dependencies]
13+
python = "^3.9"
14+
restack-ai = "^0.0.40"
15+
bosdyn-client = "^4.1.0"
16+
opencv-python = "^4.10.0.84"
17+
setuptools = "^75.6.0"
18+
19+
[tool.poetry.dev-dependencies]
20+
pytest = "^6.2" # Optional: Add if you want to include tests in your example
21+
22+
# Build system configuration
23+
[build-system]
24+
requires = ["poetry-core"]
25+
build-backend = "poetry.core.masonry.api"
26+
27+
# CLI command configuration
28+
[tool.poetry.scripts]
29+
services = "src.services:run_services"
30+
spot = "src.spot_services:run_services"
31+
schedule = "schedule_workflow:run_schedule_workflow"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import asyncio
2+
import time
3+
from src.client import client
4+
5+
async def main():
6+
7+
workflow_id = f"{int(time.time() * 1000)}-SpotWorkflow"
8+
run_id = await client.schedule_workflow(
9+
workflow_name="SpotWorkflow",
10+
workflow_id=workflow_id
11+
)
12+
13+
await client.get_workflow_result(
14+
workflow_id=workflow_id,
15+
run_id=run_id
16+
)
17+
18+
exit(0)
19+
20+
def run_schedule_workflow():
21+
asyncio.run(main())
22+
23+
if __name__ == "__main__":
24+
run_schedule_workflow()

bostondynamics_spot/src/__init__.py

Whitespace-only changes.

bostondynamics_spot/src/client.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os
2+
from restack_ai import Restack
3+
from restack_ai.restack import CloudConnectionOptions
4+
from dotenv import load_dotenv
5+
6+
# Load environment variables from a .env file
7+
load_dotenv()
8+
9+
10+
engine_id = os.getenv("RESTACK_ENGINE_ID")
11+
address = os.getenv("RESTACK_ENGINE_ADDRESS")
12+
api_key = os.getenv("RESTACK_ENGINE_API_KEY")
13+
14+
connection_options = CloudConnectionOptions(
15+
engine_id=engine_id,
16+
address=address,
17+
api_key=api_key
18+
)
19+
client = Restack(connection_options)

bostondynamics_spot/src/functions/__init__.py

Whitespace-only changes.

bostondynamics_spot/src/functions/bostondynamics/__init__.py

Whitespace-only changes.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import os
2+
import time
3+
from .spot_controller import SpotController
4+
import cv2
5+
from dotenv import load_dotenv
6+
from restack_ai.function import function, log
7+
8+
load_dotenv()
9+
10+
def capture_image():
11+
camera_capture = cv2.VideoCapture(0)
12+
rv, image = camera_capture.read()
13+
print(f"Image Dimensions: {image.shape}")
14+
camera_capture.release()
15+
16+
@function.defn()
17+
async def spot_commands():
18+
#example of using micro and speakers
19+
print("Start recording audio")
20+
sample_name = "aaaa.wav"
21+
cmd = f'arecord -vv --format=cd --device={os.environ["AUDIO_INPUT_DEVICE"]} -r 48000 --duration=10 -c 1 {sample_name}'
22+
print(cmd)
23+
os.system(cmd)
24+
print("Playing sound")
25+
os.system(f"ffplay -nodisp -autoexit -loglevel quiet {sample_name}")
26+
27+
28+
ROBOT_IP = os.getenv['ROBOT_IP']
29+
SPOT_USERNAME = os.getenv['SPOT_USERNAME']
30+
SPOT_PASSWORD = os.getenv['SPOT_PASSWORD']
31+
32+
# # Capture image
33+
34+
# Use wrapper in context manager to lease control, turn on E-Stop, power on the robot and stand up at start
35+
# and to return lease + sit down at the end
36+
with SpotController(username=SPOT_USERNAME, password=SPOT_PASSWORD, robot_ip=ROBOT_IP) as spot:
37+
38+
time.sleep(2)
39+
capture_image()
40+
# Move head to specified positions with intermediate time.sleep
41+
spot.move_head_in_points(yaws=[0.2, 0],
42+
pitches=[0.3, 0],
43+
rolls=[0.4, 0],
44+
sleep_after_point_reached=1)
45+
capture_image()
46+
time.sleep(3)
47+
48+
# Make Spot to move by goal_x meters forward and goal_y meters left
49+
spot.move_to_goal(goal_x=0.5, goal_y=0)
50+
time.sleep(3)
51+
capture_image()
52+
53+
# Control Spot by velocity in m/s (or in rad/s for rotation)
54+
spot.move_by_velocity_control(v_x=-0.3, v_y=0, v_rot=0, cmd_duration=2)
55+
capture_image()
56+
time.sleep(3)
57+

0 commit comments

Comments
 (0)