Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 233-outside goal tolerance #241

Merged

Conversation

yai-rosejo
Copy link
Collaborator

This PR is to solve multiple issues dealing with goal tolerances referenced in #233

  1. Received Goal tolerances can now be received out of order, the name of the tolerance is used to match it with the expected joint.
  2. The final goal position is used instead of the desired position to deal with a small out of sync issue.
  3. The special notations of -1.0, to ignore tolerance, and 0.0, to use default values, is now supported.

This PR does not fix the issues with the Ros_Debug_BroadcastMsg function and doubles.

Testing:
tolerance_issue_04162024.zip

  1. Upload the mr2_yrc1_h.out to the controller and then restart the controller
  2. Run the microros client in a terminal :
    sudo docker run -it --net=host -v /dev:/dev --privileged microros/micro-ros-agent:humble udp4 -p 8888
  3. Start the TrajMode:
    ros2 service call /start_traj_mode motoros2_interfaces/srv/StartTrajMode
  4. Run the fjt client:
    python3 fjt_client.py
  5. Optionally the debug_listener can also be ran to capture messages.

src/MotionControl.h Outdated Show resolved Hide resolved
@gavanderhoorn
Copy link
Collaborator

gavanderhoorn commented Apr 18, 2024

@yai-rosejo: I've used your PR as a basis and restructured it a little. See this for the diff. All WIP right now.

It's basically the same as your changes, but split into a couple of functions. My idea is we'd be able to write some tests for the new functionality this way.

I've tested it on my YRC with GP25 and it (still) works.

High-level question: did you intentionally iterate over the maxAxes? Would limiting it to just g_Ros_Controller.totalAxesCount (or feedback_FollowJointTrajectory.feedback.joint_names.size) not work as well?

@yai-rosejo
Copy link
Collaborator Author

@yai-rosejo: I've used your PR as a basis and restructured it a little. See this for the diff. All WIP right now.

It's basically the same as your changes, but split into a couple of functions. My idea is we'd be able to write some tests for the new functionality this way.

I've tested it on my YRC with GP25 and it (still) works.

High-level question: did you intentionally iterate over the maxAxes? Would limiting it to just g_Ros_Controller.totalAxesCount (or feedback_FollowJointTrajectory.feedback.joint_names.size) not work as well?

I just did a cursory look through your solution; I think it looks much better than my current one.
I iterated over all of the maxAxes due to violators being the size of maxAxes and the 'for' loop checking the differences already using maxAxes.

double violators[MAX_CONTROLLABLE_GROUPS * MP_GRP_AXES_NUM];
bzero(violators, sizeof(violators));
for (int axis = 0; axis < maxAxes; axis += 1)
{
diff = fabs(feedback_FollowJointTrajectory.feedback.desired.positions.data[axis] - feedback_FollowJointTrajectory.feedback.actual.positions.data[axis]);

@gavanderhoorn
Copy link
Collaborator

Ok. Thanks.

I'll add some tests, and then push some commits to this PR if that would be OK with you.

@yai-rosejo
Copy link
Collaborator Author

Ok. Thanks.

I'll add some tests, and then push some commits to this PR if that would be OK with you.

That works for me!

@gavanderhoorn
Copy link
Collaborator

gavanderhoorn commented Apr 19, 2024

Added some first batch of tests for Ros_ActionServer_FJT_Parse_GoalPosTolerances(..):

Click to expand
===
Performing unit tests
~~~
Ros_ActionServer_FJT_Parse_GoalPosTolerances: one or more inputs NULL, abort
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_null_args: goal_joint_tolerances NULL: PASS
Ros_ActionServer_FJT_Parse_GoalPosTolerances: one or more inputs NULL, abort
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_null_args: joint_names NULL: PASS
Ros_ActionServer_FJT_Parse_GoalPosTolerances: one or more inputs NULL, abort
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_null_args: posTolerances NULL: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_null_args: PASS
~~~
Ros_ActionServer_FJT_Parse_GoalPosTolerances: no joint tolerances specified, returning 6 defaults
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_empty_jtolerances: call: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_empty_jtolerances: all defaults: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_empty_jtolerances: PASS
~~~
Ros_ActionServer_FJT_Parse_GoalPosTolerances: pos tolerance output array too small: 1 < 2 (nr of joints)
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_out_array_too_small: out array too small: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_out_array_too_small: PASS
~~~
Ros_ActionServer_FJT_Parse_GoalPosTolerances: found position tolerance: joint5, 1.000000
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_more_jtol_than_jnames: call: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_more_jtol_than_jnames: all defaults: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_more_jtol_than_jnames: PASS
~~~
Ros_ActionServer_FJT_Parse_GoalPosTolerances: found position tolerance: joint0, 1.000000
Ros_ActionServer_FJT_Parse_GoalPosTolerances: using position tolerance: joint0
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_jtol_in_slot0: call: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_jtol_in_slot0: expected j0 pos tol: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_jtol_in_slot0: PASS
~~~
Ros_ActionServer_FJT_Parse_GoalPosTolerances: found position tolerance: joint0, 1.000000
Ros_ActionServer_FJT_Parse_GoalPosTolerances: using position tolerance: joint0
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_jtol_in_slot2: call: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_jtol_in_slot2: j0 pos tol at [2]: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_jtol_in_slot2: PASS
~~~
Ros_ActionServer_FJT_Parse_GoalPosTolerances: found position tolerance: joint1, 0.123000
Ros_ActionServer_FJT_Parse_GoalPosTolerances: using position tolerance: joint1
Ros_ActionServer_FJT_Parse_GoalPosTolerances: found position tolerance: joint2, 0.234000
Ros_ActionServer_FJT_Parse_GoalPosTolerances: using position tolerance: joint2
Ros_ActionServer_FJT_Parse_GoalPosTolerances: found position tolerance: joint1, 0.345000
Ros_ActionServer_FJT_Parse_GoalPosTolerances: using position tolerance: joint1
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_last_setting_wins: call: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_last_setting_wins: j0 pos tol at [0]: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_last_setting_wins: j2 pos tol at [1]: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_last_setting_wins: j1 pos tol at [2]: PASS
Testing Ros_Testing_Ros_ActionServer_FJT_Parse_GoalPosTolerances_last_setting_wins: PASS
~~~
Testing SUCCESSFUL
===

@gavanderhoorn
Copy link
Collaborator

@yai-rosejo: I've added tests for the two new functions I introduced, refactored things a bit and cleaned up the Git history. See the diff.

Your changes are now all squashed into the initial commit.

Could you take a look whether this would work for you? If you agree, I'll force-push these changes to your branch to update this PR.

@gavanderhoorn
Copy link
Collaborator

@yai-rosejo wrote:

@gavanderhoorn wrote:

High-level question: did you intentionally iterate over the maxAxes? Would limiting it to just g_Ros_Controller.totalAxesCount (or feedback_FollowJointTrajectory.feedback.joint_names.size) not work as well?

[..] I iterated over all of the maxAxes due to violators being the size of maxAxes and the 'for' loop checking the differences already using maxAxes.

double violators[MAX_CONTROLLABLE_GROUPS * MP_GRP_AXES_NUM];
bzero(violators, sizeof(violators));
for (int axis = 0; axis < maxAxes; axis += 1)
{
diff = fabs(feedback_FollowJointTrajectory.feedback.desired.positions.data[axis] - feedback_FollowJointTrajectory.feedback.actual.positions.data[axis]);

@ted-miller: would it be absolutely necessary to iterate over maxAxes, or would limiting those arrays and loops to just the configured nr of axes also work? I assume the latter, but wanted to make sure.

@ted-miller
Copy link
Collaborator

would it be absolutely necessary to iterate over maxAxes, or would limiting those arrays and loops to just the configured nr of axes also work? I assume the latter, but wanted to make sure.

Limiting the loops would be OK. I can't think of any justification for keeps maxaxes.

@yai-rosejo
Copy link
Collaborator Author

@yai-rosejo: I've added tests for the two new functions I introduced, refactored things a bit and cleaned up the Git history. See the diff.

Your changes are now all squashed into the initial commit.

Could you take a look whether this would work for you? If you agree, I'll force-push these changes to your branch to update this PR.

The changes look good to me. You can go ahead with the force push.

@gavanderhoorn
Copy link
Collaborator

Working on this a bit more and I'm beginning to wonder whether we shouldn't already parse the JointTolerance instances when first accepting the goal. If parsing fails -- due to unknown joints being referenced for instance -- we could refuse to execute the goal and provide a very clear error code and message to clients.

If we fail to parse at the end of an executed trajectory, we can't really do much anymore: failing the goal completely seems like a bit of a 'waste' of a good trajectory execution and failing so late for something almost 'unrelated' doesn't apply fail-early and least-surprise principles.

@ted-miller
Copy link
Collaborator

So you're suggesting separating the logic of validating the tolerance input from validating the position is within that tolerance? That makes sense to me.

We've got the Ros_MotionControl_InitTrajectory function which returns a custom Init_Trajectory_Status that could clearly identify an issue with the specified input.

My next question is how much of the goal message do we validate? So far, we've been ignoring much of the goal message, such as path_tolerance and the component_x_tolerance. Do those remain unused?

@gavanderhoorn
Copy link
Collaborator

So you're suggesting separating the logic of validating the tolerance input from validating the position is within that tolerance? That makes sense to me.

Indeed.

We've got the Ros_MotionControl_InitTrajectory function which returns a custom Init_Trajectory_Status that could clearly identify an issue with the specified input.

True, although the goal_tolerance field is not part of the trajectory.

My next question is how much of the goal message do we validate? So far, we've been ignoring much of the goal message, such as path_tolerance and the component_x_tolerance. Do those remain unused?

I would for now indeed suggest to fix & refactor the current implementation which only takes the goal_tolerance field into account, not extend it.

@gavanderhoorn
Copy link
Collaborator

Coming back to this: unknown joints in goal_tolerance will not immediately cause problems, they will simply be ignored by the current implementation.

Adding goal validation -- which would include goal_tolerance validation -- would be nice, but wouldn't be needed to resolve the current issue (ie: #233).

I'll create an issue to log the enhancement request.

@yai-rosejo: I've pushed some more commits to my version of your branch. If you could PTAL and let me know whether you'd still be ok with me force-pushing here? Thanks.

@yai-rosejo
Copy link
Collaborator Author

Coming back to this: unknown joints in goal_tolerance will not immediately cause problems, they will simply be ignored by the current implementation.

Adding goal validation -- which would include goal_tolerance validation -- would be nice, but wouldn't be needed to resolve the current issue (ie: #233).

I'll create an issue to log the enhancement request.

@yai-rosejo: I've pushed some more commits to my version of your branch. If you could PTAL and let me know whether you'd still be ok with me force-pushing here? Thanks.

I have taken a look and agree with pushing those onto this branch. This PR is focused on supporting goal functionalities, so I also agree that the goal validation should be a separate task.

@gavanderhoorn gavanderhoorn force-pushed the issue_233_outside_goal_tolerance branch from 638063b to 64c6524 Compare May 8, 2024 08:25
yai-rosejo and others added 7 commits May 14, 2024 16:46
Don't assume all joints have associated tolerances, and deal with
potentially out-of-order tolerance specifications (compared to order of
joints MotoROS2 uses internally).
Easier to test and potentially to reuse.
Not just when there are no JointTolerance instances.
Note: they are appended to the main ActionServer_FJT source file as the function(s) tested are local to that compilation unit (ie: static).
They're not needed outside this compilation unit.
Need space for at least the number of joint positions in the trajectory point.
Instead of the maximum possible number always.
Work-around doesn't seem needed anymore.
A debug log warning only right now. We could consider making it a fatal error and actually return it.
This is a temporary implementation, as it would be better to check the goal_tolerance field can be parsed when validating a new goal.
@gavanderhoorn gavanderhoorn force-pushed the issue_233_outside_goal_tolerance branch from 64c6524 to c552d58 Compare May 14, 2024 14:51
@gavanderhoorn
Copy link
Collaborator

@yai-rosejo and/or @ted-miller: could I perhaps ask you to test this implementation on your hw?

@ted-miller
Copy link
Collaborator

Definitely. I can't do it today (and probably not tomorrow), but I'll get it done. I realize this PR has lingered for quite a while now.

}

//it's OK if we are passed more JointTolerance instances than we have joints,
//as technically a client could send duplicate tolerance specs (ie: multiple
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this common practice? It seems to me that this is a bad idea. If I accidently duplicate a joint, then I would be very confused when my intended tolerance isn't applied.

Copy link
Collaborator

Choose a reason for hiding this comment

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

not common practice per se. I just didn't feel like making it an issue.

We could upgrade it to one though, and return a specific error code if duplicated joint names are passed as part of the goal.

src/ActionServer_FJT.c Outdated Show resolved Hide resolved
//couldn't find joint
if (ptol_idx == joint_names->size)
{
//TODO(gavanderhoorn): make this a fatal error?
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume (hope) that you mean fatal to the trajectory request (return not OK) and not a fatal assertion.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, even more 'local' perhaps: fatal as in: exit this function with an appropriate return code.

src/ActionServer_FJT.c Outdated Show resolved Hide resolved
@ted-miller
Copy link
Collaborator

Functionally, everything seems to be working well on hardware.

Copy link
Collaborator

@ted-miller ted-miller left a comment

Choose a reason for hiding this comment

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

I'm fine with merging this.

@ted-miller ted-miller merged commit cc03e31 into Yaskawa-Global:main Jun 11, 2024
6 checks passed
@gavanderhoorn gavanderhoorn added this to the 0.1.3 milestone Jul 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants