Architecture
The stack is 79 ROS 2 packages across 20 repositories. They form layers: each one depends on those below it and knows nothing about those above.
application sorting_app <- what you write
|
framework core_pick_and_place <- move_arm, decision taking, CLI
| core_supervisor <- lifecycle orchestration
|
services core_motion_planning <- plan, IK/FK, collision checking
| core_motion_control <- execute on a real or simulated arm
| core_modeling <- workcell description, joint states
|
contract core_interfaces <- 110 msg / srv / action definitions
|
foundation core_types core_utils core_logger core_tf
|
simulation core_plugins mesh_models <- Gazebo plugins, arm and EE models
The layer that matters most when you start is contract. Packages do not link against each other’s internals; they exchange the messages and services in Interfaces. If you want to know what a subsystem can do, read its interfaces first.
Which layer am I writing in?
You are… |
Work in |
|---|---|
building a new robot application |
application — see Build a new application |
adding a behaviour every application should share |
framework ( |
changing how motions are planned or executed |
services |
adding a new arm or gripper |
simulation ( |
Most work belongs in the application layer. Reaching into services usually means either the framework is missing something, or an interface needs extending.
The request path
A pick-and-place cycle moves through the layers like this:
CLI / UI
-> supervisor activates the application's lifecycle nodes
-> application decides what to do next (which destination)
-> move_arm (framework) plans + sends the trajectory
-> motion_planning /compute_motion_plan, /check_robot_collision
-> motion_control queues the trajectory, drives the arm
-> ArmPlugin (simulation only) applies joint commands in Gazebo
-> end effector grasp / release
Two properties of this path are worth internalising early.
The same driver runs against simulation and hardware. motion_control_ros
speaks the Motoman simple_message protocol over TCP. In simulation
core_plugins’ ArmPlugin implements the other end of that protocol on ports
50240/50241, so nothing above the driver knows whether it is talking to a real
robot. Code that works in simulation is the code that runs on hardware.
Planning and execution are separate. motion_planning produces a
time-parameterised trajectory; motion_control executes it. A trajectory that
plans cleanly can still be executed badly — see
Debug a collision.
Applications are a template, not a framework call
There is no Application base class to inherit. An application is a set of
packages that mirror core_pick_and_place one-for-one:
Framework package |
Your application provides |
|---|---|
|
a controller (HTTP/UI entry point) |
|
decision taking (what to do next) |
|
your own msg/srv/action |
|
a CLI |
|
bringup and any custom plugins |
sorting_app is the reference implementation of that mapping.
Build a new application walks through it.
Configuration is part of the API
Behaviour that looks like code is often configuration, and getting it wrong produces symptoms that look like bugs:
parameterization_config.json— velocity and acceleration limits per speed profile. Too aggressive and the arm cannot track the trajectory it was given.app_poses.json— base and pivot poses the application plans between.the
ArmPluginblock in the world SDF — how joint commands reach the simulated arm, includingdrive_mode.
See How-to guides for what each key does and what breaks when it is wrong.