OpenClaw 节点摄像头:远程拍照与视频录制实

目录

    • 摘要
    • [一、摄像头能力概览 🎥](#一、摄像头能力概览 🎥)
    • [二、摄像头基础操作详解 📷](#二、摄像头基础操作详解 📷)
      • [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.listcamera.snapcamera.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.snapcamera.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.snapcamera.clip 中通过 deviceId 参数指定特定摄像头;name 是人类可读的摄像头名称;position 表示朝向(frontback);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 图片数据,widthheight 是实际拍摄后的图片尺寸。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 摄像头朝向:frontback
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 分辨率与质量控制

maxWidthquality 两个参数共同决定了输出照片的大小和清晰度。

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 的节点命令必须通过双重门控才能执行:

  1. 节点声明门控 :节点在 WebSocket 连接时通过 connect.commands 列表声明自己支持的命令。如果节点没有声明 camera.snap,网关根本不会转发该命令。
  2. 网关策略门控 :即使节点声明了命令,网关的 gateway.nodes.allowCommandsgateway.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.snapcamera.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 最佳实践

  1. 始终先调用 camera.list:在首次使用新节点时,先列出可用摄像头,了解设备能力再决定参数。

  2. 合理选择分辨率:不是所有场景都需要最高分辨率。状态检查用 640px 足足,文档扫描才需要 2560px。

  3. 注意 delayMs 设置:macOS 节点默认 2 秒延迟是有原因的,不要随意设为 0,否则可能得到曝光不足的暗图。

  4. 视频时长最小化:只录需要的时间。30 秒视频的 base64 体积远大于 10 秒,传输和处理时间也成倍增加。

  5. 利用载荷守卫:OpenClaw 自动将照片重压缩到 5MB 以内,但最好在源头控制质量,避免重压缩导致的质量损失。

  6. 前台状态保障:在自动化脚本中,确保节点 App 保持前台。iOS 可以通过 Guided Access 模式锁定 App,Android 可以使用 Screen Pinning。

  7. 安全配置优先 :只放行实际需要的命令。如果只需要拍照不需要录像,只添加 camera.snapallowCommands


九、总结与展望 🚀

OpenClaw 节点摄像头功能为 AI 代理赋予了视觉感知能力,使其不再局限于文本交互。通过 camera.listcamera.snapcamera.clip 三大命令,开发者可以轻松实现远程拍照、视频录制和设备巡检等场景。

核心要点回顾:

  • 📷 camera.list 查看可用摄像头,是所有操作的第一步
  • 📸 camera.snap 拍照,支持分辨率、质量、朝向、延迟等精细控制
  • 🎬 camera.clip 录制视频,最长 60 秒,可选含/不含音频
  • 🔐 双重门控 + 前台约束 确保隐私安全
  • 📏 载荷守卫 自动重压缩,保证通信可靠性

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

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

图3:安全监控实战场景示意图,展示手机节点部署在监控位置的典型配置

未来,随着 OpenClaw 的持续演进,我们可以期待更多视觉能力的加入:实时视频流推送、多节点协同拍摄、AI 视觉分析深度集成等。摄像头只是开始,当 AI 代理能够"看到"世界,它的应用边界将无限扩展。


参考资料:


相关推荐
jinglong.zha1 小时前
AI视频全流程实战:广告/动画/短剧都适用,解决角色一致性+后期合成难题
人工智能·ai·音视频·光照贴图·叙事照片
qq_366566502 小时前
短视频批量翻译+配音自动化:Python脚本处理TikTok/Reels/Shorts全流程
python·chatgpt·自动化·音视频·媒体
MemoriKu2 小时前
Flutter 相册 APP 视频模态稳定化实战:从远端重构冲突到真机 Smoke Test
人工智能·python·flutter·机器学习·重构·音视频·新人首发
AC赳赳老秦2 小时前
OpenClaw + 华为云自动化:批量管理云资源、生成月度云账单分析与成本优化报告
java·开发语言·javascript·人工智能·python·mysql·openclaw
ai产品老杨3 小时前
深度解析:基于Docker构建的安防视频AI平台——如何通过GB28181/RTSP协议栈统一接入与全套源码交付,破局异构边缘计算芯片内卷
人工智能·docker·音视频
谁刺我心3 小时前
[QtCPP]Examples使用示例-(2)QtMultimedia Audio音频引擎测试mp3播放【linux/win】
音视频
换个昵称都难16 小时前
webrtc 音频模块FEC模块
网络·音视频·webrtc
qq_3665665017 小时前
视频配音自动化Pipeline:TTS选型+音色克隆+批量处理(附完整代码)
自动化·新媒体运营·音视频·音频
hz5678921 小时前
公安局远程办案用什么音视频系统?安全取证与多方协同方案
安全·架构·云计算·音视频·实时音视频·信息与通信