启动时完整连接所有配置相机,移除旧命令与单路数据格式。通过 22 项回归测试、真实 SDK 的 1–4 路录像验证、Ruff 和文档检查。
7.0 KiB
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:
# 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:
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:
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:
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.