-
Notifications
You must be signed in to change notification settings - Fork 514
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
WIP: WMD 2021 Cartesian Planning #377
Conversation
Thanks for taking this on, @bostoncleek! |
Codecov Report
@@ Coverage Diff @@
## main #377 +/- ##
==========================================
- Coverage 53.36% 52.45% -0.91%
==========================================
Files 210 213 +3
Lines 18814 19136 +322
==========================================
- Hits 10038 10035 -3
- Misses 8776 9101 +325
Continue to review full report at Codecov.
|
moveit_planners/ompl/ompl_interface/include/moveit/ompl_interface/detail/ompl_constraints.h
Outdated
Show resolved
Hide resolved
moveit_planners/ompl/ompl_interface/src/detail/ompl_constraints.cpp
Outdated
Show resolved
Hide resolved
moveit_planners/ompl/ompl_interface/src/detail/ompl_constraints.cpp
Outdated
Show resolved
Hide resolved
// TODO: Add path constraint function | ||
// Ideal: F(q_actual) - F(q_neared_pose) = 0 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Use forward kinematics on joint_values to compute EEF pose
pose1
- Use
pose_nn_
to find posepose2
that is nearest topose1
- Return
poseDistance(pose1, pose2)
moveit_planners/ompl/ompl_interface/src/detail/ompl_constraints.cpp
Outdated
Show resolved
Hide resolved
moveit_planners/ompl/ompl_interface/src/detail/ompl_constraints.cpp
Outdated
Show resolved
Hide resolved
moveit_planners/ompl/ompl_interface/src/detail/ompl_constraints.cpp
Outdated
Show resolved
Hide resolved
@mamoll Thank you this sort of feedback is very beneficial. |
If I understand well, your goal is to use the constrained planner to plan e.g. a spraying path? The constraints would be defined by discrete trajectory points? Do you plan to use the Cartesian Trajectory proposed interface? https://fzi-robot-interface-proposal.readthedocs.io/en/latest/proposal/index.html#tldr-proposed-interface I am familiar with ompl_constraints.cpp but not with the classical cartesian planning. I never used the CartesianTrajectory.msg, thus it would be nice if you could explain the idea here.. I am working on possibly a similar problem, just want to avoid dual work. |
@gautz The idea is that the end effector would follow a path (spraying, welding, drawing, etc). The As of now the user must specify the waypoints. But as you can imagine the waypoints could come from a CAM system or another path generator. You could even run canny edge detection on an imagine and convert the pixels representing contours to Cartesian space. The robot would then ideally be able to draw the picture. |
This PR instantiates a BaseConstraint-derived class called, say, ToolPathConstraint from a CartesianTrajectory.msg that represents a tool path. The class finely interpolates points along the tool path and inserts these points in a nearest neighbor data structure. For any random joint state, we can use FK to compute end effector pose and the nearest point on the tool path.
|
||
private: | ||
// TODO: Why won't NearestNeighborsGNAT allow me to store pose as Eigen Transform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bostoncleek What kind of error do you get when you try to use an Eigen Transform as the template type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was curious about this and it turns out the issue is there is no ==
operator implemented for these Eigen types:
/opt/ros/humble/include/ompl-1.6/ompl/datastructures/NearestNeighborsGNAT.h:551:102: error: no match for ‘operator==’ (operand types are ‘const Eigen::Transform<double, 3, 1>’ and ‘const Eigen::Transform<double, 3, 1>’)
551 | if (dist < nbh.top().first || (dist < std::numeric_limits<double>::epsilon() && data == key))
Can probably get around this with a thin wrapper struct or something
@bostoncleek any plans to implement a jacobian() with this generic Cartesian path approach? Or should the function() be sufficient for performance? |
Ompl can compose the derivative numerically. Of course the jacobian will allows provide an increase in performance. |
This pull request is in conflict. Could you fix it @bostoncleek? |
1 similar comment
This pull request is in conflict. Could you fix it @bostoncleek? |
@sea-bass this one, sorry about that other issue. Could you review this one and advise? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think having a nearest neighbors based path constraint for planning is worthwhile to pull in.
However, my overarching comments to get this in would be:
- Do we really need to copy-paste and modify 3 OMPL files headers here, or can we make the necessary changes to OMPL itself?
- A lot of the distance functions are already available in Eigen, and since we're converting anyway we may as well just use Eigen instead of geometry_msgs.
- The pose interpolation function does a lot of superfluous conversions from geometry_msgs to Eigen types, but combined with the above point, this could be easily addressed
And then, we want a compelling demo that maybe sets e.g., a circular path and allows users to plan within that constraint.
double step) | ||
{ | ||
// Pose interpolation | ||
// https://answers.ros.org/question/299691/interpolation-for-between-two-poses/ | ||
|
||
const Eigen::Vector3d t1(pose1.position.x, pose1.position.y, pose1.position.z); | ||
const Eigen::Vector3d t2(pose2.position.x, pose2.position.y, pose2.position.z); | ||
|
||
const Eigen::Quaterniond q1(pose1.orientation.w, pose1.orientation.x, pose1.orientation.y, pose1.orientation.z); | ||
const Eigen::Quaterniond q2(pose2.orientation.w, pose2.orientation.x, pose2.orientation.y, pose2.orientation.z); | ||
|
||
const Eigen::Vector3d interp_translation = (1.0 - step) * t1 + step * t2; | ||
const Eigen::Quaterniond interp_orientation = q1.slerp(step, q2); | ||
|
||
geometry_msgs::msg::Pose interp_pose; | ||
interp_pose.position.x = interp_translation.x(); | ||
interp_pose.position.y = interp_translation.y(); | ||
interp_pose.position.z = interp_translation.z(); | ||
|
||
interp_pose.orientation.w = interp_orientation.w(); | ||
interp_pose.orientation.x = interp_orientation.x(); | ||
interp_pose.orientation.y = interp_orientation.y(); | ||
interp_pose.orientation.z = interp_orientation.z(); | ||
|
||
return interp_pose; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now we're converting poses to Eigen objects and back for each individual interpolation point which could definitely be made more efficient by at least getting all the interpolated poses between two poses in one shot.
double step); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to interpolatePoses
or something more "verby"
{ | ||
const auto dx = pose1.position.x - pose2.position.x; | ||
const auto dy = pose1.position.y - pose2.position.y; | ||
const auto dz = pose1.position.z - pose2.position.z; | ||
return std::sqrt(dx * dx + dy * dy + dz * dz); | ||
} | ||
|
||
double arcLength(const geometry_msgs::msg::Pose& pose1, const geometry_msgs::msg::Pose& pose2) | ||
{ | ||
// Distance between two quaternions | ||
// https://math.stackexchange.com/questions/90081/quaternion-distance | ||
// https://github.com/ompl/ompl/blob/main/src/ompl/base/spaces/src/SO3StateSpace.cpp#L254-L262 | ||
// dot product between q1 and q2 | ||
auto dq = std::fabs(pose1.orientation.w * pose2.orientation.w + pose1.orientation.x * pose2.orientation.x + | ||
pose1.orientation.y * pose2.orientation.y + pose1.orientation.z * pose2.orientation.z); | ||
|
||
if (dq > 1.0 - MAX_QUATERNION_NORM_ERROR) | ||
{ | ||
return 0.0; | ||
} | ||
|
||
return std::acos(dq); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we can convert to Eigen data types, there already are functions to do this. See e.g. in cartesian_interpolator.cpp
:
double rotation_distance = start_quaternion.angularDistance(target_quaternion);
double translation_distance = (rotated_target.translation() - start_pose.translation()).norm();
So if we're doing Eigen conversions anyway in this function, we could use these functions "for free" and have the nearest neighbor use Eigen::Isometry3d
instead of geometry_msgs::msg::Pose
?
|
||
/* Author: Mark Moll, Bryant Gipson */ | ||
|
||
// This file is a slightly modified version of <ompl/datastructures/NearestNeighborsGNAT.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So can we instead update OMPL instead of copy-pasting stuff here?
By request, I'm closing this PR and capturing that we should pick up this work in a new issue : #1740 |
This PR instantiates a BaseConstraint-derived class called, say,
ToolPathConstraint from a CartesianTrajectory.msg that represents a tool path.
The class finely interpolates points along the tool path and inserts these points in a nearest neighbor data structure.
For any random joint state, we can use FK to compute end effector pose and the nearest point on the tool path.
Checklist