254 lines
8.0 KiB
Python
254 lines
8.0 KiB
Python
#
|
|
# Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
#
|
|
# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
|
|
# property and proprietary rights in and to this material, related
|
|
# documentation and any modifications thereto. Any use, reproduction,
|
|
# disclosure or distribution of this material and related documentation
|
|
# without an express license agreement from NVIDIA CORPORATION or
|
|
# its affiliates is strictly prohibited.
|
|
#
|
|
|
|
# Third Party
|
|
import pytest
|
|
import torch
|
|
|
|
# CuRobo
|
|
from curobo.geom.sdf.world import (
|
|
CollisionCheckerType,
|
|
CollisionQueryBuffer,
|
|
WorldCollisionConfig,
|
|
WorldPrimitiveCollision,
|
|
)
|
|
from curobo.geom.sdf.world_mesh import WorldMeshCollision
|
|
from curobo.geom.sdf.world_voxel import WorldVoxelCollision
|
|
from curobo.geom.types import Cuboid, VoxelGrid, WorldConfig
|
|
from curobo.types.base import TensorDeviceType
|
|
|
|
|
|
def get_world_model(single_object: bool = False):
|
|
if single_object:
|
|
|
|
world_model = WorldConfig.from_dict(
|
|
{
|
|
"cuboid": {
|
|
"block1": {"dims": [0.1, 0.2, 0.5], "pose": [0.25, 0.1, 0, 1, 0, 0, 0]},
|
|
}
|
|
}
|
|
)
|
|
else:
|
|
world_model = WorldConfig.from_dict(
|
|
{
|
|
"cuboid": {
|
|
"block2": {"dims": [0.5, 0.5, 0.5], "pose": [-0.25, 0, 0, 1, 0, 0, 0]},
|
|
"block3": {"dims": [0.1, 0.2, 0.5], "pose": [0.25, 0.1, 0, 1, 0, 0, 0]},
|
|
}
|
|
}
|
|
)
|
|
return world_model
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def world_collision(request):
|
|
world_model = get_world_model(request.param[1])
|
|
|
|
if request.param[0]:
|
|
world_model = world_model.get_mesh_world()
|
|
tensor_args = TensorDeviceType()
|
|
world_collision_config = WorldCollisionConfig.load_from_dict(
|
|
{
|
|
"checker_type": (
|
|
CollisionCheckerType.PRIMITIVE
|
|
if not request.param[0]
|
|
else CollisionCheckerType.MESH
|
|
),
|
|
"max_distance": 1.0,
|
|
"n_envs": 1,
|
|
},
|
|
world_model,
|
|
tensor_args,
|
|
)
|
|
if request.param[0]:
|
|
world_collision = WorldMeshCollision(world_collision_config)
|
|
else:
|
|
world_collision = WorldPrimitiveCollision(world_collision_config)
|
|
|
|
return world_collision
|
|
|
|
|
|
def world_voxel_collision_checker():
|
|
world_model = {
|
|
"voxel": {
|
|
"base": {"dims": [2.0, 2.0, 2.0], "pose": [0, 0, 0, 1, 0, 0, 0], "voxel_size": 0.05},
|
|
}
|
|
}
|
|
tensor_args = TensorDeviceType()
|
|
world_collision_config = WorldCollisionConfig.load_from_dict(
|
|
{
|
|
"checker_type": CollisionCheckerType.VOXEL,
|
|
"max_distance": 5.0,
|
|
"n_envs": 1,
|
|
},
|
|
world_model,
|
|
tensor_args,
|
|
)
|
|
world_collision = WorldVoxelCollision(world_collision_config)
|
|
return world_collision
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"world_collision",
|
|
[
|
|
([True, True]),
|
|
([False, True]),
|
|
([True, False]),
|
|
([False, False]),
|
|
],
|
|
indirect=True,
|
|
)
|
|
def test_voxel_esdf(world_collision):
|
|
|
|
# create voxel collision checker
|
|
world_voxel_collision = world_voxel_collision_checker()
|
|
|
|
voxel_grid = world_voxel_collision.get_voxel_grid("base")
|
|
esdf = world_collision.get_esdf_in_bounding_box(
|
|
Cuboid(name="base", pose=voxel_grid.pose, dims=voxel_grid.dims),
|
|
voxel_size=voxel_grid.voxel_size,
|
|
)
|
|
|
|
world_voxel_collision.update_voxel_data(esdf)
|
|
|
|
voxel_size = 0.01
|
|
esdf = world_collision.get_esdf_in_bounding_box(
|
|
Cuboid(name="base", pose=[0, 0, 0, 1, 0, 0, 0], dims=[1, 1, 1]), voxel_size=voxel_size
|
|
)
|
|
|
|
esdf_voxel = world_voxel_collision.get_esdf_in_bounding_box(
|
|
Cuboid(name="base", pose=[0, 0, 0, 1, 0, 0, 0], dims=[1, 1, 1]), voxel_size=voxel_size
|
|
)
|
|
esdf_data = esdf.feature_tensor
|
|
esdf_voxel_data = esdf_voxel.feature_tensor
|
|
esdf_data[esdf_data < -1.0] = 0.0
|
|
esdf_voxel_data[esdf_voxel_data < -1.0] = 0.0
|
|
error = torch.abs(esdf_data - esdf_voxel_data)
|
|
|
|
assert torch.max(error) < 2 * voxel_grid.voxel_size
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"world_collision",
|
|
[
|
|
([True, True]),
|
|
([False, True]),
|
|
([True, False]),
|
|
([False, False]),
|
|
],
|
|
indirect=True,
|
|
)
|
|
def test_primitive_voxel_sphere_distance(world_collision):
|
|
tensor_args = TensorDeviceType()
|
|
voxel_size = 0.025
|
|
world_voxel_collision = world_voxel_collision_checker()
|
|
|
|
voxel_grid = world_voxel_collision.get_voxel_grid("base")
|
|
esdf = world_collision.get_esdf_in_bounding_box(
|
|
Cuboid(name="base", pose=voxel_grid.pose, dims=voxel_grid.dims),
|
|
voxel_size=voxel_grid.voxel_size,
|
|
)
|
|
|
|
world_voxel_collision.update_voxel_data(esdf)
|
|
|
|
# create a grid and compute distance:
|
|
sample_grid = VoxelGrid(
|
|
name="test", pose=[0, 0, 0, 1, 0, 0, 0], voxel_size=voxel_size, dims=[1, 1, 1]
|
|
)
|
|
sample_spheres = sample_grid.create_xyzr_tensor()
|
|
sample_spheres = sample_spheres.reshape(-1, 1, 1, 4)
|
|
|
|
act_distance = tensor_args.to_device([0.0])
|
|
|
|
weight = tensor_args.to_device([1.0])
|
|
cuboid_collision_buffer = CollisionQueryBuffer.initialize_from_shape(
|
|
sample_spheres.shape, tensor_args, world_collision.collision_types
|
|
)
|
|
voxel_collision_buffer = CollisionQueryBuffer.initialize_from_shape(
|
|
sample_spheres.shape, tensor_args, world_voxel_collision.collision_types
|
|
)
|
|
d_cuboid = world_collision.get_sphere_distance(
|
|
sample_spheres, cuboid_collision_buffer, weight, act_distance
|
|
)
|
|
d_voxel = world_voxel_collision.get_sphere_distance(
|
|
sample_spheres, voxel_collision_buffer, weight, act_distance
|
|
)
|
|
|
|
error = torch.abs(d_cuboid.view(-1) - d_voxel.view(-1))
|
|
|
|
assert torch.max(error) - voxel_grid.voxel_size < 1e-3
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"world_collision",
|
|
[
|
|
# ([True, True]),
|
|
([False, True]),
|
|
# ([True, False]),
|
|
# ([False, False]),
|
|
],
|
|
indirect=True,
|
|
)
|
|
def test_primitive_voxel_sphere_gradient(world_collision):
|
|
tensor_args = TensorDeviceType()
|
|
world_voxel_collision = world_voxel_collision_checker()
|
|
|
|
voxel_grid = world_voxel_collision.get_voxel_grid("base")
|
|
esdf = world_collision.get_esdf_in_bounding_box(
|
|
Cuboid(name="base", pose=voxel_grid.pose, dims=voxel_grid.dims),
|
|
voxel_size=voxel_grid.voxel_size,
|
|
)
|
|
voxel_size = voxel_grid.voxel_size
|
|
|
|
world_voxel_collision.update_voxel_data(esdf)
|
|
|
|
# create a grid and compute distance:
|
|
sample_grid = VoxelGrid(
|
|
name="test", pose=[0.0, 0.0, 0, 1, 0, 0, 0], voxel_size=voxel_size, dims=[0.1, 0.1, 0.1]
|
|
)
|
|
# sample_grid = VoxelGrid(
|
|
# name="test", pose=[0.2, 0.0, 0, 1, 0, 0, 0], voxel_size=voxel_size,
|
|
# dims=[0.1, 0.1, 0.1]
|
|
# )
|
|
sample_spheres = sample_grid.create_xyzr_tensor(transform_to_origin=True)
|
|
sample_spheres = sample_spheres.reshape(-1, 1, 1, 4)
|
|
|
|
act_distance = tensor_args.to_device([0.0])
|
|
|
|
weight = tensor_args.to_device([1.0])
|
|
cuboid_collision_buffer = CollisionQueryBuffer.initialize_from_shape(
|
|
sample_spheres.shape, tensor_args, world_collision.collision_types
|
|
)
|
|
voxel_collision_buffer = CollisionQueryBuffer.initialize_from_shape(
|
|
sample_spheres.shape, tensor_args, world_voxel_collision.collision_types
|
|
)
|
|
sample_spheres_1 = sample_spheres.clone()
|
|
sample_spheres_1.requires_grad = True
|
|
d_cuboid = world_collision.get_sphere_distance(
|
|
sample_spheres_1, cuboid_collision_buffer, weight, act_distance
|
|
)
|
|
sample_spheres_2 = sample_spheres.clone()
|
|
sample_spheres_2.requires_grad = True
|
|
d_voxel = world_voxel_collision.get_sphere_distance(
|
|
sample_spheres_2, voxel_collision_buffer, weight, act_distance
|
|
)
|
|
|
|
error = torch.abs(d_cuboid.view(-1) - d_voxel.view(-1))
|
|
|
|
assert torch.max(error) - voxel_grid.voxel_size < 1e-3
|
|
|
|
cuboid_gradient = cuboid_collision_buffer.get_gradient_buffer()
|
|
|
|
voxel_gradient = voxel_collision_buffer.get_gradient_buffer()
|
|
error = torch.linalg.norm(cuboid_gradient - voxel_gradient, dim=-1)
|
|
|
|
assert torch.max(error) - voxel_grid.voxel_size < 1e-3
|