feat: Raycast implementation for ODE#976
Conversation
|
Thanks for the contribution! Could you please fill out the PR template? |
|
codecheck` passed <- This throws pagewise unrelated warnings... Note, for the tests, I needed to remove the unsupported 'banana' as this defaulted to ode, and was therefore supported. |
azeey
left a comment
There was a problem hiding this comment.
Overall, this looks good, but there a couple of issues that need to be addressed. It would also be great if we can update the test to use multiple objects in the line of sight of the ray.
| if(dCollide(ray, other, 1, &contact, sizeof(dContactGeom)) > 0) | ||
| { | ||
| if(result->mRayHits.empty()) | ||
| { | ||
| setResult(result->mRayHits.emplace_back()); | ||
| } | ||
| else | ||
| { | ||
| setResult(result->mRayHits.front()); | ||
| } |
There was a problem hiding this comment.
From Gemini:
dSpaceCollide2will callNearCallbackODEfor every geometry whose bounding box intersects the ray. By unconditionally overwriting .front(), the raycast will simply return the last object visited by ODE's spatial traversal hierarchy, which is not guaranteed to be the closest object.
I have reproduced the issue by running this on gz-sim/examples/worlds/cpu_lidar_sensor.sdf with the collision_detector changed to ode. As you see in the video below, after moving the cube in front of the cylinder, there are still rays hitting the cylinder. Sometimes, it happens the other way around. I move the cube behind the cylinder and the rays now skip the cylinder and hit the cube.
Screen.Recording.2026-07-01.at.12.27.15.mov
Suggested solution:
Tip
Recommendation: You need to keep track of the minimum distance and only overwrite if the new contact is closer. ODE conveniently populates contact.depth with the distance from the ray origin for ray intersections. You can pass a custom struct via _data instead of just RaycastResult*:
struct OdeRaycastData {
RaycastResult* result;
double minDistance = std::numeric_limits<double>::infinity();
};And in NearCallbackODE:
OdeRaycastData* data = static_cast<OdeRaycastData*>(_data);
// ... inside dCollide block:
if (contact.depth < data->minDistance)
{
data->minDistance = contact.depth;
if(data->result->mRayHits.empty())
setResult(data->result->mRayHits.emplace_back());
else
setResult(data->result->mRayHits.front());
}There was a problem hiding this comment.
This one is odd, I would have expected to only get the closest hit as I set dGeomRaySetClosestHit.
I fixed it now, note I explicitly computed the sqr dist, as I tried using depth before for the fraction and it gave me not the distance.
There was a problem hiding this comment.
Looks like it's not quite fixed on my end. I believe you forgot to update data->curContactDistSqr.
This one is odd, I would have expected to only get the closest hit as I set dGeomRaySetClosestHit.
It is giving you the closest hit for each ray/object pair, but the NearCallabackODE will be called for each object in the scene. In other words, the ray may hit the box multiple times and you'll get the closest of those, but the collision of the ray with the cylinder will be treated as a separate collision and gets its own callback.
|
@azeey ping |
|
@azeey Added a test and fixed the issue. |
azeey
left a comment
There was a problem hiding this comment.
LGTM! Thanks again for the contribution. Just a few minor comments left. Also, would you mind merging from main and fixing the conflicts?
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
🎉 New feature
Followup to #880
This adds support for ODE for the raycast implementation, and therefore to the CPU-Lidar implementation.
Checklist
codecheckpassed (See contributing)