# Datasets

> Learn how Wandelbots NOVA OS persists taught poses, command routines, and frames as datasets, and how to resolve and localize poses through a dataset's frame hierarchy.

A dataset is a named, versioned collection that you manage as one unit in a cell. It contains the
poses you teach, the frames in which they are expressed, and command routines that describe robot
movement and signal handling. Because a dataset includes its own frames, it is self-contained. It
contains everything needed to resolve its poses into `world` and localize them to any dataset frame.

> **Experimental:** Datasets are experimental. The routes are served under `/api/v2/experimental/`, and the API and
> its behavior can change in future releases.

## Dataset components

A dataset contains revisions, poses, frames, and command routines. Select a tab to learn about
each component.

**Revisions**

NOVA OS versions datasets automatically. Creating a dataset with an existing ID neither fails nor
overwrites the existing dataset. Instead, NOVA OS stores a new revision. Revisions start at `1` and
increase each time you create a dataset with the same ID. The combination of `dataset` and
`revision` identifies a specific dataset version, including its poses, frames, and command
routines.

> **Info:** Deleting a dataset removes exactly one revision, either the one you specify or the latest one.
> It does not delete the dataset's other revisions. To remove a dataset entirely, delete each of
> its revisions.

**Dataset poses**

A dataset pose is a taught pose that belongs to a dataset, identified by a `dataset_pose` ID
(the same naming rules apply as for a dataset ID). It contains a `pose` (position and orientation),
an optional
[kinematic configuration](/nova/26.7/nova-api/core-concepts/robotics-basics#cartesian-pose-and-kinematic-configuration)
that resolves the pose to a unique robot posture, and an optional `frame`, the dataset frame the
pose is expressed relative to.

A pose without a `frame` is expressed relative to `world`.

**Dataset frames**

A dataset frame is a named frame that belongs to a dataset. Each frame has a `pose` expressed
relative to its optional `reference_frame`, another frame in the same dataset.

A frame without a `reference_frame` is expressed relative to `world`. Chaining frames this way lets a dataset build
its own hierarchy of coordinate systems, e.g., a workpiece frame relative to a fixture
frame relative to `world`.

**Command routines**

A command routine is a named, persistable sequence of planning and execution overlay commands.
NOVA OS processes the entries in its `commands` array from first to last and stores the routine with
the dataset.

Every routine requires a `command_routine` ID, `motion_group_setup`, and a non-empty `commands`
array. Each entry can be a `motion_command`, `set_io`, `wait_for_io`, `pause_on_io`,
`wait_for_time`, or `marker` command. A `motion_command` uses either an explicitly authored path
or a generated path. Explicit paths support Cartesian PTP, line, circle, cubic spline, joint PTP,
and direction-constrained Cartesian PTP or joint PTP path types.

## Working with datasets

### Step 1: Create a dataset

Choose a human-readable `dataset` ID, e.g., `training-dataset`. NOVA OS does not generate this ID.

Then define the complete set of `poses`, `frames`, and `command_routines` that the revision should
contain. A pose can reference a dataset frame through `frame`; a frame can reference another
dataset frame through `reference_frame`. Send the complete dataset to
`POST /api/v2/experimental/cells/{cell}/datasets`.

Creating a dataset with an existing ID creates a new revision. Include unchanged items again,
because NOVA OS does not modify an existing revision in place.

**Dataset**

```jsonc
{
  "dataset": "training-dataset",
  "name": "Training Dataset",
  "revision": 1,
  "description": "Poses for robot training",
  "created_at": "2026-02-02T10:30:00Z",
  "updated_at": "2026-02-02T10:30:00Z",
  "poses": [...],
  "frames": [...],
  "command_routines": [...]
}
```

**Dataset poses**

```json
[
  {
    "dataset_pose": "start-position",
    "dataset": "training-dataset",
    "name": "Start Position",
    "pose": {
      "position": [0.5, 0.2, 0.3],
      "orientation": [0, 0, 1.57]
    }
  }
]
```

**Dataset frames**

```json
[
  {
    "frame": "tool-frame",
    "dataset": "training-dataset",
    "name": "Tool Frame",
    "pose": {
      "position": [0.5, 0.2, 0.3],
      "orientation": [0, 0, 1.57]
    }
  }
]
```

**Command routines**

```json
[
  {
    "command_routine": "startup-routine",
    "dataset": "training-dataset",
    "name": "Startup Routine",
    "motion_group_setup": null,
    "commands": [
      {
        "type": "wait_for_time",
       "duration_ms": 1000
      }
    ]
  }
]
```

> **Warning:** Creating a dataset is the only way to persist poses, frames, and command routines. There is no
> separate endpoint to add a single pose, frame, or command routine to an existing dataset.

### Step 2: Get a dataset

Retrieve a dataset with
`GET /api/v2/experimental/cells/{cell}/datasets/{dataset}`. Add the optional `revision` query
parameter to retrieve a specific revision; otherwise, NOVA OS returns the latest revision.

### Step 3: Delete a dataset

Delete one revision with
`DELETE /api/v2/experimental/cells/{cell}/datasets/{dataset}`. Add the optional `revision` query
parameter to delete a specific revision; otherwise, NOVA OS deletes the latest revision. To remove a
dataset entirely, delete each of its revisions.

## Resolving and localizing poses

Two operations transform poses through a dataset's frame hierarchy:

- **Resolve** (`POST .../datasets/{dataset}/frames/{frame}/resolve`): takes poses expressed
  relative to `{frame}` and returns them expressed in `world`.
- **Localize** (`POST .../datasets/{dataset}/frames/{frame}/localize`): the inverse. Takes poses
  expressed in `world` and returns them expressed relative to `{frame}`.

Both operations take a batch of poses in one request and interpret every pose in that batch
relative to the same `{frame}`. NOVA OS walks that frame's `reference_frame` chain up to `world` and
composes the transforms along the way. Poses therefore travel through the complete chain, not
only the frame named in the request. Both operations return `404` if the dataset or frame does not
exist.

Every frame in a chain belongs to the same dataset, and every chain ends at `world`. A dataset
therefore contains the complete transform chain for each of its poses: once you have fetched a
dataset, you can resolve and localize its poses yourself without further requests. These two
endpoints perform the same computation server-side.

In this example, `tool-frame` in the `training-dataset` dataset has no `reference_frame`, and its own
pose relative to `world` is `position: [0.5, 0.2, 0.3]` with zero orientation. Resolving a pose
expressed relative to that frame with
`POST /api/v2/experimental/cells/cell/datasets/training-dataset/frames/tool-frame/resolve`:

```json
[
  {
    "position": [0.1, 0.0, 0.0],
    "orientation": [0.0, 0.0, 0.0]
  }
]
```

returns the pose expressed in `world`, offset by the frame's own pose:

```json
[
  {
    "position": [0.6, 0.2, 0.3],
    "orientation": [0.0, 0.0, 0.0]
  }
]
```
