07339c3374
启动时完整连接所有配置相机,移除旧命令与单路数据格式。通过 22 项回归测试、真实 SDK 的 1–4 路录像验证、Ruff 和文档检查。
146 lines
7.0 KiB
Markdown
146 lines
7.0 KiB
Markdown
# UART finger service
|
|
|
|
Start the service with an explicit list of 1-4 camera sources. It opens and
|
|
warms up **every source first**, then opens the data UART and starts publishing.
|
|
If any source fails to prepare, all opened sources are closed and the process
|
|
exits with status 1. No partial camera set is published.
|
|
|
|
## Connect the cameras and start once
|
|
|
|
Plug the USB cameras into the Q6A. In its TTL login shell, identify their actual
|
|
capture device paths before launching; one USB camera can expose more than one
|
|
`/dev/video*` node, so do not assume every node represents a different camera.
|
|
For example, `v4l2-ctl --list-devices` shows device groups. Select one usable
|
|
capture node per camera. Use the board environment prepared by `setup/sdk_setup.sh`;
|
|
it needs Python 3.10+, the `orisys` SDK and `pyserial`.
|
|
|
|
Run from the board's `dragon-q6a` directory. These paths are examples, not detected
|
|
hardware identities:
|
|
|
|
```bash
|
|
# One camera.
|
|
python serial/example_uart.py --port /dev/ttyHS1 --sources /dev/video0
|
|
|
|
# Two cameras.
|
|
python serial/example_uart.py --port /dev/ttyHS1 --sources /dev/video0 /dev/video2
|
|
|
|
# Three cameras.
|
|
python serial/example_uart.py --port /dev/ttyHS1 --sources /dev/video0 /dev/video2 /dev/video4
|
|
|
|
# Four cameras.
|
|
python serial/example_uart.py --port /dev/ttyHS1 --sources /dev/video0 /dev/video2 /dev/video4 /dev/video6
|
|
```
|
|
|
|
The first source is sensor 1, the second sensor 2, and so on. The number of listed
|
|
sources determines the sensor count. Camera indices such as `--sources 0 2` are
|
|
also accepted. Sources must be distinct. A shared `--config finger` is the
|
|
default; repeat `--config` once per source to use different configurations.
|
|
The login console prints each sensor's source/config mapping during preparation.
|
|
There is no UART connection command: physical connection and source selection
|
|
happen before startup, and software connection happens automatically.
|
|
|
|
The TTL login console and data UART must use **different ports**. The data
|
|
UART defaults to `/dev/ttyHS1`, 115200 baud, 8N1 without flow control. It must
|
|
not be occupied by getty or another serial program. This script does not enable
|
|
UART pins or configure the board's hardware. Logs stay in the login console;
|
|
protocol records go to the data UART. `--show` is optional desktop debugging.
|
|
|
|
For a passive receiver, append `--read-only` to the same startup command. Startup
|
|
and output are identical, but incoming UART commands are discarded. Exit using
|
|
Ctrl-C in the login shell. The receiver should filter `force,` records because
|
|
startup and failures also produce status records.
|
|
|
|
## One protocol for every sensor count
|
|
|
|
After all sources are ready, the data UART reports `OK ready N`, then immediately
|
|
streams each complete processing round in source order. Every measurement has
|
|
five CSV fields, including a sensor ID even when only one camera is configured:
|
|
|
|
```text
|
|
OK ready 2
|
|
force,1,1.250000,-2.500000,3.750000
|
|
force,2,0.500000,0.100000,-0.200000
|
|
```
|
|
|
|
The fields are `force,sensor_id,FNORMAL,FSHEARX,FSHEARY`. Axes follow the SDK;
|
|
units depend on calibration and must not be assumed to be Newtons. CSV carries
|
|
no timestamp or units. Frames are processed sequentially, not synchronized
|
|
across cameras. All inputs for a round must succeed before that round is
|
|
published. A transport failure can still interrupt transmission between lines.
|
|
|
|
Control mode accepts only four commands, each acting on the entire camera set:
|
|
|
|
| Command | Result |
|
|
|---|---|
|
|
| `stop` | Pause continuous output and reply `OK stop`; keep cameras open. |
|
|
| `start` | Resume continuous output and reply `OK start`. |
|
|
| `read` | Process and publish one complete round; preserve streaming state on success. |
|
|
| `quit` | Reply `OK quit`, close all cameras and UART, then exit. |
|
|
|
|
Commands take no arguments. They are case-insensitive UTF-8 lines terminated by
|
|
LF, CR or CRLF. Partial lines survive read timeouts; commands exceeding 1024
|
|
bytes are discarded through their terminator. Unknown commands or arguments
|
|
produce `ERR`. The former protocol and source-selection options have been
|
|
removed; receivers must use this single CSV format and explicit `--sources`.
|
|
|
|
A capture or processing failure produces `ERR read sensor ID: ...` and pauses
|
|
output for the whole set. Measurements are never replaced with zeros. Sources
|
|
remain open; `start` retries processing, or restart the service to reopen them.
|
|
For recorded files, `--no-loop-video` reports `OK eof sensor ID: video ended`
|
|
and pauses the whole set at EOF. Otherwise, one rewind and tracker reset is
|
|
attempted per read. Ctrl-C always ends the process from the login console.
|
|
|
|
## Callable acquisition API
|
|
|
|
`serial/finger_api.py` has no UART dependency and opens no resources on import.
|
|
Put its directory on the caller's module search path. Use the same connection-first
|
|
lifecycle as the service:
|
|
|
|
```python
|
|
from finger_api import FingerSensorBank
|
|
|
|
bank = FingerSensorBank([0, 2], configs=["finger"])
|
|
try:
|
|
bank.connect_all()
|
|
for reading in bank.read_all():
|
|
print(reading.sensor_id, reading.normal, reading.shear_x, reading.shear_y, reading.units)
|
|
finally:
|
|
bank.close()
|
|
```
|
|
|
|
One thread owns the bank. `connect_all()` rolls back every opened runtime on
|
|
failure. `read_all()` returns a tuple only when every source succeeds; it raises
|
|
`EOFError` for file EOF and `RuntimeError` for capture/processing failure. Each
|
|
reading contains the unrolled image, three scalar forces and SDK units from its
|
|
own runtime. Older SDKs without a units property report `unknown`. Reconnection
|
|
requires closing the bank before calling `connect_all()` again.
|
|
|
|
## Backend evidence and remaining limits
|
|
|
|
The SDK's `FingerRuntime.open()` constructs a separate capture object,
|
|
`FrameUnwarper`, `TactileProcessor` and `Sensor` for each source. Live sources
|
|
have separate SDK capture workers. The service holds all runtimes open together
|
|
and reads them sequentially. SDK implementation is unchanged.
|
|
|
|
Verification on 2026-10-14 used local SDK revision
|
|
`3953747f1726b197a3f327a38c65d9878e333e12`: real SDK instances processed 1, 2, 3
|
|
and 4 distinct recorded inputs, three complete rounds for each count. Capture
|
|
handles and sensor objects were distinct; optical-flow arrays did not share
|
|
memory. All capture resources were released after each case. Run the same check
|
|
with a recording containing at least 12 frames; put generated clips and caches
|
|
in an isolated task scratch directory:
|
|
|
|
```bash
|
|
python tests/check_backend.py --recording /path/to/recording.avi --scratch .scratch/uart-connect-first/backend-run
|
|
python -m unittest discover -s tests -v
|
|
```
|
|
|
|
Unit tests separately verify startup ordering, full rollback, a uniform protocol,
|
|
removed commands/options, command framing and failure behavior. These tests do
|
|
not prove Q6A USB bandwidth or the shipped compiled ARM `orisys 0.5.0` wheel.
|
|
Run the backend check in the board's installed environment to verify that SDK.
|
|
Four live cameras, serial wiring, sustained frame rates and command latency
|
|
still require hardware validation. A slow capture/processing call delays command
|
|
handling. Serial writes time out after a configurable interval (default 1 second).
|
|
At 115200 baud/8N1, all data and replies share 11,520 bytes/s.
|