Architecture

Package Structure

The repository contains 8 packages organised in three layers: types and interfaces at the base, core planning libraries in the middle, and ROS 2 nodes and agents at the top.

digraph architecture {
   rankdir=TB;
   node [shape=box, style=filled, fontname="Helvetica"];
   edge [fontname="Helvetica", fontsize=10];

   // Layer 0 – interfaces
   motion_planning_interfaces [label="motion_planning_interfaces\n(ROS 2 msg/srv)", fillcolor="#d0e8ff"];
   motion_planning_types      [label="motion_planning_types\n(C++ type headers)", fillcolor="#d0e8ff"];

   // Layer 1 – core libs
   core_robot       [label="core_robot\n(Robot model)", fillcolor="#ffe0b2"];
   core_arm_planning[label="core_arm_planning\n(Path planning, IK, TOPPRA)", fillcolor="#ffe0b2"];
   core_motion_planning_lib [label="core_motion_planning\n(Server pool, collision util)", fillcolor="#ffe0b2"];

   // Layer 2 – ROS 2 node
   motion_planning_ros2_conversions [label="motion_planning_ros2_conversions\n(msg↔type conversions)", fillcolor="#e8f5e9"];
   motion_planning [label="motion_planning\n(ROS 2 node servers)", fillcolor="#c8e6c9"];

   // Layer 3 – agent
   motion_planning_agent [label="motion_planning_agent\n(Lifecycle node)", fillcolor="#f3e5f5"];

   // Edges
   motion_planning_types       -> core_robot;
   motion_planning_types       -> core_arm_planning;
   motion_planning_types       -> core_motion_planning_lib;
   core_robot                  -> core_arm_planning;
   core_arm_planning           -> core_motion_planning_lib;
   core_motion_planning_lib    -> motion_planning;
   motion_planning_interfaces  -> motion_planning_ros2_conversions;
   motion_planning_types       -> motion_planning_ros2_conversions;
   motion_planning_ros2_conversions -> motion_planning;
   motion_planning             -> motion_planning_agent;
}

Server Pool

ServerManager maintains a pool of pre-constructed servers for each capability. Callers acquire a server, use it, and return it — enabling parallel request handling without re-initialisation overhead.

digraph server_pool {
   rankdir=LR;
   node [shape=box, style=filled, fontname="Helvetica"];

   client  [label="Client request", shape=ellipse, fillcolor="#fff9c4"];
   sm      [label="ServerManager", fillcolor="#ffe0b2"];
   ms      [label="MotionServer\n(pool)", fillcolor="#c8e6c9"];
   iks     [label="IKServer\n(pool)", fillcolor="#c8e6c9"];
   fks     [label="FKServer\n(pool)", fillcolor="#c8e6c9"];
   ps      [label="ParameterizerServer\n(pool)", fillcolor="#c8e6c9"];
   cs      [label="CollisionServer\n(pool)", fillcolor="#c8e6c9"];

   client -> sm  [label="acquire"];
   sm -> ms;
   sm -> iks;
   sm -> fks;
   sm -> ps;
   sm -> cs;
   ms -> sm  [label="return", style=dashed];
   iks -> sm [style=dashed];
   fks -> sm [style=dashed];
   ps  -> sm [style=dashed];
   cs  -> sm [style=dashed];
}

Planning Pipeline

A motion plan request flows through four stages from goal specification to a time-parameterised joint trajectory.

digraph pipeline {
   rankdir=LR;
   node [shape=box, style=filled, fontname="Helvetica"];

   goal   [label="Goal\n(pose / joints)", shape=ellipse, fillcolor="#fff9c4"];
   ik     [label="IK\n(TRAC-IK / OPW)", fillcolor="#d0e8ff"];
   plan   [label="Path planning\n(OMPL)", fillcolor="#ffe0b2"];
   col    [label="Collision check\n(FCL)", fillcolor="#ffccbc"];
   param  [label="Time parameterisation\n(TOPPRA / TOTG)", fillcolor="#c8e6c9"];
   traj   [label="JointTrajectory", shape=ellipse, fillcolor="#fff9c4"];

   goal  -> ik   [label="if Cartesian goal"];
   ik    -> plan;
   goal  -> plan [label="if joint goal"];
   plan  -> col;
   col   -> param [label="collision-free path"];
   param -> traj;
}