做城市级AI视觉或物联网平台,经常会遇到一个问题:
设备接得越多,平台反而越难维护。
摄像头可能走ONVIF、GB/T 28181或者厂商SDK;环境传感器可能走MQTT、Modbus;还有门禁、道闸、边缘终端以及各种私有协议。
如果每增加一种设备,上层业务就增加一套适配代码,最终很容易变成:
设备品牌 × 协议 × 业务系统 = 大量重复开发。
所以设备接入中间件真正要解决的,不是"支持多少协议",而是:
怎么把不同设备转换成统一、稳定、可扩展的能力模型。
一、先把整个设备接入链路拆开
一个比较清晰的架构可以分成5层:
text
┌────────────────────────────────────┐
│ 业务应用层:AI分析 / 告警 / GIS / 业务系统 │
├────────────────────────────────────┤
│ 设备抽象层:属性 / 事件 / 服务 / 能力模型 │
├────────────────────────────────────┤
│ 协议适配层:ONVIF / GB28181 / MQTT / SDK │
├────────────────────────────────────┤
│ 接入网关层:TCP / UDP / HTTP / SIP等 │
├────────────────────────────────────┤
│ 网络与设备层:以太网 / 4G / 5G / LoRaWAN等 │
└────────────────────────────────────┘
核心原则只有一句:
上层业务不直接依赖具体厂商协议。
比如业务系统想做"摄像机预置位控制",调用的应该是统一设备服务,而不是:
text
if 海康:
调海康SDK
elif 大华:
调大华SDK
elif 宇视:
调宇视SDK
否则设备越多,技术债越重。
二、协议适配层:不要把"协议"和"设备能力"混在一起
不同协议解决的问题并不完全相同。
例如:
GB/T 28181主要用于视频监控联网场景中的注册、目录、点播和媒体传输等能力。
ONVIF更偏向IP安防设备互操作。Profile T支持高级视频流、H.264/H.265、事件和元数据等能力;Profile M进一步标准化分析元数据和事件。
MQTT则是典型的轻量级发布/订阅消息协议,很适合IoT设备和边缘节点消息通信。
所以工程上不要做成一个巨大的:
text
DeviceManager
更合理的是把协议实现做成插件。
例如:
text
drivers/
├── gb28181/
├── onvif/
├── mqtt/
├── modbus/
├── vendor_sdk/
└── custom/
每个Driver负责:
text
设备发现
→ 认证/注册
→ 建立连接
→ 数据解析
→ 状态上报
→ 指令转换
→ 心跳
→ 重连
→ 释放资源
这样新增设备时,改的是协议插件,而不是整个业务平台。
三、真正重要的是"统一设备模型"
协议适配完成以后,还需要再做一次抽象。
一个设备可以统一描述为:
json
{
"deviceId": "camera_001",
"deviceType": "camera",
"properties": {},
"events": [],
"services": [],
"capabilities": []
}
其中:
Properties表示设备当前状态,比如在线状态、码率、水位、温度。
Events表示设备产生的事件,例如离线、告警、目标出现。
Services表示设备可以执行的动作,比如抓图、PTZ控制、重启。
Capabilities表示设备具备哪些能力。
这样上层系统只需要面向统一模型开发。
底层到底是哪个品牌,业务层不需要知道。
四、视频设备要特别处理"能力差异"
这里很容易踩坑。
即使两台摄像机都支持ONVIF,也不意味着它们支持完全一样的功能。
有的支持PTZ;
有的不支持。
有的能够输出分析Metadata;
有的只能提供视频流。
ONVIF本身也是通过不同Profile定义互操作能力,而不是"支持ONVIF就什么都支持"。
所以设备模型里最好不要只有:
json
"online": true
而应该有Capability:
json
{
"capabilities": [
"LIVE_VIDEO",
"PTZ",
"SNAPSHOT",
"ANALYTICS_METADATA"
]
}
上层业务先判断能力,再决定功能是否可用。
这比大量厂商判断逻辑干净得多。
五、设备影子解决的不是"在线",而是"状态同步"
IoT场景中还有一个典型问题:
设备随时可能离线。
因此平台可以维护设备的逻辑状态,例如:
json
{
"deviceId": "sensor_001",
"reported": {
"samplingInterval": 30
},
"desired": {
"samplingInterval": 60
}
}
其中:
reported代表设备最后实际报告的状态。
desired代表平台希望设备达到的状态。
设备重新上线以后,再根据差异进行同步。
但这里需要特别注意:
设备影子不能等同于无限期指令队列。
例如:
"5分钟后开启闸门"
如果设备离线一天以后重新上线,再执行这条旧指令显然有风险。
所以异步指令至少还应该包含:
TTL、版本号、幂等ID和执行条件。
设备影子的核心是:
状态最终一致。
不是"离线期间所有命令都永久保存"。
六、消息总线:设备接入以后不要直接打业务接口
设备数量增加以后,另一个常见问题是:
设备事件直接调用业务服务。
结果任何一个业务服务变慢,都可能把接入链路拖死。
更合理的方式是:
text
设备
↓
接入网关
↓
协议标准化
↓
消息总线
↓
规则引擎 / AI / 告警 / 存储
例如:
text
camera.motion.detected
sensor.waterlevel.changed
device.offline
ai.object.detected
统一进入Kafka等消息系统。
这样接入层和业务层就能解耦。
七、规则引擎不要写死在业务代码里
城市感知系统往往需要大量规则。
例如:
text
水位 > 阈值
AND
持续时间 > 5min
AND
设备状态正常
THEN
产生异常事件
或者:
text
视频发现人员
AND
区域 = 禁入区域
AND
时间 = 非作业时段
THEN
触发复核
这类规则如果全部写进Java业务代码,每次改阈值都要发布版本。
更合理的是把:
事件 → 条件 → 动作
做成独立规则层。
复杂时序事件还可以交给Flink等流处理框架进行窗口计算和状态处理。
Flink的Checkpoint可以保存流处理状态,并在故障后恢复计算状态和流位置。
但要注意:
Checkpoint ≠ 整条系统天然Exactly Once。
端到端语义仍取决于Source、消息系统和Sink是否支持重放、事务或者幂等。
八、"接入网关无状态"这个说法其实不够准确
设备网关经常维护:
TCP连接;
SIP注册;
心跳;
Session;
设备在线状态。
因此它不可能完全无状态。
更准确的设计应该是:
连接状态本地化,业务状态外置化。
也就是:
text
本地维护:
Connection / Session
外部存储:
Device Metadata
Device Shadow
Business State
Event State
这样某个Gateway实例宕机以后,设备重新连接到其他节点时,业务状态仍然能够恢复。
目标不是:
让网关完全没有状态。
而是:
让节点故障不会造成不可恢复的业务状态丢失。
九、Redis Cluster和Sentinel不要混着写
这也是很多架构图里的常见错误。
Redis Sentinel主要解决非Redis Cluster部署下的监控和自动故障转移问题;Redis官方文档明确把它描述为"High availability for non-clustered Redis"。
所以架构设计时通常应该根据规模和业务需求选择:
text
Redis主从 + Sentinel
或者:
text
Redis Cluster
而不是简单写成:
text
Redis Cluster + Sentinel
十、城市级设备接入真正应该测什么?
不要只报一个:
"支持百万设备。"
设备接入平台至少应该拆开测:
连接能力
在线连接数、连接建立速度、重连风暴。
消息能力
消息TPS、消息大小、峰值突发。
视频能力
并发取流数、总入口带宽、转发带宽。
可靠性
单节点宕机恢复、消息积压恢复、设备重新注册。
稳定性
24小时、72小时甚至更长时间压测。
延迟
P50、P95、P99,而不是只报一个平均延迟。
例如"10万连接"这个数字本身没有意义。
你还必须说明:
text
什么CPU?
多少内存?
什么网卡?
连接是否空闲?
心跳周期多少?
单设备消息频率多少?
TLS是否开启?
没有测试环境的性能数字,基本无法横向比较。
十一、荆棘鸟智能为什么要做这一层?
对于AI视觉平台来说,多协议设备接入并不是最终目的。
真正的问题是:
怎么把不同品牌、不同协议的视频和感知设备,统一变成AI算法可以消费的数据。
在荆棘鸟智能的智慧云视ACV平台架构中,设备接入层更适合作为算法与真实设备之间的"翻译层"。
底层负责:
接设备。
中间层负责:
统一数据和能力。
上层再负责:
AI分析、事件处理和行业业务。
这样新增一个设备品牌时,不需要重写500+AI视觉算法;
增加一个新算法时,也不应该重新适配每一种摄像机。
这才是设备接入中间件真正需要解决的解耦问题。
总结
城市感知设备统一接入,真正难的不是:
"再支持一个协议。"
而是建立一套稳定的抽象:
text
设备
↓
协议
↓
统一模型
↓
事件总线
↓
规则与AI
↓
业务系统
一个可长期维护的物联网中间件,至少应该解决四件事:
协议插件化、设备模型统一、状态可恢复、业务与设备解耦。
设备规模越大,这几个基础设计越重要。
FAQ|物联网中间件常见问题
Q1:ONVIF和GB/T 28181有什么区别?
两者解决的问题存在重叠但定位不同。ONVIF主要解决IP安防产品之间的互操作,GB/T 28181主要服务于视频监控联网场景。工程中经常同时支持。
Q2:为什么需要设备抽象层?
因为同一种业务能力可能由不同厂商、不同协议实现。设备抽象层可以把底层差异转换成统一属性、事件、服务和能力模型。
Q3:设备影子有什么作用?
主要用于保存设备已上报状态和平台期望状态,支持设备离线后的状态同步。但异步控制还需要TTL、幂等和安全校验。
Q4:Kafka和MQTT是什么关系?
MQTT更偏设备侧轻量消息通信,Kafka更适合平台内部高吞吐事件流。两者可以存在于同一架构的不同位置。
Q5:设备网关应该设计成无状态吗?
不能完全无状态。长连接和协议Session通常必须保留本地状态,更合理的是尽量将业务状态外置,让节点具备故障恢复能力。
Q6:Flink Checkpoint能保证消息绝对不丢吗?
不能简单这么理解。Checkpoint可以恢复Flink状态,但端到端消息语义还与Source、Sink、事务和幂等设计有关。
Q7:城市级设备平台怎么验证能不能支撑百万设备?
必须通过分层压测验证连接、消息、视频、故障恢复和长期稳定性,并公布硬件、消息频率、网络和测试模型。单独报"百万设备"没有太大意义。
Q8:荆棘鸟智能的设备接入能力主要解决什么问题?
核心是将不同厂商、不同协议的视频和物联网设备统一接入,并通过标准设备模型向上层AI算法和业务系统提供一致的数据与能力接口。