Skip to content

Commit c7b0b89

Browse files
docs: rewrite the READMEs with diagrams
1 parent ea941eb commit c7b0b89

13 files changed

Lines changed: 2237 additions & 159 deletions

‎README.md‎

Lines changed: 85 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,115 @@
1-
<a href="https://multiset.ai"><img src="docs/img/multiset-logo.png" alt="MultiSet" height="56"></a>
1+
<a href="https://multiset.ai"><img src="docs/img/multiset-logo.png" alt="MultiSet" height="52"></a>
22

33
# multiset-python
44

5-
MultiSet visual positioning for Python. One repo, several packages: a
6-
device-agnostic VPS core, and one adapter per robot or camera.
5+
**The MultiSet Python SDKs.** Work out where a camera is from a single picture,
6+
anywhere you have already mapped with MultiSet.
7+
8+
No GPS, no markers on the walls, no beacons, no extra hardware. You send a
9+
photo; you get back where it was taken and which way the camera was facing.
710

811
[Developer Platform](https://developer.multiset.ai) ·
912
[Docs](https://docs.multiset.ai) ·
1013
[multiset.ai](https://multiset.ai)
1114

12-
| package | `import` | what it owns |
13-
|---|---|---|
14-
| [`multiset-vps`](packages/vps/) | `multiset.vps` | VPS client, `Pose`, frame conventions, rectification |
15-
| [`multiset-unitree-go2`](packages/unitree-go2/) | `multiset.unitree.go2` | WebRTC transport, front camera, discovery, Go2 calibration |
15+
That is useful wherever GPS cannot reach — indoors, underground, in a warehouse.
16+
It is how you snap a drifting robot back onto the truth, how a machine that has
17+
just booted works out where it is, and how you record where something you
18+
photographed actually stands.
1619

17-
One mapping rule, no exceptions: **every hyphen after `multiset` becomes a
18-
dot.** `multiset-unitree-go2` → `multiset.unitree.go2`.
20+
**You will need a MultiSet account first.** Sign up at the
21+
[Developer Platform](https://developer.multiset.ai), create a map, and keep your
22+
**client ID**, **client secret** and **map code** to hand — each package README
23+
shows where they go.
1924

20-
```python
21-
from multiset.unitree.go2 import Go2
22-
from multiset.vps import localize
25+
---
2326

24-
with Go2.from_env() as dog:
25-
pose = localize(dog) # the same call for every device
26-
if pose:
27-
print(pose.x, pose.y, pose.yaw_deg, pose.confidence)
28-
```
27+
# The packages
2928

30-
---
29+
| | package | install as | import as |
30+
|---|---|---|---|
31+
| **1** | [the core](packages/vps/README.md) | `multiset-vps` | `multiset.vps` |
32+
| **2** | [Unitree Go2](packages/unitree-go2/README.md) | `multiset-unitree-go2` | `multiset.unitree.go2` |
3133

32-
## Install
34+
More adapters will land alongside these. The core never changes when they do.
3335

34-
**Only need poses from images you already have?** `multiset-vps` stands alone —
35-
three dependencies, no robot, no renderer, nothing to install first:
36+
## 1 · `multiset-vps` — positioning, for any camera
3637

37-
```bash
38-
pip install "multiset-vps @ git+https://github.com/MultiSet-AI/multiset-python@vps-v0.1.0#subdirectory=packages/vps"
39-
```
38+
<picture>
39+
<source media="(prefers-color-scheme: dark)" srcset="docs/img/vps-dark.svg">
40+
<img src="docs/img/vps-light.svg" alt="A picture you already have, plus your lens numbers, is matched against your map by MultiSet VPS, and the place it was taken appears in the point cloud." width="100%">
41+
</picture>
4042

41-
**Working with a Unitree Go2?** Install PortAudio first — the adapter depends on
42-
`pyaudio`, which has no Linux or macOS wheel and builds from source:
43+
**What it is for.** Turning a picture into a position. This is the whole product
44+
in one function call.
4345

44-
```bash
45-
sudo apt-get install -y portaudio19-dev # Debian/Ubuntu, incl. Jetson
46-
brew install portaudio # macOS
47-
```
46+
**What you give it.** An image, and the four numbers that describe your lens.
47+
Nothing else. It has never heard of robots.
4848

49-
Then both packages, in one command:
49+
**What you get back.** A `Pose` — `x`, `y`, `z`, a heading, and a confidence. If
50+
the map does not recognise the picture, you get a `Pose` that is simply empty,
51+
not an exception.
5052

51-
```bash
52-
pip install \
53-
"multiset-vps @ git+https://github.com/MultiSet-AI/multiset-python@vps-v0.1.0#subdirectory=packages/vps" \
54-
"multiset-unitree-go2 @ git+https://github.com/MultiSet-AI/multiset-python@unitree-go2-v0.1.0#subdirectory=packages/unitree-go2"
53+
```python
54+
from multiset.vps import localize_image
55+
56+
pose = localize_image(image, my_lens_numbers)
57+
if pose:
58+
print(pose.x, pose.y, pose.yaw_deg)
5559
```
5660

57-
Both lines are required: pip does not follow a git URL to find the core, so the
58-
adapter's `multiset-vps>=0.1,<0.2` has to be named explicitly.
61+
Three dependencies, no hardware.
5962

60-
## Getting credentials
63+
**Everything else — installing it, configuring it, worked examples, the frame
64+
conventions and the full API — is in
65+
[packages/vps/README.md](packages/vps/README.md).**
6166

62-
You need a MultiSet account and a map before any of this returns a pose.
67+
## 2 · `multiset-unitree-go2` — for the Unitree Go2
6368

64-
1. Sign up at the [Developer Platform](https://developer.multiset.ai)
65-
2. Create a map — you get a **map code** like `MAP_XXXXXXXXXX`
66-
3. Copy your **client ID** and **client secret**
69+
<picture>
70+
<source media="(prefers-color-scheme: dark)" srcset="docs/img/go2-dark.svg">
71+
<img src="docs/img/go2-light.svg" alt="A Unitree Go2 photographs the room over WebRTC; MultiSet VPS matches the frame against your map; the robot's position appears inside the point cloud." width="100%">
72+
</picture>
6773

68-
Put all three in `.env` (see [.env.example](.env.example)):
74+
**What it is for.** Getting that same position out of a Unitree Go2, without you
75+
writing any of the hard parts.
6976

70-
```bash
71-
MULTISET_CLIENT_ID=...
72-
MULTISET_CLIENT_SECRET=...
73-
MULTISET_MAP_CODE=MAP_XXXXXXXXXX
77+
**What it does for you.** Finds the robot on your network, connects over WebRTC,
78+
pulls a frame from the front camera, straightens out the fisheye using this
79+
robot's own measured calibration, and localizes it.
80+
81+
**What you get back.** The same `Pose` — plus `dog.base_pose()` if you want the
82+
robot's position rather than the lens's.
83+
84+
```python
85+
from multiset.unitree.go2 import Go2
86+
87+
with Go2.from_env() as dog:
88+
pose = dog.localize()
89+
if pose:
90+
print(pose.x, pose.y, pose.yaw_deg, pose.confidence)
7491
```
7592

76-
API reference: [docs.multiset.ai](https://docs.multiset.ai).
93+
Builds on the core.
94+
95+
**Everything else — installing it, finding the robot, the AES key, the seven
96+
steps and the things that will cost you an afternoon — is in
97+
[packages/unitree-go2/README.md](packages/unitree-go2/README.md).**
98+
99+
---
100+
101+
One naming rule, no exceptions: **every hyphen after `multiset` becomes a dot.**
102+
`multiset-unitree-go2` imports as `multiset.unitree.go2`.
77103

78104
## The core / adapter split
79105

80-
The VPS half of this is not robot-specific: the API client, the pose type, the
81-
Y-up→Z-up conversion, the raw-vs-rectified intrinsics distinction. The transport
82-
half is entirely device-specific. So the family is **one core plus N adapters**,
83-
not N independent SDKs.
106+
<picture>
107+
<source media="(prefers-color-scheme: dark)" srcset="docs/img/packages-dark.svg">
108+
<img src="docs/img/packages-light.svg" alt="multiset-unitree-go2 or your own camera code supply a frame; multiset-vps turns it into a pose." width="100%">
109+
</picture>
84110

85-
The contract between them is one method:
111+
One core plus N adapters, not N independent SDKs. The contract between them is
112+
one method:
86113

87114
```python
88115
class FrameSource(Protocol):
@@ -111,23 +138,20 @@ multiset-python
111138
`multiset.*` distribution becomes unimportable with a `ModuleNotFoundError` that
112139
points nowhere near the cause. CI and a unit test both guard it.
113140

114-
## Development
141+
## Working on this repo
115142

116143
```bash
117-
git clone https://github.com/MultiSet-AI/multiset-python
118-
cd multiset-python
119-
uv sync --all-extras # every package editable, one venv
120-
uv run python -c "import multiset.vps, multiset.unitree.go2; print('ok')"
144+
uv sync --all-extras # both packages editable in one venv
121145
uv run pytest
122146
```
123147

124-
Python 3.10–3.12 is what CI runs. Newer works too, minus the `viz` extra:
125-
open3d has no wheel past 3.12, so `--all-extras` resolves without the viewer.
126-
127148
Editing `packages/vps/src/multiset/vps/frames.py` is live in the adapter
128149
immediately — no version bump, no reinstall. That is the property a monorepo buys
129150
and separate repos charge for.
130151

152+
Python 3.10–3.12 is what CI runs. Prerequisites, the namespace rule and the
153+
pre-PR checklist are in [CONTRIBUTING.md](CONTRIBUTING.md).
154+
131155
## Versioning and releases
132156

133157
Each package versions independently. Releases are driven entirely by **tag

0 commit comments

Comments
 (0)