# Vcad Assembly

> Build multi-part assemblies with joints and run physics simulations. Use when the user asks about robot arms, mechanisms, hinges, joints, physics simulation, reinforcement learning environments, or assembly of multiple parts. Use when this capability is needed.

- Skill: `tomevault-io/vcad-assembly` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/vcad-assembly`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/vcad-assembly/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/vcad-assembly

---


# vcad Assembly & Physics Simulation

Create assemblies from parts, connect them with joints, and run physics simulations via the vcad MCP tools.

## Assembly Structure

An assembly is defined inside `create_cad_document` alongside the parts:

```json
{
  "parts": [...],
  "assembly": {
    "instances": [...],
    "joints": [...],
    "ground": "base-1"
  }
}
```

### Instances

Each instance places a part in the assembly:

```json
{
  "id": "arm-1",
  "part": "arm",
  "name": "Lower Arm",
  "position": {"x": 0, "y": 0, "z": 50},
  "rotation": {"x": 0, "y": 0, "z": 0}
}
```

- `id` — unique identifier (referenced by joints)
- `part` — name of a part in the `parts` array
- `position` — initial position in mm (optional)
- `rotation` — initial rotation in degrees (optional)

### Joints

Joints connect two instances and define degrees of freedom:

```json
{
  "id": "shoulder",
  "name": "Shoulder Joint",
  "parent": "base-1",
  "child": "arm-1",
  "type": "revolute",
  "axis": "y",
  "parent_anchor": {"x": 0, "y": 0, "z": 50},
  "child_anchor": {"x": 0, "y": 0, "z": 0},
  "limits": [-90, 90],
  "state": 0
}
```

- `parent` — instance ID, or `null` for world/ground
- `child` — instance ID
- `type` — joint type (see below)
- `axis` — `"x"`, `"y"`, `"z"`, or custom `{x, y, z}` vector
- `parent_anchor` / `child_anchor` — connection points in local coordinates
- `limits` — `[min, max]` (degrees for revolute, mm for slider)
- `state` — initial angle or position

### Ground

Set `"ground"` to the instance ID of the fixed/base part. This anchors the kinematic chain.

## Joint Types

| Type | DOF | Description | State Unit |
|------|-----|-------------|------------|
| `fixed` | 0 | Rigid attachment, no motion | n/a |
| `revolute` | 1 | Rotation around axis (hinge) | degrees |
| `slider` | 1 | Translation along axis (piston) | mm |
| `cylindrical` | 2 | Rotation + translation on same axis | degrees |
| `ball` | 3 | Omnidirectional rotation | n/a |

## Physics Simulation Tools

The gym-style tools simulate assembly dynamics using phyz:

| Tool | Purpose |
|------|---------|
| `create_robot_env` | Initialize simulation from assembly document |
| `gym_reset` | Reset to initial state, returns observation |
| `gym_step` | Apply action, advance physics, returns observation + reward + done |
| `gym_observe` | Get current state without stepping |
| `gym_close` | Clean up simulation |

### Simulation Workflow

```
create_cad_document (with assembly)
  → create_robot_env (document, end_effector_ids)
    → gym_reset (env_id)
      → gym_step (env_id, action_type, values) [loop]
        → gym_close (env_id)
```

### create_robot_env

```json
{
  "document": "<IR document from create_cad_document>",
  "end_effector_ids": ["gripper-1"],
  "dt": 0.004167,
  "substeps": 4,
  "max_steps": 1000
}
```

Returns: `env_id`, `num_joints`, `action_dim`, `observation_dim`.

### gym_step

```json
{
  "env_id": "sim_1",
  "action_type": "torque",
  "values": [0.5, -0.3]
}
```

Action types:
- `"torque"` — apply torque in Nm to each joint
- `"position"` — set target position (degrees for revolute, mm for slider)
- `"velocity"` — set target velocity (deg/s or mm/s)

The `values` array must have one entry per joint.

Returns:
- `observation` — joint positions, velocities, end effector poses
- `reward` — scalar reward signal
- `done` — true if episode ended (max steps or termination)

### gym_reset / gym_observe

Both return the observation object:

```json
{
  "joint_positions": [0.0, 0.0],
  "joint_velocities": [0.0, 0.0],
  "end_effector_poses": [
    {"position": {"x": 0, "y": 0, "z": 100}, "orientation": {"x": 0, "y": 0, "z": 0, "w": 1}}
  ]
}
```

## Complete Example: 2-DOF Robot Arm

```json
{
  "parts": [
    {
      "name": "base",
      "primitive": {"type": "cylinder", "radius": 25, "height": 10},
      "material": "steel"
    },
    {
      "name": "arm",
      "primitive": {"type": "cube", "size": {"x": 10, "y": 10, "z": 80}},
      "material": "aluminum"
    }
  ],
  "assembly": {
    "instances": [
      {"id": "base-1", "part": "base"},
      {"id": "lower-arm", "part": "arm", "position": {"x": 0, "y": 0, "z": 10}},
      {"id": "upper-arm", "part": "arm", "position": {"x": 0, "y": 0, "z": 90}}
    ],
    "joints": [
      {
        "id": "shoulder",
        "parent": "base-1",
        "child": "lower-arm",
        "type": "revolute",
        "axis": "y",
        "parent_anchor": {"x": 0, "y": 0, "z": 10},
        "child_anchor": {"x": 5, "y": 5, "z": 0},
        "limits": [-90, 90]
      },
      {
        "id": "elbow",
        "parent": "lower-arm",
        "child": "upper-arm",
        "type": "revolute",
        "axis": "y",
        "parent_anchor": {"x": 5, "y": 5, "z": 80},
        "child_anchor": {"x": 5, "y": 5, "z": 0},
        "limits": [0, 135]
      }
    ],
    "ground": "base-1"
  }
}
```

After creating the document, run a simulation:

1. `create_robot_env` with `end_effector_ids: ["upper-arm"]`
2. `gym_reset` to get initial observation
3. `gym_step` with `action_type: "torque"` and values per joint
4. Repeat step 3 for your control loop
5. `gym_close` when done

## Tips

- Every assembly needs a `ground` instance — this is the fixed reference
- Joint anchors are in the instance's local coordinate frame
- Revolute limits are in degrees, slider limits in mm
- State outside limits is clamped automatically
- Use `position` action type for precise pose control, `torque` for dynamic simulation
- `end_effector_ids` must reference valid instance IDs in the assembly
- Forward kinematics propagates transforms from ground through the joint chain

---
> Source: [ecto/vcad](https://github.com/ecto/vcad) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-06-30 -->

