|
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> |
2 | 2 |
|
3 | 3 | # multiset-python |
4 | 4 |
|
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. |
7 | 10 |
|
8 | 11 | [Developer Platform](https://developer.multiset.ai) · |
9 | 12 | [Docs](https://docs.multiset.ai) · |
10 | 13 | [multiset.ai](https://multiset.ai) |
11 | 14 |
|
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. |
16 | 19 |
|
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. |
19 | 24 |
|
20 | | -```python |
21 | | -from multiset.unitree.go2 import Go2 |
22 | | -from multiset.vps import localize |
| 25 | +--- |
23 | 26 |
|
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 |
29 | 28 |
|
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` | |
31 | 33 |
|
32 | | -## Install |
| 34 | +More adapters will land alongside these. The core never changes when they do. |
33 | 35 |
|
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 |
36 | 37 |
|
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> |
40 | 42 |
|
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. |
43 | 45 |
|
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. |
48 | 48 |
|
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. |
50 | 52 |
|
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) |
55 | 59 | ``` |
56 | 60 |
|
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. |
59 | 62 |
|
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).** |
61 | 66 |
|
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 |
63 | 68 |
|
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> |
67 | 73 |
|
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. |
69 | 76 |
|
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) |
74 | 91 | ``` |
75 | 92 |
|
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`. |
77 | 103 |
|
78 | 104 | ## The core / adapter split |
79 | 105 |
|
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> |
84 | 110 |
|
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: |
86 | 113 |
|
87 | 114 | ```python |
88 | 115 | class FrameSource(Protocol): |
@@ -111,23 +138,20 @@ multiset-python |
111 | 138 | `multiset.*` distribution becomes unimportable with a `ModuleNotFoundError` that |
112 | 139 | points nowhere near the cause. CI and a unit test both guard it. |
113 | 140 |
|
114 | | -## Development |
| 141 | +## Working on this repo |
115 | 142 |
|
116 | 143 | ```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 |
121 | 145 | uv run pytest |
122 | 146 | ``` |
123 | 147 |
|
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 | | - |
127 | 148 | Editing `packages/vps/src/multiset/vps/frames.py` is live in the adapter |
128 | 149 | immediately — no version bump, no reinstall. That is the property a monorepo buys |
129 | 150 | and separate repos charge for. |
130 | 151 |
|
| 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 | + |
131 | 155 | ## Versioning and releases |
132 | 156 |
|
133 | 157 | Each package versions independently. Releases are driven entirely by **tag |
|
0 commit comments