ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

船岸一体边缘智能感知网络:摄像头接入与AI算法远程管理实战

船岸一体边缘智能感知网络:摄像头接入与AI算法远程管理实战 1. 一次海上断流事件暴露了摄像头接入后的真问题去年秋天我们负责的一条近海客滚船在航行途中突然和岸端管理平台失联岸上调度中心的大屏上十几个船载摄像头的画面全部凝固在断流前的最后一帧。排查到最后根因并不复杂——船上的4G聚合链路抖动加上岸端视频平台对RTSP拉流的会话超时设置太短导致整个视频接入网关被拖垮。这件事让我重新想明白一个问题把摄像头画面推回岸上这件事本身技术含量不高真正难的是在链路不稳、带宽有限的船岸场景里让感知这件事持续、可靠、可进化。我们做的这套东西内部代号叫船岸一体的边缘智能感知网络核心目标就一句话让船上能跑算法并且算法能被岸端远程管理。摄像头接入只是入口自定义算法加载才是灵魂。先说结论这套网络整体分成三层——设备接入层、边缘计算层、岸端管控层。设备接入层负责把船上海康、大华、宇视等各品牌摄像头统一接入屏蔽掉协议差异边缘计算层跑在船载工控机或者带AI算力的NVR上承担视频解码、算法推理、告警上报岸端管控层除了接收结构化告警还承担算法包的远程下发和版本管理。下面我按实际落地顺序把这套网络从架构设计到具体实施的完整过程拆开讲。2. 摄像头接入层海康设备接入的三种方式和我的取舍任何感知网络的地基都是接入。船载摄像头环境比陆上园区复杂得多品牌杂、协议乱、NVR新旧不一还有大量直接暴露在外的IP Camera。我们做过一次统计接入的设备里海康占了将近七成所以海康摄像头的接入方案基本决定了整个接入层的地基质量。2.1 海康摄像头直接接入RTSP拉流与Onvif探测的搭配最直接的方式是通过RTSP拉流。海康摄像头的RTSP地址格式大致如下rtsp://username:passwordip:554/Streaming/Channels/101这里的101代表主码流通道1102是主码流通道2201是子码流通道1。接入手写规则主码流用于岸端留存或事中取证子码流用于船端实时预览和算法轻量推理。如果摄像头有变焦或者多目后面还可能跟/Streaming/Channels/201?transportTCP这样的参数需要注意。但直接拉流有个明显问题——你不知道摄像头到底支持什么分辨率、什么编码格式、什么码率范围。这时候就要靠Onvif协议来做能力探测。海康的设备基本都支持Onvif我们用onvif-cli或者自己封装SOAP请求去拿设备的GetProfiles和GetStreamUri拿到设备真实的码流能力之后再动态拼接RTSP地址。这个步骤千万不要省我见过太多项目按照设备铭牌上的参数去配置码流结果实际拉流失败或花屏排查半天最后发现是分辨率写错了。2.2 NVR汇聚接入解决一条船几十路摄像头的接入压力船载环境里更大比例的摄像头会先接入船载NVR再通过NVR对外提供统一访问入口。这种场景下直接逐路拉取摄像头的RTSP流有两个问题一是摄像头IP段可能不固定二是NVR的管理通道和媒体通道分离逐个探测成本高。海康NVR对外提供了ISAPIIntelligent Security API我们可以通过HTTP接口查询通道列表、通道状态和RTSP地址映射关系。我这边常用的是这样几步用ISAPI的GET /ISAPI/System/deviceInfo确认设备型号和固件版本用GET /ISAPI/ContentMgmt/InputProxy/channels拿到NVR下挂的通道ID列表根据通道ID去查GET /ISAPI/ContentMgmt/InputProxy/channels/{ID}拿到通道对应的视频源信息最后拼出形如rtsp://admin:passwordnvr_ip:554/Streaming/Channels/{NVR内部通道号}01的地址。这里有一个大坑NVR的通道号和摄像头的物理连接顺序不一定一一对应。特别是做过通道改名、通道排序的老船你按通道1去拉流很可能拉到的是驾驶台而不是机舱。我们踩过之后强制要求每次接入时都要做一次通道指纹比对——拉一帧画面截取缩略图跟现场确认的物理位置比对确认无误后才录入配置库。2.3 海康4G摄像头接入的专项适配再说说最近很热的海康4G摄像头接入安防平台这件事。这个场景在船上其实比大家想象中更普遍——很多老船没有综合布线条件或者新增监控点位时拉网线成本太高于是直接装了4G摄像头靠运营商流量卡回传。海康4G摄像头的接入逻辑和有线摄像头最大的区别在于它没有固定内网IP而且默认是摄像头主动向平台发起注册和保活。所以平台侧要做两件事GB/T 28181国标接入海康4G摄像头支持GB28181协议可以配置SIP服务器地址主动注册到平台。我们在岸端部署了WVP-pro或者自己基于ZLMediaKit封装的一套GB28181信令服务让4G摄像头作为下级设备主动注册上来。这个方案最稳摄像头侧只配置服务器IP、端口和设备ID剩下的事情交给信令服务处理。4G网络保活与码率自适应4G链路不是固定带宽进出港、公海、近海不同场景下的信号强度差异很大。我们的接入网关对4G摄像头做了码率自适应——周期性检测拉流缓冲区的丢包率和延迟动态要求摄像头切换到更低的码率档位或者从主码流切到子码流。实测下来4G链路丢包率从5%降到1%以内画面虽然清晰度下降但至少不卡顿、不断流。这里给一个配置示例是我们在海康4G摄像头上常用的GB28181接入模板{ sip_server: 203.0.113.10, sip_port: 5060, device_id: 34020000001320000001, channel_id: 34020000001320000002, heartbeat_interval: 60, reg_expires: 3600, media_port_start: 10000, media_port_end: 20000, password: 加密后的鉴权密码 }需要特别提醒海康4G摄像头的密码策略和有线设备不同初始密码复杂度强制高而且Onvif用户和Web登录用户的权限绑定不一致。我们曾遇到过一次用Onvif账户能拉流但无法修改码率参数查了半天才发现是摄像头固件里Onvif用户默认没有写权限。所以大规模接入前建议先用一台设备把协议和权限矩阵测透。2.4 统一接入网关的抽象设计不管是直连摄像头、NVR汇聚还是4G国标接入最后都要进我们的统一视频接入网关。这个网关的核心是一个设备抽象层把不同品牌、不同协议的摄像头统一抽象成标准的视频源对象class VideoSource: def __init__(self, source_id, brand, protocol, stream_url): self.source_id source_id self.brand brand self.protocol protocol self.stream_url stream_url self.status offline self.metadata {} # 设备型号、通道、码率等 def get_stream(self, stream_typemain): # 根据协议类型动态拼接RTSP/GB28181拉流地址 pass def health_check(self): # 周期探测设备在线状态维护感知网络的设备台账 pass网关里的健康检查机制我建议不要只做简单的ICMP ping而是要直接尝试拉流或调用ISAPI查询设备状态因为很多摄像头Web页面能访问但流拉不出来这时设备台账里应该标记为亚健康而非在线。我们初期就是没做这一层导致岸端以为船上所有设备都正常结果多个点位画面已经冻结了几天。3. 边缘计算层的AI推理底座把算法从云端下沉到船端摄像头接入只是第一步。如果所有视频都回传岸端做AI分析对船岸链路的带宽要求太高也不现实。我们的设计理念是感知在船端决策在岸端数据按需回传。也就是说船端边缘节点要能直接跑AI算法本地完成结构化分析只把告警事件和关键帧上传。3.1 边缘节点的硬件选型逻辑选型这件事上我们没有盲目追求高算力而是从三个维度去权衡功耗与环境适应船载配电环境和陆上机房差别很大工控机功耗控制在100W以内更稳妥同时要做防潮、防盐雾处理。我们用的是无风扇工控机搭配工业级SSD和宽压电源模块实测在机舱温度55度环境下稳定运行。AI算力覆盖船端算法以目标检测人员、安全帽、区域入侵和行为识别跌倒、吸烟为主单路1080p视频做检测大约需要5-10 TOPS INT8算力。选推理卡或NPU时按船载摄像头路数两倍冗余来配避免多路解码时算力冲突。编解码能力这一点最容易被忽略。AI推理不是直接从RTSP裸流里读像素的而是需要先把视频流硬解码成YUV或RGB帧再送进模型推理完还有可能做视频编码存证。所以我们要求边缘节点必须支持至少8路1080p的硬解码和4路硬编码。最终我们定了两个档位的硬件一条普通货船配一台基础版一体机CPU 8核 30 TOPS NPU 8路解码一条大型客滚船配增强版CPU 16核 双NPU卡 32路解码。接入路数超过边缘节点承载能力时宁可少接几路也不要把所有摄像头全部接入然后跑不动算法这在项目里叫接入容量规划。3.2 边缘节点上的算法运行环境算法加载这部分我们是被现实教育过的。最初的想法很简单直接在工控机上用Python脚本挂模型推理开发确实快但等真正要管理多船多算法时问题全暴露了算法版本更新要逐台SSH登录改文件、装依赖十几条船搞得人崩溃不同算法对Python环境和依赖库的要求互相冲突装A算法把B算法的运行环境搞坏了算法进程崩溃后没有自动恢复机制要等岸端监控发现才能远程重启。后来我们改成了容器化方案每个算法一个Docker容器镜像统一上传到私有仓库船端边缘节点只负责拉取和运行。这个方案给整个系统的远程可维护性带来了质变。算法包的标准化是我们踩了多次坑之后逐渐固化的现在每个算法包必须包含以下内容algorithm-package/ ├── manifest.yaml # 算法元信息名称、版本、输入输出定义 ├── Dockerfile # 构建算法容器的脚本 ├── model/ │ ├── model.onnx # 已转换完成的推理模型 │ ├── config.json # 模型输入的尺寸、精度、锚点等参数 │ └── labels.txt # 类别标签文件 ├── src/ │ ├── preprocess.py # 预处理逻辑模块 │ ├── inference.py # 推理逻辑模块 │ └── postprocess.py # 后处理与告警判定逻辑 ├── requirements.txt # 算法依赖清单 └── test/ └── sample_video.mp4 # 算法自测样例视频其中manifest.yaml是核心它定义了算法如何与边缘节点交互name: intrusion-detection version: 2.3.0 input: type: video-stream stream_format: rtsp decode_mode: hardware max_batch: 4 output: type: event-json kafka_topic: ship-events sample_interval_frames: 25 labels: - person - helmet - head_without_helmet threshold: confidence: 0.45 iou: 0.5 ha_mode: auto-restart这个清单文件不是摆设边缘节点会读取manifest.yaml来判断要不要给容器挂载GPU/NPU设备、要不要分配独立的解码通道、事件消息要推到哪个Kafka主题。我们管这种方式叫算法即配置整个算法加载过程对现场工程师来说就是上传一个zip包不需要碰任何代码。3.3 自定义算法加载的实现路径用户自定义算法加载本质上要解决三件事第一模型怎么被边缘节点认识。我们统一要求模型转换为ONNX格式因为ONNX是不同框架的共同语言PyTorch、TensorFlow、PaddlePaddle训练的模型都能导出。转换这一步经常出问题特别是算子的兼容性。比如某些PyTorch动态算子导出后在ONNX Runtime上跑不了就会直接报错。我们提供的模型验证工具会在上传时跑一次冒烟推理用模型自带的样例视频测试一遍通不过直接打回不让问题模型进入船端。第二算法怎么被调度起来。船端边缘节点上常驻一个算法管理服务它负责监听岸端下发的算法部署指令。流程是这样的岸端上传算法zip包到对象存储生成版本号岸端管控平台下发部署指令到指定船只的边缘节点指令里带算法包下载地址、版本号和目标运行参数边缘节点下载算法包校验MD5解析manifest.yaml构建并启动Docker容器容器启动完成后自动向岸端注册算法运行状态心跳包含模型加载时间、推理帧率、当前路数等信息岸端确认算法进入running状态后下发算法与视频源绑定关系即告诉边缘节点哪几路摄像头喂给这个算法。第三算法运行中出了故障怎么办。我们做了两级容错。一级是容器本身配了restart: unless-stopped进程崩了Docker自动拉起另一级是边缘节点上的算法管理服务周期检查容器的输出——如果容器在指定时间窗口内没有上报任何推理结果就判定容器假死主动重启并给岸端发预警。这个双保险在真实船上很有用因为船载环境电压波动、网络抖动都可能让算法进程进入阻塞状态。核心代码里算法管理服务的调度逻辑大致是这个样子def deploy_algorithm(ship_id, algo_package_info): # 1. 下载并校验算法包 local_path download_package(ship_id, algo_package_info[url], algo_package_info[md5]) if not verify_md5(local_path, algo_package_info[md5]): raise PackageCorruptedException() # 2. 解析manifest manifest parse_manifest(local_path) # 3. 构建容器配置 container_cfg build_container_config(ship_id, manifest, algo_package_info[version]) # 4. 若存在旧版本先优雅停止 stop_old_container(ship_id, algo_package_info[algo_name]) # 5. 启动新容器 container_id docker_client.containers.run(**container_cfg) # 6. 等待容器健康检查通过 wait_for_healthy(container_id, timeout90) # 7. 注册到边缘节点算法目录 edge_algorithm_registry.add(ship_id, manifest, container_id) return container_id这个过程看着简单但它背后有一个关键点算法与视频源的解耦。算法运行时不关心视频源是海康还是大华是RTSP还是GB28181只管从统一的视频帧队列里取帧、推理、输出事件。视频源接入和算法加载是两个独立管线在边缘节点内部通过一个内存队列连接起来。这个设计让我们实现接入一批摄像头、动态切换喂给不同算法变得非常灵活。4. 岸端管控层从边缘节点状态到算法全生命周期管理岸端管控平台是整个网络的大脑它同时扮演两个角色一是船岸一体调度中心二是算法和设备的全生命周期管理后台。4.1 设备台账与感知网络的自动拓扑管理船岸设备最忌讳的是用一张静态表格去记录所有设备信息。船在海上跑设备IP可能变通道可能改算法可能换没有一套自动发现的机制就会陷入无穷尽的配置漂移问题。我们做的拓扑自动发现核心依赖边缘节点上定期上报的设备快照。边缘节点每60秒向岸端同步一次以下内容在线视频源列表及码流状态每个视频源的最近拉流延迟、丢包率边缘节点自身的CPU、内存、NPU利用率每个算法容器的运行状态和推理帧率。岸端收到这些快照后会自动更新设备台账并在大屏上渲染出船-边缘节点-摄像头-算法的拓扑关系。哪个摄像头掉线、哪条船算法跑飞了、哪台边缘节点算力告急全部实时可见。这里有个经验不是往柜子里加设备就叫运维让状态可视、让告警分层才是可维护性的根本。4.2 算法仓库与版本发布管理算法包在岸端平台里有完整的版本管理。我们给每个算法包分配一个全局唯一的algo_uuid每次上传新版本都会生成新的version保留历史版本记录。这样做的实际收益是——可以随时回滚。真实发生过一次我们更新了区域入侵检测算法到2.4.0版本测试环境跑了一周没发现问题灰度推送给一条船后该船反馈误报率明显上升。排查了一下发现是新版本在模型后处理里改了一个边界框过滤逻辑但船端部署的摄像头安装角度跟测试环境差异较大导致过滤规则不适用。这种情况下一条命令把算法回滚到2.3.0版本5分钟内恢复不影响业务。版本发布流程现在是这样的算法开发者在平台提交算法包平台自动跑冒烟测试样例视频推理 输出格式校验测试通过后进入测试中状态可选择绑定测试船灰度发布指定少量船只先行部署观察告警准确率、推理时延等指标全量发布或回滚。这个流程对算法开发团队的约束很大但也逼着他们养成了写清单文件、打包规范化的习惯。有几个算法团队刚开始抱怨流程太重跑到后面发现版本混乱导致的问题更痛苦就主动配合了。4.3 船岸通信链路与消息下发的可靠性船岸通信是整套网络最容易出幺蛾子的环节。卫星通信带宽贵、延迟高4G通信在公海无信号近海又受天气影响大。我们把船岸通信设计成异步消息 断点续传的模式面向操作而不是面向连接。岸端管控平台要下发算法部署指令时指令不是实时推给船的而是先进入一个指令队列我们用Redis实现当船端在下次心跳周期上线时主动拉取属于自己的指令。这样即使船只离网数小时恢复联网后也能自动补齐所有待执行指令。数据回传也是同理。边缘节点的告警事件先落船端本地存储MongoDB或SQLite再通过消息队列上一批推一批。岸端收到后回ACK船端收到ACK才删除本地记录。这个设计保证了在弱网环境下不丢事件——最多就是延迟到达是最终一致性的思想。这套机制里最关键的一个参数是心跳周期。我们在近海4G环境下默认30秒在卫星通信环境下调整到180秒。心跳太频繁会白白消耗带宽心跳太慢会导致指令到达延迟过高。每条船可以根据自己的通信条件独立配置。5. 老船改造与设备生命周期从存量设备里榨出感知能力整个项目的挑战最难打的仗不是写代码而是跟一条条实际运营的老船打交道。5.1 老船的网络基础设施状况与改造策略上船踩点的时候发现很多老船的网络基础设施比预想中还要薄弱。有的船骨干交换机还是百兆有的船摄像头的供电和网络走的是同一个综合布线还有的船整个机舱区域没有任何可用的有线网络接口。我们的原则是能用有线不用无线、能就近汇接不跨舱布缆。具体改造顺序是这样的先梳理船上的网络拓扑搞清楚哪些摄像头在哪个交换机下哪个交换机可以就近接入边缘节点如果边缘节点和摄像头不在同一个二层网络里优先通过三层路由解决避免新增布线实在无法布线的点位用网桥或者4G摄像头补盲所有新增的网络设备都要通过船检要求阻燃、防爆等级要过关。这里有一个值得分享的细节老船上的摄像头很多已经用了五六年固件版本老旧RTSP兼容性差。我们开发了一个视频源降级探测机制当拉流失败时自动尝试RTSP over TCP、UDP、组播等不同的传输方式再不行就尝试Onvif的Backchannel。实测中这一个机制把老摄像头的接入成功率从只有六成提到了九成以上。5.2 新增感知点位的预算与价值权衡每次改造都要面临增加路数和控制成本的矛盾。我的判断标准很简单这个摄像头的画面有没有算法能分析分析出来的结果有没有决策价值如果两个答案都是否那这个点位就是无效感知不如不加。但有效感知的判断不能只看当下还要考虑可扩展性。比如一个垃圾倾倒监测点今天没有合适的算法但平台已经预留了算法加载能力明天算法到位了就能直接启用。这就是边缘智能网络和老式闭路监控的本质区别闭路电视只能看边缘智能网络能想而且能换着法子想。5.3 设备巡检与生命周期管理的落地表单最后是设备巡检。船上设备环境恶劣摄像头镜头污染、红外灯衰减、防水罩破裂都是常见问题。我们给每台设备建了健康档案包括最近30天的在线率、拉流成功率、丢包率最近7天的平均码流延迟告警数量和误报率趋势设备固件版本和算法绑定情况。巡检人员上船后不是漫无目的地看设备而是打开手机端平台按重点检查列表逐项确认。大部分问题在岸端就已经预警了上船巡检更多是验证和确认。6. 这套网络踩过的坑和沉淀下来的经验6.1 算法上船的最后一公里问题算法在岸端服务器上跑得飞快一到船端边缘节点上就各种出问题这是我们在项目初期碰到最多的场景。归纳下来主要是三类第一模型的预处理要求与边缘节点的算力不匹配。比如某个算法在预处理阶段用了大量CPU算子导致NPU利用率上不去整体推理时延反而比纯CPU跑还慢。解决办法是优化预处理逻辑把能迁移到GPU/NPU的算子尽量迁移同时Filter掉不必要的图像增强步骤。第二边缘节点没有GPU/NPU设备的合适驱动或运行时版本。很多模型转换工具生成的ONNX算子在新版本ONNX Runtime上支持更好但边缘节点上装的是旧版本推理引擎结果模型加载直接报错。我们目前的策略是每个算法容器自带推理引擎依赖而不是依赖宿主机环境容器镜像构建时就把ONNX Runtime或OpenVINO这些运行时打进去。第三模型的动态shape问题。有些模型导出时保留了动态维度推理引擎需要在每次推理时重新分配显存在边缘设备上非常耗时。我们的模型转换规范里明确要求模型输入尺寸必须固定比如1x3x640x640禁止使用动态shape。模型上传时会自动检测一旦发现动态维度直接驳回。6.2 岸端与船端时间不可靠引发的告警乱序这个坑很有代表性。边缘节点在船上跑如果船上的系统时间没有同步那么告警事件的时间戳就会错乱。刚开始有几条船的事件在岸端平台上排序完全不成样子排查了两天才定位到是船端NTP不可用系统时间漂移了几个小时。解决办法是双管齐下一方面边缘节点上配置了两个NTP服务器源一个走岸端一个走北斗/GPS授时另一方面所有告警事件上报时附带上设备本地时间和消息产生时间两个字段岸端在入库时根据自己的时区配置统一转换成标准时间。这个细节如果不处理后续做数据回放和事件关联分析时会出现非常严重的逻辑混乱。6.3 消息队列选型和告警数据量评估告警数据量从纸面上算和从实际跑出来看是不一样的。我们早期按每分钟每条船最多100条告警来估值觉得Kafka绰绰有余后来发现有的船高峰期一分钟能产生上千条结构化告警事件因为一个算法可能同时触发多路摄像头的连续帧判定。如果消息队列处理不过来丢消息是小事更麻烦的是告警风暴会拖垮下游的数据分析和可视化模块。我们的调整思路是在边缘节点侧做告警聚合——多帧连续告警合并成一个事件附带上告警起止时间和关键帧列表岸端再做一次二次过滤把明显重复或置信度低的告警丢掉。这一层下来实际进入Kafka的消息量下降了约70%可靠性大幅提升。消息队列的选型上船端我们用了轻量级的EMQX支持MQTT协议岸端用Kafka中间通过一个自研桥接服务转发。MQTT的QoS机制在弱网环境下表现比Kafka的直连要好很多断线重连和消息补偿都更成熟。7. 关于项目后续演进和团队配合的几点个人体会这套船岸一体的边缘智能感知网络从第一版原型到今天稳定运营陆陆续续做了一年多。过程中最大的收获不是实现了多少项具体功能而是搞清楚了平台化思维到底意味着什么。在项目早期我们习惯性地把每个需求当成独立功能去实现结果代码里到处都是硬编码的摄像头IP、写死的算法路径。后来我们抽了一个月时间把底层重构强制自己遵守设备抽象和算法与视频源解绑这两个原则才真正做成了网络。从那以后新增一条船、新增一个算法基本不需要改一行业务代码这就是平台化的价值。如果让我给正在做类似项目的团队一个建议只有一句话先定义边界的接口再定义里面的实现。不管是摄像头接入、算法插拔还是岸端指令下发接口稳定是系统能生长出更多能力的前提。提前把接口契约定清楚后面所有迭代都会顺畅很多。最后说一个真实的小观察很多做AI的团队骨子里是排斥去现场处理老旧设备的。但恰恰是把算法模型塞进一条老船、让它跑在一条破旧摄像头的画面里还不出错才是从演示到产品最关键的一步。这种脏活累活里积累的经验比在云端调参数值钱得多。
返回列表