teleimager 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ Copyright [2025] [YuShu TECHNOLOGY CO.,LTD ("Unitree Robotics")]
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
15
+ ------------------
16
+
17
+ This code builds upon following open-source code-bases. Please visit the URLs to see the respective LICENSES:
18
+
19
+ 1) https://github.com/ARCLab-MIT/beavr-bot
20
+
21
+ ------------------
@@ -0,0 +1,378 @@
1
+ Metadata-Version: 2.4
2
+ Name: teleimager
3
+ Version: 2.0.0
4
+ Summary: TeleImager: Unitree multi-camera image streaming service
5
+ Author-email: silencht <silencht@qq.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/unitreerobotics/teleimager
8
+ Project-URL: Repository, https://github.com/unitreerobotics/teleimager
9
+ Project-URL: Issues, https://github.com/unitreerobotics/teleimager/issues
10
+ Project-URL: Documentation, https://deepwiki.com/unitreerobotics/teleimager
11
+ Keywords: camera,streaming,webrtc,zeromq,robotics,teleoperation,uvc,v4l2,realsense,gstreamer
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Multimedia :: Video :: Capture
21
+ Classifier: Topic :: Scientific/Engineering
22
+ Requires-Python: <3.13,>=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: logging_mp>=0.2.2
26
+ Requires-Dist: numpy<2,>=1.21
27
+ Requires-Dist: pyyaml
28
+ Requires-Dist: pyzmq
29
+ Requires-Dist: PyTurboJPEG
30
+ Provides-Extra: viewer
31
+ Requires-Dist: opencv-python; extra == "viewer"
32
+ Provides-Extra: server
33
+ Requires-Dist: aiohttp<4.0,>=3.9; extra == "server"
34
+ Requires-Dist: aiortc<1.15.0,>=1.14.0; extra == "server"
35
+ Requires-Dist: av<17.0.0,>=15.0.0; extra == "server"
36
+ Requires-Dist: psutil; extra == "server"
37
+ Provides-Extra: uvc
38
+ Requires-Dist: teleimager[server]; extra == "uvc"
39
+ Requires-Dist: pupil-labs-uvc; extra == "uvc"
40
+ Provides-Extra: realsense
41
+ Requires-Dist: teleimager[server]; extra == "realsense"
42
+ Requires-Dist: pyrealsense2; extra == "realsense"
43
+ Provides-Extra: all
44
+ Requires-Dist: teleimager[realsense,server,uvc,viewer]; extra == "all"
45
+ Dynamic: license-file
46
+
47
+ <div align="center">
48
+ <img src="teleimager_logo.png" alt="Teleimager Logo" width="100%">
49
+ <a href="https://www.unitree.com/" target="_blank">
50
+ <img src="https://www.unitree.com/images/0079f8938336436e955ea3a98c4e1e59.svg" alt="Unitree LOGO" width="15%">
51
+ </a>
52
+ <p align="center">
53
+ <a> English </a> | <a href="README_zh-CN.md">δΈ­ζ–‡</a>
54
+ </p>
55
+ <p align="center">
56
+ <a href="https://github.com/unitreerobotics/xr_teleoperate/wiki" target="_blank"> <img src="https://img.shields.io/badge/GitHub-Wiki-181717?logo=github" alt="Unitree LOGO"></a> <a href="https://discord.gg/ZwcVwxv5rq" target="_blank"><img src="https://img.shields.io/badge/-Discord-5865F2?style=flat&logo=Discord&logoColor=white" alt="Unitree LOGO"> <a href="https://deepwiki.com/unitreerobotics/teleimager"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a> </a>
57
+ </p>
58
+ </div>
59
+ [**TeleImager**](https://github.com/unitreerobotics/teleimager) is Unitree's image service: a **server** captures video from multiple cameras (UVC, V4L2, GStreamer, RealSense) and publishes it over the network via **ZeroMQ** or **WebRTC**, while a **client** subscribes and decodes those streams. It powers the teleoperation video pipeline of [xr_teleoperate](https://github.com/unitreerobotics/xr_teleoperate).
60
+
61
+ ## ✨ Features
62
+
63
+ - πŸ“Έ Multiple UVC / V4L2 / GStreamer / Intel RealSense cameras
64
+ - πŸ“’ Publish frames over **ZeroMQ PUB-SUB** (high quality, LAN) and **WebRTC** (low latency, browser)
65
+ - πŸ’¬ Serve image-config commands over **ZeroMQ REQ-REP**
66
+ - πŸ†” Five camera identifiers: physical path, serial number, bcd_device, vid_pid, video path
67
+ - βš™οΈ Configurable resolution and frame rate
68
+ - πŸš€ Efficient frame handoff via a triple ring buffer
69
+
70
+ ---
71
+
72
+ ## 1. πŸ“¦ Installation
73
+
74
+ TeleImager has **two roles** β€” install only what you need:
75
+
76
+ | Role | What it does | Where it runs |
77
+ |------|--------------|---------------|
78
+ | **Client** | Subscribes and decodes streams (teleoperation, data recording, your own CV code) | Workstation / robot host |
79
+ | **Server** | Captures from cameras and publishes over ZMQ/WebRTC | The robot/device the cameras are plugged into |
80
+
81
+ ### 1.0 System prerequisite (both roles)
82
+
83
+ Both roles do JPEG encode/decode through **PyTurboJPEG**, a ctypes binding that loads the system library **libjpeg-turbo (3.0+ required)** at import. pip cannot install this native library β€” do it once per machine. Pick one:
84
+
85
+ **Method 1 β€” Conda (recommended, cross-platform)**
86
+
87
+ ```bash
88
+ conda install -c conda-forge libjpeg-turbo
89
+ ```
90
+
91
+ **Method 2 β€” Homebrew (macOS)**
92
+
93
+ ```bash
94
+ brew install jpeg-turbo
95
+ ```
96
+
97
+ <details>
98
+ <summary><b>Method 3 β€” Build from source (Ubuntu/Debian)</b></summary>
99
+ ```bash
100
+ git clone https://github.com/libjpeg-turbo/libjpeg-turbo.git
101
+ cd libjpeg-turbo && mkdir build && cd build
102
+ cmake -DCMAKE_INSTALL_PREFIX=/opt/libjpeg-turbo ..
103
+ make -j$(nproc) && sudo make install
104
+ echo 'export LD_LIBRARY_PATH=/opt/libjpeg-turbo/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
105
+ source ~/.bashrc
106
+ ```
107
+
108
+ </details>
109
+
110
+ > ⚠️ Do **not** install the PyPI package named `turbojpeg` β€” it is a different, incompatible library. This project uses `PyTurboJPEG` (already pulled in by `pip install teleimager`).
111
+ > Ubuntu's `sudo apt install libturbojpeg` is often older than 3.0, so it's not recommended. If the library is missing, TeleImager raises a detailed install hint equivalent to the above on first import.
112
+
113
+ ### 1.1 Environment (conda)
114
+
115
+ ```bash
116
+ # Install Miniconda (skip if already installed). Use the aarch64 installer on Jetson/ARM.
117
+ mkdir -p ~/miniconda3
118
+ wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/miniconda.sh
119
+ bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3 && rm ~/miniconda3/miniconda.sh
120
+ source ~/miniconda3/bin/activate && conda init --all
121
+
122
+ # Create and activate the project environment
123
+ conda create -n teleimager python=3.10 -y
124
+ conda activate teleimager
125
+ ```
126
+
127
+ ### 1.2 Client install
128
+
129
+ | You need | Command |
130
+ |----------|---------|
131
+ | **Headless** β€” subscribe ZMQ β†’ BGR `ndarray` (feed your own code) | `pip install teleimager` |
132
+ | **+ Viewer** β€” on-screen OpenCV windows | `pip install "teleimager[viewer]"` |
133
+
134
+ > The viewer pulls in `opencv-python`.
135
+
136
+ ### 1.3 Server install
137
+
138
+ Pick the driver(s) for your cameras, run one install command, then add certificates at the end if you use WebRTC.
139
+
140
+ #### 1.3.1 Choose your backend (camera driver)
141
+
142
+ A *backend* is the capture driver TeleImager uses to talk to a camera. They're lazily loaded, so unused ones cost nothing; start from the base (V4L2 + WebRTC) and stack on what you have:
143
+
144
+ | Backend (camera driver) | pip extra | Notes |
145
+ |---------|-----------|-------|
146
+ | Base (V4L2) | `[server]` | Always included, no extra needed |
147
+ | + UVC | `[uvc]` | Needs USB access β€” run [setup_uvc.sh](https://github.com/unitreerobotics/teleimager/blob/main/setup_uvc.sh); the wheel already bundles `libturbojpeg` and `libusb`, no apt needed |
148
+ | + RealSense | `[realsense]` | |
149
+ | + Everything (except GStreamer) | `[all]` | Equivalent to UVC + RealSense |
150
+ | + GStreamer | no pip package | `sudo apt install python3-gi python3-gst-1.0 gstreamer1.0-plugins-{base,good,bad}` |
151
+
152
+ #### 1.3.2 Install
153
+
154
+ Pick your backend (swap the `[server]` below for the matching extra), then choose one of two ways to install:
155
+
156
+ ```bash
157
+ # Option 1 β€” from source (recommended): also gets the helper scripts setup_uvc.sh / setup_autostart.sh and WebRTC assets
158
+ git clone https://github.com/unitreerobotics/teleimager.git
159
+ cd teleimager
160
+ pip install -e ".[server]" # e.g. ".[uvc]", ".[all]"
161
+
162
+ # Option 2 β€” from PyPI (when you don't need the helper scripts)
163
+ pip install "teleimager[server]" # e.g. "teleimager[uvc]", "teleimager[all]"
164
+ ```
165
+
166
+ #### 1.3.3 Configure TLS certificates (WebRTC only)
167
+
168
+ See the [CA guide](https://github.com/unitreerobotics/xr_teleoperate/wiki/CA) for details. Or generate a self-signed pair quickly with openssl:
169
+
170
+ ```bash
171
+ # Generate a self-signed cert valid for 365 days (cert.pem / key.pem)
172
+ openssl req -x509 -newkey rsa:2048 -nodes -days 365 -keyout key.pem -out cert.pem -subj "/CN=teleimager"
173
+ ```
174
+
175
+ Then let TeleImager find it, one of two ways:
176
+
177
+ ```bash
178
+ # Option A β€” default config dir (recommended, same dir as the server yaml)
179
+ mkdir -p ~/.config/teleimager/
180
+ cp cert.pem key.pem ~/.config/teleimager/
181
+
182
+ # Option B β€” point at any path on the CLI
183
+ teleimager-server --cert /path/to/cert.pem --key /path/to/key.pem
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 2. πŸš€ Running the Server
189
+
190
+ ### 2.1 Discover connected cameras
191
+
192
+ Scan with the backend flags matching the cameras you have (`--uvc`, `--v4l2`, `--gst`, `--rs` β€” combine freely):
193
+
194
+ ```bash
195
+ teleimager-server --cf --uvc --v4l2 # add --rs for RealSense, --gst for GStreamer
196
+ ```
197
+
198
+ Example output:
199
+
200
+ ```text
201
+ [Teleimager] 🎯 Camera Finder Report
202
+ β”œβ”€ UVCCamera (3 found) [type: uvc]
203
+ β”‚ β”œβ”€ πŸ“· USB HDR Camera (Generic)
204
+ β”‚ β”‚ β”œβ”€ physical_path : /sys/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3.1/1-3.1:1.0
205
+ β”‚ β”‚ β”œβ”€ serial_number : 200901010001
206
+ β”‚ β”‚ β”œβ”€ bcdDevice : 0200 (USB device release number)
207
+ β”‚ β”‚ β”œβ”€ vid : pid : 1e45:2050 (VendorID : ProductID)
208
+ β”‚ β”‚ β”œβ”€ video_id : 0 (/dev/video0)
209
+ β”‚ β”‚ └─ modes (MJPG) [width x height @ fps]:
210
+ β”‚ β”‚ β”œβ”€ 320x240 @ [30, 60]
211
+ β”‚ β”‚
212
+ β”‚ β”‚ └─ 1920x1080 @ [30, 60]
213
+ β”‚ β”œβ”€ πŸ“· Abham Image (HHWei Technology Co., Ltd.)
214
+ β”‚ β”‚ β”œβ”€ physical_path : /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0
215
+ β”‚ β”‚ β”œβ”€ serial_number : HHW001
216
+ β”‚ β”‚ β”œβ”€ bcdDevice : 0200 (USB device release number)
217
+ β”‚ β”‚ β”œβ”€ vid : pid : 1c45:6200 (VendorID : ProductID)
218
+ β”‚ β”‚ β”œβ”€ video_id : 10 (/dev/video10)
219
+ β”‚ β”‚ └─ modes (MJPG) [width x height @ fps]:
220
+ β”‚ β”‚ β”œβ”€ 640x480 @ [5, 10, 15, 20, 25, 30]
221
+ β”‚ β”‚ ...
222
+ β”‚ β”‚ └─ 2688x1520 @ [5, 10, 15, 20, 25, 30]
223
+ β”‚ └─ πŸ“· Cherry Dual Camera (DECXIN)
224
+ β”‚ β”œβ”€ physical_path : /sys/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3.2/1-3.2:1.0
225
+ β”‚ β”œβ”€ serial_number : 01.00.00
226
+ β”‚ β”œβ”€ bcdDevice : 0217 (USB device release number)
227
+ β”‚ β”œβ”€ vid : pid : 1bcf:2d4f (VendorID : ProductID)
228
+ β”‚ β”œβ”€ video_id : 2 (/dev/video2)
229
+ β”‚ └─ modes (MJPG) [width x height @ fps]:
230
+ β”‚ β”œβ”€ 320x232 @ [10, 15, 20, 25, 30, 60, 120]
231
+ β”‚ β”œβ”€ 640x240 @ [10, 15, 20, 25, 30, 60, 120]
232
+ β”‚ β”œβ”€ 800x592 @ [10, 15, 20, 25, 30, 60, 120]
233
+ β”‚ β”œβ”€ 800x600 @ [10, 15, 20, 25, 30, 60, 120]
234
+ β”‚ β”œβ”€ 1280x480 @ [10, 15, 20, 25, 30, 60, 120]
235
+ | ...
236
+ β”‚ └─ 3200x1296 @ [10, 15, 20, 25, 30, 60]
237
+ └─ V4L2Camera (4 found) [type: v4l2]
238
+ β”œβ”€ πŸ“· USB HDR Camera (Generic)
239
+ β”‚ β”œβ”€ physical_path : /sys/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3.1/1-3.1:1.0
240
+ β”‚ β”œβ”€ serial_number : 200901010001
241
+ β”‚ β”œβ”€ bcdDevice : 0200 (USB device release number)
242
+ β”‚ β”œβ”€ vid : pid : 1e45:2050 (VendorID : ProductID)
243
+ β”‚ β”œβ”€ video_id : 0 (/dev/video0)
244
+ β”‚ └─ modes [width x height @ fps]:
245
+ β”‚ β”œβ”€ MJPG 1920x1080 @ [60.0, 30.0]
246
+ ...
247
+ β”‚ └─ YUYV 640x480 @ [30.0]
248
+ β”œβ”€ πŸ“· Abham Image (HHWei Technology Co., Ltd.)
249
+ β”‚ β”œβ”€ physical_path : /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0
250
+ β”‚ β”œβ”€ serial_number : HHW001
251
+ β”‚ β”œβ”€ bcdDevice : 0200 (USB device release number)
252
+ β”‚ β”œβ”€ vid : pid : 1c45:6200 (VendorID : ProductID)
253
+ β”‚ β”œβ”€ video_id : 10 (/dev/video10)
254
+ β”‚ └─ modes [width x height @ fps]:
255
+ β”‚ β”œβ”€ MJPG 2688x1520 @ [30.0, 25.0, 20.0, 15.0, 10.0, 5.0]
256
+ ...
257
+ β”‚ └─ YUYV 640x480 @ [30.0]
258
+ β”œβ”€ πŸ“· Cherry Dual Camera (DECXIN)
259
+ β”‚ β”œβ”€ physical_path : /sys/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3.2/1-3.2:1.0
260
+ β”‚ β”œβ”€ serial_number : 01.00.00
261
+ β”‚ β”œβ”€ bcdDevice : 0217 (USB device release number)
262
+ β”‚ β”œβ”€ vid : pid : 1bcf:2d4f (VendorID : ProductID)
263
+ β”‚ β”œβ”€ video_id : 2 (/dev/video2)
264
+ β”‚ └─ modes [width x height @ fps]:
265
+ β”‚ β”œβ”€ MJPG 3200x1296 @ [60.0, 30.0, 25.0, 20.0, 15.0, 10.0]
266
+ ...
267
+ β”‚ β”œβ”€ YUYV 640x240 @ [60.0, 30.0, 25.0, 20.0, 15.0, 10.0]
268
+ β”‚ └─ YUYV 320x232 @ [120.0, 60.0, 30.0, 25.0, 20.0, 15.0, 10.0]
269
+ └─ πŸ“· Intel(R) RealSense(TM) Depth Camera 435i (Intel(R) RealSense(TM) Depth Camera 435i)
270
+ β”œβ”€ physical_path : /sys/devices/pci0000:00/0000:00:14.0/usb1/1-11/1-11.2/1-11.2:1.3
271
+ β”œβ”€ serial_number : (none)
272
+ β”œβ”€ bcdDevice : 50d0 (USB device release number)
273
+ β”œβ”€ vid : pid : 8086:0b3a (VendorID : ProductID)
274
+ β”œβ”€ video_id : 8 (/dev/video8)
275
+ └─ modes [width x height @ fps]:
276
+ β”œβ”€ YUYV 424x240 @ [60.0, 30.0, 15.0, 6.0]
277
+ β”œβ”€ YUYV 640x480 @ [30.0, 15.0, 6.0]
278
+ β”œβ”€ YUYV 1280x720 @ [15.0, 10.0, 6.0]
279
+ └─ YUYV 1920x1080 @ [8.0]
280
+ ```
281
+
282
+ ### 2.2 Start the server
283
+
284
+ On first run the server writes a default config to `~/.config/teleimager/teleimager_server.yaml`.
285
+
286
+ Edit it to match the Camera Finder Report from section 2.1, then start the service:
287
+
288
+ ```bash
289
+ teleimager-server
290
+ ```
291
+
292
+ Useful flags: `--config <path>` (or `$TELEIMAGER_CONFIG`) to point at another config; `--no-affinity` to skip CPU-core pinning; `--isaacsim` to run in IsaacSim mode (frames from shared memory).
293
+
294
+ ### 2.3 Auto-start on boot
295
+
296
+ Once everything is verified, you can optionally install it as a [boot service](https://github.com/unitreerobotics/teleimager/blob/main/setup_autostart.sh):
297
+
298
+ ```bash
299
+ bash setup_autostart.sh # follow the prompts
300
+ ```
301
+
302
+ ---
303
+
304
+ ## 3. πŸ“Ί Running the Client
305
+
306
+ ### 3.1 Over ZMQ
307
+
308
+ With the server running, start the client on another terminal (or machine), pointing at the server's IP:
309
+
310
+ ```bash
311
+ teleimager-client --host 192.168.123.164 # default: 192.168.123.164
312
+ ```
313
+
314
+ Each camera stream opens in its own OpenCV window (requires `teleimager[viewer]`).
315
+
316
+ For headless subscription, follow the `# public api` of `TeleImageClient` in [`client.py`](src/teleimager/client.py) and subscribe from your own code.
317
+
318
+ ### 3.2 Over WebRTC
319
+
320
+ Open the server's WebRTC page in a browser and click the **start** button in the center of the page:
321
+
322
+ ```text
323
+ https://<host_ip>:<webrtc_port> # e.g. https://192.168.123.164:60001
324
+ ```
325
+
326
+ ---
327
+
328
+ ## 4. 🧠 Design Principles
329
+
330
+ ### 4.1 Why five camera identifiers?
331
+
332
+ With several cameras connected at once, the system needs a reliable way to tell them apart. TeleImager supports five identifiers, resolved in priority order:
333
+
334
+ `physical_path > serial_number > bcd_device > vid_pid > video_id`
335
+
336
+ Each has trade-offs, and manufacturers use these fields inconsistently β€” a field's "standard meaning" in the USB spec often differs from the value the vendor's firmware actually writes. The table below reflects real-world behavior.
337
+
338
+ | Identifier | What it is | 🎯 Strength | ⚠️ Weakness |
339
+ |------------|-----------|------------|-------------|
340
+ | **physical_path** | Kernel-assigned USB topology path β€” which physical port it's plugged into | Stable while the port is unchanged; independent of firmware; ideal for fixed rigs (head + wrists) | Must update config if moved to another port |
341
+ | **serial_number** | String in the USB descriptor, meant to be unique per unit | Portable across ports; the canonical per-unit device ID | Cheap cameras may share it, leave it empty, or malform it |
342
+ | **bcd_device** | BCD firmware-revision number; some vendors vary it per unit | Hardcoded in firmware, survives reboots and port changes; can act as a serial number when the vendor intends it | Often identical across all units of a model |
343
+ | **vid_pid** | 16-bit vendor:product IDs | Very stable; distinguishes models at a glance | Same-model units usually share one vid_pid |
344
+ | **video_id** β†’ `/dev/videoX` | Kernel-enumerated V4L2 node number | Simplest: just fill in the number `X` | Changes with plug order / reboot / enumeration β€” unreliable with more than one camera |
345
+
346
+ > The granularity of `bcd_device` and `vid_pid` is entirely up to the vendor. Always run `teleimager-server --cf` to see the actual values before choosing a field.
347
+
348
+ ### 4.2 Why two transport methods?
349
+
350
+ The server serves two purposes with different latency/bandwidth needs:
351
+
352
+ - **ZeroMQ PUB–SUB** β€” transport over LAN. High-quality frames, low overhead, high throughput, low latency without sacrificing quality. Best for **recording training data**.
353
+ - **WebRTC** β€” **real-time preview**, VR teleoperation, UI debugging. Low latency with adaptive bitrate, H.264 (default) / VP8, compatible with browsers and VR devices.
354
+
355
+ ### 4.3 Triple ring buffer benefits
356
+
357
+ - **No tearing** β€” read and write never touch the same slot, so the reader never reads a half-written frame.
358
+ - **Always fresh** β€” unlike a FIFO queue, stale frames are overwritten, so the reader always gets the newest frame. Critical for real-time use.
359
+
360
+ ---
361
+
362
+ ## 5. 🧐 FAQ
363
+
364
+ 1. **Serial number / other fields show `unknown` in `--cf` output?**
365
+ Some cameras need elevated permissions to expose full hardware metadata. Try:
366
+
367
+ ```bash
368
+ sudo $(which teleimager-server) --cf --uvc
369
+ ```
370
+
371
+ **Server reports `No module named 'psutil'` on start?**
372
+ `psutil` (used for optional CPU-affinity tuning) ships with `teleimager[server]`. If a partial install left it missing, add it with `pip install psutil`, or reinstall via `pip install -e ".[server]"`. Missing it now only logs a warning and skips the optimization instead of crashing.
373
+
374
+ ---
375
+
376
+ ## 6. πŸ™ Acknowledgement
377
+
378
+ Some code references: https://github.com/ARCLab-MIT/beavr-bot