You're reading the documentation for a development version. For the latest released version, please have a look at Kilted.
GSoC 2026: Physical AI Inference and Trajectory Upscaling
Learned manipulation policies (VLAs, diffusion policies, ACT) emit joint targets at roughly 20 to 50 Hz and replan every 50 to 500 ms, while the hardware underneath expects a fresh command on every control cycle at 500 Hz to 2 kHz. Forwarding those sparse waypoints as they arrive puts a velocity discontinuity and an acceleration spike at every waypoint.
This Google Summer of Code 2026 project closed that gap in ros2_controllers: the
joint_trajectory_controller (JTC) can now merge an incoming trajectory into the one it is
already executing, reconstruct missing derivatives from positions-only chunks, and a new
cartesian_trajectory_controller accepts end-effector pose chunks.
Trajectory replacement with blending
When a new trajectory arrives mid-execution, JTC previously discarded the active one. A future-stamped message killed the running trajectory the moment it arrived and ramped slowly to its first point, and joints the message omitted froze in place.
The controller now assembles a single merged trajectory at the moment the message arrives, built from four pieces:
The merged message. Prefix, bridge and suffix all come from the old trajectory; only the new points are the client’s message.
Cursor is where the robot actually is on the old trajectory. It is the playback position rather than wall-clock time, so the merge stays correct when speed scaling is active.
Prefix holds the old waypoints between the cursor and the handoff instant. The robot therefore continues along the path it was already following, preserving any obstacle avoidance planned into it, instead of cutting straight towards the new trajectory.
Bridge is a single point sampled analytically from the old trajectory’s spline at the handoff. Because it comes from the spline rather than from the measured hardware state, the seam carries a real velocity and acceleration instead of zeros.
New points are the client’s message, re-timed onto the merged timeline.
Suffix holds old waypoints beyond the end of the new message. Joints that the new message did not mention keep following their original motion and finish it, rather than freezing at their current position.
Enabled by allow_trajectory_replacement (default true); set it to false for the previous
hard-replace behaviour. See the documentation of joint_trajectory_controller for the full parameter description.
Positions-only action chunks
JTC selects its interpolation degree from the fields present in the message, so a positions-only
chunk falls back to linear interpolation: a staircase velocity and an impulse acceleration at every
knot. With positions_upsampling enabled, the controller instead:
solves the knot velocities that a global cubic spline would have, one tridiagonal system per joint in O(n), in the non-real-time subscription callback;
writes those velocities into the message, so the existing cubic Hermite sampling reproduces a C2 trajectory;
synthesizes
time_from_startfrom a policy frequency when the chunk arrives without timing.
Nothing in the real-time path changes, and messages that already carry velocities pass through untouched, so the feature is a strict superset of the previous behaviour.
One chunk, with and without upsampling. |
A stream of chunks, with and without cross-chunk continuity. |
Streaming chunks additionally need continuity across the seam between them. Rest boundary conditions at both ends of every chunk plan a stop at each seam; clamping the start of each chunk to the last commanded velocity removes it, while the chunk still ends at rest so that a policy which stops publishing leaves the robot stationary.
A pick-and-place policy running at 5 Hz. |
Jerk on the same motion; peaks drop by roughly a factor of four. |
Cartesian trajectory controller
Interpolating in joint space between two configurations does not move the tool along a straight
line, and several policy families emit end-effector poses rather than joint angles. The
cartesian_trajectory_controller subclasses JTC and accepts
trajectory_msgs/MultiDOFJointTrajectory on ~/cartesian_reference. Per message it:
interpolates the path in Cartesian space, cubic Hermite for translation and SLERP for rotation, so the rotation follows the shortest geodesic;
resamples that path densely and runs differential inverse kinematics through
kinematics_interfaceat every sample;hands the resulting ordinary joint trajectory to the base class for execution.
All of the kinematics runs once per message in the non-real-time callback, so the real-time loop stays as fast as plain JTC.
The tool centre point tracing a path on a 6-DOF arm. |
Commanded path (green) against the executed tool centre point (blue). |
Trying it out
Both demos run on mock hardware, so they need no robot.
example_19 streams positions-only chunks from a mock policy to a 2-DOF RRBot:
ros2 launch ros2_control_demo_example_19 bridge_demo.launch.py
# then, with the policy stopped, check C1/C2 continuity and the chunk seams
ros2 launch ros2_control_demo_example_19 bridge_demo.launch.py run_policy:=false gui:=false
ros2 run ros2_control_demo_example_19 verify_smoothness.py
example_20 drives a 6-DOF arm through Cartesian patterns:
ros2 launch ros2_control_demo_example_20 cartesian_demo.launch.py # try pattern:=square or line
# the verifier reports pass/fail for straight-line deviation, orientation
# SLERP, multi-pose chunks, Cartesian smoothness and joint-limit violations
ros2 launch ros2_control_demo_example_20 cartesian_demo.launch.py run_policy:=false gui:=false
ros2 run ros2_control_demo_example_20 verify_cartesian_tracking.py
Further reading
Project work product, with the full report, the design alternatives considered, the current limitations of each feature and links to every pull request.
Design documents written during the project, including the spline method comparison and the Cartesian control study.
Spinoff ideas, eight follow-on projects written up with requirements and priorities. They range from factoring the trajectory upscaling primitives into
control_toolboxso any controller can reuse them, through an IKFast plugin forkinematics_interfaceand aFollowCartesianTrajectoryaction server, to tool-frame relative commands and a comparative study of VLA policies running through this stack. Each entry states its requirements and a priority, so they can be picked up directly as contributions or as future GSoC projects.
Dissemination
This work will be presented as part of the ros2_control project update at ROSCon Global 2026 in Toronto, 22 to 24 September. The talk will cover handling policy inference and jitter inside the control loop. ROS Physical AI Special Interest Group, the OSRA group working to identify and remove the friction points that slow Physical AI development in ROS, whose proposals and code are developed in the open at ros-physical-ai. Both demos above currently run on mock hardware; the next step is running the same motions on real arms.
Acknowledgements
This project was carried out during Google Summer of Code 2026 with the ros2_control team. Thanks to Sai Kishor Kothakota, Dr. Bence Magyar and Christoph Fröhlich for the guidance and reviews throughout the project. Thanks to the ros2_control maintainers and contributors for reviewing the pull requests, and to the Open Source Robotics Foundation for hosting the project.
CC-BY Vedhas Talnikar
Mentors: Dr. Bence Magyar (Locus Robotics), Sai Kishor Kothakota (PAL Robotics)