Build a new application
This guide takes you from an empty repository to an application that brings up a workcell, plans a motion, moves the arm to a destination and drives an end effector.
There is no framework class to inherit. An application is a set of packages
that mirror core_pick_and_place one-for-one. sorting_app is the
reference implementation of that mapping, and this guide follows the same shape.
Before you start, run the example workcell (Getting started) so you have something working to compare against.
The mapping
Framework ( |
Reference ( |
Yours |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
(bringup, plugins, models) |
|
|
You do not need all of them on day one. The minimum that runs is bringup plus cli; add decision taking when the application needs to choose its own work.
Step 1 — Decide what your application does
Write this down before creating packages, because it determines which of the above you need:
What triggers work? A CLI command, an operator UI, a sensor, a PLC.
What is a cycle? For
sorting_app: an item arrives, is picked, and is placed at a destination chosen from a shelf map.What varies per site? Destinations, poses, speeds — these belong in configuration, not code.
Step 2 — Create the bringup package
Bringup owns launch files and configuration. It is the only package you truly need to see a robot move.
<app>_bringup/
launch/
app_launch/<app>.launch.py full application
node_launch/*.launch.py one file per node, composed above
config/
gazebo/sdf/world.sdf simulated workcell + plugins
config_parser/ee_tip_specific/
parameterization_config.json speed profiles
app_poses.json base and pivot poses
Copy sorting_bringup and strip it back. The pieces that must be right:
the world SDF loads
libArmPlugin.sowith the ports the driver expects (50240/50241) and the joint list of your arm``parameterization_config.json`` defines a low- and high-speed profile
``app_poses.json`` defines
base_poseandpivot_pose
Note
Start from the low-speed profile. High-speed profiles demand accelerations the arm may not be able to track, and an arm that cannot track its trajectory leaves the planned path — see Debug a collision.
Step 3 — Add the CLI
The CLI is how you drive the application before there is a UI. sorting_cli is
a thin argparse front end that issues HTTP requests to the controller:
send_commands_parser = subparsers.add_parser('send-commands')
send_commands_parser.add_argument('cmd', choices=['grasp', 'release', 'secure'])
Keep it thin. Logic belongs in the controller, so the UI and the CLI share it.
Step 4 — Add the controller
The controller exposes the application’s operations and owns its state machine.
It is a lifecycle node managed by core_supervisor: the supervisor configures
and activates it, and the controller refuses work when it is not active.
Follow pick_and_place_controller: an application per capability
(move_arm, send_commands, …), each with a router and a set of
sub-controllers, one per operation.
Step 5 — Move the arm
Use the framework rather than calling planning services directly. move_arm_app
already handles the sequence — plan to base, replay a pre-planned trajectory,
re-parameterise, send, wait:
from pick_and_place_interfaces.action import MoveArm
goal = MoveArm.Goal()
goal.destination = '5'
goal.ee_tip = 'sorting'
goal.parameterization = MoveArm.Goal.LOW_SPEED_PARAMETERIZATION
Reach for C++ and Python API only when the framework genuinely cannot express what you need.
Step 6 — Drive the end effector
End effectors are Gazebo plugins in simulation and real hardware otherwise, behind
the same services. conveyor_gripper_interfaces defines the contract; the
commands are grasp, release and secure.
Step 7 — Add decision taking
Once the application chooses its own work rather than being told, add a decision
taking package modelled on core_decision_taking: given the current state,
return the next action. Keeping this separate is what lets you test the policy
without a simulator.
Checklist
[ ] bringup launches a workcell and the arm holds position
[ ] CLI reaches the controller (HTTP 200) and the supervisor activates it
[ ] a low-speed motion to one destination completes
[ ] the collision monitor reports 0% for that motion
[ ] the end effector grasps and releases
[ ] the same cycle runs at the high-speed profile
[ ] configuration, not code, carries anything site-specific
Common mistakes
Calling planning services directly. The framework exists because the sequence around a plan (pre-plan, merge, re-parameterise, wait for completion) is easy to get subtly wrong.
Tuning speed by editing code. Speed lives in
parameterization_config.json. If the arm collides at high speed but not low,
that is a tracking problem, not a planning one.
Leaving a previous launch running. Duplicate nodes make an application accept commands and return success while nothing moves. Always confirm the previous launch is gone before starting another.