SkillsSkill for Claude

Teleop capture problem list

Give Claude one teleop collection batch, one video file per episode and camera, and get back a problem sheet for collection ops: each row names the episode, the camera, the problem, the window and a contact sheet, with an empty column for re-collect, drop or keep. Black, frozen and overexposed video is found locally with ffmpeg at no credit cost; a blocked or crooked camera is found with one Vivu search per problem type and checked on its frames. A collection log says the run succeeded; only the footage shows that the arm covered the outside camera or that the camera was mounted sideways.

Maintained by Vivu. Updated 2026-10-15.

Download

teleop-capture-problem-list.zip

9 KB. Unzips to teleop-capture-problem-list/SKILL.md. Upload the zip as it is in the Claude app, or unzip it into your skills folder for Claude Code.

SHA-256 50e38fdfb497fd24ed1bdd2a89f8f6519e19df35d0a1fed64db601664c163b0f

At a glance

What the Teleop capture problem list skill does, where it runs, what it needs, and when it asks
Looks forWindows where something near the lens covers most of the external view, where the camera is mounted sideways or tilted far off level, or where the wrist lens is covered, plus black, frozen and overexposed stretches. Logs do not record any of these. Every Vivu window is checked on its frames (a 0.5 second contact sheet, or start, middle and end frames when the window is the whole file) before it reaches the sheet.
Runs onClaude Code on your computer (terminal or the Code tab of Claude Desktop), because it needs a shell with ffmpeg and the local episode files. No residential IP and no scheduled task: run it once per batch.
Needs
  • The Vivu connector with write access, to create a private project, upload and search.
  • ffmpeg and ffprobe, for durations, the local black, freeze and brightness checks, and the contact sheets.
  • One video file per episode and camera, with the camera in the file name, so each problem type runs on the right camera.
  • A browser to open the upload page, or a browser tool that can attach local files.
Your Vivu planIndex minutes equal the length of the files you search; each problem type is one precise search, so all three are an estimated 15 credits a batch. The skill shows the cost against your plan before uploading. In our test run 12 camera streams took 5.18 minutes and 4 precise searches, an estimated 20 credits.
Asks you firstBefore uploading (cost table and compliance points), before writing the full sheet (one sample row and the field mapping), and before the sheet goes to a shared drive or tracker.

The skill does its video work through the Vivu connector. If the connector is not in your Claude yet, add it first; the skill checks that it is connected before it does anything else.

Before you run it

  • Finding a blocked or crooked camera is visual search, which is weak for Vivu: every window is a candidate until its contact sheet is checked, and an empty result does not mean the batch is clean.
  • In our test run the blocked view search had 3 real and 0 false; the first crooked camera wording had 4 false of 6, mostly wrist files, so hits on the wrong camera are rejected.
  • A person in front of the lens, a camera knocked mid episode and a covered wrist lens were not in the test batch and are untested.
  • The local black, freeze and brightness thresholds were tested on one injected clip; check each window's contact sheet before listing it.
  • Recordings often show operators or passers-by: follow your team's data policy and leave out people who did not agree, and minors without guardian consent.
  • Uploaded files stay in your Vivu project, created private, until you delete them. Claude in Chrome uploads at most 10 MB per call; larger files go through the Vivu web app.
  • The sheet stays in your working folder unless you approve a destination; it has no operator names, scores or failure causes.

Start it

Once the skill is installed, ask for the task in your own words. Naming the skill is the most reliable way to have Claude use it. For example:

Check today's teleop batch in ~/data/batch_1014 for bad recordings: blocked or crooked cameras, black or frozen video. Cameras are *_ext1, *_ext2 and *_wrist. Give me a problem sheet for collection ops.

In Claude Code you can also type /teleop-capture-problem-list. Claude asks for anything the request leaves out, most important first.

What is inside

  1. When to use
  2. Working principles
  3. What you need before starting
  4. Inputs to collect
  5. Files and state
  6. Step 1: Check the Vivu connector and the tools
  7. Step 2: Line up the batch
  8. Step 3: Run the local checks
  9. Step 4: Price the batch and ask
  10. Step 5: Create a private project and upload
  11. Step 6: Search each problem type and check every hit
  12. Step 7: Build the problem sheet
  13. Compliance
  14. Known failure modes

The full skill

This is teleop-capture-problem-list/SKILL.md from the download, as Claude reads it: the frontmatter first, then the instructions.

---
name: teleop-capture-problem-list
description: "Find teleop episodes whose cameras recorded badly (view blocked, camera crooked, black, frozen, blown out) with Vivu and ffmpeg, and get a problem sheet for collection ops."
---

Teleop capture problem list with Vivu

This skill takes one teleop collection batch (one video file per episode and camera, exported from your logs) and returns a problem sheet for collection ops: one row per recording problem with the episode file, the camera, the problem type, the window as MM:SS, a contact sheet (one frame every 0.5 seconds, or start, middle and end frames when the window is the whole file), where the finding came from (a Vivu search checked on frames, or a local ffmpeg check), and an empty decision column for re-collect, drop or keep. Black, frozen and overexposed video is found locally with ffmpeg and costs no Vivu credits. A blocked or crooked camera is found with one Vivu search per problem type, and every window is checked on its own frames before it reaches the sheet.

The value is in what only the footage shows. A collection log records the task name, the success flag and the joint states; it does not record that the operator's arm or the robot arm covered the outside camera for half the episode, that a camera was mounted sideways, or that the wrist lens was pressed against the object. A run flagged as a success can still be useless for training because the camera saw nothing. The sheet lists the problems and leaves every decision to the people who run collection.

When to use

Use when someone asks to find bad recordings in a teleop batch, check which episodes had a blocked or knocked camera, build a re-collect list for collection ops, or screen a LeRobot, RLDS or ROS bag batch for camera problems before training. For one episode, open the video. This skill does not check task labels (use a label spot-check for that), produce annotations or label exports, score operators, or say why an episode failed.

Working principles

  1. Report real counts. When a number is an estimate (credits, minutes for a future batch), say so.
  2. A Vivu result is a candidate until Claude has looked at the frames inside its window. Rejected candidates go to a separate list with the reason; they never reach the problem sheet.
  3. Stop and tell the user when a required tool or capability is missing. Do not guess around it.
  4. Ask the user before anything that costs index minutes or search credits (Step 4), before uploading (Step 5), and before the sheet leaves the working folder (Step 7).
  5. The sheet is for the data team and collection ops. It names files, cameras and windows; it carries no operator names, scores or failure causes.

What you need before starting

Check each item at the start of the run and tell the user plainly what is missing before doing anything else.

Requirement Why How to check
Vivu connector with write access create a private project, open its upload page, search vivu_get_account shows can_create_projects: true (tool names may carry a server prefix). A write call failing with "has not granted vivu.write" means the connection is read only: ask the user to reconnect Vivu with write access
A shell on the machine that holds the batch, with ffmpeg and ffprobe durations for pricing, the local black, freeze and brightness checks, frames for every check ffmpeg -version and ffprobe -version
One video file per episode and camera (mp4 or another common format), with the camera in the file name Vivu indexes video files; the camera name decides which problem types apply to a file ls the batch folder and ffprobe one file
A browser the user can open the upload page in, or a browser tool that can attach local files moving files into Vivu ask the user; for Claude in Chrome, check that it is connected

The skill runs in Claude Code on the user's computer (terminal or the Code tab of Claude Desktop), because it needs a shell and the local episode files. It needs no residential IP, since nothing is downloaded from YouTube, and it sets up no scheduled task: run it once per batch.

Inputs to collect

Ask for anything missing, most important first.

  1. The batch folder (required).
  2. Which cameras are external (fixed beside the robot) and which are wrist cameras, and how the file names say so (required). Default naming: EPISODE_CAMERA.mp4, for example ep080_ext1.mp4 and ep014_wrist.mp4.
  3. Problem types. Default: all six (blocked view, camera off, wrist lens covered, black, frozen, overexposed).
  4. Scope. Default: every file in the batch. Step 4 offers smaller scopes when the plan does not cover it.
  5. The working folder. Default: capture-check/ next to the batch folder.
  6. The Vivu project name. Default: "Capture check BATCH_ID", created private.

Files and state

Keep everything in one working folder:

capture-check/
  config.json             batch id, batch folder, camera naming rule, problem types, Vivu project id
  files.csv               episode_file, camera, path, duration_s, stored_name, video_id
  state.json              local checks done, uploaded video ids, finished searches with job ids, checked windows
  h264/                   H.264 copies of AV1 files (Step 2), same base names; nothing is written into the batch folder
  local/                  detect_NAME.log and bright_NAME.txt from Step 3
  results/PROBLEM.json    raw result of each problem search, saved as returned
  sheets/                 contact sheets and frame strips for every window
  capture_problems.csv    the problem sheet
  rejected.csv            candidates rejected on their frames, with the reason

state.json lets a rerun resume: skip files whose local check log exists, skip uploads whose video id is recorded, skip searches that already have a saved result, and skip windows already checked. Each batch gets its own working folder, so a problem is never listed twice.

Step 1: Check the Vivu connector and the tools

Goal: confirm that everything in the table above is in place.

  1. Call vivu_get_account. If the tool does not exist, tell the user to add the Vivu connector in Claude (https://mcp.vivu.ai/mcp) and stop. If can_create_projects is not true, or a later write fails with "has not granted vivu.write", ask the user to reconnect Vivu with write access and stop.
  2. Run ffmpeg -version and ffprobe -version. If either is missing, ask the user to install ffmpeg.
  3. Confirm the batch folder can be read and the camera naming rule matches the files.

Done when vivu_get_account shows can_create_projects: true, both ffmpeg commands print a version, and every file in the batch maps to an external or a wrist camera.

Step 2: Line up the batch

Goal: files.csv with one row per file, its camera and its length.

  1. If the batch is still in ROS bag, MCAP or RLDS form, export each camera stream to one mp4 per episode with the team's own export script. Log export was not exercised in our test run: the public dataset we used already ships one mp4 per camera per episode.
  2. LeRobot datasets encode video as AV1. In our test run we re-encoded each file to H.264 before uploading; uploading AV1 directly was not tested. This is a format conversion, not a way to fit an upload limit:
ffmpeg -v error -i EPISODE_FILE -c:v libx264 -pix_fmt yuv420p -crf 18 -an OUTPUT_FILE

EPISODE_FILE is the exported file. OUTPUT_FILE is capture-check/h264/ plus the same base name. When a copy exists, every later step uses the copy.

  1. Measure each file:
ffprobe -v error -show_entries format=duration -of csv=p=0 EPISODE_FILE
  1. Show the user the count per camera and any file whose camera could not be read from its name.

Done when files.csv has one row per file with camera and duration_s, and the user has seen the counts per camera.

Step 3: Run the local checks

Goal: black, frozen and overexposed windows for every file, without spending Vivu credits.

One ffmpeg pass per file reports all three:

ffmpeg -hide_banner -nostats -i EPISODE_FILE -vf "blackdetect=d=0.5:pix_th=0.10,freezedetect=n=0.003:d=1,signalstats,metadata=select:key=lavfi.signalstats.YAVG:value=180:function=greater,metadata=print:key=lavfi.signalstats.YAVG:file=capture-check/local/bright_NAME.txt" -an -f null - 2> capture-check/local/detect_NAME.log

NAME is the file's base name. Then read the results:

  1. Black: lines with black_start and black_end in detect_NAME.log.
  2. Frozen: lines with freeze_start and freeze_end. Black video does not change, so a freeze that sits inside a black window is the same problem; keep the black row only.
  3. Overexposed: bright_NAME.txt lists every frame whose average brightness (YAVG, 0 to 255) is above 180, with pts_time. Merge runs of consecutive frames into windows; a window shorter than half a second is noise. We tried negate plus blackdetect for overexposure first and it found nothing: an overexposed frame still has dark areas, so the average is the better signal.
  4. Make a contact sheet for each window (Step 6 has the command) and look at it before adding the row; a dark room can trip blackdetect and a white wall can trip the brightness check.

In our test run the 12 real camera streams raised no local alarm, and on a separate test clip where we injected black, a freeze and overexposure with ffmpeg, each of them was found at the injected time.

The thresholds come from that single injected clip. Tune pix_th, n and the brightness threshold if your cameras run dark or bright.

Done when every file has a detect log and a bright file in local/, and every window found has a contact sheet and a row ready for the sheet.

Step 4: Price the batch and ask

Goal: the user approves the cost before anything is uploaded.

  1. Index minutes = the sum of duration_s divided by 60, for the files that will be searched (skip files that are black or frozen end to end; Step 3 already settled them). Search credits = one precise search per problem type; a precise search uses 5 credits. Label it an estimate, since a rewording costs another search.
  2. Call vivu_get_usage for the plan and what remains this month. For reference, Free is 20 indexing minutes a month and 50 search credits a month; Premium is $30 a month for 180 indexing minutes a month and 500 search credits a month.
  3. Show one table:
This batch Remaining this month Fits
Index minutes sum of durations from vivu_get_usage yes or no
Search credits (estimate) problem types × 5 from vivu_get_usage yes or no
  1. If it does not fit, offer these levers in order, each with its saving: search only the external cameras (the wrist search is the least tested); search only today's new episodes; sample a fixed number of episodes per rig; move to a larger plan last. For example, 50 episodes of about 40 seconds on one camera is about 33 index minutes, more than a Free month and inside Premium; three problem types are an estimated 15 credits.

Approval was not exercised in our test run (it ran unattended on an admin account that reports no remaining allowance).

Done when the user has seen the table and said yes to a scope.

Step 5: Create a private project and upload

Goal: every file in the approved scope shows ready in Vivu.

  1. Ask the user to confirm the Compliance points before uploading.
  2. Call vivu_list_projects and reuse the project for this batch if one exists. Otherwise call vivu_create_project with the project name and visibility "private". The default visibility is the whole organization, which is wrong for lab and home recordings.
  3. Call vivu_open_upload_page with the project ID right before the upload. The upload_url is a one time sign in link that expires in 180 seconds, so request it only when the user or the browser tool is ready, never post it in a message, and request a new one if it lapses.
  4. Hand the link to the user to open in their own browser and select the files, or open it with a browser tool that can attach local files. Claude in Chrome takes at most 10 MB per upload call, so send files in batches under that. If a file cannot go through, ask the user to add it in the Vivu web app. Never split or recompress a file to fit.
  5. Poll vivu_list_videos until every video shows ready. Vivu replaces spaces and brackets in file names with underscores, so match rows back by the episode index and camera in the name and check that duration_ms agrees with duration_s. Record stored_name and video_id in files.csv and the video ids in state.json.

In our test run the upload page link was opened by an automated browser on our machine, not by a user, and the compliance confirmation was not exercised.

Done when vivu_list_videos shows every approved file as ready and each one is matched to a row in files.csv.

Step 6: Search each problem type and check every hit

Goal: for each problem type, the windows whose frames show the problem.

The standing queries, one per problem type:

Field Query Mode Applies to
blocked_view the camera's view is blocked: something right in front of the lens, such as a person's body, a sleeve or the robot arm passing close to the camera, covers most of the picture so the table and the gripper cannot be seen precise external cameras
camera_off a fixed camera standing beside the robot (not the camera on the gripper) whose whole picture is rotated sideways or tilted far off level, so the table edge, the floor and the walls run steeply diagonal or vertical across the frame precise external cameras
wrist_covered the wrist camera is pressed against an object: one surface right against the lens fills the whole picture and nothing else, not even the gripper fingers, can be seen precise wrist cameras

The chain: the local checks in Step 3 settle the problems that need no understanding of the picture, for free; one precise search per remaining problem type then finds the windows across the whole project; the 0.5 second contact sheet confirms or rejects each window. There is no fast pass: fast returns whole files with no reason, and every file here is short and gets checked anyway. The footage has no speech or on-screen text, so the flip in this skill is from what the log says (success) to what the camera shows (nothing usable).

  1. For each problem type, call vivu_search_videos with project_id, query, mode "precise" and maximum_results equal to the number of files in the project (at most 100). The value is the recall ceiling: a smaller number silently drops problems. If the batch has more than 100 files, split it into projects of 100 or fewer.
  2. Call vivu_get_search_results with the job ID until complete is true. Each call can wait up to 45 seconds, so a pending search is not a stalled one. Save the returned JSON unchanged as results/PROBLEM.json and record the job ID in state.json. The Vivu result page link from a completed search can go in the live reply; it expires after four hours, so it never goes in the sheet.
  3. A hit on a file of the wrong camera (a wrist file for blocked_view or camera_off, an external file for wrist_covered) is false: write it to rejected.csv with the reason "wrong camera" and count it.
  4. For every other window, make a contact sheet at one frame every 0.5 seconds:
ffmpeg -v error -y -ss START -t LENGTH -i EPISODE_FILE -vf "fps=2,scale=320:-1,tile=6x4" -frames:v 1 capture-check/sheets/NAME_PROBLEM.png

START and LENGTH are the window start and length in seconds (from start_ms and end_ms). For windows longer than 12 seconds, raise the tile rows (6x6 holds 18 seconds) or use fps=24/LENGTH so the sheet still holds 24 tiles. When the window is the whole file (camera_off usually is), a strip of the start, middle and end frames is enough:

ffmpeg -v error -y -ss 0.2 -i EPISODE_FILE -ss MID -i EPISODE_FILE -ss END -i EPISODE_FILE -filter_complex "[0:v][1:v][2:v]hstack=inputs=3" -frames:v 1 capture-check/sheets/NAME_PROBLEM.png

MID is half the duration, END the duration minus 0.3 seconds. Look at every sheet. Blocked view is real only when something near the lens covers more than half the picture and hides the spot where the gripper works; the arm being large in the frame while the table stays visible is not a problem. Camera off is real when the picture is rotated or tilted far off level, or the work area is out of frame; a camera that faces a wall because the task is on the wall is fine. Judge from the frames, not from the reason text, which describes the query back.

  1. Tell the user the counts per problem type: returned, real, false.

How far to trust the search: telling a blocked or crooked camera from a normal one is visual search, which is weak for Vivu, and a person in front of the lens, a camera knocked mid episode and a covered wrist lens were not in our test batch. That is why every window goes through the frames, why the sheet says where each row came from, and why an empty result is not proof the batch is clean: an empty result does not prove the footage has no such moment. Partial blockings, where the arm covers about half the picture, were mostly not returned in our test run; if they matter to you, page through the contact sheets of the files with the most arm motion.

Worked example from our test run

In our test run on 12 camera streams from a public robot manipulation dataset (5.18 minutes in total; external cameras and wrist cameras, low resolution, no audio; home and lab scenes), all files were ready within 125 seconds of the upload starting. We wrote the truth from contact sheets before any search. The batch had two clear blocked views (the robot arm swinging close to the outside camera), a few partial ones, one camera mounted sideways, one tilted far off level, hard negatives (the robot base large in the frame, a camera facing a wall for a light switch task) and wrist close-ups of cloth and towels during normal grasps. Nobody stepped in front of a lens in this dataset, no camera was knocked mid episode, and no wrist lens was covered.

The blocked_view search returned 3 windows: 3 real, 0 false, 0 missed against the clear truth. One of the 3 was a partial blocking that our pre-written rule counts as real; the other partial blockings were not returned. No wrist close-up and no hard negative came back. The median window was 8.7 seconds, wider than a blocking, so the contact sheet is what shows when it happens.

The first camera_off wording ("the camera has been knocked or mounted crooked ...") returned 6 windows that each span the whole file: 2 real, 4 false. The false ones were every wrist file in the batch (a wrist view always looks tilted) and the light switch camera. After 1 rewording on the same corpus, naming the fixed camera and describing what a tilted picture looks like, it returned 3: 2 real, 1 false, the light switch camera again. The table above uses the reworded query, and step 3 drops hits on files from the wrong camera on top of it.

The wrist_covered search returned 0, and the wrist close-ups were not returned. With no covered lens in the batch, this says nothing about recall; treat that search as untested.

The run used 4 precise searches, an estimated 20 credits; a batch with all three problem types is an estimated 15 credits.

Done when every problem type has a saved result, every returned window is judged real or false in state.json, and the user has seen the counts.

Step 7: Build the problem sheet

Goal: capture_problems.csv with one row per confirmed problem, and rejected.csv.

  1. Show the user one sample row and the field mapping below, and wait for a yes before writing the full sheet:
CSV field Source If unavailable
episode_file, camera files.csv none; every row has both
problem the problem type of the search or local check none
window start_ms and end_ms of the Vivu result, or the local check window, as MM:SS-MM:SS blank, and say why
contact_sheet the sheet from Step 3 or Step 6 blank, and say why
source "Vivu search + frame check" or "local check (blackdetect / freezedetect / average brightness)" none
check_note what Claude saw on the sheet, in a few words none
decision collection ops: re-collect, drop or keep blank

Sample row from our test run:

episode_file,camera,problem,window,contact_sheet,source,check_note,decision
ep080_ext1.mp4,external,blocked view,00:13-00:25,sheets/ep080_ext1_blocked_view.png,Vivu search + frame check,arm fills most of the picture 16-20 s; can and gripper hidden,
  1. Write capture_problems.csv: confirmed Vivu windows plus the local check windows, sorted by episode file. File names are the only link to the video; no Vivu links. Write rejected.csv with every rejected window and its reason.
  2. Tell the user the rows per problem type and the number rejected, and say which problem types were searched and which were skipped.
  3. The sheet stays in the working folder. If the user wants it in a shared drive or a tracker for collection ops, name the destination, show the rendered file and wait for approval; that was not exercised in our test run.

Done when capture_problems.csv has one row per confirmed problem with an existing contact sheet path, rejected.csv lists every rejected window, and the user has the files.

Compliance

  1. Rights to the footage: use it on data your team owns or may process. For public datasets, follow the dataset license and keep the videos for internal checks. Confirm before Step 5.
  2. People on camera: teleop recordings often catch operators or people walking past. Handle them under the team's data policy, and leave out episodes that show people who did not agree to be recorded. Confirm before Step 5.
  3. Minors: if a home collection might show children, get explicit guardian consent or leave those episodes out. Confirm before Step 5.
  4. Where the videos go: uploaded files stay in the user's Vivu project until the user deletes them. The skill creates the project as private and never deletes projects or videos unless the user asks, and then confirms first.
  5. What the skill does not do: no face or identity recognition, no operator scoring, no failure diagnosis, no annotation or label export.

Known failure modes

Symptom Cause Fix
(observed) a crooked camera search returns wrist files with reasons like "the camera mounted sideways and tilted far off level" a wrist view is tilted by nature and the first wording did not say which camera use the reworded query that names the fixed camera, and reject hits on wrist files (step 3 of Step 6)
(observed) a camera facing a wall comes back as crooked, reason "tilted at a steep diagonal angle" the task itself is on the wall (a light switch); the floor edge in perspective looks diagonal check the strip: if the gripper and the work area are in view, reject it
(observed) partial blockings, with the arm over about half the picture, are not returned the search favours clear cases page through contact sheets of the files with the most arm motion if partial blockings matter
(observed) blocked view windows are wider than the blocking itself precise windows contain the moment but are wide read when it happens from the contact sheet, not from the window bounds
(observed) freezedetect reports a freeze over a black stretch black frames do not change keep the black row and drop the freeze that sits inside it
(observed) overexposed video is not found by negate plus blackdetect an overexposed frame still has dark areas use the average brightness check in Step 3
a person in front of the lens or a camera knocked mid episode is not returned untested: our test batch had neither page through the contact sheets for batches where this is common; report counts as found, not as complete
the wrist_covered search returns nothing untested on a real covered lens; an empty result is not proof treat wrist results as candidates and check wrist sheets by hand for suspect rigs
a dark room trips blackdetect, a white wall trips the brightness check thresholds tuned on one test clip look at the contact sheet before adding the row; adjust pix_th or the brightness threshold
"has not granted vivu.write" Vivu connected read only reconnect Vivu with write access
upload page asks to sign in or shows an error the one time upload link expires after 180 seconds request a new link right before opening it
a file is rejected by the browser upload tool file above the tool's size limit the user adds that file in the Vivu web app
rows do not line up with results Vivu normalized the file name match on the episode index and camera in the name and on duration_ms

Connect Vivu, then add the skill.

The skill runs through the Vivu connector. Add it to Claude from the connector directory, then install the skill.