133a98e418
保留已连接相机,添加失败不占用编号,并要求输出停止后才能新增。通过 23 项回归测试、真实 SDK 的 1–4 路逐台添加验证、Ruff 和文档检查。
155 lines
7.1 KiB
Markdown
155 lines
7.1 KiB
Markdown
# UART finger service
|
|
|
|
Start the service first, then manually add 1-4 camera sources through UART.
|
|
Each successful `add` opens and warms up one source. Send `start` when the set
|
|
is ready. There is no camera discovery, hot-plug monitoring or automatic connection.
|
|
|
|
## Start the service and add cameras
|
|
|
|
In the Q6A TTL login shell, activate the board environment prepared by
|
|
`setup/sdk_setup.sh`. It needs Python 3.10+, the `orisys` SDK and `pyserial`.
|
|
From the board's `dragon-q6a` directory:
|
|
|
|
```bash
|
|
python serial/example_uart.py --port /dev/ttyHS1 --config finger
|
|
```
|
|
|
|
The service opens only the data UART and replies `OK ready 0`. No camera is
|
|
opened and no measurement is sent until the host adds a source and starts output.
|
|
Send these lines through the data UART, waiting for each reply:
|
|
|
|
```text
|
|
add /dev/video0
|
|
OK added 1
|
|
add /dev/video2
|
|
OK added 2
|
|
start
|
|
OK start
|
|
force,1,1.250000,-2.500000,3.750000
|
|
force,2,0.500000,0.100000,-0.200000
|
|
```
|
|
|
|
Use actual capture nodes; the paths above are examples. One USB camera can expose
|
|
multiple `/dev/video*` nodes, so do not assume every node represents a different
|
|
camera. `v4l2-ctl --list-devices` shows device groups. Select one usable capture
|
|
node per camera. Camera indices such as `add 0` are also accepted. Sources must
|
|
be distinct; `0` and `/dev/video0` are treated as the same camera.
|
|
|
|
The first successful addition is sensor 1, the next sensor 2, up to sensor 4.
|
|
No sensor count is declared in advance. A failed addition replies `ERR add ...`,
|
|
closes its newly opened runtime if one exists, and leaves existing cameras and
|
|
IDs unchanged. It does not consume an ID. The service remains available for retry.
|
|
|
|
The TTL login console and data UART must use **different ports**. Data defaults
|
|
to `/dev/ttyHS1`, 115200 baud, 8N1 without flow control. It must not be occupied
|
|
by getty or another serial program. The script does not configure UART pins.
|
|
Logs stay in the login console; protocol records go to the data UART.
|
|
`--show` is optional local desktop debugging.
|
|
|
|
## Commands and output
|
|
|
|
| Command | Behavior |
|
|
|---|---|
|
|
| `add SOURCE [CONFIG]` | Open and prepare one source while output is stopped; reply `OK added ID`. Optional config overrides the startup `--config` for this sensor only. |
|
|
| `start` | Begin continuous output for all added sensors; requires at least one sensor. Reply `OK start`. |
|
|
| `stop` | Pause continuous output for the whole set; keep all cameras open. Reply `OK stop`. |
|
|
| `read` | Publish one complete processing round; requires a sensor and preserves streaming state on success. |
|
|
| `quit` | Reply `OK quit`, close all cameras and UART, then exit. |
|
|
|
|
To extend the camera set after streaming has started, pause before adding:
|
|
|
|
```text
|
|
stop
|
|
OK stop
|
|
add /dev/video4
|
|
OK added 3
|
|
start
|
|
OK start
|
|
```
|
|
|
|
`add` while streaming replies `ERR add stop streaming first` and opens no source.
|
|
A fifth source or duplicate source is rejected. Removing/replacing cameras requires
|
|
quitting and starting a fresh service. `start`, `stop`, `read` and `quit` take no
|
|
arguments and always act on the full set. The older commands and automatic
|
|
startup options are removed.
|
|
|
|
Commands are case-insensitive UTF-8 lines terminated by LF, CR or CRLF. Partial
|
|
lines survive read timeouts. Lines exceeding 1024 bytes are discarded through
|
|
their terminator. Quote source/config paths containing spaces:
|
|
|
|
```text
|
|
add "/path/test clip.mp4" "/path/my config.json"
|
|
```
|
|
|
|
Every measurement has five CSV fields, even with only one sensor:
|
|
`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 units or
|
|
timestamp. The receiver can remain passive after sending setup commands and
|
|
`start`; it needs no further requests to receive continuous data. Filter `force,`
|
|
lines to distinguish measurements from replies and errors.
|
|
|
|
Frames are processed sequentially, not synchronized across cameras. All sources
|
|
must succeed before a processing round is published. A capture/processing failure
|
|
produces `ERR read sensor ID: ...` and stops continuous output for the full set,
|
|
without fabricating zero values or transmitting a partial processing round.
|
|
A transport failure can still interrupt transmission between lines. `start`
|
|
retries processing; restart the service to reopen an unavailable camera.
|
|
|
|
For recorded files, `--no-loop-video` replies `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 in the login shell ends the service and releases resources.
|
|
|
|
## Callable acquisition API
|
|
|
|
`serial/finger_api.py` has no UART dependency and opens no resources on import.
|
|
Place its directory on the caller's module search path. Adding sources is explicit:
|
|
|
|
```python
|
|
from finger_api import FingerSensorBank
|
|
|
|
bank = FingerSensorBank(config="finger")
|
|
try:
|
|
first_id = bank.add(0)
|
|
second_id = bank.add(2, config="finger")
|
|
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. `add()` returns a 1-based ID only after successful
|
|
preparation; it raises on failure and preserves previously added runtimes.
|
|
`read_all()` requires at least one source and returns a tuple only when all
|
|
sources succeed. File EOF raises `EOFError`; capture/processing failure raises
|
|
`RuntimeError`. Each reading contains its own unrolled image, three scalar forces
|
|
and SDK units; older SDKs without a units property report `unknown`. `close()`
|
|
attempts every runtime and clears the camera set. The API caller must pause its
|
|
own processing loop before adding; the UART service enforces this for commands.
|
|
|
|
## Backend evidence and remaining limits
|
|
|
|
The SDK's `FingerRuntime.open()` creates separate capture, unwarping and sensor
|
|
state for each source. SDK code is unchanged. Verification on 2026-10-14 used
|
|
local SDK revision `3953747f1726b197a3f327a38c65d9878e333e12`: 1, 2, 3 and 4
|
|
recorded sources were added incrementally, with complete readings after each
|
|
addition and three final rounds for each count. Existing runtime identities were
|
|
preserved; capture handles and sensor objects were distinct, and optical-flow
|
|
arrays did not share memory. Resources were released after each case.
|
|
|
|
To rerun 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-manual-add/backend-run
|
|
python -m unittest discover -s tests -v
|
|
```
|
|
|
|
Unit tests separately verify empty startup, explicit additions, failure isolation,
|
|
IDs/limits, stop-before-add, command framing, complete-round output and cleanup.
|
|
The shipped compiled ARM `orisys 0.5.0` wheel and Q6A physical camera/UART setup
|
|
remain unverified. Run the backend check inside the board's installed environment
|
|
to verify that SDK. USB bandwidth, sustained frame rates and command latency
|
|
require hardware validation. Camera opening/warmup and synchronous reads can delay
|
|
command handling. Serial writes have a configurable timeout, default 1 second.
|
|
At 115200 baud/8N1, all data and replies share 11,520 bytes/s.
|