接口参考:参数与数据格式
这里保留参数、数据格式与集成接口。首次使用先解压本系统完整包,再启用需要的入口;各入口默认 auto。
Python 拍照 SDK
公开入口都从 stereo_camera_python 导入。SDK 在当前进程调用原生相机桥和模型,不需要启动 GUI 或后台进程。
from pathlib import Path
from stereo_camera_python import Camera, StereoRuntimeError, list_devices
APP = Path(r"F:\ZhiMuStereo\apps\ZhiMuStereo-0.2.6\apps\windows\Camera")
OUTPUT = Path(r"F:\ZhiMuStereo\data\python-captures")
# Linux 改为实际安装根;sdk-python 已绑定本程序目录。
for device in list_devices(installation_root=APP):
print(device.serial, device.vid, device.pid, device.friendly_name)
try:
with Camera.open("YOUR_CAMERA_SERIAL", installation_root=APP,
calibration="this-camera.yaml", backend="auto", profile="quarter") as camera:
result = camera.capture(with_depth=True, timeout_ms=5000)
print("左右图:", result.left.shape, result.right.shape)
print("视差:", None if result.disparity is None else result.disparity.shape)
print("深度:", None if result.depth_m is None else result.depth_m.shape)
print("会话:", result.metadata.get("inference_session"))
print("保存到:", result.save(OUTPUT))
except StereoRuntimeError as exc:
print("错误码:", exc.code, "原因:", str(exc))
raise接口速查
list_devices() → list[Device]
list_devices(*, installation_root=None, bridge_library=None)返回当前设备信息,常用属性为 serial、vid、pid、mi、friendly_name、has_stable_serial、identity,或 to_dict()。没有可信稳定序列号的设备不用于自动身份绑定。
Camera.open() → Camera
Camera.open(serial, *, vid=None, pid=None, mi="", mode=None,
calibration=None, installation_root=None, config_root=None,
bridge_library=None, backend=None, profile=None,
reconnect_policy=None)serial 可以是字符串或 CameraIdentity。按唯一身份打开,重复序列号或匹配不到设备会拒绝。mode 为 CameraMode(subtype, width, height, fps_num, fps_den=1),必须与枚举结果精确匹配;SDK 拍照要求 3840×1080 SBS。
0.2.6 的 backend 省略时始终为 auto,自动优先 GPU 并回退 CPU;profile 省略时沿用该 rig 的档位默认值。显式参数优先。calibration 可为 YAML 路径或 CalibrationRecord。省略时尝试加载 config_root 中已绑定标定;自动加载失败记录警告并保留原始采集。显式传入无效 YAML 会失败。
camera.capture() → CaptureResult
capture(*, with_depth=True, profile=None, timeout_ms=5000)等待触发后的新序列、新到达帧,不使用打开时缓存的旧画面。profile=None 沿用打开时档位;可指定 quarter/half/full/auto。with_depth=False 不调用模型。
timeout_ms 约束采集等待,不约束整个推理总耗时。在 auto 模式下全部 AI 后端不可用时,结果保留原图,metadata["depth_processing_error"] 写入原因;显式指定不可用后端则抛错。
camera.controls() / set_control()
controls() → list[CameraControl]
set_control(control_id: int, value: int, *, automatic=False) → dict查询 ID、当前值、范围、步长、自动能力、平台和单位。写入后返回驱动回读,并保存本次请求用于重连恢复。具体用法见 相机控制。
result 的属性、metadata 与 save()
left / right | 处理后的左右图,NumPy BGR uint8 |
raw_sbs | 相机原始左右拼接 SBS 图,BGR uint8 |
disparity | float32 像素视差;未推理或推理降级保留原图时为 None |
depth_m | float32 米制深度;没有有效标定/Q 时为 None |
valid_mask | 处理器原始掩码;不等同于所有深度值物理可靠 |
metric_depth_available | 是否存在米制深度数组 |
frame / identity / mode | 真实采集帧、相机身份、采集模式 |
metadata | 实际 inference_session、控制回读、配置 epoch、警告、采集统计等 |
save(output_root=None) → Path | 原子保存结果并返回本次拍照目录。省略根目录使用默认 captures |
结果持有自己的数组,Camera 关闭后仍可读取。多个 capture 调用在单个 Camera 上串行处理;业务程序不要并发调用 close 与控制设置。
录像与拍照并行
默认录制原始左右拼接 SBS,格式 MJPG AVI,同时可保存 SourceA / SourceB 分路视频。Windows 录像使用默认 F 盘英文目录;当前原生 AVI 编码库对任意中文路径尚未验证。录像仅写原始 SBS 与可选 SourceA / SourceB 三路 MJPG AVI 及真实时间戳,不逐帧保存深度或点云;拍照可以按需输出本次视差与深度。GUI 中开始/停止录像,编码在独立线程,队列满时明确记录丢帧。
BUNDLE="$HOME/ZhiMuStereo/apps/ZhiMuStereo-0.2.6"
bash "$BUNDLE/camera.sh" record --serial ACTUAL_HARDWARE_SN --duration 10 --output "$HOME/ZhiMuStereo/recordings"
# 仅拼接视频可加 --source-views offfrom stereo_camera_python import Camera
# application 路线:安装资源根为 Camera 或 Camera-headless。
APP = "/actual/path/to/Camera"
with Camera.open("ACTUAL_HARDWARE_SN", installation_root=APP, config_root=APP + "/config") as camera:
recorded = camera.record("/path/to/recordings", duration_s=10)
print(recorded.status, recorded.output_path)
# 长时间业务中也可用 start_recording() / stop_recording()
# 录像期间 capture() 仍获取触发后的新帧。每次录像目录包含 raw_sbs.avi、可选 source_a.avi / source_b.avi、frames.jsonl 和 recording_manifest.json(具体文件名以 manifest 为准)。frames 记录真实帧序号、采集媒体时间戳和到达时间;AVI 按名义帧率写入,不暗中重复帧。实际时间跨度、丢帧和序号间隙在 manifest 中报告。
只有正常停止、编码完成后才将 .pending 目录原子转为完整目录;断连、编码错误或中断保留未完成状态。有限时长 CLI 适合服务器任务;硬件帧率、USB 带宽和磁盘速度决定真实录像吞吐。
曝光与相机控制
驱动决定能控制哪些参数。先枚举能力,按范围和单位设置,再读取实际值;不要把一台设备的整数值直接应用到另一种平台。
from stereo_camera_python import Camera
with Camera.open("YOUR_CAMERA_SERIAL", installation_root=r"F:\ZhiMuStereo\apps\ZhiMuStereo-0.2.6\apps\windows\Camera") as camera:
controls = camera.controls()
for control in controls:
print(control.to_dict())
# 正式业务使用查询到的 ID,不假设固定 ID 跨驱动一致。
sharpness = next((c for c in controls if "sharpness" in c.name.lower()), None)
if sharpness is not None and sharpness.writable and sharpness.supports_manual:
print(camera.set_control(sharpness.id, sharpness.default, automatic=False))| 参数 | 应怎样设置 |
|---|---|
| 曝光 | 先切换允许的自动/手动模式,再按当前平台单位设置。Linux V4L2 exposure_absolute 常用 100 μs,100 = 10 ms;Windows 的曝光整数语义不同 |
| 防频闪 | 使用驱动支持的菜单,例如关闭、50 Hz、60 Hz;auto 并非所有设备都支持 |
| 锐度 / 增益 / 亮度等 | 遵守 minimum / maximum / step,写后查看驱动回读 |
| 白平衡 | 查询自动能力;手动写温度前切换相应模式 |
| focus / zoom / pan / tilt / roll | 改变双目几何,正式标定模式锁定;变更后应重新做标定,而不是强行沿用旧 YAML |
最小 Linux V4L2 控制示例
客户源码里的 python_stereo_camera/examples/v4l2_controls.py 只依赖 Python 标准库,面向 Linux x86_64 ioctl ABI。建议使用稳定的 /dev/v4l/by-id/… 路径,先停止其他采集程序。
python3 python_stereo_camera/examples/v4l2_controls.py --device /dev/video0
# 示例数值来自实测 2UK2,其他相机先检查范围。
python3 python_stereo_camera/examples/v4l2_controls.py --device /dev/video0 \
--flicker 50 --exposure 100 --sharpness 3 --restore标定与左右目顺序
本程序导入并使用已有标定,不提供完整的标靶采集与标定求解流程。必须将正确 YAML 与正确硬件身份对应。
- 核对相机身份
枚举 VID/PID/USB serial,确认唯一设备已连接。不能只靠自填序列号建立正式绑定。
- 校验 YAML
检查 schema、K/D/R/T、Q、基线、m/mm 单位、source_order。正式真机标定单目尺寸为 1920×1080。
- 导入绑定
CLI calibration import 或 GUI 为当前唯一设备导入;生成 rigs.json 和对应 YAML。替换时先备份,再原子提交。
- 检查处理结果
Source A/B 与物理左/右可不同;看 manifest 的 source_order、rectified_size 与实际 Q。每个档位重新生成对应尺寸参数。
标定 quality_status=fail/unknown 会记录质量警告;必要几何数据可用时保留用户请求的数值输出。结构损坏、尺寸不匹配或缺少有效 Q 时不得生成米制深度。对物理测距应另用已知尺寸标靶和距离验收。
无标定 AI 可输出原始 A/B 视差,不能当作已校正或米制结果;SGBM 无标定必须显式选诊断左源 A/B,仅用于定性诊断。
数据格式与保存结果
视差与深度的原始数值读取 NPY;彩色图、16 位 PNG 和 PLY 用于相应显示/交换任务,不能代替完整浮点数组。
| 数据 | 类型 / 单位 | 说明 |
|---|---|---|
| 左右图、raw_sbs | uint8,HxWx3,BGR | NumPy / OpenCV;转到 RGB 工具前显式换通道 |
| 视差 | float32,实际处理尺寸的像素 | AI 已是像素值,不执行 SGBM 的 /16 |
| 深度 | float32,米,沿 Z 轴 | 无有效标定/Q 时不存在;可能有 NaN/Inf/负值 |
| AI 掩码 | SDK 原始 mask;文件为 0/255 图像 | AI 策略保留全有效掩码,不能理解成深度全都可靠 |
| 点云 | binary PLY,坐标米 | 仅有可用米制几何时生成;按保存规则写顶点 |
| ROS 深度消息 | 32FC1,米 | 无效值转换为 NaN,只改变 ROS 消息副本 |
保存先写 .pending 目录,完整后原子改名。目录形如 captures/相机序列号/UTC时间_帧序号/;以 capture_manifest.json 中的 files 为真实文件清单,不假设每次都有深度或点云。
import json
from pathlib import Path
import numpy as np
capture_dir = Path(r"F:\ZhiMuStereo\data\captures\SERIAL\CAPTURE_ID")
manifest = json.loads((capture_dir / "capture_manifest.json").read_text(encoding="utf-8"))
print(manifest["status"], manifest.get("metric_depth_available"))
files = manifest["files"]
if "disparity" in files:
disparity = np.load(capture_dir / files["disparity"], allow_pickle=False)
print(disparity.dtype, disparity.shape)
if "depth_m" in files:
depth = np.load(capture_dir / files["depth_m"], allow_pickle=False)
usable = np.isfinite(depth) & (depth > 0)
# 业务过滤只对副本生效;不改 SDK 的原始数组或保存文件。
filtered = np.where(usable, depth, np.nan)
print("有限正深度比例:", float(usable.mean()))manifest 记录 camera_identity、mode、frame、controls、calibration、实际处理比例、输入几何、模型会话、耗时、警告和文件路径。Python 与 C++ 的失败状态文字有差异,应按对应入口检查:Python auto 降级写 depth_processing_error;原生深度失败保留原图并返回失败状态/CLI 退出码 4。
C++ SDK 与原生接口
业务 C++ 程序通常链接 StereoCameraCpp SDK。
cmake_minimum_required(VERSION 3.24)
project(CameraConsumer LANGUAGES CXX)
find_package(StereoCameraCpp CONFIG REQUIRED)
add_executable(camera_consumer camera_consumer.cpp)
target_compile_features(camera_consumer PRIVATE cxx_std_17)
target_link_libraries(camera_consumer PRIVATE StereoCamera::runtime)#include <iostream>
#include <memory>
#include <stdexcept>
#include <opencv2/imgcodecs.hpp>
#include <stereo/calibration.hpp>
#include <stereo/pipeline.hpp>
int main(int argc, char** argv) {
if (argc != 4) {
std::cerr << "usage: camera_consumer APP_ROOT RAW_SBS CALIBRATION\n";
return 2;
}
try {
auto calibration = std::make_shared<stereo::Calibration>(
stereo::Calibration::load(argv[3]));
auto sbs = cv::imread(argv[2], cv::IMREAD_COLOR);
if (sbs.empty()) throw std::runtime_error("cannot read input");
stereo::PipelineOptions options;
options.ai.applicationRoot = argv[1];
options.ai.backend = "auto";
options.ai.profile = "quarter";
options.snapshotUsage = "offline";
options.snapshotScale = 0.25;
stereo::ProcessingPipeline pipeline(calibration, options);
auto result = pipeline.processSnapshot(sbs, 1, true);
if (!result.match) throw std::runtime_error("no disparity result");
std::cout << "disparity: " << result.match->disparity.size() << '\n';
std::cout << "metric depth: " << !result.match->depthMetres.empty() << '\n';
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}# 当前目录包含下载的 CMakeLists.txt 和 camera_consumer.cpp。
PREFIX="$HOME/zhimu-stereo/apps/camera"
cmake -S . -B "$HOME/zhimu-stereo/build/consumer" -DCMAKE_PREFIX_PATH="$PREFIX"
cmake --build "$HOME/zhimu-stereo/build/consumer" --parallel 2
"$HOME/zhimu-stereo/build/consumer/camera_consumer" "$PREFIX" \
"$HOME/input/raw_sbs.png" "$HOME/input/calibration.yaml"采集、处理、保存分别用哪些接口?
stereo/camera.hpp | makeCameraProvider()、devices()、modes()、open();ICamera 的 start/flush/waitLatest/controls/setControl/stop |
stereo/calibration.hpp | Calibration::load、rectifySbs、rectificationForScale,负责 source_order 与 Q |
stereo/pipeline.hpp | ProcessingPipeline::processSnapshot / processRectifiedSnapshot;FrameProducts 包含图像、匹配及配置 epoch |
stereo/outputs.hpp | writeSnapshot(output_root, products, calibration, scale, metadata, options) 原子保存 |
直接用 ICamera 时,拍照前 flush,并以触发时观察到的 lastSequence 为下界传入 waitLatest,拒绝旧帧。应用自己的线程调度,串行相机操作并在退出时 stop。要保留深度失败时的原图,使用 processSnapshotPreservingRaw。
消费者必须使用与 SDK 兼容的编译器、OpenCV 和 C++ ABI。Linux 不能拿 Windows DLL 改名使用;重新编译业务源码不会自动改变配套生产推理库的 glibc 要求。
ROS / ROS 2
默认使用下方 预编译 Jazzy 可选模块,离线安装后运行,无需客户编译或系统 ROS 开发环境。独立 C++ 节点链接 Qt-free SDK,不依赖 cv_bridge。以下源码构建步骤仅用于修改节点或适配新的 ROS 环境。
PREFIX="$HOME/zhimu-stereo/apps/ros-sdk"
# 先按客户构建流程构建 --headless SDK,准备该平台推理资源。
bash scripts/linux/build_ros.sh "$PREFIX" jazzy
source "$PREFIX/ros/jazzy/install/local_setup.bash"
export LD_LIBRARY_PATH="$PREFIX/lib:$PREFIX/deps/lib:$PREFIX/deps/lib64:${LD_LIBRARY_PATH:-}"
ros2 launch zhimu_stereo_ros stereo.launch.py \
application_root:="$PREFIX" config_root:="$HOME/zhimu-stereo/data/config" \
output_root:="$HOME/zhimu-stereo/data/ros-captures" serial:=YOUR_CAMERA_SERIAL \
subtype:=MJPG backend:=auto namespace:=stereo \
require_calibration:=true depth_stream:=false snapshot_profile:=quarter
# 另一终端激活同环境、source 同 overlay 后调用。
ros2 service call /stereo/capture zhimu_stereo_ros/srv/Capture \
"{with_depth: true, profile: quarter, save: true}"Humble 将构建参数 jazzy 改为 humble 并使用对应环境。Noetic 使用 build_ros.sh "$PREFIX" noetic,source …/install/setup.bash --extend,roslaunch zhimu_stereo_ros stereo.launch 与 rosservice call /stereo/capture "{with_depth: true, profile: quarter, save: true}"。
| 默认 /stereo 下的话题 | 内容 |
|---|---|
left/image_raw、right/image_raw | bgr8 原图,每目 1920×1080 |
left/camera_info、right/camera_info | 原始尺寸 K/D/R/P |
left/image_rect、right/image_rect | 实际推理档位的校正图 |
left/rectified/camera_info、right/rectified/camera_info | 实际校正尺寸的 K/D/R/P |
disparity | DisparityImage / 32FC1 像素视差,含 f 与米制基线 T |
depth/image、depth/camera_info | 32FC1 米制深度与左目校正几何,无效值 NaN |
depth/image_color | bgr8 彩色深度预览,默认显示 0.2–5 米,无效深度为黑色 |
points | PointCloud2 彩色有序点云,XYZ 为米,RGB 来自同帧左目校正图,无效 XYZ 为 NaN |
同帧结果用同一采集时间戳,时间戳是接收时刻估计,不是硬件曝光同步承诺。推理线程只保留最新帧,拍照优先;不会让慢推理累积旧流帧。
同时显示视频、深度与点云
ROS 显示扩展 0.2.6 提供 RViz 配置,打开左右视频、彩色深度预览和三维 RGB 点云;也可勾选校正图与原始 32FC1 深度。预编译 Jazzy 模块已包含 RViz,用 run-node.sh 采集,再在桌面终端用 run-visualization.sh 附加显示。源码构建路线在同一环境安装 rviz2(Jazzy / Humble)或 rviz(Noetic);已有节点用 start_camera:=false,避免重复打开相机。
ros2 launch zhimu_stereo_ros visualize.launch.py \
application_root:="$PREFIX" config_root:="$HOME/zhimu-stereo/data/config" \
output_root:="$HOME/zhimu-stereo/data/ros-captures" serial:=YOUR_CAMERA_SERIAL \
backend:=auto require_calibration:=true stream_profile:=quarter \
depth_stream:=true point_cloud:=true depth_visualization:=true
# 已在运行相机节点:只启动显示,namespace 要与相机节点一致。
ros2 launch zhimu_stereo_ros visualize.launch.py start_camera:=false namespace:=stereo
# ROS 1 相同参数,使用:
# roslaunch zhimu_stereo_ros visualize.launch ...默认 Fixed Frame 为 stereo_left_optical_frame,点云在左目校正光学坐标中,x 向右、y 向下、z 向前;单独查看不需要外部 TF。接入机器人其他固定坐标时须发布实际安装与校正对应的 TF。ROS 2 预设已使用 Best Effort、Volatile、队列 1,手工添加显示也需匹配这些 QoS。
depth_color_min_m、depth_color_max_m 只控制显示颜色,超出范围的有效深度显示端点颜色,保留数值深度与点云。缺少可信米制标定时仍可查看原始视频,不生成伪造米制深度或点云。CPU 深度和点云刷新速度取决于推理耗时,原始视频独立发布;point_cloud:=false、depth_visualization:=false 可关闭相应输出。
点云转换已针对 Windows/Linux 优化:按行读取核心 XYZ 和校正左图,保持实际分辨率、RGB、米制与 NaN 规则。没有点云或深度彩图订阅者时分别跳过相应转换,首次打开 RViz 后自动恢复;不会隐式抽点或裁剪距离。
| 点云尺寸 | Windows 转换耗时:优化前 → 后 | Linux 转换耗时:优化前 → 后 |
|---|---|---|
| full,1920×1080 | 15.09 → 10.16 ms | 10.29 → 4.21 ms |
| half,960×540 | 3.76 → 2.49 ms | 2.58 → 1.10 ms |
| quarter,480×270 | 0.987 → 0.616 ms | 0.626 → 0.245 ms |
数据来自 2026-10-08 Release 同输入交替对比的中位数,Windows 为 i9-13900HX / MSVC 19.44,Linux 为 Ryzen 7 8845HS / GCC 13.4。六种连续/独立步长/无效像素输入均逐字节等价;这是消息分配与打包的耗时,不含推理、网络与显示,不能当作整机帧率。Noetic/Humble/Jazzy 真机话题和拍照通过,Jazzy 原生 RViz 四个显示窗口及零订阅后首次恢复也通过;其他目标 OS、GPU 和物理测距仍需验收。
拍照服务状态与关键参数
/stereo/capture 请求包含 with_depth、profile、save;响应含 success/status/message、capture_id、output_path、本次左右图/校正图/视差/深度及 CameraInfo。常见状态:ok、disparity_only、busy、timeout、calibration_missing、depth_failed、save_failed、device_disconnected。
只接受一个待处理拍照。默认 capture_timeout_ms=30000;超时无法取消正在执行的 GPU 调用,期间可能仍 busy。成功与否检查 status/message,不以路径非空判断。
默认 matcher=ai、backend=auto、stream_profile=half、snapshot_profile=full、depth_stream=true、require_calibration=false。实时 AI 的实际处理尺寸仍由 core 的 live 策略确定。光学 frame ID、serial、calibration、fps、subtype、config_root、output_root 都可启动配置。参数启动后不动态修改,变化需要重启节点。
管理系统联动:自动下载标定
平台默认地址:https://camera.hzzhtt.com/。客户账号能下载已发布到该客户的运行时标定。先把平台资产 UUID 和台账编号明确绑定到相机 VID/PID/SN/MI;台账编号不是硬件序列号,不要靠名字猜配对。
GUI 点击“云端标定”,输入客户账号和绑定信息,勾选连接前自动同步。密码只保留在本次会话内,重启需重新输入。同步在打开相机之前完成;手动更新先停止当前相机,再重新打开,使新标定进入下一次处理。失败保留本地标定,并显示同步失败,不能视为已获取最新版本。
流程:发现最高已发布 revision → 固定 release_id → 校验响应头与文件 SHA-256 → 解析标定 → 原子导入所选硬件配置。重复同内容返回 unchanged,不重复覆盖;曝光等本地控制项保持原配置。该操作不发送 applied 回执,不执行云端任意命令。
BUNDLE="$HOME/ZhiMuStereo/apps/ZhiMuStereo-0.2.6"
APP="$BUNDLE/apps/linux/Camera-headless"
if [[ ! -d "$APP" ]]; then APP="$BUNDLE/apps/linux/Camera"; fi
SDK="$HOME/ZhiMuStereo/envs/sdk-0.2.6-py312"
"$SDK/sdk-python.sh" -m stereo_camera_python.management_cli sync-customer --username CUSTOMER_ACCOUNT \
--asset-id ACTUAL_ASSET_UUID --asset-code ACTUAL_LEDGER_CODE \
--vid 15AA --pid 1555 --serial ACTUAL_HARDWARE_SN --mi 00 \
--config "$APP/config" --directory "$HOME/ZhiMuStereo/data/calibration-cache"from stereo_camera_python import Camera, CameraIdentity
from stereo_camera_python.management import ManagementClient
from pathlib import Path
import getpass
bundle = Path.home() / "ZhiMuStereo/apps/ZhiMuStereo-0.2.6"
app = bundle / "apps/linux/Camera-headless"
if not app.is_dir():
app = bundle / "apps/linux/Camera"
config = app / "config"
identity = CameraIdentity("15AA", "1555", "ACTUAL_HARDWARE_SN", "00")
with ManagementClient() as cloud:
cloud.login("CUSTOMER_ACCOUNT", getpass.getpass("Password: "))
synced = cloud.sync_customer_calibration(
"ACTUAL_ASSET_UUID", "ACTUAL_LEDGER_CODE", identity,
config_root=config, directory=Path.home() / "ZhiMuStereo/data/calibration-cache")
with Camera.open(identity, installation_root=app, config_root=config) as camera:
camera.capture(with_depth=True, profile="quarter").save(Path.home() / "ZhiMuStereo/data/captures")客户账号的自动标定与参数任务权限分开:参数 dispatch 需要具备相应资产/仓库可见范围的授权账号,applied 回执需要仓库或质检权限;普通 customer 标定账号不能据此读取或操作全部参数任务。曝光等参数下发继续使用 download-dispatch、apply-configuration 和明确硬件回读;平台格式携带操作系统与控制单位,Windows 曝光值不能直接用于 Linux。只下载参数不会修改硬件。生产客户账户的权限、网络和实际发布流程仍需客户账号验收。