Skip to content

PoC of streaming API for external_control#528

Merged
urfeex merged 11 commits into
UniversalRobots:masterfrom
acmorrow:streaming
Jul 6, 2026
Merged

PoC of streaming API for external_control#528
urfeex merged 11 commits into
UniversalRobots:masterfrom
acmorrow:streaming

Conversation

@acmorrow

@acmorrow acmorrow commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

@urfeex - Here is what I had Claude put together to demonstrate the possibility of keeping open trajectories, per #524.

It includes:

  • The addition of the STREAM_START and STREAM_END control messages to the TrajectoryControlMesssage enumeration.
  • An integration test for streaming and an example program.
  • Support for configuring the cmake build with a target IP for integration testing. Not perfect since it gets baked into the build, but not terrible either.
  • Argument parsing fixes such that setting the robot IP on the command line works for various integration tests.
  • Updates to the external_contro.urscript to support stream start and stream end.

I'm using this right now and it seems to work for some basic cases. I haven't stress tested it or thought deeply about the various edge cases or error scenarios that this may need to cover.

The goal here is really just to put some code behind the ideas over in #524 so we can discuss further. I'm happy to rework any of this as necessary.

I will also leave some comments directly on the review for a few things I want to call out.


Note

High Risk
Changes real-time robot motion control in external_control.urscript and the reverse-interface protocol; incorrect streaming semantics or edge-case bugs could truncate trajectories or report wrong completion results.

Overview
Adds open-ended trajectory streaming alongside the existing finite TRAJECTORY_START path: producers open a stream with TRAJECTORY_STREAM_START, send motion primitives without a fixed upfront count, then close with TRAJECTORY_STREAM_END (and the same total count on TRAJECTORY_CANCEL in streaming mode).

external_control.urscript implements streaming with a STREAMING_SENTINEL point budget, trajectory_streaming state, guarded STREAM_END/cancel handling (so stray or duplicate end messages do not corrupt finite trajectories), and refined underrun vs. clean-drain behavior in trajectoryThread.

The C++ API documents the new TrajectoryControlMessage values on ReverseInterface / UrDriver. A new trajectory_streaming example and architecture/docs describe the handshake, producer pacing, and result codes. Headless integration tests cover success, underrun, cancel, and several regression cases for malformed streaming control sequences.

Reviewed by Cursor Bugbot for commit f98096d. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread resources/external_control.urscript Outdated
Comment thread resources/external_control.urscript
Comment thread resources/external_control.urscript Outdated
Comment thread examples/trajectory_streaming.cpp
Comment thread tests/CMakeLists.txt
@acmorrow

Copy link
Copy Markdown
Contributor Author

@urfeex - I see the bot has flagged a few things, I'll take a look at those and post an update.

@acmorrow

Copy link
Copy Markdown
Contributor Author

@urfeex - Also, an update: today we got this running on a physical UR5e and we were able to stream to the arm. So, there is at least some evidence that this is feasible in the real world.

@acmorrow

acmorrow commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

@urfeex - Pushed an update to address the deficiencies that the cursor bot tagged. I had claude write some tests, we confirmed they failed, made fixes, confirmed they passed.

Comment thread resources/external_control.urscript

@urfeex urfeex left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if we could move the IP-address refactoring to a separate PR, so this one gets a bit smaller in scope.

From a first glance, this looks quite clean. We would need some documentation updates, both on the trajectory point interface and one for the example (which could contain a lot of the text written in the example itself right now).

* \param trajectory_action One of the values of TrajectoryControlMessage. The value selects which
* trajectory-control action the URScript dispatcher takes, and dictates how \p point_number is
* interpreted:
* - TRAJECTORY_CANCEL (-1): Cancels the currently executing trajectory. \p point_number is unused.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens to the remaining points if a trajectory is canceled? If I understand things correctly, the buffer cleanup will try to clean a very high number of points and then hit the socket read timeout as defined in clearTrajectoryPointsThread(). We could make this more effective (avoid the timeout) by passing the total number of points along as done with TRAJECTORY_STREAM_END. It should still work passing along 0 to not break behavior.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tested this and adding trajectory_points_left = params_mult[3] - (STREAMING_SENTINEL - trajectory_points_left) in the script code section for elif params_mult[2] == TRAJECTORY_MODE_CANCEL: made the cleanup function clean the remaining number of points not timing out on reading from the socket.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@urfeex - I just want to confirm before I make the change: you suggest that for writeTrajectoryControlMessage(TRAJECTORY_CANCEL, ..., if the trajectory was started with TRAJECTORY_STREAM_START, then point_number becomes meaningful, and should equal the total number of points sent, just like it would be for TRAJECTORY_STREAM_END, and then around line 1196 in external_control.urscript, if we are in streaming mode we adjust the point count as indicated above?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly. Do you think, that makes sense?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes total sense. I will get an update pushed.

* calls the producer will issue on the trajectory socket. URScript reads exactly that many points
* and then completes.
* - TRAJECTORY_STREAM_START (2): Begins an open-ended (streaming) trajectory. The producer streams
* spline points and signals end-of-stream with TRAJECTORY_STREAM_END. \p point_number is unused.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this restricted to splines only? From what I can see the same mechanism also works for sending movej, movel, etc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tested this with the motion sequence from the instruction_executor example. This worked like a charm.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's great: it isn't a mechanism we happen to use right now, so not something I was thinking about testing. I could change that language from spline points to points or primitives?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about motion segments or motion primitives?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll do motion primitives as that is what spline points actually reduce to, so it is I think the most accurate.

Comment thread resources/external_control.urscript
@urfeex

urfeex commented Jun 26, 2026

Copy link
Copy Markdown
Member

I've added the two issues that this PR will close when being merged.

@acmorrow

Copy link
Copy Markdown
Contributor Author

@urfeex - No problem about splitting out the testing changes, they were in their own commit anyway. I've made a new issue (#530) and a new PR (#531). I just made a new branch and cherry-picked out the relevant commit from this one.

We will need to sort out how to drop out that commit from this review since it is the base commit. Probably, we just review what is here until satisfied, and then I'll do a final rebase above master and force-push to drop out the now vacuous testing changes commit, but lmk if you want to handle it differently.

@urfeex

urfeex commented Jun 26, 2026

Copy link
Copy Markdown
Member

Yes, a rebase on master once the testing changes are merged should be the way to go.

@urfeex

urfeex commented Jun 29, 2026

Copy link
Copy Markdown
Member

@acmorrow I've merged #531, so you should be able to rebase this ontop of master.

@acmorrow

Copy link
Copy Markdown
Contributor Author

@urfeex - OK, rebased and force pushed.

@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.00%. Comparing base (757d512) to head (f98096d).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #528      +/-   ##
==========================================
- Coverage   79.06%   79.00%   -0.06%     
==========================================
  Files         115      115              
  Lines        6767     6768       +1     
  Branches     2988     2991       +3     
==========================================
- Hits         5350     5347       -3     
- Misses       1043     1048       +5     
+ Partials      374      373       -1     
Flag Coverage Δ
check_version_ur10-3.15.8 10.85% <ø> (-0.56%) ⬇️
check_version_ur10e-10.11.0 11.10% <ø> (-0.01%) ⬇️
check_version_ur10e-5.15.2 11.10% <ø> (-0.10%) ⬇️
check_version_ur12e-10.12.1 11.10% <ø> (-0.01%) ⬇️
check_version_ur12e-5.25.1 11.87% <ø> (+0.35%) ⬆️
check_version_ur15-10.12.1 11.15% <ø> (-0.01%) ⬇️
check_version_ur15-5.25.1 11.29% <ø> (-0.06%) ⬇️
check_version_ur16e-10.12.1 11.10% <ø> (-0.01%) ⬇️
check_version_ur16e-5.25.1 11.10% <ø> (-0.77%) ⬇️
check_version_ur18-10.12.1 11.10% <ø> (-0.10%) ⬇️
check_version_ur18-5.25.1 11.29% <ø> (-0.01%) ⬇️
check_version_ur20-10.12.1 11.10% <ø> (-0.01%) ⬇️
check_version_ur20-5.25.1 11.10% <ø> (-0.37%) ⬇️
check_version_ur3-3.14.3 11.34% <ø> (-0.81%) ⬇️
check_version_ur30-10.12.1 11.10% <ø> (-0.06%) ⬇️
check_version_ur30-5.25.1 11.10% <ø> (-0.42%) ⬇️
check_version_ur3e-10.11.0 11.10% <ø> (-0.01%) ⬇️
check_version_ur3e-5.9.4 11.10% <ø> (-0.06%) ⬇️
check_version_ur5-3.15.8 11.34% <ø> (-0.04%) ⬇️
check_version_ur5e-10.11.0 11.10% <ø> (-0.10%) ⬇️
check_version_ur5e-5.12.8 11.10% <ø> (-0.06%) ⬇️
check_version_ur7e-10.11.0 11.10% <ø> (-0.10%) ⬇️
check_version_ur7e-5.22.2 11.10% <ø> (-0.06%) ⬇️
check_version_ur8long-10.12.1 11.10% <ø> (-0.10%) ⬇️
check_version_ur8long-5.25.1 11.10% <ø> (-0.06%) ⬇️
python_scripts 75.90% <ø> (ø)
start_ursim 82.18% <ø> (-3.02%) ⬇️
ur5-3.14.3 74.43% <ø> (-0.21%) ⬇️
ur5e-10.11.0 69.29% <ø> (-0.01%) ⬇️
ur5e-10.12.0 70.34% <ø> (-0.23%) ⬇️
ur5e-10.7.0 68.93% <ø> (+0.07%) ⬆️
ur5e-5.9.4 75.24% <ø> (-0.06%) ⬇️
ur7e-10.13.0 70.39% <ø> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@acmorrow

Copy link
Copy Markdown
Contributor Author

@urfeex - Pushed two commits: one with the pure comment change about splines, and another with the improvements around cancellation taking the number of points.

Comment thread resources/external_control.urscript Outdated
@acmorrow
acmorrow requested a review from urfeex June 29, 2026 20:11
@acmorrow

acmorrow commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@urfeex - Just checking in on this, as I'm not aware of anything more you had asked to change in this review. But if I've overlooked a previous comment or you have additional thoughts, please let me know and I'll be happy to iterate on them.

@urfeex

urfeex commented Jul 1, 2026

Copy link
Copy Markdown
Member

No, I consider that to be in my field right now. This review is on my short list already, I will get to this before the end of the week!

@urfeex urfeex left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have two cosmetic suggestions, but we are still missing the updated sphinx documentation.

Specifically, the reverse interface documentation in https://github.com/UniversalRobots/Universal_Robots_Client_Library/blob/master/doc/architecture/reverse_interface.rst needs a major update and should outline the communication contract as you've done in the example.

Also, we have accompanying sphinx pages for each example in https://github.com/UniversalRobots/Universal_Robots_Client_Library/tree/master/doc/examples, so that should be added, as well. As I have written above, this should contain most of the comments from the cpp file, so the cpp example gets a bit more compact to read on its own.

Comment thread include/ur_client_library/control/reverse_interface.h Outdated
Comment thread resources/external_control.urscript Outdated
acmorrow and others added 2 commits July 2, 2026 09:59
Co-authored-by: Felix Exner <feex@universal-robots.com>
@acmorrow
acmorrow requested a review from urfeex July 2, 2026 15:04

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 3708a9d. Configure here.

Comment thread resources/external_control.urscript
@acmorrow

acmorrow commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@urfeex - I had claude put together the desired documentation. It got a little overzealous since my ambient instructions for my local development include running clang-format, and it decided that was something to do at the same time. Unfortunately, I don't know the exact version of clang-format you use, so it is possible that this isn't going to work out well.

I also asked claude to change around some of the existing language from the example, where it had gotten overly focused on sockets and buffers, and instead focus on the client obligation to keep the arm from starving for work. I think it reads better now, overall.

As always, happy for any feedback you have on this!

@urfeex urfeex left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have only a couple of small suggestions. If the tests succeed, I can apply them and merge.

Thank you @acmorrow for putting all the effort into this!

Comment thread doc/architecture/reverse_interface.rst Outdated
Comment thread doc/architecture/reverse_interface.rst Outdated
Comment thread doc/examples/trajectory_streaming.rst Outdated
@mergify

mergify Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Co-authored-by: Felix Exner <feex@universal-robots.com>
@acmorrow

acmorrow commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

I have only a couple of small suggestions. If the tests succeed, I can apply them and merge.

Yes, suggestions all reasonable and I see you committed them. I do see some failed checks but I think you are better positioned to decide what they mean.

Thank you @acmorrow for putting all the effort into this!

No problem! We are really excited about being able to do this, and very happy that it turned out to be within reach on such a short timeline. Thanks for all the review feedback and helping to land it.

@urfeex

urfeex commented Jul 6, 2026

Copy link
Copy Markdown
Member

Everything I expect to succeed, succeeds. Happily merging this :-)

@urfeex urfeex added the enhancement New feature or request label Jul 6, 2026
@urfeex
urfeex merged commit b437116 into UniversalRobots:master Jul 6, 2026
87 of 98 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

2 participants