1 - 快速入门
连接系统集成商 MQTT Broker,接收位移数据并调用设备接口
本页按“准备设备 → 启动接收端 → 配置设备连接 → 验证数据 → 调用 RPC → 接收告警抓拍”的顺序完成一次接入。
设备和接收端连接的是同一个系统集成商 MQTT Broker。接收端只负责订阅和调用;Broker 地址、端口及认证信息由系统集成商提供。
1. 准备设备和运行环境
在 App 中完成设备的标靶配置和初始化,记下设备 ID。验证位移前,设备必须处于测量状态并能看到有效标靶。
在能够访问 Broker 的电脑或服务器上安装 Git、Docker 和 Docker Compose v2。以下命令在 Bash 终端执行。
2. 配置并启动接收程序
先在接收端所在的电脑或服务器上下载示例,并填写系统集成商提供的 Broker 域名或 IP、端口,以及设备 ID:
git clone https://github.com/inteagle-vision/inteagle-vdm-mqtt-examples.git
cd inteagle-vdm-mqtt-examples
export VDM_DEVICE_ID=YOUR_DEVICE_ID
export MQTT_HOST=YOUR_BROKER_HOST
export MQTT_PORT=1883 # 替换为 Broker 的实际端口
是否需要认证及认证信息由系统集成商指定。需要用户名、密码时,填写分配给接收程序的账号:
export MQTT_USERNAME=YOUR_USERNAME
export MQTT_PASSWORD=YOUR_PASSWORD
选择数据格式并启动:
export VDM_PAYLOAD_FORMAT=protobuf
./run_demo.sh protobuf
export VDM_PAYLOAD_FORMAT=json
./run_demo.sh json
脚本在后台启动接收程序并连接上述 Broker。保持这些服务运行,再配置设备端连接;以下命令均在仓库根目录执行。
3. 让设备连接 Broker
在 App 中添加系统集成商 MQTT 连接:
| 配置 | 填写内容 |
|---|
| Broker 地址 | 系统集成商提供的域名或 IP,公网、内网均可 |
| 端口 | 系统集成商指定的 MQTT 端口 |
| 用户名、密码 | 按系统集成商为设备分配的认证信息填写;是否需要认证以接入要求为准 |
| Payload | 第 2 步选择的 Protobuf 或 JSON |
保存并启用连接。设备连接成功后,向 vdm/{deviceId}/telemetry 发布位移,接收程序订阅同一设备的主题。设备端 Payload 必须与接收程序选择的格式一致。
4. 确认收到位移数据
查看接收程序的输出:
docker compose logs -f python-data go-data java-data javascript-data
READY 只表示订阅已建立,不代表设备已经上报数据。看到包含标靶 ID 和非空位移数组的 telemetry 行,才表示位移链路已打通。例如:
{
"schemaVersion": 1,
"displacement": {
"sampleFrequencyHz": 2,
"firstSampleTimestampMs": "1734567890000",
"targets": [{"targetId": "T01", "dx": [0.01, 0.02], "dy": [0.0, 0.01]}]
}
}
{
"disp": {
"t": 1734567890,
"f": 2,
"d": {"T01": {"dx": [0.01, 0.02], "dy": [0.0, 0.01]}}
}
}
T01 是示例标靶 ID,X / Y 位移单位为 mm;2 Hz 表示数组中相邻样本相隔 0.5 秒。Protobuf 的首个样本时间使用 Unix 毫秒,JSON 使用 Unix 秒。
确认接收程序收到包含标靶 ID 和位移数组的消息。telemetry 还可能包含环境和设备状态,完整字段见 遥测数据。按 Ctrl+C 只退出日志查看,后台接收程序仍会运行。
5. 查询设备属性
位移数据正常后,任选一种语言发送一次 RPC(远程调用),查询设备 ID、型号、固件版本和测量状态:
| 语言 | 在当前终端执行 |
|---|
| Java | docker compose run --rm --no-deps java-data --query-attributes |
| Go | docker compose run --rm --no-deps go-data --query-attributes |
| Python | docker compose run --rm --no-deps python-data --query-attributes |
| JavaScript | docker compose run --rm --no-deps javascript-data --query-attributes |
看到 RESPONSE 且 code=0,说明请求已到达设备并收到成功响应。程序随后继续接收数据;按 Ctrl+C 退出本次运行。
请求发送到 vdm/{deviceId}/rpc/req,响应来自 vdm/{deviceId}/rpc/resp。后续可调用标靶管理接口和测量控制接口,完整格式见 RPC 格式。
6. 接入告警与抓拍
位移和 RPC 接通后,再配置告警。按 告警配置示例查询能力、创建规则,并订阅 vdm/{deviceId}/3A。等级和参数见 告警参考。
需要抓拍时,为规则配置 SNAPSHOT 动作。仅配置一个已启用的系统集成商 MQTT 连接,且未另行指定或关闭抓拍上传时,设备默认向该连接发送;多个连接时需指定接收目标。
启动抓拍接收程序:
./run_demo.sh "$VDM_PAYLOAD_FORMAT" --evidence
docker compose logs -f python-evidence go-evidence java-evidence javascript-evidence
各接收程序独立保存图像包,校验通过后调用 ackEvidencePackage。VERIFIED 表示完整包校验通过,ACKED 表示设备已接受确认。完整代码、保存位置与补传方法见 抓拍接入说明。
联调排查
| 现象 | 检查项 |
|---|
| 设备无法连接 Broker | 设备端填写的 Broker 域名或 IP、端口、认证信息及网络连通性 |
| 程序就绪但没有消息 | App 中连接是否在线、设备 ID 和 Payload 格式是否正确 |
| 有环境数据但没有位移 | 设备是否正在测量,标靶是否已初始化并有有效观测 |
| 消息解析失败 | 设备与接收程序所选格式是否一致 |
| RPC 超时 | 设备是否在线,是否允许请求 Topic 发布和响应 Topic 订阅 |
测试结束后执行 docker compose --profile evidence down 停止服务。抓拍文件保留在 Docker 数据卷中。
在本机运行单一语言时,参见 GitHub SDK 与运行步骤。
需要自行搭建联调 Broker 时,参见 NanoMQ 联调环境。
2 - API 参考
VDM 设备接口技术规格
本文按“连接与消息格式 → 主题 → RPC → 遥测 → 告警与抓拍 → 错误码”的顺序组织。接入时先阅读连接参数、RPC 格式和主题结构,再按需要查阅具体接口。
连接参数
| 参数 | 说明 |
|---|
| 协议 | MQTT v3.1.1 |
| Payload | Protobuf(推荐)/ JSON |
快速入门 · 型号支持范围 · GitHub 示例
RPC 格式
RPC 请求发布到 vdm/{deviceId}/rpc/req,响应从 vdm/{deviceId}/rpc/resp 接收。
请求结构:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=1)
request.get_targets.SetInParent()
payload = request.SerializeToString(deterministic=True)
{"reqId": 1, "method": "getTargets", "params": {}}
| Protobuf 字段 | JSON 字段 | 必填 | 说明 |
|---|
req_id | reqId | 是 | 请求 ID。必须是非零 32-bit signed integer;设备会在响应中原样返回 |
request oneof | method | 是 | RPC 方法;每条请求只设置一个 oneof 字段 |
| oneof 中的方法消息 | params | 否 | RPC 参数;无参数的方法使用 Empty |
调用限制:
- 单个 RPC payload 最大 64 KiB。
- 总 RPC 请求支持短时突发,持续高频请求会返回限流错误。
- 标靶增删改、初始化、启动/停止测量、抓图、重启、补光灯/电机写操作使用更严格限流。
- 集成方收到限流码
4 时,应退避重试,不要立即循环重发。
响应结构:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
response = pb.RpcResponse()
response.ParseFromString(response_payload) # rpc/resp 收到的 Payload
assert response.schema_version == 1
assert response.req_id == 1
assert response.code == 0
targets = response.get_targets.targets
{"reqId": 1, "code": 0, "data": {"targets": []}}
| Protobuf 字段 | JSON 字段 | 说明 |
|---|
req_id | reqId | 请求 ID,和请求中的 ID 一致 |
code | code | 结果码,0 表示成功,非 0 表示失败 |
response oneof | data | 方法对应的响应数据;成功时必须与请求方法一致 |
失败响应不携带方法数据;成功且无返回数据时,JSON 省略 data。错误说明见错误码。
请求和响应通过 req_id 关联。同一连接中,尚未收到响应的请求不得复用 req_id;重复 ID 会被拒绝。
主题结构
| 主题 | 方向 | 说明 | Protobuf 消息类型 |
|---|
vdm/{deviceId}/rpc/req | 下行 | RPC 指令 | inteagle.vdm.mqtt.v1.RpcRequest |
vdm/{deviceId}/rpc/resp | 上行 | RPC 响应 | inteagle.vdm.mqtt.v1.RpcResponse |
vdm/{deviceId}/telemetry | 上行 | 遥测数据 | inteagle.vdm.mqtt.v1.Telemetry |
vdm/{deviceId}/attributes | 上行 | 属性上报 | inteagle.vdm.mqtt.v1.Attributes |
vdm/{deviceId}/event | 上行 | 事件上报 | inteagle.vdm.mqtt.v1.Event |
vdm/{deviceId}/3A | 上行 | 告警上报 | inteagle.vdm.mqtt.v1.Alarm |
vdm/{deviceId}/image | 上行 | 普通图片或告警抓拍图像包分块 | 不使用 Protobuf,按首字节区分类型 1/2 |
Protobuf Schema
声明 schema_version 的 V1 根消息必须设置为 1。Python 项目可以直接使用已生成的 pb2 文件 和 package 文件。
完整代码见 GitHub SDK;告警配置运行步骤见 告警接入示例。
公共字段约束
| Protobuf 字段 | JSON 字段 | 类型 | 约束 |
|---|
target_id | targetId | string | 标靶 ID,1-64 字节;去除前后空白后不能为空,不能包含控制字符,同一请求内不能重复 |
sensor_id | sensorId | uint32 | 视觉传感器 ID。单目设备通常为 0;双目设备为 0 或 1 |
roi.x / roi.y | 相同 | uint32 | ROI 左上角像素坐标;坐标基于原始相机画面 |
roi.width / roi.height | 相同 | uint32 | ROI 宽高,必须大于 0,且 ROI 应落在当前画面范围内 |
distance_m | distance | float | 标靶到设备的测量距离,单位 m,必须大于等于 0.1 |
role | role | enum | 标靶角色,枚举见下表;新建时默认 MP |
target_model | targetModel | enum | 标靶型号,枚举见下表 |
skip_measurement | skipMeasurement | bool | 是否跳过测量。false 或不传表示参与测量;true 表示保留配置但测量流程跳过该标靶 |
role 枚举含义:
| Protobuf 值 | JSON 值 | 名称 | 说明 |
|---|
TARGET_ROLE_RP | RP | Reference Point | 基准点,作为位移计算参考,通常布置在稳定区域 |
TARGET_ROLE_MP | MP | Measuring Point | 测点,普通待测标靶,默认角色 |
TARGET_ROLE_CP | CP | Control Point | 控制点,用于监测或补偿系统漂移,通常布置在稳定区域 |
role 使用建议:
MP 是默认和最常用角色。需要持续输出位移的被测位置,例如梁体、塔体、边坡或结构构件上的监测标靶,通常设置为 MP。RP 用作基准点。只有同一视野内存在相对稳定、不随被测体移动的标靶时,才建议设置为 RP;如果现场没有可靠稳定参考点,不要强行设置 RP。CP 用作控制点或校核点。通常布置在稳定位置,用来观察安装稳定性、温漂、整体漂移或测量质量;它不是默认的主测点。- 简单接入或不确定角色时,新增标靶可以不传
role,设备按 MP 处理。
target_id 未设置时,支持自动生成 ID 的接口会由设备生成 UUID;JSON 对应字段为 targetId。
targetModel 枚举含义:
| Protobuf 值 | JSON 值 | 说明 |
|---|
TARGET_MODEL_T10 | T10 | T10 标靶型号 |
TARGET_MODEL_T20 | T20 | T20 标靶型号 |
TARGET_MODEL_T50 | T50 | T50 标靶型号 |
TARGET_MODEL_T100 | T100 | T100 标靶型号 |
TARGET_MODEL_T200 | T200 | T200 标靶型号 |
标靶管理 RPC 字段
initRefTargets
对指定标靶执行参考位置初始化/标定。设备会根据保存的标靶配置采集图像并计算初始参考位置;请求中也可以提供 ROI、测量距离、视觉传感器或标靶型号作为本次初始化的覆盖值。调用前应保证 ROI 已对准标靶,且设备没有正在进行其它测量或初始化流程。
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
targets | targets | repeated | 是 | 要初始化标靶参考位置的标靶数组,不能为空,最多 128 个 |
targets[].target_id | targets[].targetId | string | 是 | 标靶 ID,遵循公共字段约束;用于绑定初始化结果和后续位移数据 |
targets[].sensor_id | targets[].sensorId | optional uint32 | 否 | 视觉传感器 ID。不传时使用已保存配置;单目设备默认 0,双目设备建议显式传 0 或 1 |
targets[].roi | targets[].roi | Roi | 否 | 标靶 ROI;未保存标靶配置时必须提供 |
targets[].distance_m | targets[].distance | optional float | 否 | 测量距离,单位 m;未保存标靶配置时必须提供 |
targets[].target_model | targets[].targetModel | optional enum | 否 | 型号枚举见公共字段约束;未保存且未传入时默认 T100 |
标靶增删改请使用 addTargets、setTargets 或 deleteTargets。角色和跳过测量状态使用 addTargets 或 setTargets 配置。
最简请求与带覆盖值的请求:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
minimal = pb.RpcRequest(schema_version=1, req_id=1)
minimal.init_ref_targets.targets.add(target_id="1")
minimal_payload = minimal.SerializeToString(deterministic=True)
request = pb.RpcRequest(schema_version=1, req_id=2)
request.init_ref_targets.targets.add(
target_id="1",
sensor_id=0,
roi=pb.Roi(x=1000, y=500, width=400, height=400),
distance_m=5.0,
target_model=pb.TARGET_MODEL_T100,
)
payload = request.SerializeToString(deterministic=True)
{
"reqId": 1,
"method": "initRefTargets",
"params": {
"targets": [
{
"targetId": "1"
}
]
}
}
独立请求:
{
"reqId": 2,
"method": "initRefTargets",
"params": {
"targets": [
{
"targetId": "1",
"sensorId": 0,
"roi": {
"x": 1000,
"y": 500,
"width": 400,
"height": 400
},
"distance": 5.0,
"targetModel": "T100"
}
]
}
}
成功时,RpcResponse.code == 0 且 response.HasField("init_ref_targets")。
成功响应表示设备已启动参考位置初始化任务;最终结果通过 event 主题上报。若至少一个标靶初始化成功,设备会同时通过 attributes 主题上报当前 target_list 摘要,initialized_at_s 为 Unix 秒时间戳。
addTargets
在已有标靶基础上添加新标靶。
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
targets | targets | repeated | 是 | 标靶配置数组,不能为空,最多 128 个 |
targets[].target_id | targets[].targetId | optional string | 否 | 不设置时由设备生成 UUID |
targets[].sensor_id | targets[].sensorId | optional uint32 | 双目设备必填 | 单目设备默认 0 |
targets[].roi | targets[].roi | Roi | 是 | 标靶 ROI 区域 |
targets[].distance_m | targets[].distance | float | 是 | 测量距离,单位 m |
targets[].role | targets[].role | optional enum | 否 | 角色枚举见公共字段约束;默认 MP |
targets[].target_model | targets[].targetModel | optional enum | 否 | 型号枚举见公共字段约束 |
targets[].skip_measurement | targets[].skipMeasurement | optional bool | 否 | 是否跳过测量,默认 false |
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=3)
request.add_targets.targets.add(
target_id="2",
sensor_id=0,
roi=pb.Roi(x=800, y=400, width=400, height=400),
distance_m=6.0,
role=pb.TARGET_ROLE_MP,
target_model=pb.TARGET_MODEL_T100,
skip_measurement=False,
)
payload = request.SerializeToString(deterministic=True)
# 解析成功响应后读取新增标靶 ID
response = pb.RpcResponse.FromString(response_payload)
added_ids = list(response.add_targets.added_target_ids)
{
"reqId": 3,
"method": "addTargets",
"params": {
"targets": [
{
"targetId": "2",
"sensorId": 0,
"roi": {
"x": 800,
"y": 400,
"width": 400,
"height": 400
},
"distance": 6.0,
"role": "MP",
"targetModel": "T100",
"skipMeasurement": false
}
]
}
}
getTargets
获取当前标靶列表。请求使用 Empty,成功响应返回强类型 TargetInfo:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=4)
request.get_targets.SetInParent()
payload = request.SerializeToString(deterministic=True)
response = pb.RpcResponse.FromString(response_payload)
for target in response.get_targets.targets:
initialized_at = target.initialized_at_s if target.HasField("initialized_at_s") else None
print(target.target_id, target.distance_m, target.role, initialized_at)
{
"reqId": 4,
"method": "getTargets",
"params": {}
}
| Protobuf 字段 | JSON 字段 | 说明 |
|---|
targets[].target_id | targets[].targetId | 标靶 ID |
targets[].sensor_id | targets[].sensorId | 视觉传感器 ID |
targets[].roi | targets[].roi | 标靶 ROI |
targets[].distance_m | targets[].distance | 测量距离,单位 m |
targets[].role | targets[].role | 标靶角色枚举 |
targets[].target_model | targets[].targetModel | 标靶型号枚举 |
targets[].skip_measurement | targets[].skipMeasurement | 是否跳过测量 |
targets[].initialized | targets[].initialized | 是否已经初始化参考位置 |
targets[].initialized_at_s | targets[].initializedAt | optional Unix 秒时间戳 |
setTargets
按 target_id 修改已有标靶。每个元素除 target_id 外,至少设置 sensor_id、roi、distance_m、role、target_model、skip_measurement 中的一个更新字段。
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
targets | targets | repeated | 是 | 标靶配置数组,不能为空,最多 128 个 |
targets[].target_id | targets[].targetId | string | 是 | 要更新的标靶 ID |
targets[].sensor_id | targets[].sensorId | optional uint32 | 否 | 更新视觉传感器 ID |
targets[].roi | targets[].roi | optional Roi | 否 | 更新 ROI 区域 |
targets[].distance_m | targets[].distance | optional float | 否 | 更新测量距离,单位 m |
targets[].role | targets[].role | optional enum | 否 | 更新 MP、RP 或 CP 角色 |
targets[].target_model | targets[].targetModel | optional enum | 否 | 更新 T10、T20、T50、T100 或 T200 型号 |
targets[].skip_measurement | targets[].skipMeasurement | optional bool | 否 | 更新是否跳过测量 |
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=5)
request.set_targets.targets.add(
target_id="2",
distance_m=6.5,
role=pb.TARGET_ROLE_CP,
target_model=pb.TARGET_MODEL_T200,
skip_measurement=True,
)
payload = request.SerializeToString(deterministic=True)
{
"reqId": 5,
"method": "setTargets",
"params": {
"targets": [
{
"targetId": "2",
"distance": 6.5,
"role": "CP",
"targetModel": "T200",
"skipMeasurement": true
}
]
}
}
成功响应必须设置 response.set_targets;未出现在请求中的 optional 字段不会被修改。
deleteTargets
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
target_ids | targetIds | repeated string | 是 | 要删除的标靶 ID,不能为空,最多 128 个且不能重复 |
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=6)
request.delete_targets.target_ids.append("2")
payload = request.SerializeToString(deterministic=True)
{
"reqId": 6,
"method": "deleteTargets",
"params": {
"targetIds": [
"2"
]
}
}
成功响应必须设置 response.delete_targets。
基础 RPC 字段
getAttr
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
keys | keys | repeated string | 否 | 最多 32 个且不能重复;空列表返回全部基础属性。key 名沿用公开属性名,例如 deviceId、deviceStatus、measureStatus、sampleFreq |
status 是 measureStatus 的兼容别名。
deviceStatus 是设备整体工作状态,独立于 measureStatus(测量状态)。设备进入低功耗前,JSON 属性上报 "deviceStatus":"Sleeping";Protobuf 使用 Attributes.device_status = DEVICE_STATUS_SLEEPING。设备唤醒启动后会上报 Idle。deviceStatus 只读,不能通过 setAttr 设置。
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=7)
request.get_attr.keys.extend([
"deviceId", "deviceStatus", "measureStatus", "sampleFreq", "showROI", "showTs", "showSensorId",
])
payload = request.SerializeToString(deterministic=True)
response = pb.RpcResponse.FromString(response_payload)
attrs = response.get_attr.attributes
print(attrs.device_id, attrs.device_status, attrs.measurement_status, attrs.sample_frequency_hz)
{
"reqId": 7,
"method": "getAttr",
"params": {
"keys": [
"deviceId",
"deviceStatus",
"measureStatus",
"sampleFreq",
"showROI",
"showTs",
"showSensorId"
]
}
}
setAttr
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
sample_frequency_hz | sampleFreq | optional uint32 | 否 | 采样频率,单位 Hz,范围 1-60 |
report_metrics.values | reportMetrics | repeated enum | 否 | 当前只接受 REPORT_METRIC_DX 和 REPORT_METRIC_DY,不能为空且不能重复;缺省为两者都上报 |
show_roi | showROI | optional bool | 否 | 告警抓拍图像是否叠加 ROI |
show_timestamp | showTs | optional bool | 否 | 告警抓拍图像是否叠加时间戳 |
show_sensor_id | showSensorId | optional bool | 否 | 告警抓拍图像是否叠加视觉传感器 ID |
target_list 会在标靶配置变化时通过 attributes 主题上报,也可以通过 getTargets 查询;getAttr 不主动返回标靶列表。JSON 对应字段为 targets。
SetAttributesRequest 必须至少设置一个可写字段。设备 ID、型号、固件版本、分辨率、测量状态和标靶列表为只读字段,不能通过 setAttr 修改。
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=8)
request.set_attr.sample_frequency_hz = 5
request.set_attr.report_metrics.values.extend([pb.REPORT_METRIC_DX, pb.REPORT_METRIC_DY])
request.set_attr.show_roi = True
request.set_attr.show_timestamp = True
request.set_attr.show_sensor_id = True
payload = request.SerializeToString(deterministic=True)
{
"reqId": 8,
"method": "setAttr",
"params": {
"sampleFreq": 5,
"reportMetrics": [
"dx",
"dy"
],
"showROI": true,
"showTs": true,
"showSensorId": true
}
}
syncTime
使用 NTP 同步设备系统时间。同步成功后,设备会保存本次使用的 NTP 服务器配置。
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
ntp_server | ntpServer | optional string | 否 | NTP 服务器;不传时使用 ntp.aliyun.com,最长 253 字节且不能包含空白 |
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
default_ntp = pb.RpcRequest(schema_version=1, req_id=9)
default_ntp.sync_time.SetInParent()
request = pb.RpcRequest(schema_version=1, req_id=10)
request.sync_time.ntp_server = "ntp.aliyun.com"
payload = request.SerializeToString(deterministic=True)
{
"reqId": 9,
"method": "syncTime",
"params": {}
}
独立请求:
{
"reqId": 10,
"method": "syncTime",
"params": {
"ntpServer": "ntp.aliyun.com"
}
}
如果 NTP 服务器不可达、地址格式不合法或同步失败,设备返回非 0 code。该接口会触发网络同步和本地配置保存,属于受限流保护的写操作。
reboot / startMeasurement / stopMeasurement
这些接口使用 Empty 请求消息:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
start = pb.RpcRequest(schema_version=1, req_id=11)
start.start_measurement.SetInParent()
stop = pb.RpcRequest(schema_version=1, req_id=12)
stop.stop_measurement.SetInParent()
reboot = pb.RpcRequest(schema_version=1, req_id=13)
reboot.reboot.SetInParent()
{
"reqId": 11,
"method": "startMeasurement",
"params": {}
}
独立请求:
{
"reqId": 12,
"method": "stopMeasurement",
"params": {}
}
独立请求:
{
"reqId": 13,
"method": "reboot",
"params": {}
}
startMeasurement 和 stopMeasurement 会触发 attributes 主题上报 measurement_status;JSON 对应字段为 measureStatus。低功耗休眠前上报独立的 device_status;JSON 对应 deviceStatus。设备断电前的上报是有界尽力发送,没有逐条云端确认;设备离线并不单独证明其处于休眠。reboot 成功后设备会断开并重新连接 MQTT,调用方应等待设备重新上线。
测量控制 RPC 字段
setLightLevel
单灯或所有灯使用统一亮度:
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
all_lights_level | level | uint32 | 是 | 所有补光灯统一亮度挡位,范围 0-8 |
双目设备需要分别设置多路补光灯时,也可以传 lights:
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
per_light.lights | lights | repeated | 是 | 补光灯配置数组,不能为空,最多 16 个 |
lights[].light_id | lights[].id | uint32 | 是 | 补光灯 ID |
lights[].level | lights[].level | uint32 | 是 | 亮度挡位,范围 0-8 |
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
all_lights = pb.RpcRequest(schema_version=1, req_id=14)
all_lights.set_light_level.all_lights_level = 4
per_light = pb.RpcRequest(schema_version=1, req_id=15)
per_light.set_light_level.per_light.lights.add(light_id=0, level=3)
per_light.set_light_level.per_light.lights.add(light_id=1, level=6)
{
"reqId": 14,
"method": "setLightLevel",
"params": {
"level": 4
}
}
独立请求:
{
"reqId": 15,
"method": "setLightLevel",
"params": {
"lights": [
{
"id": 0,
"level": 3
},
{
"id": 1,
"level": 6
}
]
}
}
getLightLevel 使用 Empty 请求;响应通过 WhichOneof("selection") 判断返回统一亮度还是逐灯数组:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=16)
request.get_light_level.SetInParent()
response = pb.RpcResponse.FromString(response_payload)
selection = response.get_light_level.WhichOneof("selection")
{
"reqId": 16,
"method": "getLightLevel",
"params": {}
}
snapshot
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
sensor_id | sensorId | optional uint32 | 否 | 视觉传感器 ID,单目设备默认 0 |
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=17)
request.snapshot.sensor_id = 0
payload = request.SerializeToString(deterministic=True)
{
"reqId": 17,
"method": "snapshot",
"params": {
"sensorId": 0
}
}
snapshot RPC 成功响应只表示已触发抓图;JPEG 二进制图片通过 vdm/{deviceId}/image 主题上报。当前标准快照图片由设备侧处理为 640x480。
showROI、showTs 和 showSensorId 控制告警抓拍图像的叠加内容,不影响 snapshot 普通快照。
型号专用 RPC
电机参数见 O1、O2-H、X1和 X2-H;ISP 参数见 X1。
ispCtl
支持 getStatus、setConfig 和 setExposurePreset 三个 action。
setConfig 的曝光时间、增益和旋转角度必须满足型号页面声明的范围;旋转角度只能为
0、90、180 或 270,单相机增益范围为 1024..16384。
ispCtl 成功时只由外层 code=0 表示成功,不重复返回 data.success。按 action 和设备能力,data 只可能包含
vdmModeEnabled、exposureLocked、exposureMode、nrLevel、enable3dnr、maxGain、
exposurePreset、exposureInfo、cameraCount、flip、mirror、rotation、frameWidth
和 frameHeight;exposureInfo 只包含 iso、expTime、aGain、dGain、ispDGain
和 aveLum。操作失败统一通过外层非零 code 返回,不携带 data。
getCruisePaths
getCruisePaths 成功时,JSON data 与 Protobuf get_cruise_paths.data 表示相同的数组结构:
[
{
"id": 1,
"enabled": true,
"order": 10,
"cruisePoints": [
{"id": 1, "targets": ["T01"], "xAngle": 45.0, "yAngle": -5.0}
]
}
]
路径只返回 id、enabled、order、cruisePoints;巡航点只返回 id、targets、
xAngle 和 yAngle。
告警抓拍图像 RPC 字段
仅配置一个已启用的系统集成商 MQTT 连接,且未另行指定或关闭抓拍上传时,设备默认向该连接发送告警抓拍。规则须配置 SNAPSHOT 动作;多个系统集成商 MQTT 连接时需指定接收目标。系统集成商平台校验并保存图像包后调用 ackEvidencePackage 确认。
查看 GitHub 抓拍接收示例 · 查看告警配置示例
告警抓拍图像操作使用对应告警的 eventId。
getEvidenceStatus / retryEvidence
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
event_id | eventId | uint64 | 是 | 触发抓拍的告警状态变化 ID;JSON 建议用十进制字符串保持精度 |
kind | kind | optional EvidenceKind | 否 | 未设置时默认为 SNAPSHOT |
getEvidenceStatus 查询该次告警抓拍图像的处理结果;retryEvidence 请求设备重新发送对应图像包。两者都返回 EvidenceStatusResponse:found=false 表示未找到,found=true 时通过协议字段 evidence 返回 AlarmEvidence。
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
status_request = pb.RpcRequest(schema_version=1, req_id=80)
status_request.get_evidence_status.event_id = 9001
status_request.get_evidence_status.kind = pb.EVIDENCE_KIND_SNAPSHOT
retry_request = pb.RpcRequest(schema_version=1, req_id=81)
retry_request.retry_evidence.event_id = 9001
retry_request.retry_evidence.kind = pb.EVIDENCE_KIND_SNAPSHOT
{
"reqId": 80,
"method": "getEvidenceStatus",
"params": {
"eventId": "9001",
"kind": "SNAPSHOT"
}
}
独立请求:
{
"reqId": 81,
"method": "retryEvidence",
"params": {
"eventId": "9001",
"kind": "SNAPSHOT"
}
}
ackEvidencePackage
| Protobuf 字段 | JSON 字段 | 类型 | 必填 | 说明 |
|---|
event_id | eventId | uint64 | 是 | 触发抓拍的告警事件 ID |
kind | kind | EvidenceKind | 是 | 抓拍图片使用 SNAPSHOT |
package_sha256 | packageSha256 | string | 是 | 类型 2 Header 中 32 字节 packageSha256 的 64 位小写十六进制字符串 |
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
ack_request = pb.RpcRequest(schema_version=1, req_id=82)
ack_request.ack_evidence_package.event_id = 9001
ack_request.ack_evidence_package.kind = pb.EVIDENCE_KIND_SNAPSHOT
ack_request.ack_evidence_package.package_sha256 = "0" * 64 # 替换为 Header 中的实际值
{
"reqId": 82,
"method": "ackEvidencePackage",
"params": {
"eventId": "9001",
"kind": "SNAPSHOT",
"packageSha256": "0000000000000000000000000000000000000000000000000000000000000000"
}
}
只有完整 USTAR 图像包已持久化且通过长度、SHA-256 和成员安全校验时才能确认。设备只接受从原始接收该图像包的同一逻辑 MQTT 连接发出的 ACK。
告警规则与历史查询
先了解告警等级与生命周期,再查询能力、配置规则并接收告警。相关 RPC 如下:
| 方法 | RpcRequest/RpcResponse oneof 字段 | 字段号 |
|---|
getAlarmCaps | get_alarm_caps | 90 |
listAlarmRules | list_alarm_rules | 91 |
applyAlarmRules | apply_alarm_rules | 92 |
getAlarmState | get_alarm_state | 93 |
listAlarmHistory | list_alarm_history | 94 |
设备级电压规则不传 targetIds,详情包含电压和阈值。标靶级规则的选择范围以 getTargets 和 getAlarmCaps 为准。
告警能力、当前状态与历史响应
get_alarm_caps.api_version / data.apiVersion 是告警管理接口版本,区别于 Protobuf 根消息的 schema_version=1。alarm_service_ready / alarmServiceReady 表示告警服务就绪,alarm_history_ready / alarmHistoryReady 表示历史查询可用;能力响应中的 levels、transitions 和 rule_types / ruleTypes 决定客户端可提供的选项。
| 方法 | 请求参数(Protobuf / JSON) | 响应与处理方式 |
|---|
getAlarmCaps | 空请求 | 返回服务就绪状态、规则类型、等级、动作及资源上限 |
listAlarmRules | 可选 cursor / cursor | items 为规则列表;返回 next_cursor / nextCursor 时继续分页,不自行解析游标;配置变化导致的分页冲突使用外层 code=6,此时从首页重查 |
getAlarmState | 空请求 | active 只包含当前活动告警;每项返回生命周期 ID、规则 ID、类型和当前等级;标靶 ID 对应 active[].target_id / active[].detail.targetId,设备级电压告警无此字段 |
listAlarmHistory | 可选 cursor、limit、alarm_id / cursor、limit、alarmId | items 按生命周期返回记录,包含该生命周期的最近一次转换;不是完整的逐事件列表。alarmId 在两种格式的查询请求中都使用十进制字符串;分页使用 next_cursor / nextCursor |
listAlarmHistory.limit 缺省为 20,设备将传入值限制在 1~50;alarmId 与 cursor 不能同时传入。
历史记录的 state 为 ALARM_INCIDENT_STATE_ACTIVE=1 / ACTIVE 或 ALARM_INCIDENT_STATE_CLEARED=2 / CLEARED。结束记录省略 level,通过 transition 区分恢复与取消。last_event_id / lastEventId 对应最近一次状态变化,可与 3A 的 eventId 关联;历史 ID 字段在两种格式中均为十进制字符串。
时间单位不能混用: 3A 的 ts 是 Unix 秒;历史中的 started_at / startedAt、updated_at / updatedAt、cleared_at / clearedAt 均为 Unix 毫秒。历史抓拍摘要的 captured_at / capturedAt、updated_at / updatedAt 也保持毫秒。
applyAlarmRules:新增、更新、禁用与删除
所有配置操作都调用 applyAlarmRules,请求发到 vdm/{deviceId}/rpc/req,响应从 vdm/{deviceId}/rpc/resp 接收。建议依次执行:getAlarmCaps 查询能力 → getTargets 确认标靶 → listAlarmRules 获取现有完整规则 → applyAlarmRules 提交 → listAlarmRules 回读。
| Protobuf 请求字段 | JSON params 字段 | 类型 | 说明 |
|---|
apply_alarm_rules.upsert | upsert | 规则数组 | 新增或完整替换指定规则;每项 Protobuf AlarmRuleDefinition 只设置一个规则分支 |
apply_alarm_rules.delete_ids | deleteIds | uint32 数组 | 删除指定已有规则,ID 必须非零且不重复 |
upsert 和 deleteIds 可同时提交,合计至少一项,当前每批最多 16 项,以 getAlarmCaps.limits.batchRules 为准。当前规则配置请求体上限为 16 KiB,区别于外层 RPC 的 64 KiB 上限。不要在同一批中重复更新同一个 ID,或同时更新和删除同一个 ID;更新和删除的 ID 必须已经存在。
提交按整批校验、编译、持久化并生效:任意一项失败,整批配置不生效。返回成功表示规则已保存,是否能产生告警还取决于规则是否启用、目标是否满足测量条件及有效观测是否达到阈值。
规则公共字段
以下 Protobuf common 位于所选规则分支下,例如 displacement_limit.common;JSON 公共字段直接位于 upsert[] 对象内。
| Protobuf 字段 | JSON 规则字段 | 必填 | 说明 |
|---|
common.id | id | 更新时 | 新建必须省略,由设备分配;更新必须是已有非零 uint32,不能通过指定任意新 ID 创建规则 |
common.name | name | 否 | 规则名称,当前最多 64 UTF-8 字节,以能力上限为准 |
common.enabled | enabled | 否 | 缺省为 true;显式 false 保存禁用规则,仍会校验字段合法性 |
common.actions | actions | 否 | 默认无动作;动作类型及适用规则来自 getAlarmCaps.actions |
| 规则 oneof 分支 | type | 是 | 下表中的规则类型,不能在 Protobuf 的同一规则项设置多个分支 |
upsert 是完整规则替换,不是字段补丁。例如仅发送 {"id":12,"enabled":false} 不合法;必须带上 type 及该类型的全部必填字段。更新时省略可选字段会恢复其缺省含义,例如省略 enabled 会重新启用,省略 actions 会去掉原动作,省略数值规则的 recoverForMs 会使用默认恢复时间。
各类规则需要的字段
| Protobuf 分支 | JSON type | 必填业务字段(JSON 名称) | 可选业务字段 |
|---|
displacement_limit | DISP_LIMIT | targetIds、metric、direction、levels | recoverForMs |
displacement_rate | DISP_RATE | targetIds、metric、direction、windowMs、levels | recoverForMs |
target_lost | TARGET_LOST | targetIds、levels、recoverForMs | 无 |
voltage_low,并设置 alarm_type=ALARM_TYPE_INPUT_VOLTAGE_LOW | VIN_LOW | levels | recoverForMs |
voltage_low,并设置 alarm_type=ALARM_TYPE_BATTERY_VOLTAGE_LOW | VBAT_LOW | levels | recoverForMs |
Protobuf 对应字段名分别为 target_ids、metric、direction、window_ms、levels、recover_for_ms。TARGET_LOST 的等级使用 lost_for_ms / lostForMs,其余类型使用 enter 和 enter_for_ms / enterForMs。电压规则不接受 targetIds、metric、direction;速率规则通过窗口确定单位,不额外传 unit;未定义的字段会被拒绝。
返回值与重复提交
| 场景 | Protobuf RpcResponse | JSON 响应 |
|---|
| 新增规则成功 | code=0,apply_alarm_rules.created_ids 返回新 ID | {"reqId":90,"code":0,"data":{"createdIds":[12]}} |
| 仅更新、禁用或删除成功 | code=0,选择 apply_alarm_rules 分支,created_ids 为空 | {"reqId":91,"code":0} |
| 失败 | 非零 code,不设置业务 response 分支 | 例如 {"reqId":91,"code":2},不含 data |
表中的 ID 仅为示例。createdIds 只包含本批新增规则的 ID,按新增项在 upsert 中出现的顺序返回,已有规则更新不会占位。reqId 只关联请求响应,不是配置事务 ID;若新增请求响应超时,先回读规则确认是否已创建,避免使用新 reqId 重发后产生重复规则。删除已经不存在的规则也会失败,不应当作无条件幂等操作。
公共错误码按错误码处理:2 表示参数错误,3 表示不支持,4 表示限流,5 表示超时,6 表示配置冲突;其他非零值作为失败处理。
告警等级与规则参数
先调用 getAlarmCaps 确认服务是否就绪,以及当前固件支持的规则类型、等级、动作和资源上限。三级含义见告警等级(3A)。规则的 levels 至少配置一级,未配置的等级不参与判定;JSON 使用 ALERT、ALARM、ACTION 作为 key,Protobuf 使用 levels.alert、levels.alarm、levels.action 消息字段。
| Protobuf 字段 | JSON 字段 | 说明 |
|---|
levels.<等级>.enter | levels.<等级>.enter | 数值类规则的临界阈值;位移单位 mm,电压单位 V,速率单位由窗口决定 |
levels.<等级>.enter_for_ms | levels.<等级>.enterForMs | 连续越限多少毫秒后进入该等级;缺省为 0,在有效观测越限时即可进入 |
levels.<等级>.lost_for_ms | levels.<等级>.lostForMs | 仅用于 TARGET_LOST,连续丢失时长,必须大于 0 |
recover_for_ms | recoverForMs | 整条规则共用的降级/恢复确认时长,必须大于 0;数值规则缺省为 2000 ms,可通过能力响应的 numeric_recover_for_ms / defaults.numericRecoverForMs 查询;丢失规则必须显式提供 |
window_ms | windowMs | 仅用于 DISP_RATE;1000~10000 ms、步长 100 ms 对应 mm/s,3600000 对应 mm/h,86400000 对应 mm/day;具体支持范围以能力响应为准 |
数值规则只有一个 enter 阈值,同时用于进入和退出判断,不接受另一个 exit 阈值。判定方式如下:
| 类型 | 进入条件 | 回到当前等级安全侧 | 已启用等级从 ALERT 到 ACTION 的配置顺序 |
|---|
DISP_LIMIT / DISP_RATE | 方向换算后的值 > enter,并满足 enterForMs | 值 <= enter | enter 严格递增,且均大于 0 |
VIN_LOW / VBAT_LOW | 电压 < enter,并满足 enterForMs | 电压 >= enter | enter 严格递减,电压越低等级越高 |
TARGET_LOST | 应当可见的标靶连续未检出,达到 lostForMs | 连续重新检出达到 recoverForMs 后恢复 | lostForMs 严格递增 |
数值告警回到安全侧后,还需连续满足 recoverForMs 才会降级或恢复;降到较低等级后仍是活动告警。丢失告警重新检出并满足恢复时间后直接恢复,不逐级降级。无效观测、乱序、观测超时或标靶当前不应可见等情况会中断未完成的计时,不能作为告警已经恢复的依据。
以下创建一个 X 方向双向位移规则,仅启用 ALERT 和 ACTION,只上报告警。阈值为说明协议用的示例值,实际值由项目监测要求确定;targetId="1" 必须是当前可见且允许配置位移规则的标靶。请求发布到 vdm/{deviceId}/rpc/req。
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
request = pb.RpcRequest(schema_version=1, req_id=90)
rule = request.apply_alarm_rules.upsert.add().displacement_limit
rule.common.name = "X 方向位移"
rule.common.enabled = True
rule.target_ids.append("1")
rule.metric = pb.ALARM_METRIC_DX
rule.direction = pb.DISPLACEMENT_DIRECTION_BIDIRECTIONAL
rule.levels.alert.enter = 5.0
rule.levels.alert.enter_for_ms = 1000
rule.levels.action.enter = 10.0
rule.levels.action.enter_for_ms = 1000
rule.recover_for_ms = 2000
payload = request.SerializeToString(deterministic=True)
{
"method": "applyAlarmRules",
"reqId": 90,
"params": {
"upsert": [{
"name": "X 方向位移",
"enabled": true,
"type": "DISP_LIMIT",
"targetIds": ["1"],
"metric": "DX",
"direction": "BIDIRECTIONAL",
"levels": {
"ALERT": {"enter": 5.0, "enterForMs": 1000},
"ACTION": {"enter": 10.0, "enterForMs": 1000}
},
"recoverForMs": 2000
}]
}
}
新建规则省略 id,从响应 created_ids / createdIds 获取设备分配的 ID;更新时使用该 ID 并提交完整规则。以上规则可直接从正常触发 ACTION,也可先触发 ALERT 后升级至 ACTION,不会产生未启用的 ALARM。
actions 省略或为空数组表示只告警、不抓拍。配置 SNAPSHOT 时可通过动作的 levels、transitions 选择触发时机;省略选择器时默认使用规则已启用的等级和 TRIGGERED。JSON 显式空选择器数组会被拒绝;Protobuf repeated 选择器为空等同于省略。动作失败不会回滚告警状态。速率告警抓拍仅支持 1000~10000 ms 短窗口;小时/日窗口仅上报告警。
同一规则绑定多个标靶时,每个标靶独立计时和维护告警生命周期。更新规则参数后,已有活动告警继续使用触发时的规则定义直到恢复;删除、禁用规则或解绑标靶会取消相应活动告警。
完整更新或禁用示例
下例假设新增示例创建的规则 ID 为 12。禁用它时仍提交完整位移规则;若要更新阈值并启用,将 enabled 改为 true 并修改相应 enter。禁用活动规则会产生 CANCELLED,已有告警不会被标记成测量恢复。
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
update_request = pb.RpcRequest(schema_version=1, req_id=91)
rule = update_request.apply_alarm_rules.upsert.add().displacement_limit
rule.common.id = 12 # 替换成设备实际返回的 created_ids
rule.common.name = "X 方向位移"
rule.common.enabled = False
rule.target_ids.append("1")
rule.metric = pb.ALARM_METRIC_DX
rule.direction = pb.DISPLACEMENT_DIRECTION_BIDIRECTIONAL
rule.levels.alert.enter = 5.0
rule.levels.alert.enter_for_ms = 1000
rule.levels.action.enter = 10.0
rule.levels.action.enter_for_ms = 1000
rule.recover_for_ms = 2000
payload = update_request.SerializeToString(deterministic=True)
{
"reqId": 91,
"method": "applyAlarmRules",
"params": {
"upsert": [{
"id": 12,
"name": "X 方向位移",
"enabled": false,
"type": "DISP_LIMIT",
"targetIds": ["1"],
"metric": "DX",
"direction": "BIDIRECTIONAL",
"levels": {
"ALERT": {"enter": 5.0, "enterForMs": 1000},
"ACTION": {"enter": 10.0, "enterForMs": 1000}
},
"recoverForMs": 2000
}]
}
}
删除示例
删除只需 ID,不需要重传规则。若规则正在告警,会终止其活动生命周期。
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
remove_request = pb.RpcRequest(schema_version=1, req_id=92)
remove_request.apply_alarm_rules.delete_ids.append(12)
payload = remove_request.SerializeToString(deterministic=True)
{"reqId":92,"method":"applyAlarmRules","params":{"deleteIds":[12]}}
回读与分页示例
listAlarmRules 不接受 limit 或 ID 过滤参数,页大小由设备控制。第一页不传 cursor,后续使用响应中的游标,直至响应不再携带游标。
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
list_request = pb.RpcRequest(schema_version=1, req_id=93)
list_request.list_alarm_rules.SetInParent()
# 后续页:list_request.list_alarm_rules.cursor = 上一页响应.next_cursor
payload = list_request.SerializeToString(deterministic=True)
{"reqId":93,"method":"listAlarmRules","params":{}}
JSON 后续请求将 params 替换为 {"cursor":"上一页返回的 nextCursor"},并使用新的 reqId。回读时默认值可能省略,例如缺省的 recoverForMs=2000、空动作数组和默认动作选择器;客户端应按默认语义补齐编辑表单。阈值使用浮点数,JSON 回读可能出现 0.20000000298023224 这样的二进制浮点展开;按数值及合理误差比较,不按小数字符串判断配置是否保存成功。
遥测数据
设备通过同一个 telemetry 主题上报位移数据、环境数据和设备状态数据。
主题: vdm/{deviceId}/telemetry
位移数据:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
message = pb.Telemetry(schema_version=1)
message.displacement.sample_frequency_hz = 3
message.displacement.first_sample_timestamp_ms = 1734567890123
target = message.displacement.targets.add(target_id="1")
target.dx.extend([0.01, 0.02, 0.03])
target.dy.extend([0.00, 0.01, 0.01])
payload = message.SerializeToString(deterministic=True)
{"disp":{"t":1734567890,"f":3,"d":{"1":{"dx":[0.01,0.02,0.03],"dy":[0.00,0.01,0.01]}}}}
| Protobuf 字段 | JSON 字段 | 说明 |
|---|
sample_frequency_hz | disp.f | 本次位移数组的采样频率,单位 Hz。例如 5 Hz 表示相邻样本约 200 ms |
first_sample_timestamp_ms | disp.t | 本批次首个样本的 Unix 毫秒时间戳;JSON 的 disp.t 保持既有 Unix 秒格式 |
targets[].target_id | disp.d 的 key | 标靶 ID |
targets[].dx | disp.d.{targetId}.dx | X 方向位移数组,单位 mm |
targets[].dy | disp.d.{targetId}.dy | Y 方向位移数组,单位 mm |
位移数据包含 X/Y 方向。reportMetrics=["dx"] 或 ["dy"] 时,JSON 省略未选择的方向 key,Protobuf 对应 repeated 字段为空;缺省值为 ["dx","dy"]。非空数组使用同一批次采样网格,云端按 first_sample_timestamp_ms + round(i × 1000 / sample_frequency_hz) 还原第 i 个样本时间。
环境数据:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
message = pb.Telemetry(schema_version=1)
message.environment.ts = 1781686005
message.environment.temperature_c = 38.57
message.environment.humidity_percent_rh = 25.86
message.environment.pressure_hpa = 1002.8
payload = message.SerializeToString(deterministic=True)
{"env":{"t":1781686005,"temperature":38.57,"humidity":25.86,"pressure":1002.8}}
| Protobuf 字段 | JSON 字段 | 说明 |
|---|
environment.ts | env.t | Unix 时间戳,单位秒 |
environment.temperature_c | env.temperature | 温度,单位 °C |
environment.humidity_percent_rh | env.humidity | 湿度,单位 %RH |
environment.pressure_hpa | env.pressure | 气压,单位 hPa |
设备状态数据:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
message = pb.Telemetry(schema_version=1)
message.device_status.ts = 1781686005
message.device_status.input_voltage_v = 12.11
message.device_status.lte_dbm = -71
payload = message.SerializeToString(deterministic=True)
{"device":{"t":1781686005,"vIn":12.11,"lteDbm":-71}}
| Protobuf 字段 | JSON 字段 | 说明 |
|---|
device_status.ts | device.t | Unix 时间戳,单位秒 |
device_status.input_voltage_v | device.vIn | 设备主电源输入电压,额定工作电压 12V,单位 V |
device_status.lte_dbm | device.lteDbm | LTE 接收信号强度,单位 dBm |
device_status 的公开状态字段以本表为准。除时间戳外,仅开放 vIn 和 lteDbm;二者均无有效采集值时不发送设备状态消息。
Protobuf 解析时使用 HasField("displacement")、HasField("environment") 或 HasField("device_status") 判断消息体。选择 JSON 时,对应判断 disp、env 或 device。
告警上报
设备通过主题 vdm/{deviceId}/3A 上报告警生命周期事件。连接选择 Protobuf 时使用 inteagle.vdm.mqtt.v1.Alarm;选择 JSON 时使用本节的 JSON 字段。
告警等级(3A)
严重程度从低到高为 ALERT < ALARM < ACTION。等级表示当前风险程度,是否触发抓拍等动作由规则的 actions 另行配置。
| Protobuf 枚举 | 数值 | JSON level | 含义 | 展示建议 |
|---|
ALARM_LEVEL_ALERT | 1 | ALERT | 预警,关注变化趋势 | 黄色 |
ALARM_LEVEL_ALARM | 2 | ALARM | 报警,需要处理 | 橙色 |
ALARM_LEVEL_ACTION | 3 | ACTION | 行动级/紧急,需要立即响应 | 红色 |
颜色是界面展示建议,不是设备传输字段。
level 为可选字段:TRIGGERED、ESCALATED、DEESCALATED 和活动状态的 SYNCED 携带当前等级;RECOVERED、CANCELLED 不设置 level。Python 应使用 HasField("level") 判断是否存在,不能把读取到的默认值 0 当成一个“正常等级”。JSON 结束事件直接省略 level,不要要求它为 "NORMAL" 或 null。
告警类型
| Protobuf 枚举 | 数值 | JSON type | 含义与适用范围 | Protobuf detail 分支 |
|---|
ALARM_TYPE_DISPLACEMENT_LIMIT | 1 | DISP_LIMIT | 位移超限,真实标靶限 MP | displacement |
ALARM_TYPE_DISPLACEMENT_RATE_LIMIT | 2 | DISP_RATE | 位移变化速率超限,真实标靶限 MP | displacement_rate |
ALARM_TYPE_TARGET_LOST | 3 | TARGET_LOST | 应当可见的标靶持续丢失,适用 MP/RP/CP | target_lost |
ALARM_TYPE_INPUT_VOLTAGE_LOW | 4 | VIN_LOW | 设备输入电压过低 | voltage |
ALARM_TYPE_BATTERY_VOLTAGE_LOW | 5 | VBAT_LOW | 设备电池电压过低 | voltage |
实际可配置范围以 getAlarmCaps 为准。设备离线由云端根据连接或最后在线时间判定,不属于设备主动上报的上述五种类型。
生命周期转换
| Protobuf 枚举 | 数值 | JSON transition | 含义 | alarmId 与 level |
|---|
ALARM_TRANSITION_TRIGGERED | 1 | TRIGGERED | 首次满足某个启用等级的条件 | 新 alarmId,携带进入的等级 |
ALARM_TRANSITION_ESCALATED | 2 | ESCALATED | 升至更高的启用等级,可跨级 | 原 alarmId,携带升级后的等级 |
ALARM_TRANSITION_DEESCALATED | 3 | DEESCALATED | 降至较低的启用等级,仍未恢复 | 原 alarmId,携带降级后的等级 |
ALARM_TRANSITION_RECOVERED | 4 | RECOVERED | 满足恢复条件,生命周期结束 | 原 alarmId,省略等级 |
ALARM_TRANSITION_CANCELLED | 5 | CANCELLED | 规则删除、禁用或标靶解绑等操作终止告警 | 原 alarmId,省略等级;不表示测量值已恢复 |
ALARM_TRANSITION_SYNCED | 6 | SYNCED | 显式同步已有活动状态 | 原 alarmId,携带当前等级;不算再次触发 |
设备状态不变化时不重复产生告警,也没有周期性告警心跳;重试使用原 eventId。云端不能因一段时间没有收到 3A 消息就自动判为恢复,重连后可用 getAlarmState 核对当前活动告警,用 listAlarmHistory 查询历史。
例如只启用 ALERT 和 ACTION 时,一个生命周期可以是:
stateDiagram-v2
[*] --> 正常
正常 --> ALERT: TRIGGERED
正常 --> ACTION: TRIGGERED,直接满足高等级
ALERT --> ACTION: ESCALATED
ACTION --> ALERT: DEESCALATED
ALERT --> 结束: RECOVERED 或 CANCELLED
ACTION --> 结束: RECOVERED 或 CANCELLED
结束 --> [*]图中的“正常”和“结束”用于说明生命周期,不是 AlarmLevel 枚举。下次重新触发会创建新的 alarmId。
消息字段与类型详情
| Protobuf 字段 | JSON 字段 | 类型与说明 |
|---|
schema_version | 无 | uint32,当前必须为 1 |
event_id | eventId | uint64 / 十进制字符串,一次状态变化的 ID |
alarm_id | alarmId | uint64 / 十进制字符串,一次完整生命周期的 ID |
rule_id | ruleId | uint32,对应触发告警的规则 ID |
alarm_type | type | AlarmType / 上表的业务类型字符串 |
level | level | optional AlarmLevel / 大写等级字符串,结束事件不设置 |
transition | transition | AlarmTransition / 上表的转换字符串 |
ts | ts | uint64 / Number,Unix 秒时间戳 |
detail(oneof) | detail | 与告警类型匹配的详情;Protobuf 只设置对应分支 |
各类型的详情如下,表中 Protobuf 字段相对于其分支列出:
| 类型 | Protobuf 字段 → JSON detail 字段 | 含义 |
|---|
DISP_LIMIT | target_id → targetId;metric → metric;direction → direction;value_mm → value;limit_mm → limit | 标靶、指标、方向、观测位移和阈值,数值单位为 mm;JSON 不额外上报 unit |
DISP_RATE | target_id → targetId;metric → metric;direction → direction;value → value;limit → limit;unit → unit;window_ms → windowMs | 观测速率、阈值、单位和窗口长度;单位为 mm/s、mm/h 或 mm/day |
TARGET_LOST | target_id → targetId | 只有标靶 ID,不携带 value、limit 或丢失时长 |
VIN_LOW / VBAT_LOW | value_v → value;limit_v → limit | 电压和阈值,单位 V;不携带 targetId 或 unit |
value、limit 及其 Protobuf 对应字段均可省略,缺失不等于 0;SYNCED 不携带这两个观测字段。详情是事件产生时冻结的数据,不能用当前规则配置反推历史事件的阈值。降级或恢复事件的 limit 表示离开等级的阈值。
当前位移和速率规则开放 ALARM_METRIC_DX=1 / DX、ALARM_METRIC_DY=2 / DY。方向枚举为:
| Protobuf 枚举 | 数值 | JSON direction | 用于比较的值 |
|---|
DISPLACEMENT_DIRECTION_POSITIVE | 1 | POSITIVE | 原始值 |
DISPLACEMENT_DIRECTION_NEGATIVE | 2 | NEGATIVE | 原始值取反 |
DISPLACEMENT_DIRECTION_BIDIRECTIONAL | 3 | BIDIRECTIONAL | 原始值的绝对值 |
事件中的 value 保留原始正负号,limit 使用正的阈值幅值。例如 DX + NEGATIVE 的 value=-5.2、limit=5.0 表示负 X 方向越限。
上报示例
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
message = pb.Alarm(
schema_version=1,
event_id=7,
alarm_id=6,
rule_id=12,
alarm_type=pb.ALARM_TYPE_DISPLACEMENT_LIMIT,
level=pb.ALARM_LEVEL_ALERT,
transition=pb.ALARM_TRANSITION_TRIGGERED,
ts=1734567890,
)
message.displacement.target_id = "1"
message.displacement.metric = pb.ALARM_METRIC_DX
message.displacement.direction = pb.DISPLACEMENT_DIRECTION_BIDIRECTIONAL
message.displacement.value_mm = 5.2
message.displacement.limit_mm = 5.0
payload = message.SerializeToString(deterministic=True)
{
"eventId": "7",
"alarmId": "6",
"ruleId": 12,
"type": "DISP_LIMIT",
"level": "ALERT",
"transition": "TRIGGERED",
"ts": 1734567890,
"detail": {
"targetId": "1",
"metric": "DX",
"direction": "BIDIRECTIONAL",
"value": 5.2,
"limit": 5.0
}
}
alarmId 表示一次完整告警生命周期,eventId 表示该生命周期中的一次状态变化。RECOVERED 或 CANCELLED 表示该 alarmId 生命周期结束。系统集成商平台以 deviceId + alarmId 作为生命周期主键,以 deviceId + eventId 作为状态变化和抓拍图像去重键;不要根据 ID 位布局互相推导。
同一生命周期的恢复消息示例(新的 eventId,沿用 alarmId,省略 level):
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
recovered = pb.Alarm(
schema_version=1, event_id=8, alarm_id=6, rule_id=12,
alarm_type=pb.ALARM_TYPE_DISPLACEMENT_LIMIT,
transition=pb.ALARM_TRANSITION_RECOVERED, ts=1734567900,
)
recovered.displacement.CopyFrom(pb.DisplacementAlarmDetail(
target_id="1", metric=pb.ALARM_METRIC_DX,
direction=pb.DISPLACEMENT_DIRECTION_BIDIRECTIONAL,
value_mm=4.0, limit_mm=5.0,
))
payload = recovered.SerializeToString(deterministic=True)
{
"eventId": "8",
"alarmId": "6",
"ruleId": 12,
"type": "DISP_LIMIT",
"transition": "RECOVERED",
"ts": 1734567900,
"detail": {
"targetId": "1",
"metric": "DX",
"direction": "BIDIRECTIONAL",
"value": 4.0,
"limit": 5.0
}
}
ackEvidencePackage 仅确认抓拍图像包已完整接收,不会确认或清除告警;ACTION 等级也不代表抓拍已成功,应通过 event 中的图像状态或 getEvidenceStatus 查询结果。
事件上报
设备通过 vdm/{deviceId}/event 上报事件。
标靶参考位置初始化结果:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
message = pb.Event(
schema_version=1,
ts=1781767001,
event_type=pb.EVENT_TYPE_REF_INIT_RESULT,
)
message.ref_init_result.successful_target_ids.append("1")
message.ref_init_result.failed_targets.add(
target_id="2",
code=102,
)
payload = message.SerializeToString(deterministic=True)
{
"type": "REF_INIT_RESULT",
"ts": 1781767001,
"detail": {
"ok": ["1"],
"fail": [
{"id": "2", "code": 102}
]
}
}
ref_init_result.successful_target_ids 为成功标靶 ID 列表,failed_targets 为失败标靶列表。
失败项携带数值错误码。JSON 对应字段为 detail.ok 和 detail.fail。
其他公共事件如下:
Protobuf EventType | JSON type | 明细 |
|---|
EVENT_TYPE_CRUISE_REACHED | CRUISE_REACHED | 只包含 pathId、pointId、pan、tilt 中已知字段 |
EVENT_TYPE_TARGET_TRACKING | TARGET_TRACKING | targetId 和稳定状态 LOST 或 TRACKING |
规则产生的标靶丢失通过 3A 上报。
初始化时间通过 Attributes.target_list.targets[].initialized_at_s 或 getTargets 获取;JSON 对应字段为 targets[].initializedAt。
属性上报
设备通过 vdm/{deviceId}/attributes 上报属性。连接成功后会上报基础属性;属性变化时会上报增量属性。attributes 不是历史状态查询通道,订阅方需要当前标靶快照时应调用 getTargets。
基础属性示例:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
message = pb.Attributes(
schema_version=1,
device_id="XXXXXXXXXXXX",
device_model="V1",
firmware_version="1.2.3",
resolution="3840x2160",
measurement_status=pb.MEASUREMENT_STATUS_IDLE,
sample_frequency_hz=5,
show_roi=True,
show_timestamp=True,
show_sensor_id=True,
)
message.report_metrics.values.extend([
pb.REPORT_METRIC_DX,
pb.REPORT_METRIC_DY,
])
payload = message.SerializeToString(deterministic=True)
{
"deviceId": "XXXXXXXXXXXX",
"deviceModel": "V1",
"fwVer": "1.2.3",
"resolution": "3840x2160",
"measureStatus": "Idle",
"sampleFreq": 5,
"reportMetrics": ["dx", "dy"],
"showROI": true,
"showTs": true,
"showSensorId": true
}
标靶属性变化时,target_list 按精简摘要格式上报;单独调用 getTargets 只通过 RPC 响应返回,不额外触发 Attributes.target_list:
from generated import inteagle_vdm_mqtt_v1_pb2 as pb
message = pb.Attributes(schema_version=1)
target = message.target_list.targets.add(
target_id="1",
sensor_id=0,
roi=pb.Roi(x=1000, y=500, width=400, height=400),
distance_m=5.0,
role=pb.TARGET_ROLE_MP,
target_model=pb.TARGET_MODEL_T100,
skip_measurement=False,
initialized=True,
initialized_at_s=1781767000,
)
payload = message.SerializeToString(deterministic=True)
{
"targets": [
{
"targetId": "1",
"sensorId": 0,
"roi": {"x": 1000, "y": 500, "width": 400, "height": 400},
"distance": 5.0,
"role": "MP",
"targetModel": "T100",
"skipMeasurement": false,
"initialized": true,
"initializedAt": 1781767000
}
]
}
| Protobuf 字段 | JSON 字段 | 说明 |
|---|
target_list.targets[].target_id | targets[].targetId | 标靶 ID |
target_list.targets[].sensor_id | targets[].sensorId | 视觉传感器 ID |
target_list.targets[].roi | targets[].roi | 标靶 ROI |
target_list.targets[].distance_m | targets[].distance | 测量距离,单位 m |
target_list.targets[].role | targets[].role | 标靶角色枚举 |
target_list.targets[].target_model | targets[].targetModel | 标靶型号枚举 |
target_list.targets[].skip_measurement | targets[].skipMeasurement | 是否跳过测量 |
target_list.targets[].initialized | targets[].initialized | 是否已经初始化参考位置 |
target_list.targets[].initialized_at_s | targets[].initializedAt | optional Unix 秒时间戳 |
targets 属性只包含对外集成需要的摘要字段。
API 支持矩阵
| API | V1 | V1 Pro | V1 Lite | V2-H | V2-W | V2-K | O1 | O2-H | X1 | X2-H |
|---|
| 基础 API | | | | | | | | | | |
| getAttr | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| setAttr | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| reboot | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| syncTime | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 标靶管理 | | | | | | | | | | |
| initRefTargets | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| addTargets | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| getTargets | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| setTargets | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| deleteTargets | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 测量控制 | | | | | | | | | | |
| startMeasurement | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| stopMeasurement | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| setLightLevel | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| getLightLevel | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| snapshot | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 告警抓拍图像(0.8.5+) | | | | | | | | | | |
| getEvidenceStatus | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| retryEvidence | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| ackEvidencePackage | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 告警管理(JSON / Protobuf) | | | | | | | | | | |
| getAlarmCaps | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| listAlarmRules | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| device_status=pb.DEVICE_STATUS_IDLE, | | | | | | | | | | |
| applyAlarmRules | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| getAlarmState | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| listAlarmHistory | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| ISP 控制 | | | | | | | | | | |
| ispCtl | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 电机控制 | | | | | | | | | | |
| setMotorAngle | - | - | - | - | - | - | ✓ | ✓ | ✓ | ✓ |
| getMotorAngle | - | - | - | - | - | - | ✓ | ✓ | ✓ | ✓ |
| setMotorZero | - | - | - | - | - | - | ✓ | ✓ | ✓ | ✓ |
| enableMotor | - | - | - | - | - | - | ✓ | ✓ | ✓ | ✓ |
| disableMotor | - | - | - | - | - | - | ✓ | ✓ | ✓ | ✓ |
| 巡航控制 | | | | | | | | | | |
| getCruisePaths | - | - | - | - | - | - | ✓ | ✓ | ✓ | ✓ |
错误码
JSON code 与 Protobuf RpcResponse.code 含义一致:0 表示成功,其他值表示失败。
“deviceStatus”: “Idle”,
| 码 | 说明 | 适用型号 |
|---|
| 0 | 成功 | 全部 |
| 1 | 通用失败(无法进一步分类) | 全部 |
| 2 | 参数缺失、类型错误或取值无效 | 全部 |
| 3 | 方法、action 或 Payload 格式不支持 | 全部 |
| 4 | 请求被限流 | 全部 |
| 5 | 请求执行超时 | 全部 |
| 6 | 资源状态已变化,请刷新后重试 | 全部 |
| 100 | 标靶不存在,或对当前连接不可见 | 全部 |
| 102 | 标靶初始化失败 | 全部 |
| 104 | 标靶丢失 | 全部 |
| 200 | 测量未启动 | 全部 |
| 201 | 测量已在运行 | 全部 |
| 300 | 电机不可用 | O1, O2-H, X1, X2-H |
| 302 | 电机运动中 | O1, O2-H, X1, X2-H |
| 303 | 电机到达限位 | X1, X2-H |
| 310 | 垂直电机不可用 | O1, O2-H |
| 400 | 巡航功能不可用 | O1, O2-H, X1, X2-H |
| 403 | 巡航已在运行 | O1, O2-H, X1, X2-H |
RPC 失败仅通过外层数字 code 表示,不携带业务数据。
图片数据
设备通过 image 主题上报普通图片和告警抓拍图像包分块。该 Topic 始终使用二进制 Payload,不使用 JSON 或 Protobuf 包装。
主题: vdm/{deviceId}/image
格式分派
解析器必须先检查第 0 字节,不能把所有 image Payload 都按旧 8 字节 Header 解析:
| 第 0 字节 | 格式 | Payload |
|---|
1 | 普通图片 | 8 字节可扩展 Header + 完整 JPEG |
2 | 告警抓拍图像包分块 | 76 字节固定 Header + 一个 USTAR 分块 |
普通图片继续使用可扩展 Header:
| 偏移 | 长度 | 字段 | 说明 |
|---|
| 0 | 1 | version | 消息类型 / Header 版本,当前 0x01 |
| 1 | 1 | headerLen | Header 总长度(字节) |
| 2 | 1 | sensorId | 视觉传感器 ID(单目为 0,双目为 0 或 1) |
| 3 | 1 | type | 图片类型(见下表) |
| 4 | 4 | timestamp | Unix 秒时间戳(uint32,大端) |
headerLen | 剩余 | imageData | 完整 JPEG 二进制数据 |
图片类型 (type):
| 值 | 类型 | 说明 |
|---|
| 0 | snapshot | RPC 触发的快照 |
| 1 | 3A | 告警触发的抓图 |
| 2 | periodic | 定时抓图 |
普通图片示例
01 08 00 01 67 72 B3 D6 FF D8 FF E0 ...
│ │ │ │ └──────────┴─ timestamp: 1735533526
│ │ │ └─ type: 1 (3A)
│ │ └─ sensorId: 0
│ └─ headerLen: 8
└─ version: 1
headerLen 可能在未来版本增加。解析时应从 headerLen 位置取 JPEG,不应将未识别的扩展字段当作图片字节。
类型 2 使用 76 字节固定 Header。除单字节字段和原始 SHA-256 外,所有整数均使用大端序:
| 偏移 | 长度 | 字段 | 说明 |
|---|
| 0 | 1 | messageType | 固定为 2 |
| 1 | 1 | headerLen | 固定为 76 |
| 2 | 1 | packageFormat | 固定为 1,表示确定性 USTAR |
| 3 | 1 | evidenceKind | 抓拍图像固定为 1(SNAPSHOT) |
| 4 | 8 | eventId | 触发抓拍的告警状态变化 ID,必须非 0 |
| 12 | 8 | packageLength | 完整 USTAR 包长度,范围 1..32 MiB |
| 20 | 32 | packageSha256 | 完整 USTAR 包的 SHA-256 原始字节 |
| 52 | 4 | chunkIndex | 当前分块索引,从 0 开始 |
| 56 | 4 | chunkCount | 完整抓拍图像包的分块数 |
| 60 | 8 | chunkOffset | 当前分块在完整抓拍图像包中的字节偏移 |
| 68 | 4 | chunkLength | Header 后当前分块的字节数 |
| 72 | 4 | flags | 当前必须为 0 |
| 76 | chunkLength | chunkData | USTAR 分块字节 |
分块大小固定为 128 KiB,只有最后一块可以更短。解析器必须检查:
chunkCount == ceil(packageLength / 128 KiB)。chunkOffset == chunkIndex × 128 KiB,chunkLength 等于该位置的预期长度。- Payload 总长度严格等于
76 + chunkLength,flags == 0。 - 第一块的 TAR Header 必须包含
ustar 标识。 - 重组后的字节数与
packageLength 相同,SHA-256 与 packageSha256 相同。 - USTAR 第一项必须是
manifest.json,所有成员必须是安全相对路径下的普通文件。
抓拍图像包分块重组与确认
MQTT QoS 1 允许重复投递,不保证业务消息与其他 Topic 的到达顺序。推荐使用磁盘优先、有界的重组流程:
- 以
deviceId + eventId + packageSha256 + chunkIndex 作为分块幂等键,相同键再次到达时必须比对字节内容。 - 收到新分块时按
chunkOffset 写入临时文件,并限制同时未完成的事件数和磁盘占用。 - 全部分块到齐后校验 USTAR、总长度和 SHA-256,再原子替换为最终包文件。
- 完整包持久化后,保存本地接收回执并从同一 MQTT 连接调用
ackEvidencePackage。
生产实现应持久化已收分块 bitmap,使接收服务重启后可以继续去重和重组。官方示例仓库的 python/evidence_receiver.py 提供可运行的落盘、校验和 ACK 参考实现。
官方 Python、Go、Java 和 JavaScript/TypeScript SDK 均提供一致的有界磁盘重组、完整性与安全 USTAR 校验和原子接收回执能力;Python 文件另外提供可直接运行的接收进程示例。
图片规格
| 项目 | 规格 |
|---|
| 格式 | JPEG |
| 普通快照标准分辨率 | 640x480 |
| 普通图片典型大小 | 50KB - 200KB |
| 单个告警抓拍图像包上限 | 32 MiB |
| 单次告警抓拍图像数上限 | 64 |
11 - X1
X1 单目双轴旋转式视觉位移计 API 参考手册
型号: X1 | 版本: 1.0 | 更新: 2026-09-11
X1 是单目双轴旋转式视觉位移计,支持水平和垂直云台控制及自动巡航。
1. 快速开始
连接与数据接收见快速入门。完整代码见 GitHub SDK 和告警接入示例。
2. API 列表
基础 API
| 方法 | 说明 |
|---|
getAttr | 获取属性 |
setAttr | 设置属性 |
reboot | 重启设备 |
syncTime | 同步时间 |
标靶管理 API
| 方法 | 说明 |
|---|
initRefTargets | 初始化标靶参考位置 |
addTargets | 添加标靶 |
getTargets | 获取标靶列表 |
setTargets | 更新标靶配置 |
deleteTargets | 删除标靶 |
测量控制 API
| 方法 | 说明 |
|---|
startMeasurement | 启动测量 |
stopMeasurement | 停止测量 |
setLightLevel | 设置补光灯发光挡位 |
getLightLevel | 获取补光灯发光挡位 |
snapshot | 获取快照 |
电机控制 API
| 方法 | 说明 |
|---|
setMotorAngle | 设置电机角度 |
getMotorAngle | 获取电机角度 |
setMotorZero | 设置电机零点 |
enableMotor | 启用电机 |
disableMotor | 禁用电机 |
ISP 控制 API
| 方法 | 说明 |
|---|
ispCtl | ISP 图像参数控制(曝光、降噪、翻转/镜像等) |
巡航查询 API
3. API 详情
标靶管理 API 的完整字段约束参考 API参考。
initRefTargets
对指定标靶执行参考位置初始化/标定。通常先通过 addTargets 或 setTargets 保存标靶配置;初始化时可只传 targetId。如需覆盖保存配置,可在本次请求中提供 roi、distance 或 targetModel。
Protobuf / JSON 示例。
addTargets
在已有标靶基础上添加新标靶。
Protobuf / JSON 示例。
getTargets
获取当前标靶列表。
响应字段:
| 字段 | 类型 | 说明 |
|---|
targets[].targetId | String | 标靶 ID,1-64 字节,非空,不包含控制字符 |
targets[].sensorId | Integer | 视觉传感器 ID,单目设备为 0 |
targets[].roi | Object | 标靶 ROI 区域 |
targets[].distance | Float | 测量距离,单位 m |
targets[].role | String | 标靶角色:MP 为被测点,RP 为稳定基准点,CP 为控制/校核点 |
targets[].targetModel | String | 标靶型号,取值 T10、T20、T50、T100、T200 |
targets[].skipMeasurement | Boolean | 是否跳过测量 |
Protobuf / JSON 示例。
setTargets
更新已有标靶的配置。
Protobuf / JSON 示例。
deleteTargets
删除指定标靶。
Protobuf / JSON 示例。
setMotorAngle
设置电机角度。
参数:
| 字段 | 类型 | 范围 | 说明 |
|---|
pan | Float | -180 ~ 180 | 水平角度 (°) |
tilt | Float | -90 ~ 90 | 垂直角度 (°) |
speed | Integer | 1-100 | 转动速度 (%) |
请求:
{"reqId": 1, "method": "setMotorAngle", "params": {"pan": 45.0, "tilt": -10.0, "speed": 50}}
getMotorAngle
获取当前电机角度。
请求:
{"reqId": 1, "method": "getMotorAngle", "params": {}}
响应:
{"reqId": 1, "code": 0, "data": {"pan": 45.0, "tilt": -10.0}}
ispCtl
ISP 图像参数控制,支持曝光、降噪、画面翻转/镜像/旋转等设置。
getStatus - 获取当前 ISP 状态
请求:
{"reqId": 1, "method": "ispCtl", "params": {
"action": "getStatus"
}}
响应:
{"reqId": 1, "code": 0, "data": {
"vdmModeEnabled": true,
"exposureLocked": false,
"exposureMode": "manual",
"nrLevel": "mediumNoise",
"enable3dnr": true,
"exposurePreset": "medium",
"cameraCount": 1,
"flip": false,
"mirror": false,
"rotation": 0,
"exposureInfo": {
"iso": 200,
"expTime": 20000,
"aGain": 1024,
"dGain": 1024,
"ispDGain": 256,
"aveLum": 128
}
}}
setConfig - 设置画面翻转/镜像/旋转
支持画面翻转、镜像和旋转。
注意:翻转/镜像/旋转设置需要重启设备后生效。 设置后配置会自动持久化,多相机设备(如双目型号)所有相机会同时生效。
参数:
| 字段 | 类型 | 必填 | 说明 |
|---|
flip | Boolean | 否 | 画面垂直翻转 |
mirror | Boolean | 否 | 画面水平镜像 |
rotation | Integer | 否 | 画面旋转角度,仅支持 0/90/180/270 |
请求 - 启用垂直翻转:
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setConfig",
"config": {
"flip": true
}
}}
请求 - 启用水平镜像:
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setConfig",
"config": {
"mirror": true
}
}}
请求 - 同时启用翻转和镜像(等效 180° 旋转):
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setConfig",
"config": {
"flip": true,
"mirror": true
}
}}
请求 - 恢复默认(关闭翻转和镜像):
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setConfig",
"config": {
"flip": false,
"mirror": false
}
}}
请求 - 设置旋转 90°:
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setConfig",
"config": {
"rotation": 90
}
}}
请求 - 设置旋转 270°:
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setConfig",
"config": {
"rotation": 270
}
}}
请求 - 取消旋转:
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setConfig",
"config": {
"rotation": 0
}
}}
响应:
setConfig - 设置曝光参数
参数:
| 字段 | 类型 | 必填 | 说明 |
|---|
exposureMode | String | 否 | 曝光模式:auto, semiAuto, manual |
manualExpTime | Integer | 否 | 手动曝光时间 (微秒),范围 100-100000 |
manualAgain | Integer | 否 | 手动模拟增益,1024=1x, 4096=4x, 8192=8x |
nrLevel | String | 否 | 降噪等级:off, lowNoise, mediumNoise, highNoise, veryHighNoise |
enable3dnr | Boolean | 否 | 是否启用 3DNR 时域降噪 |
请求 - 手动曝光 20ms + 4x 增益:
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setConfig",
"config": {
"exposureMode": "manual",
"manualExpTime": 20000,
"manualAgain": 4096
}
}}
setConfig - 多相机独立配置
通过 isp 数组对每个相机独立配置曝光参数。
参数(isp 数组项):
| 字段 | 类型 | 必填 | 说明 |
|---|
cameraId | Integer | 是 | 相机索引 (0, 1, …) |
expTime | Integer | 否 | 曝光时间 (微秒),范围 1000-40000 |
gain | Integer | 否 | 增益,范围 1024-16384 |
flip | Boolean | 否 | 画面垂直翻转(重启后生效) |
mirror | Boolean | 否 | 画面水平镜像(重启后生效) |
rotation | Integer | 否 | 画面旋转角度 0/90/180/270(重启后生效) |
请求:
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setConfig",
"isp": [
{"cameraId": 0, "expTime": 20000, "gain": 4096, "flip": true, "mirror": false}
]
}}
setExposurePreset - 设置曝光档位
参数:
| 字段 | 类型 | 必填 | 说明 |
|---|
exposurePreset | String | 是 | 档位:low, medium, high |
| 档位 | 曝光时间 | 增益 | 场景 |
|---|
low | 10ms | 1x | 近距离/强 IR 补光 |
medium | 20ms | 1x | 标准模式 |
high | 30ms | 10x | 远距离/弱 IR 补光 |
请求:
{"reqId": 1, "method": "ispCtl", "params": {
"action": "setExposurePreset",
"exposurePreset": "medium"
}}
4. 遥测数据
设备通过 vdm/{deviceId}/telemetry 上报位移和设备状态;环境字段按设备传感器能力提供。字段、单位及 Protobuf / JSON 示例见遥测数据。
5. 公共告警、事件和错误