目录
-
- 摘要
- [一、摄像头能力概览 🎥](#一、摄像头能力概览 🎥)
- [二、摄像头基础操作详解 📷](#二、摄像头基础操作详解 📷)
-
- [2.1 camera.list ------ 查看可用摄像头](#2.1 camera.list —— 查看可用摄像头)
- [2.2 camera.snap ------ 远程拍照](#2.2 camera.snap —— 远程拍照)
- [2.3 camera.clip ------ 视频录制](#2.3 camera.clip —— 视频录制)
- [三、摄像头选择与参数配置 🔧](#三、摄像头选择与参数配置 🔧)
-
- [3.1 前置 vs 后置 vs 外置](#3.1 前置 vs 后置 vs 外置)
- [3.2 分辨率与质量控制](#3.2 分辨率与质量控制)
- [3.3 delayMs 参数与曝光稳定](#3.3 delayMs 参数与曝光稳定)
- [四、视频录制功能解析 🎬](#四、视频录制功能解析 🎬)
-
- [4.1 录制参数深度解析](#4.1 录制参数深度解析)
- [4.2 音频控制](#4.2 音频控制)
- [4.3 录制时长策略](#4.3 录制时长策略)
- [五、命令策略与权限模型 🔐](#五、命令策略与权限模型 🔐)
-
- [5.1 双重门控机制](#5.1 双重门控机制)
- [5.2 放行摄像头命令](#5.2 放行摄像头命令)
- [5.3 前台运行约束](#5.3 前台运行约束)
- [5.4 各平台权限差异](#5.4 各平台权限差异)
- [六、实战案例 💡](#六、实战案例 💡)
-
- [6.1 案例一:远程安全监控](#6.1 案例一:远程安全监控)
- [6.2 案例二:设备巡检验证](#6.2 案例二:设备巡检验证)
- [6.3 案例三:定时拍照任务](#6.3 案例三:定时拍照任务)
- [七、架构全景与数据流 🏗️](#七、架构全景与数据流 🏗️)
-
- [7.1 端到端数据流](#7.1 端到端数据流)
- [7.2 CLI 命令速查表](#7.2 CLI 命令速查表)
- [八、常见问题与最佳实践 ❓](#八、常见问题与最佳实践 ❓)
-
- [8.1 常见错误及解决方案](#8.1 常见错误及解决方案)
- [8.2 最佳实践](#8.2 最佳实践)
- [九、总结与展望 🚀](#九、总结与展望 🚀)
📸 当 AI 代理拥有了"眼睛",它就不再只是文字的搬运工------它能看到你的设备、你的环境、你的世界。OpenClaw 节点摄像头功能,让 AI 代理通过 iOS、Android 和 macOS 节点远程拍照与录制视频,为安全监控、设备巡检、定时记录等场景打开了全新的可能。
摘要
OpenClaw 是一个自托管的多渠道 AI 代理网关,支持 iOS、Android 和 macOS 节点的摄像头远程控制。本文深入讲解 OpenClaw 节点摄像头的三大核心命令------camera.list、camera.snap、camera.clip,涵盖摄像头选择、分辨率配置、闪光灯控制、视频录制参数等关键细节。通过远程安全监控、设备巡检验证、定时拍照任务三个实战案例,展示如何将摄像头能力融入 AI 工作流。同时解析命令策略、权限模型、前台约束和安全限制,帮助开发者快速构建基于 OpenClaw 的视觉感知应用。
一、摄像头能力概览 🎥
OpenClaw 的节点(Node)是连接到网关的伴侣设备,可以是 iOS 手机、Android 手机或 macOS 电脑。节点通过 WebSocket 与网关通信,暴露一系列命令表面(command surface),其中 camera.* 命令族就是让 AI 代理"看见世界"的关键入口。
#mermaid-svg-Gm0dcI0AsMnOEBQ2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .error-icon{fill:#552222;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .marker.cross{stroke:#333333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 p{margin:0;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .cluster-label text{fill:#333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .cluster-label span{color:#333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .cluster-label span p{background-color:transparent;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .label text,#mermaid-svg-Gm0dcI0AsMnOEBQ2 span{fill:#333;color:#333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node rect,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node circle,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node ellipse,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node polygon,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .rough-node .label text,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node .label text,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .image-shape .label,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .rough-node .label,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node .label,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .image-shape .label,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .icon-shape .label{text-align:center;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node.clickable{cursor:pointer;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .arrowheadPath{fill:#333333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .cluster text{fill:#333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .cluster span{color:#333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .icon-shape,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .icon-shape p,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .icon-shape .label rect,#mermaid-svg-Gm0dcI0AsMnOEBQ2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Gm0dcI0AsMnOEBQ2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 节点层
OpenClaw 网关
策略检查
允许
允许
允许
base64 媒体
base64 媒体
base64 媒体
工具调用
AI 代理
大模型
工具调用
node.invoke
网关核心
路由 & 会话管理
命令策略
allowCommands / denyCommands
iOS 节点
camera.snap / clip
Android 节点
camera.snap / clip
macOS 节点
camera.snap / clip
上图展示了 AI 代理通过网关调用节点摄像头的完整链路:代理发起工具调用 → 网关进行命令策略检查 → 转发到目标节点 → 节点执行摄像头操作 → 返回 base64 编码的媒体数据。
支持的节点类型
| 节点类型 | 拍照(camera.snap) | 录像(camera.clip) | 默认启用 | 特殊说明 |
|---|---|---|---|---|
| iOS 节点 | ✅ jpg | ✅ mp4 | ✅ 默认开启 | 前台运行要求 |
| Android 节点 | ✅ jpg | ✅ mp4 | ✅ 默认开启 | 需运行时权限授权 |
| macOS 节点 | ✅ jpg | ✅ mp4 | ❌ 默认关闭 | 需手动开启 Allow Camera |
⚠️ 重要提醒 :
camera.snap和camera.clip属于隐私敏感命令,需要在网关配置中通过gateway.nodes.allowCommands显式放行。这是 OpenClaw 安全设计的一部分------默认拒绝,显式允许。
二、摄像头基础操作详解 📷
2.1 camera.list ------ 查看可用摄像头
在拍照或录像之前,第一步永远是了解设备上有哪些摄像头可用。camera.list 命令返回设备上所有可用摄像头的详细信息。
命令格式:
bash
# CLI 方式
openclaw nodes camera list --node <idOrNameOrIp>
# 底层 RPC 方式
openclaw nodes invoke --node <idOrNameOrIp> \
--command camera.list --params '{}'
返回数据结构:
json
{
"devices": [
{
"id": "com.apple.avfoundation.device.0",
"name": "后置摄像头",
"position": "back",
"deviceType": "wideAngleCamera"
},
{
"id": "com.apple.avfoundation.device.1",
"name": "前置摄像头",
"position": "front",
"deviceType": "wideAngleCamera"
}
]
}
返回的 devices 数组中每个对象包含四个字段:id 是摄像头的唯一标识符,用于在 camera.snap 和 camera.clip 中通过 deviceId 参数指定特定摄像头;name 是人类可读的摄像头名称;position 表示朝向(front 或 back);deviceType 描述摄像头类型(如广角、超广角、长焦等)。
💡 实用技巧 :对于大多数手机设备,
camera.list通常返回前置和后置两个摄像头。但在多摄手机(如三摄、四摄)上,可能返回更多设备。使用deviceId精确指定可以避免拍错摄像头。
2.2 camera.snap ------ 远程拍照
camera.snap 是最常用的摄像头命令,用于拍摄一张照片并返回 jpg 格式的 base64 编码数据。
基础用法:
bash
# 默认拍摄前置和后置两张照片
openclaw nodes camera snap --node my-iphone
# 只拍前置摄像头
openclaw nodes camera snap --node my-iphone --facing front
# 只拍后置摄像头
openclaw nodes camera snap --node my-iphone --facing back
📝 注意 :CLI 的
camera snap默认会拍摄两个朝向 的照片(前置 + 后置),输出两行 MEDIA 路径。这是因为 AI 代理通常需要同时了解用户面前和用户背后的情况。如果只需要一个方向,务必指定--facing参数。
完整参数详解:
camera.snap 支持的参数如下表所示:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
facing |
string | front |
摄像头朝向:front(前置)或 back(后置) |
maxWidth |
number | 1600 | 最大宽度(像素),超出会等比缩放 |
quality |
number | 0.9 | JPEG 压缩质量,范围 0~1 |
format |
string | jpg |
输出格式,目前仅支持 jpg |
delayMs |
number | 0(iOS)/ 2000(macOS) | 拍照前延迟毫秒数,等待曝光稳定 |
deviceId |
string | 无 | 指定摄像头 ID(来自 camera.list) |
返回数据结构:
json
{
"format": "jpg",
"base64": "/9j/4AAQSkZJRgABAQ...",
"width": 1600,
"height": 1200
}
返回的 base64 字段包含完整的 JPEG 图片数据,width 和 height 是实际拍摄后的图片尺寸。OpenClaw 内置了载荷守卫机制:照片会被自动重压缩,确保 base64 编码后的体积不超过 5MB,避免因消息过大导致通信失败。
高级参数组合示例:
bash
# 高分辨率 + 高质量拍摄(适用于文档扫描)
openclaw nodes camera snap --node my-iphone \
--facing back --max-width 2560 --quality 0.95
# 低分辨率快速拍摄(适用于状态检查)
openclaw nodes camera snap --node my-iphone \
--facing front --max-width 640 --quality 0.5
# 指定特定摄像头 ID(多摄设备)
openclaw nodes camera snap --node my-iphone \
--device-id "com.apple.avfoundation.device.2"
# 带延迟拍摄(等待曝光稳定)
openclaw nodes camera snap --node my-macbook \
--delay-ms 3000 --max-width 1920
上述四个示例分别展示了不同场景下的参数组合:文档扫描需要高分辨率和高质量;状态检查则优先速度和体积;多摄设备通过 deviceId 精确选择;macOS 节点由于摄像头启动较慢,通常需要更长的延迟时间。
2.3 camera.clip ------ 视频录制
camera.clip 命令用于录制短视频片段,输出 mp4 格式的 base64 编码数据。
基础用法:
bash
# 录制 10 秒视频(含音频)
openclaw nodes camera clip --node my-iphone --duration 10s
# 录制 3 秒无声视频
openclaw nodes camera clip --node my-iphone --duration 3000 --no-audio
# 指定后置摄像头录制
openclaw nodes camera clip --node my-iphone --facing back --duration 5s
完整参数详解:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
facing |
string | front |
摄像头朝向:front 或 back |
durationMs |
number | 3000 | 录制时长(毫秒),上限 60000(60秒) |
includeAudio |
boolean | true | 是否包含音频 |
format |
string | mp4 |
输出格式,目前仅支持 mp4 |
deviceId |
string | 无 | 指定摄像头 ID(来自 camera.list) |
返回数据结构:
json
{
"format": "mp4",
"base64": "AAAAIGZ0eXBpc29t...",
"durationMs": 10000,
"hasAudio": true
}
⚠️ 时长限制 :视频录制时长硬性上限为 60 秒 。这是为了防止 base64 编码后的载荷过大导致通信失败。60 秒的 720p 视频经 base64 编码后可能达到数十 MB,已经接近实用上限。如果需要更长录制,建议分段录制或使用屏幕录制(
screen.record)。
三、摄像头选择与参数配置 🔧
3.1 前置 vs 后置 vs 外置
选择正确的摄像头是获得理想拍摄效果的第一步。不同朝向的摄像头适用于不同场景:
#mermaid-svg-HXr9Ohglbb1kpQ0Q{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .error-icon{fill:#552222;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .marker.cross{stroke:#333333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HXr9Ohglbb1kpQ0Q p{margin:0;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .cluster-label text{fill:#333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .cluster-label span{color:#333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .cluster-label span p{background-color:transparent;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .label text,#mermaid-svg-HXr9Ohglbb1kpQ0Q span{fill:#333;color:#333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .node rect,#mermaid-svg-HXr9Ohglbb1kpQ0Q .node circle,#mermaid-svg-HXr9Ohglbb1kpQ0Q .node ellipse,#mermaid-svg-HXr9Ohglbb1kpQ0Q .node polygon,#mermaid-svg-HXr9Ohglbb1kpQ0Q .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .rough-node .label text,#mermaid-svg-HXr9Ohglbb1kpQ0Q .node .label text,#mermaid-svg-HXr9Ohglbb1kpQ0Q .image-shape .label,#mermaid-svg-HXr9Ohglbb1kpQ0Q .icon-shape .label{text-anchor:middle;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .rough-node .label,#mermaid-svg-HXr9Ohglbb1kpQ0Q .node .label,#mermaid-svg-HXr9Ohglbb1kpQ0Q .image-shape .label,#mermaid-svg-HXr9Ohglbb1kpQ0Q .icon-shape .label{text-align:center;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .node.clickable{cursor:pointer;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .arrowheadPath{fill:#333333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HXr9Ohglbb1kpQ0Q .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HXr9Ohglbb1kpQ0Q .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HXr9Ohglbb1kpQ0Q .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .cluster text{fill:#333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .cluster span{color:#333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HXr9Ohglbb1kpQ0Q rect.text{fill:none;stroke-width:0;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .icon-shape,#mermaid-svg-HXr9Ohglbb1kpQ0Q .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .icon-shape p,#mermaid-svg-HXr9Ohglbb1kpQ0Q .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .icon-shape .label rect,#mermaid-svg-HXr9Ohglbb1kpQ0Q .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HXr9Ohglbb1kpQ0Q .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HXr9Ohglbb1kpQ0Q .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HXr9Ohglbb1kpQ0Q :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 人物/自拍
环境/文档/物体
特定镜头
摄像头选择决策
拍摄目标是什么?
前置摄像头
facing: front
后置摄像头
facing: back
指定 deviceId
来自 camera.list
特点:镜像、
低分辨率、广角
特点:正常方向、
高分辨率、多摄可选
特点:超广角/长焦/
微距等特殊镜头
前置摄像头(front) 适合拍摄用户本人或用户面前的内容。注意前置摄像头拍摄的画面通常是镜像的,这在人像自拍时看起来更自然,但在拍摄文字或文档时可能造成困扰。
后置摄像头(back) 是大多数场景的首选。后置摄像头通常拥有更高的像素、更好的光学性能,且不会产生镜像问题。在文档扫描、环境监控、物体识别等场景中,后置摄像头是最佳选择。
外置/特殊摄像头 通过 deviceId 指定。现代手机通常配备多个后置摄像头(广角、超广角、长焦、微距),camera.list 会列出所有可用设备。通过 deviceId 可以精确选择特定镜头,例如使用超广角拍摄大范围场景,或使用长焦拍摄远处细节。
3.2 分辨率与质量控制
maxWidth 和 quality 两个参数共同决定了输出照片的大小和清晰度。
maxWidth 参数:指定图片的最大宽度(像素)。如果实际拍摄宽度超过此值,图片会被等比缩放到目标宽度以内。默认值为 1600 像素,这是一个在清晰度和体积之间的良好平衡点。
quality 参数:JPEG 压缩质量,范围 0 到 1。0 表示最低质量(最小体积),1 表示最高质量(最大体积)。默认值 0.9 在绝大多数场景下都能提供优秀的视觉质量。
不同场景的推荐配置如下表:
| 场景 | maxWidth | quality | 预期体积 | 说明 |
|---|---|---|---|---|
| 文档扫描 | 2560 | 0.95 | ~1-2MB | 高分辨率保留文字细节 |
| 人脸识别 | 1280 | 0.8 | ~200-400KB | 中等分辨率足够 |
| 环境监控 | 1600 | 0.7 | ~300-500KB | 平衡清晰度与传输速度 |
| 快速状态检查 | 640 | 0.5 | ~50-100KB | 最小体积优先速度 |
| 高质量存档 | 2560 | 1.0 | ~2-4MB | 最高质量不压缩 |
3.3 delayMs 参数与曝光稳定
delayMs 参数控制拍照前的等待时间,让摄像头有时间完成自动曝光和白平衡调整。
- iOS 节点 :默认
delayMs为 0,因为 iOS 的 AVFoundation 框架在启动捕获会话时会自动等待曝光稳定。 - macOS 节点 :默认
delayMs为 2000ms(2 秒),因为 macOS 的 FaceTime 摄像头启动较慢,需要更多时间完成曝光调整。 - Android 节点:行为与 iOS 类似,Camera2 API 会自动处理曝光。
在光线条件较差的环境下(如夜间、室内弱光),建议适当增加 delayMs 值(如 1000-3000ms),确保摄像头有充足时间完成曝光补偿。
四、视频录制功能解析 🎬
4.1 录制参数深度解析
视频录制是摄像头功能中更复杂的部分,涉及时长、音频、格式等多个维度的配置。
摄像头硬件 节点设备 网关 AI 代理 摄像头硬件 节点设备 网关 AI 代理 #mermaid-svg-YbcFHSalUB5dCErP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YbcFHSalUB5dCErP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YbcFHSalUB5dCErP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YbcFHSalUB5dCErP .error-icon{fill:#552222;}#mermaid-svg-YbcFHSalUB5dCErP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YbcFHSalUB5dCErP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YbcFHSalUB5dCErP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YbcFHSalUB5dCErP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YbcFHSalUB5dCErP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YbcFHSalUB5dCErP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YbcFHSalUB5dCErP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YbcFHSalUB5dCErP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YbcFHSalUB5dCErP .marker.cross{stroke:#333333;}#mermaid-svg-YbcFHSalUB5dCErP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YbcFHSalUB5dCErP p{margin:0;}#mermaid-svg-YbcFHSalUB5dCErP .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YbcFHSalUB5dCErP text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-YbcFHSalUB5dCErP .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-YbcFHSalUB5dCErP .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-YbcFHSalUB5dCErP .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-YbcFHSalUB5dCErP .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-YbcFHSalUB5dCErP #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-YbcFHSalUB5dCErP .sequenceNumber{fill:white;}#mermaid-svg-YbcFHSalUB5dCErP #sequencenumber{fill:#333;}#mermaid-svg-YbcFHSalUB5dCErP #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-YbcFHSalUB5dCErP .messageText{fill:#333;stroke:none;}#mermaid-svg-YbcFHSalUB5dCErP .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YbcFHSalUB5dCErP .labelText,#mermaid-svg-YbcFHSalUB5dCErP .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-YbcFHSalUB5dCErP .loopText,#mermaid-svg-YbcFHSalUB5dCErP .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-YbcFHSalUB5dCErP .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-YbcFHSalUB5dCErP .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-YbcFHSalUB5dCErP .noteText,#mermaid-svg-YbcFHSalUB5dCErP .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-YbcFHSalUB5dCErP .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YbcFHSalUB5dCErP .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YbcFHSalUB5dCErP .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YbcFHSalUB5dCErP .actorPopupMenu{position:absolute;}#mermaid-svg-YbcFHSalUB5dCErP .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-YbcFHSalUB5dCErP .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YbcFHSalUB5dCErP .actor-man circle,#mermaid-svg-YbcFHSalUB5dCErP line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-YbcFHSalUB5dCErP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 录制中... 10秒 camera.clip {durationMs:10000, facing:"back"} 命令策略检查 (allowCommands) node.invoke camera.clip 前台状态检查 启动摄像头 + 麦克风 预览流就绪 录制完成 编码为 mp4 + base64 载荷守卫检查 (<5MB) {format:"mp4", base64:"...", durationMs:10000} 返回视频数据
上图展示了视频录制的完整时序流程,从代理发起请求到最终返回视频数据,经过了策略检查、前台检查、硬件启动、录制编码、载荷守卫等多个环节。
4.2 音频控制
includeAudio 参数控制是否在视频中录制音频。默认值为 true,即同时录制音频和视频。
包含音频的场景:
- 安全监控:需要听到现场声音
- 会议记录:保留语音内容
- 环境感知:声音是重要信息源
不包含音频的场景:
- 静态环境监控:无需声音,减小体积
- 隐私敏感场景:避免录制他人对话
- Android 权限受限:未获得
RECORD_AUDIO权限时
在 Android 上,录制音频需要 RECORD_AUDIO 运行时权限。如果用户拒绝了该权限,使用 --no-audio 仍然可以录制无声视频,否则会返回 *_PERMISSION_REQUIRED 错误。
4.3 录制时长策略
60 秒的硬性上限并不意味着每次都要录满。合理的时长选择应该基于实际需求:
| 场景 | 推荐时长 | 理由 |
|---|---|---|
| 快速状态确认 | 3-5秒 | 只需确认当前状态 |
| 人员出入记录 | 10-15秒 | 捕获完整动作 |
| 设备运行检查 | 15-30秒 | 观察设备运行状态 |
| 异常事件记录 | 30-60秒 | 记录事件完整过程 |
五、命令策略与权限模型 🔐
5.1 双重门控机制
OpenClaw 的节点命令必须通过双重门控才能执行:
- 节点声明门控 :节点在 WebSocket 连接时通过
connect.commands列表声明自己支持的命令。如果节点没有声明camera.snap,网关根本不会转发该命令。 - 网关策略门控 :即使节点声明了命令,网关的
gateway.nodes.allowCommands和gateway.nodes.denyCommands配置也会进行二次过滤。denyCommands始终优先于allowCommands。
#mermaid-svg-rYDM4tvLPkIfYEbn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rYDM4tvLPkIfYEbn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rYDM4tvLPkIfYEbn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rYDM4tvLPkIfYEbn .error-icon{fill:#552222;}#mermaid-svg-rYDM4tvLPkIfYEbn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rYDM4tvLPkIfYEbn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rYDM4tvLPkIfYEbn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rYDM4tvLPkIfYEbn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rYDM4tvLPkIfYEbn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rYDM4tvLPkIfYEbn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rYDM4tvLPkIfYEbn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rYDM4tvLPkIfYEbn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rYDM4tvLPkIfYEbn .marker.cross{stroke:#333333;}#mermaid-svg-rYDM4tvLPkIfYEbn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rYDM4tvLPkIfYEbn p{margin:0;}#mermaid-svg-rYDM4tvLPkIfYEbn defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-rYDM4tvLPkIfYEbn g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-rYDM4tvLPkIfYEbn g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-rYDM4tvLPkIfYEbn g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-rYDM4tvLPkIfYEbn g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-rYDM4tvLPkIfYEbn g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-rYDM4tvLPkIfYEbn .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-rYDM4tvLPkIfYEbn .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-rYDM4tvLPkIfYEbn .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-rYDM4tvLPkIfYEbn .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-rYDM4tvLPkIfYEbn .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-rYDM4tvLPkIfYEbn .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-rYDM4tvLPkIfYEbn .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-rYDM4tvLPkIfYEbn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rYDM4tvLPkIfYEbn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rYDM4tvLPkIfYEbn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rYDM4tvLPkIfYEbn .edgeLabel .label text{fill:#333;}#mermaid-svg-rYDM4tvLPkIfYEbn .label div .edgeLabel{color:#333;}#mermaid-svg-rYDM4tvLPkIfYEbn .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-rYDM4tvLPkIfYEbn .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-rYDM4tvLPkIfYEbn .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-rYDM4tvLPkIfYEbn .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-rYDM4tvLPkIfYEbn .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-rYDM4tvLPkIfYEbn .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rYDM4tvLPkIfYEbn .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rYDM4tvLPkIfYEbn #statediagram-barbEnd{fill:#333333;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rYDM4tvLPkIfYEbn .cluster-label,#mermaid-svg-rYDM4tvLPkIfYEbn .nodeLabel{color:#131300;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-rYDM4tvLPkIfYEbn .note-edge{stroke-dasharray:5;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-note text{fill:black;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram-note .nodeLabel{color:black;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagram .edgeLabel{color:red;}#mermaid-svg-rYDM4tvLPkIfYEbn #dependencyStart,#mermaid-svg-rYDM4tvLPkIfYEbn #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-rYDM4tvLPkIfYEbn .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rYDM4tvLPkIfYEbn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 节点连接
网关策略拒绝
网关策略允许
返回命令禁止错误
前台状态检查
节点在后台
节点在前台
NODE_BACKGROUND_UNAVAILABLE
权限检查
权限被拒绝
权限已授予
*_PERMISSION_REQUIRED
执行命令
返回结果
Declared
NotAllowed
Allowed
ForegroundCheck
Background
InForeground
PermissionCheck
NoPermission
HasPermission
Executing
上图展示了命令从声明到执行的完整状态流转。可以看到,即使通过了策略检查,还需要经过前台状态检查和权限检查两道关卡。
5.2 放行摄像头命令
由于 camera.snap 和 camera.clip 属于隐私敏感命令,需要在网关配置中显式放行:
json5
// ~/.openclaw/openclaw.json
{
gateway: {
nodes: {
// 放行摄像头拍照和录像
allowCommands: ["camera.snap", "camera.clip"],
// 如果需要同时放行屏幕录制
// allowCommands: ["camera.snap", "camera.clip", "screen.record"],
}
}
}
修改配置后需要重启网关使配置生效:
bash
openclaw gateway restart
5.3 前台运行约束
所有摄像头命令都要求节点处于前台状态。 这是一条硬性约束,无法绕过。
- iOS 节点 :App 必须在前台且可见,后台调用返回
NODE_BACKGROUND_UNAVAILABLE。 - Android 节点:同 iOS,App 必须在前台。Android 的后台摄像头访问受系统严格限制。
- macOS 节点:macOS 菜单栏应用通常始终"在前台",但如果是最小化状态可能无法访问摄像头。
这一设计是出于隐私和安全考虑:防止 AI 代理在用户不知情的情况下偷偷拍照或录像。节点必须在前台,意味着用户能看到 App 界面,从而知晓摄像头正在被使用。
5.4 各平台权限差异
| 平台 | 摄像头权限 | 麦克风权限 | 默认启用 | 权限提示 |
|---|---|---|---|---|
| iOS | 系统权限弹窗 | 系统权限弹窗 | ✅ 是 | 首次使用时弹出 |
| Android | 运行时权限 | 运行时权限 | ✅ 是 | 可在设置中预授权 |
| macOS | 系统权限(TCC) | 系统权限(TCC) | ❌ 否 | 需在设置中手动开启 |
六、实战案例 💡
6.1 案例一:远程安全监控
场景描述:办公室部署了一台 Android 手机作为监控节点,AI 代理定期拍照检查办公室安全状况。
实现方案:
bash
#!/bin/bash
# security-monitor.sh - 远程安全监控脚本
# 用法: ./security-monitor.sh <node-id>
NODE_ID="${1:-office-android}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
OUTPUT_DIR="/tmp/security-monitor"
mkdir -p "$OUTPUT_DIR"
echo "[$(date)] 开始安全监控拍照..."
# 1. 拍摄后置摄像头(监控办公室全景)
openclaw nodes camera snap --node "$NODE_ID" \
--facing back --max-width 1920 --quality 0.85 \
> "$OUTPUT_DIR/back_${TIMESTAMP}.jpg"
# 2. 拍摄前置摄像头(监控门口方向)
openclaw nodes camera snap --node "$NODE_ID" \
--facing front --max-width 1280 --quality 0.7 \
> "$OUTPUT_DIR/front_${TIMESTAMP}.jpg"
# 3. 录制 10 秒视频片段(含音频,检测异常声音)
openclaw nodes camera clip --node "$NODE_ID" \
--facing back --duration 10s \
> "$OUTPUT_DIR/video_${TIMESTAMP}.mp4"
echo "[$(date)] 监控数据已保存到 $OUTPUT_DIR"
ls -lh "$OUTPUT_DIR"/*"${TIMESTAMP}"*
上述脚本实现了完整的安全监控流程:先拍后置全景照,再拍前置门口照,最后录制 10 秒含音频的视频。三份数据分别从不同角度和模态捕获现场信息,AI 代理可以综合分析图片和视频来判断是否存在异常。
定时执行配置:
bash
# 每 30 分钟执行一次监控
crontab -e
# 添加: */30 * * * * /path/to/security-monitor.sh office-android
💡 进阶优化:可以将拍摄结果直接发送到飞书或 Telegram 群组,实现远程实时查看。结合 AI 视觉模型,还能自动识别异常情况(如非工作时间有人出现)并触发告警。
6.2 案例二:设备巡检验证
场景描述:运维人员需要远程验证机房设备的指示灯状态和仪表读数,使用 OpenClaw 节点摄像头代替现场巡检。
实现方案:
bash
#!/bin/bash
# device-inspection.sh - 设备巡检验证脚本
# 用法: ./device-inspection.sh <node-id> [device-name]
NODE_ID="${1:-server-room-ipad}"
DEVICE="${2:-core-switch}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
OUTPUT_DIR="/tmp/inspection/$DEVICE"
mkdir -p "$OUTPUT_DIR"
echo "=========================================="
echo "设备巡检: $DEVICE"
echo "时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "节点: $NODE_ID"
echo "=========================================="
# 高分辨率拍摄设备面板(后置摄像头,最高质量)
echo "[1/3] 拍摄设备正面面板..."
openclaw nodes camera snap --node "$NODE_ID" \
--facing back --max-width 2560 --quality 0.95 \
> "$OUTPUT_DIR/panel_front_${TIMESTAMP}.jpg"
# 稍等片刻,拍摄设备背面
echo "[2/3] 拍摄设备背面接线..."
sleep 2
openclaw nodes camera snap --node "$NODE_ID" \
--facing back --max-width 2560 --quality 0.95 \
> "$OUTPUT_DIR/panel_back_${TIMESTAMP}.jpg"
# 录制 5 秒视频观察指示灯闪烁模式
echo "[3/3] 录制指示灯状态..."
openclaw nodes camera clip --node "$NODE_ID" \
--facing back --duration 5s --no-audio \
> "$OUTPUT_DIR/indicator_${TIMESTAMP}.mp4"
echo "=========================================="
echo "巡检完成!数据保存在: $OUTPUT_DIR"
echo "文件列表:"
ls -lh "$OUTPUT_DIR"/*"${TIMESTAMP}"*
echo "=========================================="
这个巡检脚本的亮点在于:使用 2560 像素宽度和 0.95 质量拍摄,确保仪表读数和指示灯颜色在图片中清晰可辨;录制 5 秒无声视频用于观察指示灯的闪烁模式(某些故障状态通过闪烁频率编码);分正反面拍摄,全面覆盖设备状态。
6.3 案例三:定时拍照任务
场景描述:施工工地需要每小时拍摄一张进度照片,持续记录施工进展。
实现方案:
bash
#!/bin/bash
# timelapse-capture.sh - 定时拍照任务
# 用法: ./timelapse-capture.sh <node-id> <interval-minutes>
NODE_ID="${1:-site-camera}"
INTERVAL="${2:-60}" # 默认每60分钟
OUTPUT_DIR="/tmp/timelapse/$(date +%Y%m%d)"
COUNT=0
MAX_COUNT=24 # 最多拍24张(一天)
mkdir -p "$OUTPUT_DIR"
echo "延时拍照任务启动"
echo "节点: $NODE_ID | 间隔: ${INTERVAL}分钟 | 最大: $MAX_COUNT张"
while [ $COUNT -lt $MAX_COUNT ]; do
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
COUNT=$((COUNT + 1))
echo "[${COUNT}/${MAX_COUNT}] $(date '+%H:%M:%S') 拍摄中..."
# 拍摄后置摄像头,中等质量(平衡体积与清晰度)
openclaw nodes camera snap --node "$NODE_ID" \
--facing back --max-width 1600 --quality 0.8 \
> "$OUTPUT_DIR/progress_${TIMESTAMP}.jpg"
# 检查拍摄是否成功
if [ $? -eq 0 ]; then
echo " ✅ 保存成功: progress_${TIMESTAMP}.jpg"
else
echo " ❌ 拍摄失败,可能节点不在前台"
fi
# 等待下一次拍摄
if [ $COUNT -lt $MAX_COUNT ]; then
echo " ⏳ 等待 ${INTERVAL} 分钟..."
sleep $((INTERVAL * 60))
fi
done
echo "延时拍照任务完成!共拍摄 $COUNT 张"
echo "输出目录: $OUTPUT_DIR"
ls -lh "$OUTPUT_DIR/"
这个定时拍照脚本使用循环结构实现持续拍摄,每小时拍摄一张 1600 像素宽、0.8 质量的照片。拍摄失败时会给出明确提示(通常是节点不在前台),而不是静默跳过。24 张照片覆盖一整天的施工进展,后续可以用 ffmpeg 合成延时视频。
七、架构全景与数据流 🏗️
7.1 端到端数据流
#mermaid-svg-rFZMWgF5SeOopFe1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rFZMWgF5SeOopFe1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rFZMWgF5SeOopFe1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rFZMWgF5SeOopFe1 .error-icon{fill:#552222;}#mermaid-svg-rFZMWgF5SeOopFe1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rFZMWgF5SeOopFe1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rFZMWgF5SeOopFe1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rFZMWgF5SeOopFe1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rFZMWgF5SeOopFe1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rFZMWgF5SeOopFe1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rFZMWgF5SeOopFe1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rFZMWgF5SeOopFe1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rFZMWgF5SeOopFe1 .marker.cross{stroke:#333333;}#mermaid-svg-rFZMWgF5SeOopFe1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rFZMWgF5SeOopFe1 p{margin:0;}#mermaid-svg-rFZMWgF5SeOopFe1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rFZMWgF5SeOopFe1 .cluster-label text{fill:#333;}#mermaid-svg-rFZMWgF5SeOopFe1 .cluster-label span{color:#333;}#mermaid-svg-rFZMWgF5SeOopFe1 .cluster-label span p{background-color:transparent;}#mermaid-svg-rFZMWgF5SeOopFe1 .label text,#mermaid-svg-rFZMWgF5SeOopFe1 span{fill:#333;color:#333;}#mermaid-svg-rFZMWgF5SeOopFe1 .node rect,#mermaid-svg-rFZMWgF5SeOopFe1 .node circle,#mermaid-svg-rFZMWgF5SeOopFe1 .node ellipse,#mermaid-svg-rFZMWgF5SeOopFe1 .node polygon,#mermaid-svg-rFZMWgF5SeOopFe1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rFZMWgF5SeOopFe1 .rough-node .label text,#mermaid-svg-rFZMWgF5SeOopFe1 .node .label text,#mermaid-svg-rFZMWgF5SeOopFe1 .image-shape .label,#mermaid-svg-rFZMWgF5SeOopFe1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-rFZMWgF5SeOopFe1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rFZMWgF5SeOopFe1 .rough-node .label,#mermaid-svg-rFZMWgF5SeOopFe1 .node .label,#mermaid-svg-rFZMWgF5SeOopFe1 .image-shape .label,#mermaid-svg-rFZMWgF5SeOopFe1 .icon-shape .label{text-align:center;}#mermaid-svg-rFZMWgF5SeOopFe1 .node.clickable{cursor:pointer;}#mermaid-svg-rFZMWgF5SeOopFe1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rFZMWgF5SeOopFe1 .arrowheadPath{fill:#333333;}#mermaid-svg-rFZMWgF5SeOopFe1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rFZMWgF5SeOopFe1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rFZMWgF5SeOopFe1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rFZMWgF5SeOopFe1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rFZMWgF5SeOopFe1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rFZMWgF5SeOopFe1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rFZMWgF5SeOopFe1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rFZMWgF5SeOopFe1 .cluster text{fill:#333;}#mermaid-svg-rFZMWgF5SeOopFe1 .cluster span{color:#333;}#mermaid-svg-rFZMWgF5SeOopFe1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rFZMWgF5SeOopFe1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rFZMWgF5SeOopFe1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-rFZMWgF5SeOopFe1 .icon-shape,#mermaid-svg-rFZMWgF5SeOopFe1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rFZMWgF5SeOopFe1 .icon-shape p,#mermaid-svg-rFZMWgF5SeOopFe1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rFZMWgF5SeOopFe1 .icon-shape .label rect,#mermaid-svg-rFZMWgF5SeOopFe1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rFZMWgF5SeOopFe1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rFZMWgF5SeOopFe1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rFZMWgF5SeOopFe1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 节点侧
AI 代理侧
网关侧
用户侧
消息
tool_call:
node.invoke
允许
允许
允许
base64
jpg/mp4
分析结果
回复
聊天应用
飞书/Telegram/WhatsApp
会话路由
命令策略
allow/deny
会话管理
大模型
GPT-4/Claude等
视觉理解
图片/视频分析
camera.list
camera.snap
camera.clip
摄像头硬件
上图展示了从用户发送消息到 AI 代理返回分析结果的完整数据流。关键路径是:用户消息 → 网关路由 → 大模型决策 → 工具调用(node.invoke)→ 策略检查 → 节点执行 → 硬件捕获 → base64 返回 → 视觉理解 → 大模型生成回复 → 用户收到结果。
7.2 CLI 命令速查表
| 命令 | 用途 | 关键参数 |
|---|---|---|
openclaw nodes camera list |
列出可用摄像头 | --node |
openclaw nodes camera snap |
拍照 | --node, --facing, --max-width, --quality, --delay-ms, --device-id |
openclaw nodes camera clip |
录制视频 | --node, --facing, --duration, --no-audio, --device-id |
openclaw nodes status |
查看节点状态 | 无 |
openclaw nodes describe |
查看节点详情 | --node |
openclaw devices list |
查看配对请求 | 无 |
openclaw devices approve |
批准配对请求 | <requestId> |
八、常见问题与最佳实践 ❓
8.1 常见错误及解决方案
| 错误 | 原因 | 解决方案 |
|---|---|---|
NODE_BACKGROUND_UNAVAILABLE |
节点 App 在后台 | 将节点 App 切到前台 |
CAMERA_DISABLED |
摄像头被用户禁用 | 在节点设置中开启 Allow Camera |
*_PERMISSION_REQUIRED |
缺少系统权限 | 在系统设置中授予 CAMERA/RECORD_AUDIO 权限 |
| 命令被策略拒绝 | 未在 allowCommands 中放行 | 在网关配置中添加 camera.snap/camera.clip |
| base64 载荷过大 | 图片/视频体积超限 | 降低 maxWidth 或 quality,缩短录制时长 |
8.2 最佳实践
-
始终先调用 camera.list:在首次使用新节点时,先列出可用摄像头,了解设备能力再决定参数。
-
合理选择分辨率:不是所有场景都需要最高分辨率。状态检查用 640px 足足,文档扫描才需要 2560px。
-
注意 delayMs 设置:macOS 节点默认 2 秒延迟是有原因的,不要随意设为 0,否则可能得到曝光不足的暗图。
-
视频时长最小化:只录需要的时间。30 秒视频的 base64 体积远大于 10 秒,传输和处理时间也成倍增加。
-
利用载荷守卫:OpenClaw 自动将照片重压缩到 5MB 以内,但最好在源头控制质量,避免重压缩导致的质量损失。
-
前台状态保障:在自动化脚本中,确保节点 App 保持前台。iOS 可以通过 Guided Access 模式锁定 App,Android 可以使用 Screen Pinning。
-
安全配置优先 :只放行实际需要的命令。如果只需要拍照不需要录像,只添加
camera.snap到allowCommands。
九、总结与展望 🚀
OpenClaw 节点摄像头功能为 AI 代理赋予了视觉感知能力,使其不再局限于文本交互。通过 camera.list、camera.snap、camera.clip 三大命令,开发者可以轻松实现远程拍照、视频录制和设备巡检等场景。
核心要点回顾:
- 📷 camera.list 查看可用摄像头,是所有操作的第一步
- 📸 camera.snap 拍照,支持分辨率、质量、朝向、延迟等精细控制
- 🎬 camera.clip 录制视频,最长 60 秒,可选含/不含音频
- 🔐 双重门控 + 前台约束 确保隐私安全
- 📏 载荷守卫 自动重压缩,保证通信可靠性

图1:OpenClaw 节点摄像头架构概念图,展示了网关、节点和摄像头硬件之间的连接关系

图2:摄像头命令执行流程图,从代理发起调用到返回媒体数据的完整过程

图3:安全监控实战场景示意图,展示手机节点部署在监控位置的典型配置
未来,随着 OpenClaw 的持续演进,我们可以期待更多视觉能力的加入:实时视频流推送、多节点协同拍摄、AI 视觉分析深度集成等。摄像头只是开始,当 AI 代理能够"看到"世界,它的应用边界将无限扩展。
参考资料: