GRIIP SDK

Prev Next

The GRIIP SDK is a Python library for building Physical AI powered automation applications that run on your Machine Motion AI. The steps are:

  1. Populate the configuration file config.yaml.
  2. Write a few lines of code using the GRIIP SDK.
  3. Push this code to your Machine Motion.
  4. Run your Physical AI powered robotics application.

The GRIIP SDK handles the tedious parts of a complex vision-powered pick-and-place application for you. The only logic you write is logic specific to your setup; for example, a check the robot should do before placing.

At Vention, the GRIIP SDK lets us program and deploy vision-powered robotics applications in a matter of days.


1. Configure your cell

Everything about your physical setup lives in one file: the robot you're driving, the tool on the end of it, the camera watching the scene, and the parts you want to pick. There is no hardcoded geometry and there are no magic numbers buried in code.

# config.yaml
robot:
  model: ur10e
  ip: 192.168.5.2

tool:
  type: gripper
  model: robotiq_2f140
  tcp_offset: [0, 0, 0.245]

camera:
  name: wrist_cam
  sensor: stereo

environment:
  collision_geometry: ./environment/bin_and_walls.yaml

positions:
  home: [0.0, -1.57, 1.57, -1.57, -1.57, 0.0]
  drop: [-1.5, -1.2, 1.0, -1.4, -1.5, 0.0]

parts:
  - id: vp15
    mesh: ./parts/vp15.stl
    grasps: ./parts/vp15_grasps.json
    placing:
      strategy: insert
      target: named_pose.receptacle

That's a full cell description. Swap the robot model, point at a different mesh, and the same application code picks a different part on a different arm.

2. Write a few lines of code

A complete vision-powered bin picking application:

from griip_sdk import GRIIP

griip = GRIIP("./config.yaml")
griip.init(application="bin_picking", part_id="vp15")

griip.start_pick_and_place()

Behind those three lines, GRIIP captures stereo images, estimates depth, detects parts, estimates 6DoF poses, generates and collision-checks grasps, plans trajectories, and runs the full pick-scoop-place cycle continuously until the bin is empty.

3. Push it to your Machine Motion

Deploy from your laptop to the machine with one command using the Vention CLI:

vention push --host 192.168.5.10 --app ./my_picking_app

The CLI bundles your application and config, ships it to the Machine Motion AI, and installs it alongside the on-box GRIIP perception and planning services.

4. Run it

vention run --host 192.168.5.10 my_picking_app

Your application is now live on the cell. Telemetry and pick KPIs stream out of the box while the robot works.


Make it yours

The default loop is a good start, but real cells have quirks. The SDK exposes hooks at every decision point of the cycle, so your logic runs exactly where it needs to and you never rewrite the loop:

from griip_sdk import GRIIP, PlaceCandidate, Sensor

FIXTURE_SENSOR_IP = "192.168.5.21"

fixture_clear_sensor = Sensor(FIXTURE_SENSOR_IP)

griip = GRIIP("./config.yaml")
griip.init(application="bin_picking", part_id="vp15")

def check_before_place(candidate: PlaceCandidate) -> bool:
    # Don't place until the fixture's presence sensor reports the slot is clear
    return fixture_clear_sensor.read()

griip.start_pick_and_place(
    check_before_place=check_before_place,
)

There are hooks for every phase (check_before_pick, check_before_place, on_pick_success, on_pick_failure, on_bin_empty), so customizing a cell takes a dozen lines instead of a thousand.


# API reference

The GRIIP object

One object represents the entire SDK. Construct it with your config, initialize it for your application type, and every call in this reference is a method on it.

from griip_sdk import GRIIP

griip = GRIIP("./config.yaml")

griip.init(
    application="bin_picking",   # "bin_picking" | "pick_and_place" | "kitting" | "custom"
    part_id="vp15",
    mock=False,                  # run against simulated hardware for development
)

init() connects to the cell, validates your configuration against the physical setup, and wires up the right orchestration for your application type. Use it as a context manager and it handles teardown for you:

with GRIIP("./config.yaml") as griip:
    griip.init(application="bin_picking", part_id="vp15")
    ...

Three levels of control

The SDK is layered. Every level is built from the one below it, and you choose how deep to go:

  1. The managed loop. Call start_pick_and_place() and let GRIIP run the entire application. You customize behavior through the config file and callbacks, and write nothing else.
  2. Your own loop. Skip the managed loop and compose your application from full-cycle building blocks: pick(), place(), move_to(), release(), stop(). GRIIP still handles perception, planning, and execution inside each call; you decide the sequence.
  3. The primitives. Go all the way down to the individual engine capabilities (capture(), estimate_depth(), detect_objects(), estimate_pose(), streaming pick generation, path planning) and build something that isn't pick-and-place at all.

Level 1: the managed loop

One call runs a full, production-hardened cycle: perception, grasp generation, motion, placement, and failure recovery, continuously, until stopped or the bin is empty.

griip.start_pick_and_place()

All customization flows in through two channels, so the loop itself never needs to be rewritten. The config covers placing strategy, grasp ordering, retry thresholds, motion speeds, and region of interest, so most tuning never touches your code. Callbacks carry your logic, injected at every decision point of the cycle:

griip.start_pick_and_place(
    check_before_pick=...,    # veto a pick candidate before the robot commits
    check_before_place=...,   # veto or gate the placement
    on_pick_success=...,
    on_pick_failure=...,
    on_bin_empty=...,
)

If the managed loop with config and callbacks covers your cell, and for most pick-and-place cells it does, this is the whole SDK you'll ever touch.

Level 2: build your own loop

Sometimes the sequence itself is custom: parts routed to different stations, an inspection step between pick and place, a coordinating PLC calling the shots. In that case, write your own loop from the same building blocks the managed loop is made of:

pick = griip.pick(max_attempts=3)

Executes a single complete pick and returns a PickResult: whether it succeeded, the in-hand object pose, and structured failure_reasons when things go wrong. You decide what happens next.

griip.place()

Executes the placement leg for the part currently in hand, using the placing strategy from your config.

griip.move_to(griip.positions.drop)
griip.release()

Collision-free motion to any configured position or pose, and gripper control, for the steps between picks and places that are unique to your cell.

griip.stop()

Gracefully winds down, finishes the current motion, and parks the robot.

Level 3: the primitives

Every capability of the GRIIP perception and planning engine is exposed directly, so you can compose your own pipeline when the application isn't pick-and-place at all. These are the same primitives pick() and place() are built from.

griip.perception

image      = griip.perception.capture()                     # grab from the cell camera
depth      = griip.perception.estimate_depth(image)          # stereo -> dense depth
detections = griip.perception.detect_objects(image, depth)   # find parts in the scene
pose       = griip.perception.estimate_pose(image, depth, detections[0])  # 6DoF pose
in_hand    = griip.perception.estimate_in_hand_pose()        # refine pose of the part in the gripper

griip.picking

for pick_candidate in griip.picking.generate_picks(image, depth):
    ...   # candidates stream in as they're planned — take the first good one

griip.placing

placement = griip.placing.generate_placement()   # refined pose + valid IK solutions for the place

griip.planning

griip.planning.configure(environment=...)                    # load collision world
trajectory = griip.planning.plan_path(start=..., target=...) # collision-free path

# Example applications

Level 1: bin picking in five lines

from griip_sdk import GRIIP

with GRIIP("./config.yaml") as griip:
    griip.init(application="bin_picking", part_id="vp15")
    griip.start_pick_and_place()

Level 1: guarding picks with a custom check

Still the managed loop; your logic rides along as a callback.

from griip_sdk import GRIIP, PickCandidate

FRAGILE_ZONE_RADIUS = 0.08

def check_before_pick(candidate: PickCandidate) -> bool:
    return candidate.object_pose.distance_to(fixture_center) > FRAGILE_ZONE_RADIUS

with GRIIP("./config.yaml") as griip:
    griip.init(application="bin_picking", part_id="vp15")
    griip.start_pick_and_place(check_before_pick=check_before_pick)

Level 2: a loop of your own

The sequence is yours; GRIIP owns the hard parts inside each call.

from griip_sdk import GRIIP

with GRIIP("./config.yaml") as griip:
    griip.init(application="pick_and_place", part_id="pcb")

    while True:
        pick = griip.pick(max_attempts=3)
        if not pick.succeeded:
            notify_operator(pick.failure_reasons)
            griip.stop()
            break

        if incoming_tray_is_full():
            griip.move_to(griip.positions.overflow_drop)
            griip.release()
        else:
            griip.place()

Level 3: building your own pipeline from the primitives

When your application doesn't look like pick-and-place at all (inspection, counting, custom manipulation), compose it from the same engine:

from griip_sdk import GRIIP

with GRIIP("./config.yaml") as griip:
    griip.init(application="custom", part_id="vp15")

    image = griip.perception.capture()
    depth = griip.perception.estimate_depth(image)
    detections = griip.perception.detect_objects(image, depth)

    print(f"{len(detections)} parts on the table")

    for detection in detections:
        pose = griip.perception.estimate_pose(image, depth, detection)
        griip.move_to(compute_camera_vantage(pose))
        run_quality_inspection(griip.perception.capture())

Same config, same hardware, same SDK, running a completely different application.