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

Support for raycast #555

Open
eduardo98m opened this issue Mar 20, 2024 · 8 comments
Open

Support for raycast #555

eduardo98m opened this issue Mar 20, 2024 · 8 comments

Comments

@eduardo98m
Copy link

Are there any plans in the future to support raycasting?

From what I have read in other forums a quick fix might be to create a very long and thin cylinder, or use a small sphere in continuous collision detection mode.

@jcarpent
Copy link
Contributor

jcarpent commented Mar 20, 2024

What are you exact needs @eduardo98m? Which concrete applications do you foresee?
@eduardo98m We would be happy to help you to contribute to hpp-fcl by implementing this feature. Do you agree?

From what I have read in other forums a quick fix might be to create a very long and thin cylinder, or use a small sphere in continuous collision detection mode.

These solutions won't be efficient. You should rather look at faster solutions ;)

@eduardo98m
Copy link
Author

Hello @jcarpent

I basically need to get the point of contact between a ray (defined with a start and an endpoint) and any other collider.

The application i want to get from theses is being able to simulate a Lidar sensor (or ultrasonic, infrared, etc. exterioceptive sensor)

Here is an rough sketch of the functionality:

image

Here is also my basic implementation using a capsule collider (I figured it would be faster than a cylinder):

(The code below is part of my physics engine project, thats why I perform the collision test with multiple objects).

std::vector<vec3> World::raycast(vec3 start, vec3 end)
{

    // Create a capsule in hpp::fcl
    scalar lenght = ti::magnitude(end - start);
    scalar radius = 0.001; // if we take 1 m as the unit, this should be 1 [mm]

    std::shared_ptr<hpp::fcl::Capsule> ray_collider = std::make_shared<hpp::fcl::Capsule>(radius, lenght);

    vec3 position = 0.5*(start + end);

    vec3 direction = ti::normalize(end - start);
    vec3 up = {0.0, 1.0, 0.0};

    // Create a quaternion representing the capsule orientation
    quat orientation = ti::quat_from_axis_angle(direction, 0.0f);

    // Rotate the quaternion by 90 degrees around the up vector to align the capsule with the raycast
    orientation = orientation * ti::quat_from_axis_angle(up, M_PI / 2.0f);


    // Now we need to get the orientation of the capsule from the two points
    hpp::fcl::Transform3f transform = ti::get_eigen_transform(position,
                                                              orientation);

    std::vector<vec3> points;
    // Now we need to check the capsule collider with the rest of the bodies of the world
    for (int i = 0; i < this->bodies.size(); i++)
    {
        hpp::fcl::CollisionResult col_res;
        hpp::fcl::CollisionRequest col_req;

        hpp::fcl::collide(ray_collider.get(),
                          transform,
                          this->bodies[i].collider_info.get(),
                          ti::get_eigen_transform(this->bodies[i].position,
                                                  this->bodies[i].orientation),
                          col_req,
                          col_res);

        if (col_res.isCollision())
        {
            vec3 normal = {0.0, 0.0, 0.0};
            vec3 contrac_point_1 = {0.0, 0.0, 0.0};
            vec3 contrac_point_2 = {0.0, 0.0, 0.0};
            hpp::fcl::Contact contact = col_res.getContact(0);
            points.push_back(ti::from_eigen(contact.pos));
        }
    }

    return points;
}

I am not really sure how to implement the feature directly in hpp::fcl, but im guessing that creating a ray collider and figuring out its signed distance function might be the way to go.

@jcarpent
Copy link
Contributor

Thanks @eduardo98m for sharing your thoughts on the topic.

An efficient solution should rely on the support function, derived for each primitive and which is at the core of the GJK and EPA algorithms for checking the collision between convex objects.

@lmontaut
Copy link
Contributor

Hi @eduardo98m,
If you represent your rays by capsules with zero radius, hppfcl will treat your capsule as a segment. You can then call collide and things should work out of the box. Let us know if this solution works for you :)

@eduardo98m
Copy link
Author

I tried using the capsule with zero radius (and also tried the cylinder), and im getting some strange results. For some reason I only get one contact per collision, and the points I get while colliding with a sphere collider are not really the ones that are expected. I made a video so it could be better visualized:
https://youtu.be/EnQ7Yq9Gm5E

Watch the video

image

Here is the code of the raycast (Im not sure if there is a parameter in the collision request i should change to get better results):

std::vector<vec3> World::raycast(vec3 start, vec3 end)
{

    // Create a capsule in hpp::fcl
    scalar lenght = ti::magnitude(end - start);
    scalar radius = 0; //

    std::shared_ptr<hpp::fcl::Cylinder> ray_collider = std::make_shared<hpp::fcl::Cylinder>(radius, lenght);

    vec3 position = 0.5 * (start + end);
    vec3 direction = ti::normalize(end - start);
    vec3 up = {0.0, 0.0, 1.0};
    vec3 cross = ti::cross(up, direction);
    vec3 axis = cross/ti::magnitude(cross);
    scalar angle  = ti::acos(ti::dot(direction, up));
    quat orientation = ti::quat_from_axis_angle(axis, angle) ;
    
    hpp::fcl::CollisionObject* ray = new hpp::fcl::CollisionObject(ray_collider);
    ray->setTransform(ti::get_eigen_transform(position, orientation));

    ray->computeAABB();

    std::vector<vec3> points;
    // Now we need to check the capsule collider with the rest of the bodies of the world
    for (int i = 0; i < this->bodies.size(); i++)
    {

        hpp::fcl::CollisionObject* col_obj = new hpp::fcl::CollisionObject(this->bodies[i].collider_info);
        col_obj->setTransform(
                ti::get_eigen_transform(this->bodies[i].position,
                                                  this->bodies[i].orientation)
        );
        col_obj->computeAABB();

        if (ray->getAABB().contain(col_obj->getAABB())){
            hpp::fcl::CollisionResult col_res;
            hpp::fcl::CollisionRequest col_req;

            col_req.num_max_contacts = 16;

            hpp::fcl::collide(ray,
                            col_obj,
                            col_req,
                            col_res);

            if (col_res.isCollision())
            {
                // vec3 point;
                // scalar min_dist = INFINITY;
                // std::cerr << "N contacs : " << col_res.numContacts() << "\n"; 
                // for (int i = 0; i< col_res.numContacts(); i++ ){
                //     vec3 candidate = ti::from_eigen(col_res.getContact(i).pos);
                //     // scalar dist = ti::magnitude(start - candidate);
                //     // if (dist < min_dist){
                //     //     min_dist = dist;
                //     //     point = candidate;
                //     // }

                //     points.push_back(candidate);
                // }
                points.push_back(ti::from_eigen(col_res.nearest_points[1]));
            }
        }
        
    }

    return points;
}

@lmontaut
Copy link
Contributor

Hey @eduardo98m, thanks a lot for the video! That's really helpful. I read your issue too quickly and didn't realize you also needed the projection of the ray onto the object. I thought you only to know yes/no collision.

The contact points you are getting are coherent with collision detection in the sense that it corresponds to the middle point of the separating vector (i.e the vector that brings the two shapes in touching collision).
Anyway, it's indeed not the point you want in your application.

Let me think more about your problem and see if there is an easy way to solve it with hpp-fcl and get back to you!

@lmontaut
Copy link
Contributor

Hi @eduardo98m, one way to solve your problem might be to do two successive calls to collide and distance:

  • First is a collide call between your geometry and the ray represented by a capsule with zero radius. This tells you whether or not the ray is colliding with the geometry.
  • If it is the case, then you can call distance between the geometry and the origin of the ray. The origin of the ray can be represented as a sphere with zero radius. You will get 2 witness points, one should be the origin of the ray and the other one will be the orthogonal projection of the origin of the ray onto the geometry.

Let us know if this works for you!

@eduardo98m
Copy link
Author

Hello @lmontaut sorry for the late response, I tried what your suggestion but im not getting the desired result, because i don't get the points the capsule is passing trough the geometry (P2 in the figure), but the nearest point of the colliding shape to the origin of the ray (P1).

Maybe i am getting something wrong, or there is an operation that im missing, but so far thats what I have been able to get.

example(1)

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

No branches or pull requests

3 participants