Removes slow robotStateMsgToRobotState call from pilz#3589
Removes slow robotStateMsgToRobotState call from pilz#3589riv-tnoble wants to merge 2 commits intomoveit:masterfrom
Conversation
|
For reference, we are already using #3518, so this PR is the second round of profiling and optimisation compared to master. |
|
Unsure why the pipelines are failing. Looks like networking issues with apt? |
rhaschke
left a comment
There was a problem hiding this comment.
I'm afraid your approach is not viable: The request's start_state not only comprises initial joint values, but also attached collision objects.
| const auto& names = jm->getVariableNames(); | ||
| for (std::size_t i = 0, j = jm->getFirstVariableIndex(); i < jm->getVariableCount(); ++i, ++j) | ||
| start_joint_position[names[i]] = positions[j]; | ||
| start_joint_position[names[i]] = req.start_state.joint_state.position[j]; |
There was a problem hiding this comment.
This only considers joint_state and ignores multi_dof_joint_state. Moreover, I think the order of joints is mangled if there are multi-dof joints.
There was a problem hiding this comment.
Does JointModel::getVariableNames() handle multi-dof joints? I may have (mistakenly) assumed that the code was designed to work with single-dof joints only, and that the extra check in the above if was redundant.
There was a problem hiding this comment.
Yes. getVariableNames() will return a vector with multiple elements for multi-dof joints.
| start_state.update(); | ||
| start_scene = std::move(ps); | ||
|
|
||
| start_scene = std::move(scene->diff()); |
There was a problem hiding this comment.
std::move can be dropped here. This is only relevant for passing arguments, isn't it?
There was a problem hiding this comment.
Potentially, yes. I decided to leave it as it was already there, but happy to remove if not necessary.
I agree that this specific method ignores attached collision objects, however it seems the previous code was doing that anyway? The |
|
As an addition to the above point, I tried planning a contact motion with a collision object, and the output appears to be what I'd expect: For reference, "components" is the part in green, "ee23" is the tool in red, and the thin tip at the end of the tool (also red, just touching the surface in white) is a separate collision object with contacts disabled with the "component", therefore the collision is due to the bulk of the tool colliding with the back half of the "component" |
|
I'll do some more checks with collisions in the meantime. We do some postprocessing after we get output from MoveIt, so I want to make sure the collisions are being detected before then (though I don't think we do any collision checks ourselves). Would you mind re-running the pipelines? Hopefully if this is an issue, it'll be caught by a test which checks for collisions. |
Previously, the start_state was including attached collision objects from the request's start_state. This was important (and only recently changed) to consider these collision objects during planning. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #3589 +/- ##
==========================================
- Coverage 47.15% 46.31% -0.83%
==========================================
Files 603 603
Lines 58999 59104 +105
Branches 6969 6998 +29
==========================================
- Hits 27814 27368 -446
- Misses 30773 31320 +547
- Partials 412 416 +4 ☔ View full report in Codecov by Sentry. |
|
I re-triggered CI. The current failures are due to real issues. |
I was more meaning that the I did notice that As an aside, we're side-stepping using Pilz's plan sequence service due to error propagation documented here #3569, so we're basically doing iteration over the poses ourselves, and calling Perhaps a better solution would be to fix #3569, and ensure that Pilz's plan sequence service only performs the conversion of the expensive parts of the I'd still like to know that this full conversion of the |
Thanks for doing that. I'll wait for a reply to my previous comment to decide whether this code should be fixed, or whether the proposed alternative is a better idea. |
moveit#3569 describes propagation of error from updating collision objects with each update of the start state during a sequence motion plan. moveit#3589 attempts to speed up plans by updating only joints positions when updating the start state, though this may cause the RobotState to ignore collision objects entirely if the state being updated has no knowledge of them. This PR attempts to fix both issues by only updating the joints of a sequence motion, without copying across attached collision objects, in the hope that the start state being updated is already populated with attached collision object information.
Yes, the planning scene's state was updated through this method. |
Thanks, that helps me understand the impact of the change, though I'm still a little unsure whether this poses an issue. My understanding is that the Requiring the planner to update the scene's state with objects would seem to suggest that the objects have changed without the scene knowing beforehand, or that they are expected to change mid-plan. The latter seems unlikely to me, and the former (i.e. ensuring that objects are up-to-date at the start of a plan) feels like a responsibility that should lie with the code calling the planner, rather than part of the planner itself. I suppose my confusion boils down to the following question: would it be a valid use case to take a scene with no attached objects, and attempt to create a plan with objects that are not already known to the scene? |
This is correct. However, you can pass a different start state for planning via the planning request (planning doesn't need to use the current state). This start state should be represented as a state diff, which only requires to specify diffs w.r.t. the current scene state. |
Ah, yeah, that makes sense and thanks for the clarification. Would you mind taking a look at #3590? I think that might be a more suitable solution. We're only calling Pilz to plan individual motions successively to get around issues we were seeing with the built-in sequence planning functionality. As mentioned there, the idea would be to do a full copy of the start state once before the sequence, then only for joints as each waypoint is planned. |
Ah, I think this is where our problem lies. We're currently passing the whole state in, not a diff. Thank you for your help. |
|
I've tried rewriting our code to simply pass a RobotState msg with updated joints and I thought maybe the planning scene was incorrect and didn't have this info, but oddly (after adding some print statements to the function changed in this PR, and reverting it back to its original form) it seems that the attached bodies are present, but get wiped after the robot state diff is applied: |
|
I think I've found the issue. We set the start state using MoveGroupInterface::setStartState() with a moveit_msgs::RobotState with We then call MoveGroupInterface::plan(), which calls constructGoal(), which in turn calls constructMotionPlanRequest(). constructMotionPlanRequest turns the start state back into a message, but only sets So essentially, we're setting the start state as a diff, but the flag gets flipped before being passed to pilz, which then interprets the lack of attached collision objects in the message as a sign that it should clear them all from the state, rather than treating it as a diff. This is verified by adding some extra prints: |
|
Specifically, this line explicitly sets I'd advocate for the opposite for a few reasons:
I can add a test along the lines of... ...and rework the logic of these methods to implement this functionality, however, I'm unsure whether changing the outputs of these functions will have downstream implications. That being said, I think only code that falls under point 1 will be affected, and this seems to be a case where the output is invalid anyway (it contains the contents of a diff, but is not considered so by the flag) |
|
The |
Thanks for the guidance. Currently working on modifying the code and I'll raise a separate PR.
With this in mind, my understanding would be:
Even so, the logs seem to show that the final message does not contain a full state. It contains the contents of the original diff, simply with the diff flag set to false. This seems like a bug to me? |
|
If your message doesn't comprise the full state, you probably passed an empty RobotState, with no attached collision objects defined yet. The existing objects are only cleared when the |
|
Closing in favour of #3592 |

Description
I've been spending some time profiling our motion planning pipeline, which has become quite slow recently.
Using a collision object we use to generate raster paths (~66k faces), and a path of ~400 cartestian points, we were achieving planning times of ~30s.
I did some profiling of our own code, and found that around half the time was spent converting from
moveit_msgs::RobotStatetomoveit::core::RobotStateviamovit::core::robotStateMsgToRobotState()for every point in our path. I suspect the main bottleneck was converting all of the attachment bodies (and their meshes). Rewriting the code to perform this conversion once at the start of path generation has reduced the time to ~30s, which appears to be spent mainly on the MoveIt side.Of this remaining 30s, 23s are spent within pilz's
TrajectoryGenerator::generate()in theMotionPlanInfoconstructor, which, again, spends the majority of its time callingmoveit::core::robotStateMsgToRobotState().Time analysis of
TrajectoryGenerator::generate()(Data & Pandas Summary)TrajectoryGenerator__generate.csv

Time analysis of
MotionPlanInfo::MotionPlanInfo()(Data & Pandas Summary)MotionPlanInfo__MotionPlanInfo.csv

In my understanding, the purpose of the constructor is to set the
start_sceneandstart_joint_positionfields of this class. I've rewritten the implementation to take these values from theMotionPlanRequestparameter, rather than converting. Doing so has reduced the time spent inTrajectoryGenerator::generate()from ~23s to ~0.5s.New time analysis of
TrajectoryGenerator::generate()(Data & Pandas Summary)TrajectoryGenerator::generate.csv

Since only the
start_scene, which is const, andstart_joint_positionare set in this constructor, the resultingMotionPlanInfoshould be unaffected by the changes.My only concern is whether the (now removed) call to
moveit::core::RobotState::updatemay be having side effects beyond the scope of the method.I'd appreciate any thoughts and suggestions regarding this change,
Tom
Checklist