The GRIIP SDK is a Python library for building Physical AI powered automation applications that run on your Machine Motion AI. The steps are:
- Populate the configuration file
config.yaml. - Write a few lines of code using the GRIIP SDK.
- Push this code to your Machine Motion.
- 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:
- 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. - 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. - 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.