Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
29ea12d
Mark version as `pre`
ted-miller Jul 15, 2025
1d457dc
Add service `start_rt_mode`
ted-miller Jul 15, 2025
6a0f6ef
ignore libmicroros folders
ted-miller Jul 15, 2025
7e07786
Add motion mode `MOTION_MODE_RT`
ted-miller Jul 15, 2025
4edb0c5
Change `StartRtMode` to use new message type
ted-miller Jul 15, 2025
87a3291
Manage interpolation task for each `MOTION_MODE`
ted-miller Jul 15, 2025
0f4a4ef
call library function
ted-miller Jul 16, 2025
f1ff1e0
Add RealTimeMotionControl.c to rpoject
ted-miller Jul 16, 2025
6d70c33
RealTimeMotionControl header
ted-miller Aug 12, 2025
932ced4
Make rt port number configurable
ted-miller Aug 14, 2025
e8ccaf2
Initial framework ready for testing
ted-miller Aug 14, 2025
404e879
subtraction was swapped
ted-miller Aug 14, 2025
67a48f3
Add timeout and clear motion mode
ted-miller Aug 14, 2025
c4d270a
Add RT fields to the yaml file
ted-miller Aug 14, 2025
be2d6a5
allow infinite timeout
ted-miller Aug 14, 2025
7f0feb9
Trying to make RT task able to be restarted
ted-miller Aug 14, 2025
ff34234
Remove gemini comments
ted-miller Aug 15, 2025
eb5ef13
Wrong socket id was preventing restart after timeout
ted-miller Aug 15, 2025
508dafc
I didn't get to use this product name for YMConnect. This will suffice.
ted-miller Aug 15, 2025
c1fb8e7
Add fb position to reply packet
ted-miller Aug 15, 2025
00a0f71
Add cartesian support
ted-miller Aug 22, 2025
4870a16
Apply feedback from Jimmy
ted-miller Sep 5, 2025
bf21d1b
Allow milliseconds for timeout
ted-miller Sep 19, 2025
7920aac
Make MAX_SEQUENCE_DIFFERENCE configurable
ted-miller Sep 19, 2025
3499120
User must call `stop_traj_mode` for consistency
ted-miller Sep 19, 2025
fb6e89f
Don't care about `Ros_setsockopt` return code
ted-miller Sep 19, 2025
9af33ba
Revert "ignore libmicroros folders"
ted-miller Sep 19, 2025
c241fdf
Remove `NOT_IMPLEMENTED` subcode
ted-miller Sep 19, 2025
4ab6157
excess whitespace
ted-miller Sep 19, 2025
14c147d
Add enums to elaborate ordering
ted-miller Sep 19, 2025
fa2add1
Add tool data
ted-miller Sep 19, 2025
62d9a8c
Unused variables
ted-miller Sep 19, 2025
3c2a5f9
Add cartesian and command positions to reply message
ted-miller Sep 19, 2025
a649731
Clarify `Ros_CtrlGroup_GetPulsePosCmd` comment
ted-miller Sep 26, 2025
db6b9b1
FSU detection
ted-miller Sep 26, 2025
afc8470
End session if API errors out (alarm or estop)
ted-miller Sep 26, 2025
b61eb17
Socket must persist for multiple sessions to work
ted-miller Sep 26, 2025
cd4d0e9
use absolute value
ted-miller Sep 26, 2025
2358575
Attempt (fail) to fix initialization problem.
ted-miller Sep 29, 2025
e95e412
`mpGetToolNo` was always getting R1
ted-miller Sep 29, 2025
7b6d54c
Purge stale packets at start of connection
ted-miller Sep 29, 2025
b35083b
Don't monitor speed limit for rotations
ted-miller Sep 29, 2025
c0895e9
Handle rollover of sequence id
ted-miller Sep 29, 2025
6f75377
Documentation
ted-miller Sep 30, 2025
4e3cbce
Satisfy REUSE and md_lint
ted-miller Sep 30, 2025
1c717f3
Whitespace
ted-miller Sep 30, 2025
3c8a1f5
Spelling
ted-miller Oct 2, 2025
be07e28
Report robot's configuration to the invoker
ted-miller Oct 2, 2025
a47be77
incoming increment can be negative
ted-miller Oct 2, 2025
4941a80
Ensure `prevRtCmdPosition` is set for all axes
ted-miller Oct 2, 2025
7c678f9
Tighten tolerance for `MAX_INCREMENT_DEVIATION_FOR_FSU_DETECTION`
ted-miller Oct 2, 2025
42ebb95
Fix warning on first cmd ID received
ted-miller Oct 2, 2025
28d7b11
Invalid boolean logic
ted-miller Oct 2, 2025
e96194f
Client should use the same timeout as the server
ted-miller Oct 2, 2025
cec6bf5
Purge UDP buffer before triggering next command
ted-miller Oct 2, 2025
66eb98e
header format
ted-miller Oct 10, 2025
7dad1ed
scale cartesian speed limit with interpolation clock
ted-miller Oct 10, 2025
8d6eae1
Consistent quotations for port numbers
ted-miller Oct 10, 2025
a213192
Repeat code. Remove it and explain what's happening
ted-miller Oct 10, 2025
fefa69d
Reply with error code if user requests invalid mode
ted-miller Oct 20, 2025
b57fc9e
rebase changed error codes
ted-miller Oct 20, 2025
c51090e
Correct subcode in link
ted-miller Oct 20, 2025
2f710a7
Use string; not enum
ted-miller Oct 29, 2025
c2593af
Documentation clarification based on review
ted-miller Nov 25, 2025
b410850
decrease `Timeout for real time motion commands`
ted-miller Dec 15, 2025
dcfe324
decrease `max_sequence_diff_for_rt_msg`
ted-miller Dec 15, 2025
184cdd8
Move `rt_udp_port_number` to range specified in manual
ted-miller Dec 15, 2025
92e3475
reflect port change in docs
ted-miller Dec 15, 2025
4339067
Implement `version` field
ted-miller Dec 15, 2025
b337a78
Verify incoming packet type
ted-miller Dec 15, 2025
47290e7
Implement feedback state
ted-miller Dec 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ License: CC0-1.0
Files: doc/img/logo.png
Copyright: 2023 Yaskawa America, Inc.
License: CC-BY-NC-ND-4.0

Files: doc/img/RtFlow.png doc/img/RtFlow.vsdx
Copyright: 2023 Yaskawa America, Inc.
License: CC-BY-NC-ND-4.0
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
<!--
SPDX-FileCopyrightText: 2022-2023, Yaskawa America, Inc.
SPDX-FileCopyrightText: 2022-2023, Delft University of Technology
SPDX-FileCopyrightText: 2022-2025, Yaskawa America, Inc.
SPDX-FileCopyrightText: 2022-2025, Delft University of Technology

SPDX-License-Identifier: CC-BY-SA-4.0
-->

# Changelog

## Forthcoming

MotoROS2 is now built against `micro_ros_motoplus` version TODO

New functionality:

- Add new motion mode for real-time control of the robot. This pipes the user commands directly to the motion API with minimal overhead. ([#449](https://github.com/Yaskawa-Global/motoros2/pull/449))

## 0.2.1 (2025-06-26)

MotoROS2 is now built against `micro_ros_motoplus` version `20250328`.
Expand Down
30 changes: 24 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--
SPDX-FileCopyrightText: 2022-2023, Yaskawa America, Inc.
SPDX-FileCopyrightText: 2022-2023, Delft University of Technology
SPDX-FileCopyrightText: 2022-2025, Yaskawa America, Inc.
SPDX-FileCopyrightText: 2022-2025, Delft University of Technology

SPDX-License-Identifier: CC-BY-SA-4.0
-->
Expand Down Expand Up @@ -605,7 +605,12 @@ Instead, write a `FollowJointTrajectory` action *client* script or use a motion

### Commanding motion

The ROS API of MotoROS2 for commanding motion is similar to that of `motoman_driver` (with MotoROS1), and client applications are recommended to implement a similar flow of control to keep track of the state of the robot before, during and after trajectory and motion execution.
There are three methods of commanding motion using MotoROS2.
`FollowJointTrajectory` action server, point streaming, and real-time incremental control.

#### - [FollowJointTrajectory](doc/ros_api.md#follow_joint_trajectory) action server

The ROS API of MotoROS2 for commanding motion is similar to that of motoman_driver (with MotoROS1), and client applications are recommended to implement a similar flow of control to keep track of the state of the robot before, during and after trajectory and motion execution.

The following provides a high-level overview of the behaviour a client application should implement to successfully interact with the [FollowJointTrajectory](doc/ros_api.md#follow_joint_trajectory) action server offered by MotoROS2.
While not all steps are absolutely necessary, checking for errors and monitoring execution progress facilitates achieving robust execution and minimises unexpected behaviour in both client and server.
Expand All @@ -632,7 +637,22 @@ As a final check, inspect the `error_code` field of the result to ascertain exec
1. if there are more trajectories to execute, return to step 2.
Otherwise call the [stop_traj_mode](doc/ros_api.md#stop_traj_mode) service to exit MotoROS2's trajectory execution mode

Interaction with the *point streaming* interface (MotoROS2 `0.0.15` and newer) would be similar, although no `FollowJointTrajectory` action client would be created, no goals would be submitted and monitoring robot status would be done purely by subscribing to the [robot_status](doc/ros_api.md#robot_status) topic (instead of relying on an action client to report trajectory execution status).
#### - [QueueTrajectoryPoint](doc/ros_api.md#queue_traj_point) point streaming

Interaction with the *point streaming* interface (MotoROS2 `0.0.15` and newer) would be similar to the process above, although no `FollowJointTrajectory` action client would be created, no goals would be submitted and monitoring robot status would be done purely by subscribing to the [robot_status](doc/ros_api.md#robot_status) topic (instead of relying on an action client to report trajectory execution status).

Rather than submitting a complete trajectory in a single goal, an indefinite number of points are submitted to the robot one at a time.
The execution of the robot will be identical to the behavior of `FollowJointTrajectory`.

#### - [StartRtMode](doc/ros_api.md#start_rt_mode) real-time incremental motion

This activates a separate UDP server which listens for real-time position increments.
It is intended to be used in a closed loop system which requires low level control of the motion.
This UDP server is activated using a ROS2 service.
However, the protocol for commanding/monitoring motion is *not* based on ROS2 communication.

This control mode minimizes overhead as much as possible by routing the user commands directly to the motion API.
See [R/T Motion Control](doc/rt_control.md) for information on the protocol implementation.

### With MoveIt

Expand Down Expand Up @@ -844,9 +864,7 @@ The following items are on the MotoROS2 roadmap, and are listed here in no parti
- native (ie: Agent-less) communication
- support asynchronous motion / partial goals
- complete ROS parameter server support (there is currently no support for `string`s in RCL)
- real-time position streaming interface (skipping MotoROS2's internal motion queue)
- Cartesian motion interfaces
- velocity control (based on `mpExRcsIncrementMove(..)`)
- integration with ROS logging (`rosout`)
- publishing static transforms to `tf_static`
- integrate a UI into the teach pendant / Smart Pendant
Expand Down
33 changes: 33 additions & 0 deletions config/motoros2_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,36 @@ publisher_qos:
# OPTIONS: USER_LAN1, USER_LAN2
# DEFAULT: (all available network ports)
#debug_broadcast_port: USER_LAN1

#-----------------------------------------------------------------------------
# For the real time motion interface, which port should the UDP messages
# be transmitted on?
#
# DEFAULT: 22000
#rt_udp_port_number: 22000

#-----------------------------------------------------------------------------
# Timeout for real time motion commands. If a command packet is not received
# within this number of milliseconds, the motion mode will be cancelled. This
# applies to all packets within R/T session.
#
# Additionally, if the client does not receive a reply packet within this
# amount of time, then it should be assumed that the session is dead.
#
# Setting this to '-1' will never timeout. In that case, you must explicitly
# call '/stop_traj_mode' to stop the motion mode.
#
# DEFAULT: 5000 (5.000 seconds)
#timeout_for_rt_msg: 5000

#-----------------------------------------------------------------------------
# When using the real time motion interface, each command packet must increment
# the sequence ID. If too many packets are lost during communication, then it
# will be assumed that the PC is not in sync with the robot.
#
# If the sequence ID of an incoming packet is different from the previous
# command by a value greater than this, then the connection will be dropped.
# The motion mode must be reactivated to be used again.
#
# DEFAULT: 3
#max_sequence_diff_for_rt_msg: 3
Binary file added doc/img/RtFlow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/img/RtFlow.vsdx
Binary file not shown.
19 changes: 17 additions & 2 deletions doc/ros_api.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--
SPDX-FileCopyrightText: 2023, Yaskawa America, Inc.
SPDX-FileCopyrightText: 2023, Delft University of Technology
SPDX-FileCopyrightText: 2023 - 2025, Yaskawa America, Inc.
SPDX-FileCopyrightText: 2023 - 2025, Delft University of Technology

SPDX-License-Identifier: CC-BY-SA-4.0
-->
Expand Down Expand Up @@ -133,6 +133,21 @@ Check the relevant fields of the `RobotStatus` messages to determine overall con

The `reset_error` service can be used to attempt to reset errors and alarms

### start_rt_mode

Type: [motoros2_interfaces/srv/StartRtMode](https://github.com/yaskawa-global/motoros2_interfaces/srv/StartRtMode.srv)
Copy link
Collaborator

Choose a reason for hiding this comment

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

If/when we merge the related PR, this should be replaced by a permalink.


Attempts to enable servo drives, activate the [real-time UDP server](rt_control.md), and set the job-cycle mode to allow execution of `INIT_ROS`.
This allows the user to send incremental motion to the robot at the rate returned by the service (`period`).

See [R/T Motion Control](rt_control.md) for information on the protocol implementation.

Note: this service may fail if controller state prevents it from transitioning to R/T mode.
Inspect the `result_code` field to determine the cause.
Check the relevant fields of the `RobotStatus` messages to determine overall controller status.

The `reset_error` service can be used to attempt to reset errors and alarms

### stop_traj_mode

Type: [std_srvs/srv/Trigger](https://github.com/ros2/common_interfaces/blob/37ebe90cbfa91bcdaf69d6ed39c08859c4c3bcd4/std_srvs/srv/Trigger.srv)
Expand Down
150 changes: 150 additions & 0 deletions doc/rt_control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<!--
SPDX-FileCopyrightText: 2025, Yaskawa America, Inc.
SPDX-FileCopyrightText: 2025, Delft University of Technology

SPDX-License-Identifier: CC-BY-SA-4.0
-->

# R/T Motion Control

The real-time motion control server is intended to be used in a closed loop system.
It allows the user to command incremental offsets at the rate of the robot controller's interpolation clock.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not a native speaker, but wouldn't offsets always be incremental? Or does this emphasise the fact it's relative to the previous state/increment?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, my intent was to emphasize that it's relative to the previous state

This control mode minimizes overhead as much as possible by routing the user commands directly to the MotoPlus motion API, mpExRcsIncrementMove.

## Activation

This control mode is activated using the [start_rt_mode](ros_api.md#start_rt_mode) service.
The user must specify the `control_mode` to indicate whether the increments will be joint offsets (radians) or cartesian TCP offsets (meters / radians).

If this service is successful, it will return a `result_code` of `Ready (1)`.
Otherwise, please examine the `result_code` and `message` files in the response for more information.

The service will also return a `period` in milliseconds.
This indicates the rate at which increment commands will be expected by the robot.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are expected or must be provided?

I'd expect the latter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's really an expectation.

You will get really bad performance if you don't maintain that rate. But there is no hard requirement to do it.

The default period for a single manipulator is 4 milliseconds.
However, that value will increase as additional axes or manipulators are added to the system.

## Usage

### Command Flow

Once activated, a UDP server will listen on port `22000` (default).
The user then sends the first increment with a the `sequenceId` field set to `0`.
After that, the user must wait until the robot replies before sending the next increment.
Each subsequent command must increment the `sequenceId`. Additionally, each subsequent command must not be sent until the robot replies to the previous command.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Saying that "each subsequent command must not be sent until the robot replies to the previous command" seems incompatible with the max_sequence_diff_for_rt_msg.

I think that we should consider removing max_sequence_diff_for_rt_msg, and just stopping/alarming if a packet arrives out of sequence. That would enforce the idea that each command cannot be sent until the robot replies to the previous one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I disagree. But I was struggling to articulate why. So here's Gemini :)

The max_sequence_diff_for_rt_msg parameter in motoros2_config.yaml is a real-world safeguard. The system uses UDP, which is a "fire-and-forget" protocol that does not guarantee packet delivery. This parameter acknowledges that packets can be lost. It provides a tolerance window, allowing the connection to survive minor network issues instead of dropping on the first lost packet. If the robot receives command N and the next one it sees is N+3, it assumes packets N+1 and N+2 were lost and continues, as long as the jump (3) is less than or equal to max_sequence_diff_for_rt_msg (default 10).

This suggestion would make the real-time motion mode extremely brittle and sensitive to network conditions. A single lost UDP packet between the client and the robot would cause the motion to stop and the session to be dropped. The user would then have to call /stop_traj_mode and /start_rt_mode to recover. This would be highly disruptive in any production environment that doesn't have a perfect, real-time network.

In conclusion, the current implementation is a practical compromise. The documentation describes how the client should behave, while the configuration parameter makes the server resilient enough to handle the inevitable reality of packet loss over UDP

Copy link
Collaborator

@gavanderhoorn gavanderhoorn Nov 24, 2025

Choose a reason for hiding this comment

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

I believe I would at least partly agree with @jimmy-mcelwain -- and I disagree with Gemini.

Real-time command interfaces are supposed to be deterministic; allowing out-of-order packets, or dropped packets breaks that determinism, or at least weakens it.

re: "sensitive to network conditions": we're essentially using UDP here as a stand-in for a deterministic network protocol, but without changing that transport layer or any of the network layers (fi ethernet), which is non-deterministic itself by default (collision handling fi). To make it work well enough, we can reduce the chances of non-deterministic behaviour occurring by essentially making a two-host network segment where both take turns to use the medium. That means: direct cable, no switches, and only RT traffic on the connection.

Allowing for lost datagrams improves (developer) UX, but if too many datagrams go missing, control performance suffers.

I would argue users interested in using a real-time external motion interface care about control performance and are likely willing go through the hassle of setting up sufficiently performant network infrastructure.

The documentation describes how the client should behave, while the configuration parameter makes the server resilient enough to handle the inevitable reality of packet loss over UDP

it's never made explicit, but I believe this assumes a shared connection. Otherwise there is very little chance of packet loss -- unless there is some network failure besides the RT code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

To make it work well enough, we can reduce the chances of non-deterministic behaviour occurring by essentially making a two-host network segment where both take turns to use the medium. That means: direct cable, no switches, and only RT traffic on the connection.

I agree with you in principle. But I don't think this is realistic in a true industrial environment. I have seen a lot of "inadvisable" (stupid) configurations.

I'm fine with lowering max_sequence_diff_for_rt_msg. But I don't think it can be realistically eliminated.

This will occur at the rate of the `period` from the [start_rt_mode](ros_api.md#start_rt_mode) service.

If a command is not received with 30 seconds (default), then the session times out and is dropped.
At that point, the server must be reactivated by calling `stop_traj_mode` and `start_rt_mode`.
A "keep-alive" can be used by sending a command with zero increments.

Additionally, if the client does not receive a reply packet within this amount of time, then it should be assumed that the session is dead.

<img src="img/RtFlow.png" alt="Command Flow" />

### Data format (command)
Copy link
Collaborator

@gavanderhoorn gavanderhoorn Nov 24, 2025

Choose a reason for hiding this comment

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

Commenting here, as it's about both the command as well as the reply.

This is not an exhaustive list, but I would recommend to add:

  • a proper header structure, containing:
    • a version field: clients should implement version checks and refuse to work with servers of incompatible versions (and vice-versa). This should make it at least a little 'safer' to introduce breaking changes on the server side

    • a packet_type field: it's likely this interface will be extended in the future, which would then require some way of identifying which packets are being exchanged. Even if it absolutely won't be extended, this field could function as part of the 'magic' of the packet, making it harder to interpret 'random' data as valid motion commands / status data and to sync on incoming streams

    • a timestamp field: we could perhaps also rely on the determinism of the controller and assert that sequenceId * period == timestamp, but that would:

      • require more math on the client side, and
      • makes it harder to deal with overflow (which the server implementation already does, but still requires work on the client), and
      • makes the client responsible for matching the (calculated) stamp with its local clock (fi when correlating sensor data to robot motion). We already have a stamp on the MotoROS2 side, which when using NTP / micro-ROS synchronisation facilitates correlating MotoROS2 produced data with other sources. This would serve a similar goal

      we can still use the stamp as a sequence nr of course (in the end it's just a 'unique' numerical ID anyway), so clients should return the 'stamp' sent to them in the last state packet as part of their command packet.

Optional, but recommended:

  • add some reserved fields to allow for changes to packet definitions without breaking (de)serialisation of old traffic and/or breaking older implementations

For the state packet specifically:

  • a way to encode the full robot status (perhaps as a mirror of RobotStatus), perhaps as a bitmask. This would also include the FSU state (or perhaps FSU state deserves its own field, also a bitmask). The goal is for this protocol to be usable without ROS as well, so it can't delegate broadcasting robot/controller state to RobotStatus messages, as those might not be available in all usage scenarios (and even if they are available, they would not be (easily) synced with RT state traffic, nor would consuming them on the client be necessarily RT safe)
  • a field encoding the currently active command mode (see also below)

For the command packet:

  • a field indicating which control mode the client currently expects the server to have active: this serves both as a sanity check (client sending joint data while server expects Cartesian offsets), as well as an explicit indication as to in which way delta values are supposed to be interpreted (instead of implicitly relying on a piece of internal state being correctly maintained by MotoROS2).

For both (motion related packets) again:

  • it would be good to add a field stating whether absolute or relative mode is being used: right now this interface only supports offsets (ie: relative commands, or velocity), but it would not be too difficult to have the server also support an absolute command mode (ie: position). That would require changes to how offsets are interpreted & processed. By having clients specify which mode they are using (and at this point only relative mode would be supported, so anything else would result in errors), we prepare for when this changes and could do so without breaking anyone's client implementation.

My main motivation for these suggestions would be to make it possible to interpret data in command and state packets completely independently of the state of MotoROS2, another server and/or a client implementation. That makes both implementing MotoROS2, that other (hypothetical) server and clients easier to implement, as well as things like protocol dissectors and (de)serialisation libraries.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@gavanderhoorn What do you think about this?

struct RtPacket
{
    UINT16 version;
    UINT16 packet_type;	//see enum xxxx

    UINT32 sequenceId;
	
    double delta[MAX_GROUPS][MAX_AXES]; //[8][8]
    int toolIndex[MAX_GROUPS]; //[8]
	
    char reserved[32];	//for future expansion
}
struct RtReply
{
    UINT32 sequenceEcho;
    
    double feedbackPositionJoints[MAX_GROUPS][MAX_JOINTS]; //[8][8]
    double feedbackPositionCartesian[MAX_GROUPS][MAX_JOINTS]; //[8][8]

    double previousCommandPositionJoints[MAX_GROUPS][MAX_AXES]; //[8][8]
    double previousCommandPositionCartesian[MAX_GROUPS][MAX_AXES]; //[8][8]

    RobotStatus status; //literal clone of the /robot_status structure

    bool fsuInterferenceDetected;
}

a timestamp field

Personally, I don't see why that's necessary. Since this is meant to be used in real time, why do I care about the stamp? All I really care about is the sequence identifier. I'm expecting that the user has done any time parameterization.

For the state packet specifically:
a way to encode the full robot status (perhaps as a mirror of RobotStatus), perhaps as a bitmask. This would also include the FSU state (or perhaps FSU state deserves its own field, also a bitmask)

I've already got the fsuInterferenceDetected field. I'm not sure what additional information we can include. I guess it's possible to indicate how much interference was detected...

For the command packet:
a field indicating which control mode the client currently expects the server to have active...
...
For both:
it would be good to add a field stating whether absolute or relative mode is being used

These could be part of the packet_type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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


The command packet is a *packed* `RtPacket` structure.

```c
//##########################################################################
// !All data is little-endian!
//##########################################################################
struct RtPacket
Copy link
Collaborator

Choose a reason for hiding this comment

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

should this perhaps also include a bitmask/integer array indicating to which groups increments are being commanded?

That would make it an even thinner wrapper around mpExRcsIncrementMove(..) while making it possible to not have to send 0 increments for groups that should not move.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't want to give the impression that a user can send one packet to control R1 and another packet to control R2. The current implementation makes it very clear that the whole system must be controlled by a single command packet.

{
int version;

UINT32 sequenceId;
double delta[MAX_GROUPS][MAX_AXES]; //[8][8]
int toolIndex[MAX_GROUPS]; //[8]
}
```

The `version` must match the version number expected by the server.
If it does not match the expected value, the packet will be rejected and the connection will be dropped.
The current version is `1`.

This contains a sequence ID, the increments for each control group, and the tool number to use for each control group.

#### Joints

When the `control_mode` is `JOINT_ANGLES (1)`, the order of the joints in the `delta` array must be in the order of `S L U R B T E 8`.
Please note that for seven axis robots, the `E` joint is phyically mounted in the middle of the arm.
But it must be sent at the end of the joint array.

See `JointIndices` enum.

```c
enum JointIndices
{
Joint_S = 0, //radians
Joint_L,
Joint_U,
Joint_R,
Joint_B,
Joint_T,
Joint_E,
Joint_8,

MAX_JOINTS
}
```

#### Cartesian

When the `control_mode` is `CARTESIAN (2)`, the order of the joints in the `delta` array must be in order of `X Y Z Rx Ry Rz Re 8`.

See `CartesianIndices` enum.

```c
enum CartesianIndices
{
TCP_X = 0, //meters
TCP_Y,
TCP_Z,

TCP_Rx, //radians
TCP_Ry,
TCP_Rz,
TCP_Re,

TCP_8, //pulse

MAX_AXES
}
```

Please note that rotations are applied in the order of `Z Y X`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just thinking out loud: what about supporting quaternion input? Would remove the burden of figuring out the correct rotation order from clients.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

My brain doesn't like quaternions. My first instinct is, "no".

But... quats would be consistent with our /tf publisher. So, "maybe"

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ehhh... I really prefer yaw, pitch, roll. Since the idea is to be a minimal wrapper around the motion API, I think the data should be as close as possible to the 'final form'.

@jimmy-mcelwain Do you have an opinion?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I just remembered that jimmy is out all week.

Copy link
Collaborator

@jimmy-mcelwain jimmy-mcelwain Dec 22, 2025

Choose a reason for hiding this comment

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

Since the idea is to be a minimal wrapper around the motion API, I think the data should be as close as possible to the 'final form'.

I think that I would agree with this, but does the statement:

Please note that rotations are applied in the order of Z Y X.

Make sense in this context?

If we are talking about pose, yes it is important that we are saying that the rotations are applied in the order of Z Y X. But as a truly "instantaneous" angular velocity, I do not think that it makes sense. Now I don't know how mpExRcsIncrementMove works in the back end. I don't know if it really is calculating a rotation about Z then Y then X for a given increment and then moving there, or if it is figuring out a path that gives it the angular velocity of the Rx Ry Rx divided by the time increment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So... it turns out that position is reported by the robot using Z Y X, as expected. But, when commanding incremental motion, the rotations are applied in order X Y Z.

So a quaternion may make this easier on the user. What do you guys think? We can hide the rotational order in the backend.


Additionally, I found that rotations get complicated when commanding such small rapid increments.

increment (0,0,0, 90, 90, 0) is not the same as doing this:

for 90 times
    increment (0,0,0, 1, 1, 0)

So I think that becomes hard for a user to calculate rotations.


### Data format (reply)

The command packet is a *packed* `RtReply` structure.
This will echo the sequence ID, provide feedback position, and provide commanded position.

Additionally, there is a flag to indicate if the Functional Safety Unit (FSU) reduced the speed of the **previous** command cycle.
This indicates that the robot did not complete the full increment as commanded.

```c
//##########################################################################
// !All data is little-endian!
//##########################################################################
struct RtReply
{
UINT32 sequenceEcho;

double feedbackPositionJoints[MAX_GROUPS][MAX_JOINTS]; //[8][8]
double feedbackPositionCartesian[MAX_GROUPS][MAX_JOINTS]; //[8][8]

double previousCommandPositionJoints[MAX_GROUPS][MAX_AXES]; //[8][8]
double previousCommandPositionCartesian[MAX_GROUPS][MAX_AXES]; //[8][8]

bool fsuInterferenceDetected;
}
```

## Deactivation

Other motion modes may not be used at the same time as the real-time motion control server.
The service to start those modes will fail when invoked.
By calling `stop_traj_mode`, the R/T server will be disposed.
At that time, another motion mode may be used.
6 changes: 3 additions & 3 deletions doc/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@

After correcting the configuration, the [changes will need to be propagated to the Yaskawa controller](../README.md#updating-the-configuration).

### Alarm: 8011[23 - 54]
### Alarm: 8011[23 - 54] or [56 - 58] or [66 - 67]

Check failure on line 585 in doc/troubleshooting.md

View workflow job for this annotation

GitHub Actions / alarm_lint

failed to parse 'Alarm: 8011[23 - 54] or [56 - 58] or [66 - 67]'

*Example:*

Expand Down Expand Up @@ -611,7 +611,7 @@
[xx]
```

Where `[xx]` is a subcode in the ranges `[23 - 54]` or `[56 - 58]`.
Where `[xx]` is a subcode in the ranges `[23 - 54]` or `[56 - 58]` or `[66 - 67]`.

*Solution:*
These alarms are often caused by version incompatibilities between ROS 2 (on the client PC), micro-ROS (as part of MotoROS2) and/or the micro-ROS Agent.
Expand Down Expand Up @@ -662,7 +662,7 @@

### Alarm: 8011[56 - 58]

Please refer to [Alarm: 8011[23 - 54]](#alarm-801123---54).
Please refer to [Alarm: 8011[23 - 54]](#alarm-801123---54-or-56---58-or-66---67).

### Alarm: 8011[59]

Expand Down
2 changes: 1 addition & 1 deletion src/ActionServer_FJT.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ void Ros_ActionServer_FJT_ResetProgressTracker()
//TODO: do multidof too
}

//Called from TrajectoryMotionControl::Ros_MotionControl_IncMoveLoopStart
//Called from TrajectoryMotionControl::Ros_MotionControl_NonRtIncMoveLoopStart
void Ros_ActionServer_FJT_UpdateProgressTracker(MP_EXPOS_DATA* incrementData)
{
if (fjt_active_goal_handle == NULL)
Expand Down
5 changes: 5 additions & 0 deletions src/CommunicationExecutor.c
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,11 @@ void Ros_Communication_StartExecutors(SEM_ID semCommunicationExecutorStatus)
g_messages_QueueTrajPoint.response, Ros_ServiceQueueTrajPoint_Trigger);
motoRos_RCLAssertOK_withMsg(rc, SUBCODE_FAIL_ADD_SERVICE_QUEUE_POINT, "Failed adding service (%d)", (int)rc);

rc = rclc_executor_add_service(
&executor_motion_control, &g_serviceStartRtMode, &g_messages_StartRtMode.request,
&g_messages_StartRtMode.response, Ros_ServiceStartRtMode_Trigger);
motoRos_RCLAssertOK_withMsg(rc, SUBCODE_FAIL_ADD_SERVICE_START_RT_MODE, "Failed adding service (%d)", (int)rc);

rc = rclc_executor_add_service(
&executor_motion_control, &g_serviceSelectMotionTool, &g_messages_SelectMotionTool.request,
&g_messages_SelectMotionTool.response, Ros_ServiceSelectMotionTool_Trigger);
Expand Down
3 changes: 2 additions & 1 deletion src/CommunicationExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
// service reset 1
// service start_traj_mode 1
// service start_point_queue_mode 1
// service start_rt_mode 1
// service stop_traj_mode 1
// service queue_traj_point 1
// service select_tool 1
#define QUANTITY_OF_HANDLES_FOR_MOTION_EXECUTOR (9)
#define QUANTITY_OF_HANDLES_FOR_MOTION_EXECUTOR (10)

// total number of handles =
// timers + 1
Expand Down
Loading