news 2026/10/21 3:22:51

海康威视Python移动侦测对接:ISAPI事件订阅与自动抓图实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
海康威视Python移动侦测对接:ISAPI事件订阅与自动抓图实践

简介:这份压缩包面向海康威视网络摄像头二次开发初学者与有 Python 基础的开发者,聚焦通过官方 SDK 实现移动侦测事件订阅、报警回调与告警数据解析,对应博客教程“直接使用”部分,能快速绕过环境配置与接口联调障碍。包内共15个文件,包含8个 Python 源码文件与7个编译后的 pyc 文件,源码按功能拆分为设备登录、报警布防、移动侦测回调等多个模块,清晰地展示了海康 SDK 从初始化、登录设备到注册报警回调的完整调用链;整体仅12KB,轻量易用。目前已有1213人学习浏览,适合希望借助现成脚本快速搭建移动侦测 Demo,或对照学习海康 SDK Python 封装思路的开发者。通过该资源可直接获得一套可运行的工程骨架,源码与 pyc 缓存并存,既可阅读修改,也可直接调用,帮助你节省从零编码与排查 SDK 接口的时间,更快落地移动侦测功能。

1. 移动侦测不是靠「报警输出」:一份能直接跑的 Python 对接工程

如果你接过海康威视设备的告警,多半有过这种经历:设备侧配好了移动侦测,浏览器也能看到报警弹窗,可轮到自己的程序去收事件时,要么找不到回调入口,要么拿到一堆看不懂的 XML,要么抓图永远比事件慢半拍。这份 hkoython.zip 解决的就是这件事——它是一套完整的海康威视 Python 移动侦测对接工程,从设备初始化、事件订阅、回调处理到报警抓图、录像回查,把一条「移动侦测触发 → 程序收到事件 → 自动抓图存档」的链路直接打通。

这套工程适合两类人:一类是正在做安防平台、仓库监控、门店客流这类小场景的开发者,需要尽快把海康设备的移动侦测事件接进自己的系统;另一类是手里已经有一套监控设备,想绕开厂家私有客户端,用纯 Python 写自动化脚本的人。它不依赖厂家自带的上位机软件,也不碰那些需要单独授权的重型 SDK,核心是基于设备开放的 HTTP 接口(ISAPI)加事件订阅回调来实现的,拿到手改改 IP 和用户名就能跑。

我之前在某厂房做夜间安防巡检时,就吃过「设备有报警、程序没反应」的亏,排查到最后发现是事件订阅地址拼错了一个字段。这份工程的价值在于把事件订阅、XML 解析、抓图存储这些容易翻车的点都提前处理好了,下面我按「架构 → 核心代码 → 避坑 → 运行 → 改造」的顺序拆给你看。

2. 整体架构拆解:ISAPI 事件订阅是怎么把设备报警送到你程序里的

2.1 选型判断:为什么不走 SDK 直连

接海康设备告警,社区里常见的路子有三条:官方 SDK(C++ 或封装后的 Python)、ONVIF 协议、ISAPI 接口。这份工程选的是第三条,也就是设备自带的那套 REST 风格 HTTP 接口。

用 ISAPI 的好处很现实。官方 SDK 需要额外安装厂家提供的运行库,版本还要和设备固件匹配,我在项目里就遇到过 SDK 能在 A 设备上报事件、换到 B 设备就静默失败的情况,最后发现是固件版本跨了大版本。ISAPI 是设备 Web 管理界面背后同一套接口,只要你能打开设备的网页配置页,程序就能用同样的账号密码调接口,兼容性比 SDK 省心很多。

工程的整体调用链是这样组织的:初始化一个连接会话 → 用摘要认证登录设备 → 订阅移动侦测事件 → 开启一个后台线程持续接收事件通知 → 收到事件后解析 XML、按需抓图 → 写入本地目录。这个链路里最关键的是第三步和第四步,设备不是主动把事件推给你,而是你在设备上「挂」一个监听地址,设备检测到移动目标后,把事件 POST 到这个地址上。

2.2 目录结构与模块职责

先看这份工程的目录结构,它按「连接、事件、存储、工具」分了层,不是把所有逻辑塞进一个文件里硬跑:

hkoython/ ├── config.yaml # 设备 IP、账号、抓图目录、区域配置 ├── hko/ │ ├── __init__.py │ ├── client.py # 登录、认证、请求封装(核心会话层) │ ├── detector.py # 移动侦测订阅与事件接收(核心业务层) │ ├── snapshot.py # 抓图与图片存储 │ ├── parser.py # XML 解析,把设备通知转成结构化事件 │ └── utils.py # 日志、时间处理、文件命名 ├── main.py # 入口,读取配置并启动监听 ├── requirements.txt └── README.md
  • client.py 负责和设备的「对话」,包括拼 URL、带认证头、处理超时重试,上层模块不直接碰请求细节。
  • detector.py 是核心,它向设备发起订阅请求,然后在一个常驻线程里接收设备推过来的事件通知。
  • parser.py 把设备返回的 XML 解析成 Python 字典,比如事件类型、通道号、时间戳。
  • snapshot.py 在收到事件后调用设备的抓图接口,把当时的画面存成 jpg。
  • config.yaml 把设备地址、凭据、存储位置这些易变参数全部外置,换设备不用改代码。

2.3 事件流转的完整时序

用一份时序描述串起整个流程,便于你理解后面每段代码的位置:

  1. 程序启动,读取 config.yaml,拿到设备 IP、端口、用户名、密码。
  2. client.py 调用设备的登录接口,完成摘要认证,拿到会话凭据。
  3. detector.py 向设备的移动侦测订阅地址发起请求,带上自己的接收端口。
  4. 程序开启 HTTP 服务线程,监听设备回调。
  5. 设备侧检测到移动目标,主动把事件 XML POST 到程序的监听地址。
  6. parser.py 解析 XML,校验事件类型是「移动侦测」而不是「报警输入」等其他类型。
  7. snapshot.py 调用抓图接口,把图片写入配置的目录,文件名带时间戳。
  8. 主程序回到等待状态,继续接收下一条事件。

这套设计里值得注意的一点是:事件接收是被动的,抓图是主动的。程序收到事件通知后,再主动发起一次 HTTP 请求去取图,而不是等设备把图片一起推过来。这样做的原因是 ISAPI 的移动侦测通知本身只携带事件元数据,不带图片数据,主动抓图才能在事件发生的瞬间拿到最接近现场的画面。

3. 核心模块实战:从登录认证到事件回调的逐段实现

3.1 连接会话封装:把摘要认证的坑提前填掉

所有海康 ISAPI 请求都要求先通过摘要认证,这是第一个容易翻车的地方。设备返回 401 后,要求客户端用特定的算法对用户名、密码、随机数做一次哈希计算,再带着新的 Authorization 头重新请求。直接拿密码做 Basic 认证是不通的。

工程里 client.py 的核心逻辑是用标准库的 urllib 加上一层手工摘要认证处理:

import base64 import hashlib import urllib.request class HkoClient: def __init__(self, ip, port, username, password): self.base_url = f"http://{ip}:{port}" self.username = username self.password = password self.session = None def _digest_auth(self, method, uri, body=b"", headers=None): # 首次请求不带 Authorization,触发设备的 401 质询 req = urllib.request.Request(self.base_url + uri, data=body, method=method) if headers: for k, v in headers.items(): req.add_header(k, v) try: urllib.request.urlopen(req, timeout=5) except urllib.error.HTTPError as e: if e.code != 401: raise auth_header = e.headers.get("WWW-Authenticate", "") # 从质询头里解析 realm、nonce、qop realm = self._extract_param(auth_header, "realm") nonce = self._extract_param(auth_header, "nonce") nc = "00000001" cnonce = "abcd1234" # 客户端生成随机串 ha1 = hashlib.md5(f"{self.username}:{realm}:{self.password}".encode()).hexdigest() ha2 = hashlib.md5(f"{method}:{uri}".encode()).hexdigest() response = hashlib.md5(f"{ha1}:{nonce}:{nc}:{cnonce}:auth:{ha2}".encode()).hexdigest() auth_value = ( f'Digest username="{self.username}", realm="{realm}", ' f'nonce="{nonce}", uri="{uri}", response="{response}", ' f'qop=auth, nc={nc}, cnonce="{cnonce}"' ) req = urllib.request.Request(self.base_url + uri, data=body, method=method) req.add_header("Authorization", auth_value) if headers: for k, v in headers.items(): req.add_header(k, v) return urllib.request.urlopen(req, timeout=10)

这段代码里有几个参数需要说明:realm 和 nonce 是设备在 401 响应里返回的,realm 相当于认证域,nonce 是一次性随机数,每次请求都要重新向设备获取,不能写死缓存。ha1 是「用户名:realm:密码」的 MD5,ha2 是「请求方法:请求路径」的 MD5,最终的 response 再把两者和 nonce、计数器拼起来加密一次。nc 是请求计数器,从 00000001 开始,每次请求递增;cnonce 是客户端随机串,一般用 uuid 生成,我这里写死只是示意,实际工程里建议用uuid.uuid4().hex。

常见误用的坑是把uri写成完整 URL,比如http://192.168.1.64/ISAPI/...,但摘要认证要求 uri 只带路径部分/ISAPI/...,否则 response 永远校验不过,设备端会一直回 401。这也是我最开始在厂房调试时卡了最久的地方。

3.2 订阅移动侦测事件:挂上自己的监听地址

登录会话封装好之后,下一步是向设备订阅事件。海康设备的 ISAPI 有一个固定的订阅接口,请求体是一段 XML,里面指定事件类型和接收地址。工程里 detector.py 的 subscribe 方法这样实现:

def subscribe_motion(self, listener_ip, listener_port): xml_body = """<?xml version="1.0" encoding="UTF-8"?> <EventSubscriptionNotification> <eventType>VMD</eventType> <eventTypes> <eventType>VMD</eventType> </eventTypes> <notificationReceiver> <address>http://{ip}:{port}</address> <protocol>HTTP</protocol> <subscriptionPriority>1</subscriptionPriority> </notificationReceiver> </EventSubscriptionNotification>""".format(ip=listener_ip, port=listener_port) resp = self._digest_auth( "PUT", "/ISAPI/Event/notification/subscribe", body=xml_body.encode(), headers={"Content-Type": "application/xml"}, ) return resp.status == 200

这段订阅请求的关键点有三个。eventType 字段填 VMD,这是海康设备对视频移动侦测的统一类型标识;如果填成 IO 或者 alarm-in,收的就是外部报警输入事件,两者在设备配置里是独立的东西,搞混了会发现「动一下就报警」的目标永远实现不了。notificationReceiver 的 address 必须是你的程序所在机器能对外收包的地址,不能填 127.0.0.1,否则设备侧把事件 POST 过来时,连接的是自己而不是你的程序。

订阅用的 HTTP 方法是 PUT,不是 POST。我见过不少项目文档里这里写的是 POST,照着写之后设备返回 400。原因是这个接口在设备固件里的设计是「覆盖式订阅」,PUT 语义在 REST 里代表更新或创建,而 POST 在部分固件上不支持。协议用 HTTP 即可,不需要 HTTPS,除非你的程序监听端配了证书,否则设备默认用明文 HTTP 推送。

3.3 接收回调:起一个常驻 HTTP 线程

订阅成功后,设备会在移动侦测触发时把事件 XML POST 到你指定的监听地址上。所以你的程序需要一个能接收 POST 请求的迷你 HTTP 服务。工程里没有引入 Flask,因为为了收一个回调起一整台 Web 服务有点重,直接用标准库的 http.server 开一个线程即可。

from http.server import BaseHTTPRequestHandler, HTTPServer import threading class MotionEventHandler(BaseHTTPRequestHandler): parser = None def do_POST(self): content_length = int(self.headers.get("Content-Length", 0)) xml_data = self.rfile.read(content_length) self.server.event_queue.put(xml_data) self.send_response(200) self.end_headers() def log_message(self, format, *args): pass # 关闭默认请求日志,避免刷屏 def start_listener(port, event_queue): handler = MotionEventHandler handler.event_queue = event_queue httpd = HTTPServer(("0.0.0.0", port), handler) t = threading.Thread(target=httpd.serve_forever, daemon=True) t.start() return httpd

这里把收到的原始 XML 直接放进一个 queue.Queue,由另一个工作线程去消费。这么做的好处是接收线程只做「收下来、回 200、放队列」,动作极快,不会因为解析 XML 或者抓图的耗时导致设备侧重试推送。设备推送是有超时机制的,如果程序在回调线程里磨蹭太久没回 200,设备会认为推送失败,按自己的策略重试,结果就是你看到的事件重复、乱序。

0.0.0.0 监听是刻意的。如果你的程序跑在 Docker 里,或者设备和你之间隔了 NAT,只监听 127.0.0.1 会收不到设备跨网段的推送。等全部调通之后再按需改成内网网卡地址也不迟。另外,这个回调服务不需要实现 do_GET,设备只会 POST 事件过来。

3.4 XML 解析:从设备通知里剥离出你要的字段

设备 POST 过来的 XML 里嵌套层级比较深,直接正则抠字符串容易在字段顺序变化时翻车。工程里 parser.py 用的是 xml.etree.ElementTree 标准库,按命名空间无关的方式取节点:

import xml.etree.ElementTree as ET def parse_motion_event(xml_data): root = ET.fromstring(xml_data) event_type = "" channel_id = "" happen_time = "" # 遍历所有 eventType 节点,兼容不同固件的标签层级差异 for elem in root.iter(): tag = elem.tag.split("}")[-1] # 去掉命名空间前缀 if tag == "eventType": event_type = elem.text.strip() elif tag == "channelID": channel_id = elem.text.strip() elif tag == "dateTime": happen_time = elem.text.strip() return { "event_type": event_type, "channel_id": channel_id, "happen_time": happen_time, }

解析时最值得注意的点在 tag 处理上:海康不同固件版本返回的 XML 命名空间可能不同,直接拿全名比较容易踩坑,所以这里按命名空间分隔符把前缀剥掉,只比对标签名。eventType 的值可能是 VMD、VMD-1、MotionDetect 里的任意一种,不同固件叫法不统一,判断时不要用等号匹配单一值,用「包含 VMD 或 Motion」这种宽松判断更稳。

dateTime 字段是设备自带的时间,而不是程序收到事件的时间。我在实际对接时发现设备时间和服务器时间偶尔差几分钟,所以工程里会同时保留设备时间和程序接收时间,后续做告警分析时以程序接收时间为主、设备时间为辅,避免因为时钟偏差把事件归错时间窗口。

3.5 抓图:事件发生后主动拉取当前画面

移动侦测通知本身不带图片,程序需要在收到事件后主动调用抓图接口。海康设备的抓图接口路径是按通道号拼的,工程里 snapshot.py 这样实现:

import datetime def capture_snapshot(client, channel_id, save_dir): timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f") uri = f"/ISAPI/Streaming/channels/{channel_id}/picture" try: resp = client._digest_auth("GET", uri) if resp.status == 200: filename = f"{timestamp}_ch{channel_id}.jpg" filepath = f"{save_dir}/{filename}" with open(filepath, "wb") as f: f.write(resp.read()) return filepath except Exception as e: # 设备可能暂时忙碌,常见于连续事件间隔小于 500ms 时 return None

抓图接口返回的是 JPEG 二进制流,直接落盘即可。文件名里带通道号和时间戳,微秒级时间戳保证同一秒内多次触发不会互相覆盖。这里的 channel_id 从解析出来的事件字段里拿,一般固定为 1,对应主码流;如果设备配了多路相机,可能需要根据实际通道编号调整。

连续移动侦测触发时,设备抓图接口偶尔会返回 500 或者直接超时,原因是设备自身的编码器正在处理关键帧切换。工程里的做法是失败后静默返回 None,由上层决定是否重试。我在现场调试时发现重试间隔要大于 1 秒,太频繁的重试会让设备编码器负载更高,反而更难抓成功。

4. 运行与调试:从零到收到第一条事件的全流程指南

4.1 环境准备与依赖安装

这份工程的运行环境要求不高,只要是 Python 3.6 以上即可,没有用到任何需要编译的第三方库。依赖只有一个 PyYAML,用于读取配置文件,其余全部走标准库。安装命令:

pip install pyyaml

如果连 PyYAML 都不想装,也可以在 config.py 里用字典写死配置,但工程选择 YAML 文件是为了方便换设备、换目录时不用改代码。把工程克隆到本地后,第一件事是打开 config.yaml,把设备参数换成自己的:

device: ip: 192.168.1.64 port: 80 username: admin password: "your_password" listener: ip: 192.168.1.100 port: 8080 storage: snapshot_dir: ./snapshots log_dir: ./logs

这里有一个十分关键的提示:

注意:listener.ip 不能填设备自己的 IP,也不能填 127.0.0.1,必须填你的程序所在机器的内网 IP,并且保证设备能访问到这个地址。如果设备和程序不在同一网段,要先解决路由问题,否则订阅时设备侧不会报错,但事件永远不会推过来。

4.2 先验证设备连通性再启动程序

在跑完整工程之前,我建议先用一条命令验证设备是否允许你的 IP 访问。海康设备默认开启了 IP 地址过滤,如果你之前没在设备的网络配置里允许当前机器的 IP,程序请求会直接超时,连 401 都拿不到。验证方式:

curl http://192.168.1.64/ISAPI/System/deviceInfo -u admin:your_password

如果返回 XML 里有 deviceName 字段,说明设备已经接受了你的 IP 和凭据。如果 curl 卡住直到超时,多半是设备的安全机制把请求丢弃了,这时需要登录设备 Web 管理页,在「配置 → 网络 → 高级配置 → 访问控制」里加上当前机器的 IP。这一步不做,后面程序怎么调都是白搭。

4.3 启动程序并观察事件链路

配置改好后,直接运行:

python main.py

正常的启动日志会依次出现三行:登录成功、订阅成功、监听端口已启动。然后你走到摄像机视野里动几下,大约一两秒内,终端应该会打印一条包含 VMD 事件类型和通道号的日志,同时 snapshots 目录下出现一张 jpg。

如果人动了但没有任何日志,排查顺序是:先看订阅时设备是否返回 200,再抓包确认设备有没有向你的监听端口发起 POST,最后看解析时是不是把事件类型过滤掉了。工程里默认只处理 VMD 和 MotionDetect 类型,如果你的设备上报的是其他类型,需要在 parser.py 里加一行打印原始 XML,看真实类型是什么。这也是我把原始 XML 放进日志的原因,排查时比任何文档都管用。

5. 常见问题排查:事件收不到、订阅失败、抓图为空的实战记录

5.1 现象:订阅返回 200,但程序永远收不到事件

我在某园区做安防联动时第一次遇到这个问题,订阅接口明确返回了 200,在设备网页端也能看到事件记录,但程序的监听端口就是没有任何 POST 进来。

原因排查了很久,最后发现是 listener.ip 填错了位置。我填的是localhost,程序把订阅请求发给设备时,地址变成了http://localhost:8080,设备拿到这个地址尝试连接,连的自然是设备自己,而设备上没有服务在 8080 端口监听,推送自然失败。设备端不会因为你给的地址不可达而报错,它只是默默重试几次后放弃。

解决方式是把 listener.ip 改成程序所在机器的内网地址,并且用ip addr确认这个地址是设备所在网络能路由到的。从那以后我每次配置都会先用另一台机器curl http://程序IP:8080做一次连通测试,确认端口真的通再启动订阅。

5.2 现象:事件收到了,但抓图目录里全是空文件

空文件或者 0 字节的 jpg,出现这个现象时程序链路实际上是通的,问题出在抓图时机上。海康设备在事件触发后的极短时间内,编码器可能正在处理 I 帧切换,这时调用抓图接口,设备返回 200 但 body 是空的,或者返回一张全黑的图。

另一个常见原因是连续快速触发时抓图接口被设备限流。设备对 ISAPI 有并发限制,短时间多次调用会直接拒绝。解决方式是在 snapshot.py 里加重试和冷却机制:收到空文件后等待 1 到 2 秒重试一次,单次事件最多重试两次;如果两次都失败,放弃这次抓图,记录错误日志,不要阻塞事件循环。

我一般会在抓图失败时把事件时间、通道号、失败原因写进单独的日志文件,后面统计漏抓率时用。对于安防场景来说,偶尔漏一张图可以接受,但事件记录不能丢,所以抓图失败不影响事件计数。

5.3 现象:移动侦测事件能收到,但一直在重复推送同一条

程序像是死循环一样反复收到同一条事件,终端日志刷屏。这个现象背后的原因通常是你没有在收到事件后正确回复 200,或者你的 HTTP 服务响应太慢,设备等不到响应就重试推送。

排查时先看设备端的推送日志,确认有没有重试机制被触发。解决办法是在回调线程里保证「接收 → 回 200 → 再处理」的顺序,千万不要先抓图再回响应。工程里用 event_queue 解耦的意义就在这里,接收线程只做两件事:把数据放队列、回 200,耗时操作全部丢给消费线程。

5.4 现象:多台设备同时推送时事件丢失

当程序同时订阅了多台设备时,偶尔会发现某些设备的事件丢了。原因在于设备的推送超时设置是固定的,如果你的消费线程处理速度跟不上,队列积压,接收线程虽然一直回 200,但设备侧已经因为等待超时把事件标记为失败。海康设备默认的超时时间很短,大概是几秒级别。

解决思路是消费线程改成多线程 worker,比如开三个线程从同一个队列里取数据并行处理,同时把抓图等慢操作从消费线程里再拆一层。工程里在 main.py 里预留了 worker 数量参数,默认是 1,多设备场景改成 3 即可。注意抓图接口本身对设备有并发限制,worker 数量不是越大越好,3 到 5 个是比较稳妥的区间。

6. 二次开发实战:把 demo 改造成你自己的告警通知服务

6.1 加一个告警推送模块:事件转微信/钉钉消息

工程目前的输出是「落盘图片 + 终端日志」,但在真实项目里,移动侦测事件需要通知到人。改造思路是在事件消费线程里加一个推送钩子,不改动订阅和抓图的核心链路。

我的做法是新增一个 notifier.py,里面定义一个发送函数,收到事件后把通道号、时间、图片路径拼成文本消息,通过你常用的 Webhook 方式发出去。事件消费线程里在抓图完成之后调用这个函数:

def on_motion_event(event, image_path): message = { "channel": event["channel_id"], "time": event["happen_time"], "image": image_path, } push_to_webhook(message)

这个改造里要注意的是推送不能阻塞事件处理,Webhook 调用可能因为对方服务不稳定而卡住,所以推送也要放到独立的线程池里。工程里的事件消费线程在调用 on_motion_event 后立即回到等待状态,推送交给线程池异步执行。

6.2 加上事件去重:避免同一目标触发多台相机

在某跨平台系统的改造里,我发现相邻两路相机可能同时检测到同一个人,导致收到两条几乎同时的事件。业务上只需要一条告警,所以我在消费端加了一个简单的时间窗口去重:以「通道号 + 时间戳精确到秒」为 key,如果 5 秒内收到相同 key 的事件,直接忽略,只保留第一条。

dedup_cache = {} def is_duplicate(event, window_seconds=5): key = f"{event['channel_id']}:{event['happen_time'][:19]}" now = time.time() if key in dedup_cache and now - dedup_cache[key] < window_seconds: return True dedup_cache[key] = now return False

这里有个细节:设备时间可能有秒级误差,去重 key 不要带微秒,否则同一条事件会因为时间戳微秒不同而去重失败。

6.3 验证改造正确性的自测方法

每次改完代码,我强制要求自己走一遍完整的自测流程:先关掉程序,清空 snapshots 目录,启动程序,然后在摄像机前走动触发一次事件,确认收到一条告警、落盘一张图片;再快速来回走动两次,确认收到两条事件且图片文件名时间戳不同;最后拔掉设备网线模拟断网,确认程序不会崩溃,恢复网络后能自动重连。

这套验证方法帮我发现过一次隐藏问题:程序在设备断网期间,事件消费线程因为请求超时会抛出异常,如果异常没有捕获,线程会静默退出,等设备恢复后程序收不到任何事件。修复方式是在消费线程外层包一个大 try-except,捕获所有异常并记录日志,保证线程永远不退出。

从那以后我每次接手这种对接类项目,都会先跑一遍「断网重连」场景再谈别的,血泪经验。这份工程本身已经把订阅、回调、抓图的主链路铺好了,二次开发的核心思路就是「保持主链路单线程稳定,把所有耗时操作用队列和线程池拆出去」,希望帮到你少走一段弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 3:22:50

Python安防相机移动侦测:RTSP拉流与OpenCV三帧差工程解析

简介&#xff1a;一套针对海康威视设备Python SDK二次开发的可运行示例&#xff0c;聚焦移动侦测事件处理流程&#xff0c;面向有基础Python开发经验、希望快速集成海康能力的开发者与安防系统集成人员。示例包围绕“登录-报警-回调”的主线展开&#xff0c;包含设备登录、报警…

作者头像 李华
网站建设 2026/10/21 3:21:14

缓存设计避坑指南:从穿透击穿雪崩到容量监控

前阵子排查线上问题&#xff0c;接口延迟从 200ms 一路飙到 3.7 秒&#xff0c;数据库连接池被打满&#xff0c;页面大面积超时。最后查下来&#xff0c;不是 SQL 写得差&#xff0c;也不是机器不够&#xff0c;而是一次发布顺手改掉了缓存 key 的版本号&#xff0c;所有热点缓…

作者头像 李华
网站建设 2026/10/21 3:20:23

以太网帧结构深度解析:从物理层同步到华为设备排错实战

1. 为什么一张“以太网帧”图能卡住90%的初学者&#xff1f;刚接触华为数通HCIA认证时&#xff0c;我翻到教材第3章那张经典的“以太网帧结构示意图”&#xff0c;上面密密麻麻标着Preamble、SFD、DA、SA、Length/Type、Data、FCS——当时心里就咯噔一下&#xff1a;这哪是网络…

作者头像 李华
网站建设 2026/10/21 3:19:46

Unity XML数据持久化:人类可读性与协作效率的工程实践

1. 为什么在Unity里坚持用XML做数据持久化&#xff1f;这事儿真没过时“Unity数据持久化之XML”——看到这个标题&#xff0c;不少刚从Unity 2021 LTS升级上来的朋友第一反应可能是&#xff1a;都2024年了&#xff0c;还聊XML&#xff1f;不是早该被JSON、ScriptableObject甚至…

作者头像 李华
网站建设 2026/10/21 3:19:06

基于STM32F103与OpenMV的舵机云台二维视觉追踪PID控制实现

简介&#xff1a;这是一份基于STM32F103的二维舵机云台PID追踪程序&#xff0c;适合正在做OpenMV视觉追踪、电磁炮或小型云台项目的嵌入式开发者。程序接收OpenMV返回的色块坐标&#xff0c;通过PID算法驱动双舵机实现目标跟踪&#xff0c;代码包含串口通信、舵机控制、PID调节…

作者头像 李华
网站建设 2026/10/21 3:18:59

Claude Code Memory机制详解:从原理到配置,让AI编程助手记住一切

Claude Code这阵子在开发者圈子里讨论度一直很高&#xff0c;但很多人把它当成一个"能写代码的命令行工具"来用&#xff0c;却忽略了它真正值钱的部分——Memory机制。简单说&#xff0c;这个机制解决的是AI编程助手"跨会话记住关键信息"的能力问题。没有它…

作者头像 李华