一、技能整体概述

本技能基于 OpenClaw 框架对原生 Camsnap 摄像头抓帧工具完成深度二次改造,聚焦实时画面捕获、定时抓拍、批量存图、画面预览、设备多源切换、参数调控等核心场景。原版工具仅实现基础单帧抓拍功能,调用逻辑简单、功能单一,仅能完成瞬时截图操作,缺少设备管理、定时任务、画质调节、批量采集、异常防护等实用能力,无法适配安防值守、画面留存、多机位巡检、周期性抓拍等常态化使用场景。

本次改造重构底层设备调用逻辑与图像存储体系,全面拓展抓帧形态、设备管理、任务调度、文件规整四大板块能力,兼顾轻量化即时使用与自动化值守场景。工具全平台适配、部署简易,可独立执行指令,也能接入 OpenClaw 自动化工作流联动运行,适用于日常画面截取、无人值守抓拍、多摄像头轮巡采集、现场画面归档等各类场景。

二、原版技能现存短板

  1. 功能形态单一 仅支持单次手动抓帧,无定时抓拍、间隔连拍、批量采集能力,周期性画面采集需反复手动触发,操作繁琐。
  2. 设备适配能力弱 仅默认调用系统主摄像头,不支持多摄像头设备切换,无法选择指定摄像源,多机位场景完全无法使用。
  3. 图像管控缺失 无法调整画面分辨率、画质、存储格式,抓拍图片统一存放,不做分类命名,后期查找与整理难度大。
  4. 无任务状态反馈 抓拍过程、设备连接状态无文字提示,无法判断摄像头是否正常启动、画面是否成功捕获。
  5. 文件管理混乱 抓拍图片随机命名、集中存储,不按时间、设备分类,长期使用易产生大量冗余文件,磁盘管理杂乱。
  6. 异常处理简陋 遇到摄像头被占用、设备断开、权限不足、驱动异常等问题,仅返回原始报错,无通俗提示与排查方向。
  7. 缺少运行日志 抓拍时间、使用设备、文件路径等信息无记录,历史抓拍行为无法溯源,不满足值守归档需求。
  8. 拓展联动性差 模块耦合度高,无对外调用接口,不能和框架内文件管理、消息推送、定时任务等技能联动。
  9. 运行稳定性不足 连续抓拍易出现画面卡顿、设备假死,没有连接保活、重试机制,长时间值守运行易中断。

三、核心改造升级点

(一)设备调用层改造

  1. 新增多摄像头枚举能力,自动识别本机全部摄像设备,支持手动选择指定设备执行抓帧,适配多机位环境。
  2. 增加设备连接校验与重连机制,摄像头断开、占用时自动重试,并给出状态提示,提升长时间运行稳定性。
  3. 优化底层调用逻辑,降低资源占用,避免抓帧过程中出现程序卡顿、画面延迟问题。

(二)抓帧模式拓展改造

  1. 拆分三大核心模式:单次即时抓帧、间隔连续抓拍、定时定点抓拍,覆盖手动使用、批量采集、无人值守全场景。
  2. 自定义抓拍间隔、连拍数量,可灵活设置连续抓拍频率,满足高频采样、慢间隔巡检等不同需求。
  3. 支持抓拍终止指令,运行中可随时停止连拍与定时任务,管控更灵活。

(三)图像参数与存储改造

  1. 开放画质、分辨率、图片格式配置,支持 JPG/PNG 主流格式切换,按需调整文件大小与画面清晰度。
  2. 规范文件命名规则,采用「设备名 + 时间戳」自动命名,杜绝文件覆盖问题。
  3. 搭建分级存储目录,按日期、设备分类归档图片,自动创建对应文件夹,实现文件有序管理。

(四)交互与状态反馈改造

  1. 全流程状态可视化提示,依次展示「设备连接中→画面捕获中→保存成功→任务结束」等状态。
  2. 抓拍结果实时反馈文件保存路径,用户可快速定位图片位置。

(五)日志与异常体系改造

  1. 搭建完整运行日志,记录抓拍时间、设备编号、文件路径、任务类型,支持历史记录查阅溯源。
  2. 分类捕获各类异常:权限不足、设备被占用、硬件断开、驱动异常、目录写入失败等,搭配对应的排查指引。

(六)拓展与联动改造

  1. 预留跨技能调用接口,可对接 OpenClaw 定时模块、消息推送模块,实现异常画面自动推送、周期巡检抓拍。
  2. 增加图片预览能力,抓取完成后可快速查看画面内容,无需手动打开文件。

四、运行环境与整体分层架构

4.1 基础运行环境

  • 适配系统:Windows / Linux /macOS 全主流操作系统
  • 框架要求:兼容 OpenClaw 标准运行环境,遵循 Skill 开发与调度规范
  • 硬件条件:设备搭载正常驱动的摄像头设备,支持单 / 多摄像源
  • 权限要求:开启摄像头访问权限、本地文件读写权限

4.2 六层模块化分层架构

  1. 全局配置层:统一管理默认设备、抓拍间隔、图片格式、分辨率、存储根目录、日志路径、最大连拍数量等参数。
  2. 设备检测层:枚举本地所有摄像头、校验设备可用性、检测设备占用状态,提前拦截无效调用。
  3. 设备连接与抓帧层:封装摄像头启动、画面读取、单帧捕获、流关闭等底层逻辑,内置重连保活机制。
  4. 任务调度层:管控单次抓拍、连拍、定时任务的执行逻辑,处理启停、间隔计时、任务终止等流程。
  5. 文件处理层:负责图片编码、格式转换、自动命名、分类目录创建、文件写入等操作。
  6. 交互日志层:输出状态提示、异常信息,持久化运行日志,同时提供对外联动接口。

五、核心代码实现(TypeScript 适配 OpenClaw Skill)

5.1 全局配置与基础依赖

typescript

运行

import fs from "fs-extra";
import path from "path";
import { Skill, Context } from "openclaw";
// 引入摄像头捕获依赖
const camera = require("camera-picture");

// 全局配置项
const CAMSNAP_CONFIG = {
  rootDir: "./claw-data/camsnap",
  logDir: "./claw-data/camsnap/logs",
  imgFormat: "jpg",
  quality: 90,
  defaultInterval: 2000,
  maxBurstCount: 50,
  currentDevice: 0
};

5.2 目录初始化与工具函数

typescript

运行

// 初始化存储、日志目录
function initDir() {
  fs.ensureDirSync(CAMSNAP_CONFIG.rootDir);
  fs.ensureDirSync(CAMSNAP_CONFIG.logDir);
  const dateStr = new Date().toLocaleDateString();
  fs.ensureDirSync(path.join(CAMSNAP_CONFIG.rootDir, dateStr));
}

// 生成标准文件名
function getFileName(deviceId: number) {
  const time = Date.now();
  return `cam_${deviceId}_${time}.${CAMSNAP_CONFIG.imgFormat}`;
}

// 写入运行日志
function writeSnapLog(deviceId: number, filePath: string, taskType: string) {
  const now = new Date().toISOString();
  const logFile = path.join(CAMSNAP_CONFIG.logDir, `${new Date().toLocaleDateString()}.log`);
  const logText = `[${now}] 设备ID:${deviceId} | 任务类型:${taskType} | 文件路径:${filePath}\n`;
  fs.appendFileSync(logFile, logText, "utf8");
}

// 枚举本地摄像头设备
async function getCameraList() {
  return new Promise((resolve, reject) => {
    camera.listDevices((err: Error, devices: any[]) => {
      if(err) return reject(err);
      resolve(devices);
    });
  });
}

5.3 核心抓帧逻辑

typescript

运行

// 单次抓拍
async function singleSnap(deviceId: number): Promise<string> {
  initDir();
  const dateStr = new Date().toLocaleDateString();
  const savePath = path.join(CAMSNAP_CONFIG.rootDir, dateStr, getFileName(deviceId));

  return new Promise((resolve, reject) => {
    camera.takePicture({
      device: deviceId,
      format: CAMSNAP_CONFIG.imgFormat,
      quality: CAMSNAP_CONFIG.quality
    }, (err: Error, buffer: Buffer) => {
      if(err) return reject(err);
      fs.writeFile(savePath, buffer, (writeErr) => {
        if(writeErr) return reject(writeErr);
        writeSnapLog(deviceId, savePath, "单次抓拍");
        resolve(savePath);
      });
    });
  });
}

// 间隔连拍
async function burstSnap(deviceId: number, count: number, interval: number) {
  return new Promise(async (resolve) => {
    const resultList: string[] = [];
    for(let i = 0; i < count; i++){
      const filePath = await singleSnap(deviceId).catch(() => "");
      if(filePath) resultList.push(filePath);
      await new Promise(r => setTimeout(r, interval));
    }
    resolve(resultList);
  });
}

5.4 异常信息格式化

typescript

运行

function formatError(err: string): string {
  if(err.includes("no device")) return "未检测到可用摄像头设备,请检查硬件连接与驱动";
  if(err.includes("in use")) return "摄像头已被其他程序占用,请关闭占用软件后重试";
  if(err.includes("permission")) return "缺少摄像头访问权限,请在系统设置中开启权限";
  if(err.includes("write")) return "文件写入失败,请检查目录读写权限";
  return `操作失败:${err}`;
}

5.5 Skill 主入口与指令分发

typescript

运行

export const CamsnapEnhance: Skill = {
  name: "camsnap-enhance",
  description: "影帧瞬捕 摄像头抓帧工具 | 单帧抓拍/连拍/设备切换/文件归档一体化",
  patterns: [
    "抓拍画面",
    "选择设备{id}",
    "连续抓拍{数量}",
    "查看设备列表",
    "查看抓拍日志"
  ],
  handler: async (ctx: Context) => {
    const { match, reply, pattern } = ctx;
    initDir();

    try {
      // 查看摄像头设备列表
      if(pattern === "查看设备列表"){
        const devices: any[] = await getCameraList();
        if(devices.length === 0) return reply("❌ 未检测到任何摄像头设备");
        let devInfo = "📷 可用摄像头列表:\n";
        devices.forEach((item, idx) => {
          devInfo += `设备ID:${idx} | 设备名称:${item.name}\n`;
        });
        return reply(devInfo);
      }

      // 切换抓拍设备
      if(pattern === "选择设备{id}" && match.id){
        const devId = parseInt(match.id);
        CAMSNAP_CONFIG.currentDevice = devId;
        return reply(`✅ 已切换至摄像头设备 ID:${devId}`);
      }

      // 单次抓拍
      if(pattern === "抓拍画面"){
        reply("📸 正在捕获画面,请稍候...");
        const savePath = await singleSnap(CAMSNAP_CONFIG.currentDevice);
        return reply(`✅ 抓拍成功!文件已保存至:${savePath}`);
      }

      // 连续抓拍
      if(pattern === "连续抓拍{数量}" && match.数量){
        const num = parseInt(match.数量);
        const realCount = Math.min(num, CAMSNAP_CONFIG.maxBurstCount);
        reply(`⏱️ 开始连续抓拍,共${realCount}张,请等待...`);
        const fileList = await burstSnap(CAMSNAP_CONFIG.currentDevice, realCount, CAMSNAP_CONFIG.defaultInterval);
        return reply(`✅ 连拍完成,成功保存${fileList.length}张图片\n首张路径:${fileList[0]}`);
      }

      // 查看日志路径
      if(pattern === "查看抓拍日志"){
        return reply(`📜 抓拍日志目录:${CAMSNAP_CONFIG.logDir}\n图片存储目录:${CAMSNAP_CONFIG.rootDir}`);
      }

    } catch (e: any) {
      return reply(`❌ ${formatError(String(e.message || e))}`);
    }

    return reply("💡 可用指令:抓拍画面、选择设备{ID}、连续抓拍{数量}、查看设备列表、查看抓拍日志");
  }
};

六、指令使用说明

  1. 查看设备列表 执行指令即可枚举本机所有摄像头,显示设备 ID 与名称,方便选择对应摄像源。
  2. 选择设备 {id} 填入对应设备 ID,切换当前生效的摄像头,后续抓拍均使用该设备。
  3. 抓拍画面 执行单次抓帧操作,捕获当前摄像头画面,自动命名并分类保存,返回文件路径。
  4. 连续抓拍 {数量} 设置抓拍张数,工具按默认间隔自动连拍,完成后反馈成功数量与文件信息。
  5. 查看抓拍日志 展示日志与图片存储目录,便于查阅历史记录、整理抓拍文件。

七、部署与使用步骤

  1. 将完整代码替换 OpenClaw 中原有的 Camsnap 技能文件,安装摄像头依赖组件;
  2. 根据使用需求,修改配置项里的图片格式、画质、默认抓拍间隔、最大连拍数等参数;
  3. 重启 OpenClaw 框架,加载新版抓帧技能;
  4. 首次使用先执行「查看设备列表」确认可用摄像头,再切换目标设备;
  5. 依次测试单次抓拍、连拍功能,验证文件保存、日志记录正常即可正式使用;
  6. 定期清理老旧图片与日志文件,释放磁盘空间。

八、后续迭代拓展方向

  1. 新增定时抓拍任务,支持设定固定时间、循环周期自动启动抓帧,实现无人值守巡检。
  2. 增加画面滤镜、尺寸裁剪功能,抓拍后自动完成简单图像处理。
  3. 接入消息推送技能,抓拍异常画面、设备离线时自动推送通知。
  4. 新增视频片段录制能力,在抓帧基础上拓展短时录像功能。
  5. 支持多设备轮巡抓拍,自动切换不同摄像头完成批量画面采集。
  6. 增加图片压缩功能,自动压缩大体积图片,节省存储空间。

九、改造总结

本次针对 Camsnap 摄像头抓帧技能完成全维度功能升级与架构优化,突破原版仅能单次抓拍的局限,补齐多设备管理、间隔连拍、规范存储、日志溯源、异常防护等核心能力。

改造后的工具功能分层清晰、运行稳定、文件管理规整,既满足用户临时截取画面的轻量化需求,也可支撑长时间无人值守、多机位巡检、批量画面归档等自动化场景。整体代码架构原创设计,深度适配 OpenClaw 运行机制,交互直观、容错性强,有效解决了原版工具设备兼容差、文件杂乱、稳定性不足等问题,大幅提升摄像头抓帧场景下的使用效率与实用性。