Initial commit: CloudView generic networked point-cloud viewer
Standalone Open3D viewer that listens on a TCP/Unix socket and renders NDJSON point-cloud streams from any producer. Decoupled server/protocol/store layers (no Open3D dependency, testable headless) plus a lazy Open3D render loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
88
README.md
Normal file
88
README.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# CloudView
|
||||
|
||||
A small, **generic** point-cloud viewer. It listens on a socket, accepts
|
||||
newline-delimited JSON (NDJSON) from any number of producers, and renders the
|
||||
combined cloud in an interactive [Open3D](https://www.open3d.org/) window. It is
|
||||
not tied to any one robot: anything that can write JSON lines to a socket — a
|
||||
Crazyflie, a rover, a LIDAR rig, a log replay — gets live 3D visualization for
|
||||
free.
|
||||
|
||||
RangeView is the first producer (its 2D map stays the at-a-glance live
|
||||
diagnostic; the full 3D cloud is streamed here), but the wire format is the only
|
||||
contract, so other systems can feed the same viewer.
|
||||
|
||||
```
|
||||
producers (any language) viewer
|
||||
┌───────────────────────┐ NDJSON over ┌──────────────────────┐
|
||||
│ rangeview ───────────┼── tcp:// ────────▶ CloudView (Open3D) │
|
||||
│ rover-2 ───────────┼── unix:// ───────▶ - merges all sources │
|
||||
│ lidar-rig ───────────┼──────────────────▶ - colour by height │
|
||||
└───────────────────────┘ └──────────────────────┘
|
||||
```
|
||||
|
||||
## Install & run
|
||||
|
||||
```sh
|
||||
# with uv (resolves from the committed uv.lock)
|
||||
uv run main.py # listen on tcp://127.0.0.1:9870
|
||||
uv run main.py -l tcp://0.0.0.0:9870 # accept from other machines
|
||||
|
||||
# or a plain venv
|
||||
python -m venv .venv && . .venv/bin/activate
|
||||
pip install -r requirements.txt # open3d + numpy
|
||||
python -m cloudview
|
||||
```
|
||||
|
||||
Listen addresses (repeatable, TCP and/or Unix):
|
||||
|
||||
```sh
|
||||
uv run main.py -l tcp://0.0.0.0:9870 -l unix:///tmp/cloud.sock
|
||||
```
|
||||
|
||||
Viewer keys: **C** toggle colour (by height / per-source), **R** refit the
|
||||
camera, **B** cycle background, mouse to orbit/zoom/pan.
|
||||
|
||||
## Wire protocol (NDJSON)
|
||||
|
||||
One JSON object per line over a stream socket. `source` keys a distinct cloud,
|
||||
so one viewer shows several robots at once. Coordinates are floats in whatever
|
||||
frame the producer uses.
|
||||
|
||||
| message | fields | effect |
|
||||
|---------|--------|--------|
|
||||
| `hello` | `source`, `name` | announce/label a stream |
|
||||
| `points` | `source`, `points` (`[[x,y,z],...]`), `frame`, `color` (optional `[r,g,b]` 0..1) | append a batch |
|
||||
| `clear` | `source` | drop that source's cloud |
|
||||
| `pose` | `source`, `position` `[x,y,z]`, `yaw` (deg) | record robot pose |
|
||||
|
||||
Points are batched per message to keep overhead low. If a source supplies no
|
||||
`color`, the viewer colours its points by height; otherwise the given colour is
|
||||
used as a solid.
|
||||
|
||||
### Producing from any language
|
||||
|
||||
It is just JSON lines — test it with `nc`:
|
||||
|
||||
```sh
|
||||
printf '%s\n' \
|
||||
'{"type":"hello","source":"demo","name":"nc test"}' \
|
||||
'{"type":"points","source":"demo","points":[[0,0,0],[1,0,0.5],[0,1,1]]}' \
|
||||
| nc 127.0.0.1 9870
|
||||
```
|
||||
|
||||
Python producers can reuse RangeView's `cloud_publisher.CloudPublisher` (a
|
||||
non-blocking client with auto-reconnect and full-cloud replay), or just write
|
||||
`json.dumps(msg) + "\n"` to a socket.
|
||||
|
||||
## Layout
|
||||
|
||||
- `cloudview/protocol.py` - NDJSON encode/decode + address parsing.
|
||||
- `cloudview/store.py` - thread-safe per-source cloud store.
|
||||
- `cloudview/server.py` - TCP/Unix socket server; one reader thread per producer.
|
||||
- `cloudview/viewer.py` - Open3D render loop (height/per-source colouring).
|
||||
- `cloudview/app.py` - CLI entry point.
|
||||
|
||||
Networking and rendering are decoupled: the server fills the store from any
|
||||
producer; the viewer polls the store and rebuilds geometry only when it changes.
|
||||
The server/protocol layers have no Open3D dependency, so they are testable
|
||||
headless.
|
||||
Reference in New Issue
Block a user