Add a 3D model

Show a 3D model of your robot or machine in the Grid dashboard, moving with the live values of your application.

The last step gives your dashboard a digital twin: a 3D model of your robot or machine in the Grid 3D widget, with its parts moved by parameters from your application. The guide below is the 3D models page of the Grid documentation, shown here so you can finish the walkthrough without leaving it.

The Grid 3D widget shows a 3D model, a digital twin, of your robot or machine, and moves its parts with live parameters from your application: a joint turns when the real joint turns. You build the model in Blender or a CAD tool, export it as a glTF file, and write a small JSON file that links each moving part to a parameter.

The video shows the whole workflow, from creating a model to linking it to an application:

Prepare the model

The widget moves an object by setting its position, rotation or scale relative to its parent. So how you build the model decides how it moves:

Base z Link1 Link2 Link1 origin at joint 1 rotates about its own z: axis "rz" Link2 origin at joint 2 the pivot, not the centre Parent chain in Blender Base Link1 Link2 A child moves with its parent: turn Link1 and Link2 turns along with it.
  1. Build with z up. The camera treats z as up. In Blender’s glTF exporter, switch +Y Up off under Transform.
  2. Put each moving part’s origin at its pivot. In Blender, place the 3D cursor on the joint and use Object > Set Origin > Origin to 3D Cursor.
  3. Parent the parts in a chain, base to tool, so each part moves with the one before it.
  4. Apply rotation and scale to every moving part (Object > Apply > Rotation & Scale), so its local axes line up with the joint.
  5. Give moving parts simple names, without spaces, dots or other special characters. See the name rules below.
  6. Export as .glb, the binary format. Tick Apply Modifiers under Geometry if you use modifiers.

Keep the file small, so the dashboard loads quickly on a tablet: the stock robot models are 4 to 8 MB. There is no hard limit. Draco-compressed files are not supported. A .gltf text file also works, as long as its buffers and textures are embedded in it.

How object names change on import

The importer changes some characters in object names. Use the changed name in the JSON file:

In Blender In the widget Example
A space Becomes _ Upper Arm becomes Upper_Arm
. : / [ ] Removed Cube.001 becomes Cube001
The same name twice in one file _1, _2 and so on are added The second Link becomes Link_1

The widget writes the loaded scene to the browser console, so you can look up the final names there.

Write the model JSON

The JSON file tells the widget which model files to load and which parameter moves which object. The paths of model files are relative to the JSON file. This minimal file loads one model and turns the object Link1 with the first joint position:

{
  "files": [
    { "file": "robot.glb" }
  ],
  "objects": [
    {
      "name": "Link1",
      "links": [
        { "link": "root/ManipulatorControl/jointPositionsActual", "channel": 0, "axis": "rz" }
      ]
    }
  ]
}

Each entry in links has these fields:

Field What it does Default
link The parameter path. Required
channel The element of an array parameter. Ignored for a single value 0
axis What the value sets, see the next table. Required
gain, offset The widget uses value × gain + offset 1, 0
value A fixed value, used while the parameter has no data
axis Sets Values read
x, y, z Position along one local axis, in m 1
rx, ry, rz Rotation about one local axis, in rad 1
position Position x, y, z 3, from channel on
rotationZYX Rotation z, y, x 3, from channel on
pose Position x, y, z, then rotation z, y, x: a pose such as a tool pose 6, from channel on
s, sx, sy, sz Scale: all axes, or one 1
visible Shows the object when the value is 0.5 or more 1
opacity Transparency, from 0 to 1 1

The widget does not convert units: positions are in metres and rotations in radians, relative to the object’s parent. For a parameter in degrees, use gain 0.0174533; for millimetres, 0.001.

Example: the joints of a stock robot

The 3D model of the store package MCX-Anthropomorphic-Robot.gui links every joint the same way. Joint 1 turns Link1 about its z axis:

{
  "name": "Link1",
  "link": "root/ManipulatorControl/jointPositionsActual",
  "channel": 0,
  "axis": "rz",
  "castShadow": true,
  "addEnvironmentMap": true
}

This older form puts link, channel and axis directly on the object, for a single link. The tool frame follows the tool pose with "axis": "pose" on root/ManipulatorControl/manipulatorToolPoseActual.

To find an object inside another one, separate the names with *: "Robot2*Link1" finds Link1 under Robot2. Use this when you load two models with the same object names. Give each one its own name in files, because duplicates across files are not renamed.

The file can also set the camera, lights, an environment map and a trace line of the tool path. See the model JSON reference for every option.

Add it to the dashboard

  1. Upload the model files and the JSON file to your Grid project, in the same folder or with matching relative paths.
  2. Add a 3D widget to your canvas.
  3. In its model property, select the JSON file.

The widget has two more settings: frequency divider, how often the parameters update (default 100), and disable view navigation, which locks the camera.

Things to know

  • Materials are shared. opacity changes the object’s material, and every other object in the same file that uses that material changes with it. Give an object its own material before you make it transparent.
  • Shadows cost computing power. Objects cast no shadows unless you set castShadow or receiveShadow on them. Keep these to a few objects on slower devices.
  • Only changed values are applied, so a parameter that does not change does not cost anything.

Next: Step 7: final check, to test every step.