| name | vcad-assembly |
| description | 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. |
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:
{
"parts": [...],
"assembly": {
"instances": [...],
"joints": [...],
"ground": "base-1"
}
}
Instances
Each instance places a part in the assembly:
{
"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:
{
"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
{
"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
{
"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:
{
"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
{
"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:
create_robot_env with end_effector_ids: ["upper-arm"]
gym_reset to get initial observation
gym_step with action_type: "torque" and values per joint
- Repeat step 3 for your control loop
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