关于 UVC PTZ Camera
UVC PTZ Camera 是面向 USB UVC 云台摄像头的 MCP 服务,供客户端控制转向、变焦和取景。它通过比较操作前后的画面判断是否真正移动,而不是依赖设备报告的位置。
主要功能
- 01查询摄像头、轴向与范围
- 02按目标角度瞄准或相对微调
- 03平滑扫转与变焦
- 04比较画面确认移动结果
- 05规划并执行镜头动作
- 06人脸检测跟踪主体
适用场景
- 01将摄像头对准指定方向
- 02规划全景与缩放镜头动作
- 03确认设备命令是否改变画面
- 04通过人脸检测跟随人物
MCP 相关
MCP 服务向客户端提供摄像头状态、瞄准、微调、扫转、变焦、取景和镜头规划等工具。移动结果通过比较操作前后的画面确认,并标注设备报告的位置作为参考。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx uvc-ptz-camera-mcpUVC_PTZ_STATE_DIRWhere to store what has been learned about each camera. Defaults to ~/.uvc-ptz-camera-mcp. Holds angles, never credentials.UVC_PTZ_SIM_SOURCEPath to an image to use as the simulator's panorama instead of the synthesised one.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
17收录的工具列表 · 更新于 2 周前
此工具无需参数。
详细介绍
uvc-ptz-camera-mcp
一个用于USB(UVC)云台摄像头的 MCP 服务:瞄准、微调、平滑扫动、变焦和查看画面——每次移动都会通过对比前后画面来确认。
无需账号,无需云端,无需厂商 SDK。一台设备,一根 USB 线。
camera_status the camera, its axes and ranges, and whether it is real
aim point an axis at an absolute value, confirmed from the picture
nudge move relative to where the device says it is
sweep a smooth timed move, streamed at 15 Hz
zoom set zoom as a multiplier (1x .. the camera's maximum)
recentre every axis back to its default
look one frame, returned as an image
aim_learn remember "this direction is the desk"
aim_list what has been recorded for this camera
go_to point at a recorded direction
plan_shot compile a multi-step shot and report what it would do, moving nothing
run_shot execute a multi-step move, verified after every waypoint
mark_view store the current picture under a label
check_view has the picture changed since that label?
track_start follow a subject: detect, measure, take a step, look again
track_status what the follow loop is doing, iteration by iteration
track_stop stop following and report what it saw and what it moved
构图时,先调用 plan_shot,再调用 run_shot。规划会编译与执行器完全相同的步骤,报告每一步的执行计划、各轴移动的距离和耗时,而且不会触碰摄像头——这样 Agent 就能根据摄像头的实际范围反复调整“先拍全景,再放大对准我”的计划,避免摄像头移动后才发现问题。
跟随
track_start 会启动一个循环,让摄像头持续对准目标:检测目标、测量目标与画面中心的距离、执行有界步进,然后再次查看画面。与这里的其他移动操作一样,它只根据画面做判断,因为摄像头自身报告的朝向并不可信。目标丢失、目标稳定进入死区、摄像头停止响应或达到 max_seconds 后,循环会自行停止;track_status 会列出每次迭代及测得的偏移量和执行的步进。
有两个值取决于支架而非设备,因此它们是明确的参数,而不是靠巧妙猜测得出:gain 表示每像素偏移对应的移动角度,invert 用于反转方向。选错时,残余误差会如实增大;每次迭代的移动上限为 25 度,因此增益设置错误时只会缓慢收敛,不会一次命令就让摄像头横跨整个范围。
检测使用 OpenCV 内置的人脸级联分类器(pip install 'uvc-ptz-camera-mcp[tracking]'),无需下载模型、无需账号,首次使用时也无需联网。它能检测人脸:此功能会跟随人,而非汽车或狗。循环及其计算已通过模拟器测试,但跟随模式从未在真实硬件上运行过——请将其视为这里验证最少的功能。
为什么要开发这个项目
这类摄像头会报告它们实际上并未移动到的位置。在参考设备(处于摄像头模式、使用标准 UVC 控件的 DJI Osmo Pocket 4P)上测得:
- 命令摄像头水平转到
180后,读回值是180,但视频证明摄像头根本没有移动; - 一次完整的 32 秒运行中,每次采样读回值都是
0,但画面明显发生了变化; - 水平转到
120时读回值是119,转到200时读回值是199。
Agent 若依据这些值行动,就会报告从未发生过的移动。因此,此服务将设备报告的每个值都视为提示——会返回并标记这些值,但绝不信任它们——而是通过对比画面来判断是否发生了移动(moved)。
阈值也不是猜出来的。它是根据带标签的硬件画面校准的:每组“视图相同”的画面对得分均 ≤ 0.104(96 秒静止基线,以及硬件忽略的两条命令);每组“视图变化”的画面对得分均 ≥ 0.624(一次水平转动、一次俯仰、一次 12× 变焦和两次瞄准步进)。发布的阈值取中点,即 0.364,两类之间相差 6 倍,测试套件会断言它仍能区分两者。带标签的画面对及指标产生的数值都保存在仓库中(tests/fixtures/),因此可以重新核验这个数值,而不是盲目相信它——这里保存的是测量结果,而非图片:本仓库不会提交摄像头拍摄的画面或视频,如果出现此类内容,测试就会失败。校准所依据的测量数据见 docs/measurements.md。
如何让 Agent 保持诚实
moved来自图像。device_reported会与它一同返回,并标注为提示。- 未产生变化的移动是错误,而不是悄无声息地成功——经过有限次数的重试后,工具会失败,并指出请求的内容和观测到的内容。
- 已经位于目标位置时,无需移动也算成功:
moved: false,且不会报错。 - 阻塞工作绝不会在事件循环上运行(COM 调用、ffmpeg 捕获),并由一项测试确保:该测试会记录访问摄像头时所在的线程。
- 观测是显式的:除非你调用了某个工具,否则不会拍摄图像。
- 写入被拒绝时会中止拍摄,不会继续驱动已停止响应的摄像头。
安装
uvx uvc-ptz-camera-mcp # run without installing
pip install uvc-ptz-camera-mcp # or install it
若要在 Windows 上使用真实摄像头,还需要 DirectShow 扩展:
pip install "uvc-ptz-camera-mcp[dshow]"
然后将主机指向它——--print-config 会输出使用正确解释器的配置片段:
uvc-ptz-mcp --print-config
uvc-ptz-mcp --list-devices # what to pass to --device
uvc-ptz-mcp --backend simulator # try it with no camera attached
配置放在哪里
Claude Desktop、Cursor、VS Code 等都使用相同的配置格式——完整配置如下,因为无需进行身份验证:
{
"mcpServers": {
"ptz-camera": {
"command": "uvx",
"args": ["uvc-ptz-camera-mcp"]
}
}
}
如果机器上有多个摄像头,请添加 "--device", "Osmo"(--list-devices 打印出的名称中的任意子串);如果没有摄像头,则添加 "--backend", "simulator"。这两项也可以通过环境变量设置,而不必写入参数:UVC_PTZ_DEVICE、UVC_PTZ_BACKEND 和 UVC_PTZ_STATE_DIR。Claude Desktop bundle 使用的就是这种方式;即使主机重启,设置也会保留,无需再次编辑配置。命令行标志的优先级高于环境变量;空值表示“未设置”(主机留空某个选项时会写入 "",而不是不写)。Claude Desktop 也支持将附在每个版本发布页上的 .mcpb bundle 一键安装。
模式,以及如何判断当前使用的模式
| 后端 | 说明 |
|---|---|
auto(默认) | 优先使用真实摄像头;若不可用,则改用模拟器并说明原因 |
dshow | Windows DirectShow:真实设备路径 |
simulator | 完全不需要硬件:根据下方测量数据校准的模型 |
每个结果都会包含 "simulated": true 或 false,而 camera_status 会在模拟器代替真实摄像头时报告原因。它绝不会悄悄冒充硬件:调用方绝不能误以为自己正在驱动摄像头,而实际上驱动的是模型。
模拟器根据参考设备校准而成,并非凭空编造:写入后约 0.4 s 开始移动,平移约 1 s 完成,4× 变焦约需 2.5 s,写入可能会被静默丢弃,且读回结果可能会回显硬件从未应用过的请求。它通过裁剪宽幅全景图来渲染画面,因此模拟平移确实会改变像素——如果模拟器使用静态图像,验证就可能在没有实际检查的情况下通过。
诚实说明局限
- 真实设备路径仅支持 Windows(DirectShow)。Linux 后端会使用
v4l2;接口已就绪,但尚未编写实现。 - 不支持曝光、对焦或白平衡。 参考摄像头完全没有 Processing Unit,只支持平移、倾斜、旋转和变焦。
- 在 Windows 上无法访问厂商的 Extension Unit,因此无法通过软件控制其中的功能(在参考摄像头上,这指内置的主体跟踪功能)。实测结果:设备筛选器拒绝
IKsControl,IKsTopologyInfo列出了三个节点且没有厂商节点,并且对所有节点调用CreateNodeInstance均失败。Linux 可通过uvcvideo的UVCIOC_CTRL_QUERY访问该单元;这属于单独的后端。 - 主体移动看起来就像摄像头在移动。 帧比较无法区分两者;此度量经过校准,可在场景静止时正常工作,而
check_view用于判断视图,而非移动。 - 一台不可用的设备不能让其他设备也无法显示。 一台已注册但不可用的虚拟摄像头在绑定其 DirectShow moniker 时抛出错误,导致本应返回两台正常摄像头的列表操作中止。现在枚举时会跳过无法加载的设备并说明原因;后端查找指定摄像头时也遵循相同规则。
- 实测延迟决定了控制上限:从命令发出到设备移动约需 0.4 秒,因此控制循环的频率只能达到每秒几次,而非几十次。这是硬件的延迟下限,不是软件造成的。
- DirectShow 路径经过测试,但尚未投入使用。 参考设备在开发中途才归还,因此硬件路径已测试到接口层,并且已根据设备正确设定类型,测试工作则由模拟器承担。请将首次连接真实摄像头运行视为正式验收测试。
开发
pip install -e ".[dshow]" numpy pytest pytest-asyncio ruff
python -m pytest -q # 39 tests, three layers
python -m ruff check . && python -m ruff format --check .
测试套件经过刻意分层:针对模拟器的进程内工具接口测试(映射、验证和如实报告失败)、编译器与度量的纯单元测试,以及作为子进程运行的真实 stdio 握手测试——其中还包括一次使用不存在的设备名称运行的测试,因为启动时就崩溃的服务对所有主机和列出该服务的目录来说都不可见。
参考设备
基于 DJI Osmo Pocket 4P 的网络摄像头模式开发:VID_2CA3 / PID_0023,以标准 UVC 摄像头控件提供平移 −38…215°、倾斜 −33…105°、旋转 ±35°、变焦 100…1200(1×–12×)。任何 UVC PTZ 摄像头都提供相同的控件接口;启动时会从设备读取范围,而非预先假定,也不会硬编码任何厂商专属内容。
与 DJI 无关联,未获 DJI 认可或支持。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。