{"id":49739945,"url":"https://github.com/UM-ARM-Lab/pytorch_volumetric","last_synced_at":"2026-05-26T07:00:45.477Z","repository":{"id":75037644,"uuid":"604436894","full_name":"UM-ARM-Lab/pytorch_volumetric","owner":"UM-ARM-Lab","description":"Volumetric structures such as voxels and SDFs implemented in pytorch","archived":false,"fork":false,"pushed_at":"2026-03-26T23:11:05.000Z","size":7156,"stargazers_count":190,"open_issues_count":2,"forks_count":15,"subscribers_count":16,"default_branch":"master","last_synced_at":"2026-03-27T08:44:16.085Z","etag":null,"topics":["chamfer-distance","kinematics","pytorch","robot","sdf","signed-distance-field","signed-distance-functions","voxel","voxel-grid","voxels"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/UM-ARM-Lab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-02-21T03:48:41.000Z","updated_at":"2026-03-26T23:09:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"50caa50f-e533-4abc-82b3-2f078ab175fa","html_url":"https://github.com/UM-ARM-Lab/pytorch_volumetric","commit_stats":{"total_commits":85,"total_committers":3,"mean_commits":"28.333333333333332","dds":"0.14117647058823535","last_synced_commit":"67d307fd45cafed0a966afd5bdedef282c6561b8"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/UM-ARM-Lab/pytorch_volumetric","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UM-ARM-Lab%2Fpytorch_volumetric","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UM-ARM-Lab%2Fpytorch_volumetric/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UM-ARM-Lab%2Fpytorch_volumetric/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UM-ARM-Lab%2Fpytorch_volumetric/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/UM-ARM-Lab","download_url":"https://codeload.github.com/UM-ARM-Lab/pytorch_volumetric/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UM-ARM-Lab%2Fpytorch_volumetric/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33508317,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T03:12:49.672Z","status":"ssl_error","status_checked_at":"2026-05-26T03:12:47.976Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chamfer-distance","kinematics","pytorch","robot","sdf","signed-distance-field","signed-distance-functions","voxel","voxel-grid","voxels"],"created_at":"2026-05-09T17:00:38.275Z","updated_at":"2026-05-26T07:00:45.464Z","avatar_url":"https://github.com/UM-ARM-Lab.png","language":"Python","funding_links":[],"categories":["[Libraries](#awesome-collision-detection)"],"sub_categories":[],"readme":"## Pytorch Volumetric\n\n- signed distance field (SDF) pytorch implementation with parallelized query for value and gradients\n- voxel grids with automatic expanding range\n- unidirectional chamfer distance (points to mesh)\n- robot model to SDF with parallelized query over robot configurations and points\n\nSDF slice animated over a KUKA robot\n\n![pv_sdf_slice](https://github.com/user-attachments/assets/d9081706-6463-4716-82ec-b278969d24f4)\n\n\n## Performance\n\nSDF query benchmarks for a YCB power drill mesh (resolution 0.01). Measured with `pytest-benchmark`.\n\n**GPU (CUDA, RTX 3090):**\n\n| Benchmark | 100 pts | 1K pts | 5K pts | 20K pts | 100K pts |\n|---|---|---|---|---|---|\n| MeshSDF | 0.89 ms | 6.9 ms | 5.8 ms | 20.9 ms | 91 ms |\n| CachedSDF (bounding box) | 0.49 ms | 0.51 ms | 0.53 ms | 0.55 ms | **0.70 ms** |\n| CachedSDF (GT fallback) | 0.64 ms | 0.67 ms | 0.70 ms | 0.71 ms | **0.84 ms** |\n| MeshSDF + backward | 1.07 ms | 7.1 ms | 6.0 ms | 21.0 ms | 91 ms |\n\n**CPU (Apple M3 Max):**\n\n| Benchmark | 100 pts | 1K pts | 5K pts | 20K pts | 100K pts |\n|---|---|---|---|---|---|\n| MeshSDF | 193 us | 3.1 ms | 3.7 ms | 11.9 ms | 57.6 ms |\n| CachedSDF (bounding box) | 64 us | 112 us | 353 us | 854 us | **2.6 ms** |\n| CachedSDF (GT fallback) | 97 us | 143 us | 396 us | 902 us | **2.7 ms** |\n| MeshSDF + backward | 239 us | 3.2 ms | 3.8 ms | 12.3 ms | 58.3 ms |\n\nCachedSDF provides **22-130x speedup** over MeshSDF. At 100K points, CachedSDF queries\ncomplete in **0.70 ms on GPU** and **2.6 ms on CPU**, compared to 92 ms and 57.6 ms for MeshSDF.\n\nRun benchmarks yourself with `pytest tests/test_benchmarks.py -v`.\n\n## Installation\n\n```shell\npip install pytorch-volumetric\n```\n\nFor development, clone repository somewhere, then `pip3 install -e .` to install in editable mode.\nFor testing, run `pytest` in the root directory.\n\n## Usage\n\nSee `tests` for code samples; some are also shown here\n\n### SDF from mesh\n\n```python\nimport pytorch_volumetric as pv\n\n# supposing we have an object mesh (most formats supported) - from https://github.com/eleramp/pybullet-object-models\nobj = pv.MeshObjectFactory(\"YcbPowerDrill/textured_simple_reoriented.obj\")\nsdf = pv.MeshSDF(obj)\n```\n\nAn `open3d` mesh can be provided via the `mesh=` argument to `MeshObjectFactory`. When doing so, transform parameters\nsuch as scale are ignored.\n\n### Cached SDF\n\n```python\nimport pytorch_volumetric as pv\n\nobj = pv.MeshObjectFactory(\"YcbPowerDrill/textured_simple_reoriented.obj\")\nsdf = pv.MeshSDF(obj)\n# caching the SDF via a voxel grid to accelerate queries\ncached_sdf = pv.CachedSDF('drill', resolution=0.01, range_per_dim=obj.bounding_box(padding=0.1), gt_sdf=sdf)\n```\n\nBy default, query points outside the cache will be compared against the object bounding box.\nTo instead use the ground truth SDF, pass `out_of_bounds_strategy=pv.OutOfBoundsStrategy.LOOKUP_GT_SDF` to\nthe constructor.\n\nNote that the bounding box comparison will always under-approximate the SDF value, but empirically it is sufficient\nfor most applications when querying out of bound points. It is **dramatically faster** than using the ground truth SDF.\n\n#### TSDF (Truncated SDF)\n\nFor planning and collision checking, SDF values far from the surface are irrelevant. Setting `truncation_distance`\ncreates a much smaller voxel grid (only covering the near-surface region) and returns the truncation value for\nout-of-bounds points:\n\n```python\ncached_sdf = pv.CachedSDF('drill', resolution=0.01,\n                            range_per_dim=obj.bounding_box(padding=0.1),\n                            gt_sdf=sdf,\n                            truncation_distance=0.1)  # OOB points return 0.1\n```\n\nWith `padding=truncation_distance`, grids are ~150x smaller (e.g. 43MB → 0.3MB for a robot link).\n\n### Composed SDF\nMultiple SDFs can be composed together to form an SDF that is convenient to query. This may be because your scene\nis composed of multiple objects and you have them as separate meshes. Note: the objects should not be overlapping or\nshare faces, otherwise there will be artifacts in the SDF query in determining interior-ness. \n\n```python\nimport pytorch_volumetric as pv\nimport pytorch_kinematics as pk\n\nobj = pv.MeshObjectFactory(\"YcbPowerDrill/textured_simple_reoriented.obj\")\n\n# 2 drills in the world\nsdf1 = pv.MeshSDF(obj)\nsdf2 = pv.MeshSDF(obj)\n# need to specify the transform of each SDF frame\ntsf1 = pk.Translate(0.1, 0, 0)\ntsf2 = pk.Translate(-0.2, 0, 0.2)\nsdf = pv.ComposedSDF([sdf1, sdf2], tsf1.stack(tsf2))\n```\n\n### SDF value and gradient queries\n\nSuppose we have an `ObjectFrameSDF` (such as created from above)\n\n```python\nimport numpy as np\nimport pytorch_volumetric as pv\n\n# get points in a grid in the object frame\nquery_range = np.array([\n    [-1, 0.5],\n    [-0.5, 0.5],\n    [-0.2, 0.8],\n])\n\ncoords, pts = pv.get_coordinates_and_points_in_grid(0.01, query_range)\n# N x 3 points \n# we can also query with batched points B x N x 3, B can be any number of batch dimensions\nsdf_val, sdf_grad = sdf(pts)\n# sdf_val is N, or B x N, the SDF value in meters\n# sdf_grad is N x 3 or B x N x 3, the normalized SDF gradient (points along steepest increase in SDF)\n\n# for faster queries when only values are needed (e.g. collision checking):\nsdf_val, _ = sdf(pts, compute_grad=False)\n```\n\n### Plotting SDF Slice\n\n```python\nimport pytorch_volumetric as pv\nimport numpy as np\n\n# supposing we have an object mesh (most formats supported) - from https://github.com/eleramp/pybullet-object-models\nobj = pv.MeshObjectFactory(\"YcbPowerDrill/textured_simple_reoriented.obj\")\nsdf = pv.MeshSDF(obj)\n# need a dimension with no range to slice; here it's y\nquery_range = np.array([\n    [-0.15, 0.2],\n    [0, 0],\n    [-0.1, 0.2],\n])\npv.draw_sdf_slice(sdf, query_range)\n```\n\n![drill SDF](https://i.imgur.com/TFaGmx6.png)\n\n### Robot Model to SDF\n\nFor many applications such as collision checking, it is useful to have the\nSDF of a multi-link robot in certain configurations.\nFirst, we create the robot model (loaded from URDF, SDF, MJCF, ...) with\n[pytorch kinematics](https://github.com/UM-ARM-Lab/pytorch_kinematics).\nFor example, we will be using the KUKA 7 DOF arm model from pybullet data\n\n```python\nimport os\nimport torch\nimport pybullet_data\nimport pytorch_kinematics as pk\nimport pytorch_volumetric as pv\n\nurdf = \"kuka_iiwa/model.urdf\"\nsearch_path = pybullet_data.getDataPath()\nfull_urdf = os.path.join(search_path, urdf)\nchain = pk.build_serial_chain_from_urdf(open(full_urdf).read(), \"lbr_iiwa_link_7\")\nd = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nchain = chain.to(device=d)\n# paths to the link meshes are specified with their relative path inside the URDF\n# we need to give them the path prefix as we need their absolute path to load\ns = pv.RobotSDF(chain, path_prefix=os.path.join(search_path, \"kuka_iiwa\"))\n```\n\nBy default, each link will have a `MeshSDF`. To instead use `CachedSDF` for faster queries\n\n```python\ns = pv.RobotSDF(chain, path_prefix=os.path.join(search_path, \"kuka_iiwa\"),\n                link_sdf_cls=pv.cache_link_sdf_factory(resolution=0.02, padding=1.0, device=d))\n```\n\nFor planning with many configurations, use TSDF for smaller grids and `compile()` for faster GPU queries:\n\n```python\ns = pv.RobotSDF(chain, path_prefix=os.path.join(search_path, \"kuka_iiwa\"),\n                link_sdf_cls=pv.cache_link_sdf_factory(resolution=0.02, padding=0.1,\n                                                        truncation_distance=0.1, device=d))\ns.compile()  # optional: torch.compile for ~2x GPU speedup on no-grad queries\n```\n\nWhich when the `y=0.02` SDF slice is visualized:\n![sdf slice](https://i.imgur.com/Putw72A.png)\n\nWith surface points corresponding to:\n![wireframe](https://i.imgur.com/L3atG9h.png)\n![solid](https://i.imgur.com/XiAks7a.png)\n\nQueries on this SDF is dependent on the joint configurations (by default all zero).\n**Queries are batched across configurations and query points**. For example, we have a batch of\njoint configurations to query\n\n```python\nth = torch.tensor([0.0, -math.pi / 4.0, 0.0, math.pi / 2.0, 0.0, math.pi / 4.0, 0.0], device=d)\nN = 200\nth_perturbation = torch.randn(N - 1, 7, device=d) * 0.1\n# N x 7 joint values\nth = torch.cat((th.view(1, -1), th_perturbation + th))\n```\n\nAnd also a batch of points to query (same points for each configuration):\n\n```python\ny = 0.02\nquery_range = np.array([\n    [-1, 0.5],\n    [y, y],\n    [-0.2, 0.8],\n])\n# M x 3 points\ncoords, pts = pv.get_coordinates_and_points_in_grid(0.01, query_range, device=s.device)\n```\n\nWe set the batch of joint configurations and query:\n\n```python\ns.set_joint_configuration(th)\n# N x M SDF value\n# N x M x 3 SDF gradient\nsdf_val, sdf_grad = s(pts)\n```\n\n#### Differentiable w.r.t. joint configurations\n\nSDF queries support autograd through the FK chain, enabling gradient-based optimization of joint\nconfigurations (e.g. trajectory optimization for collision avoidance):\n\n```python\nth = torch.tensor([0.0, -math.pi / 4.0, 0.0, math.pi / 2.0, 0.0, math.pi / 4.0, 0.0],\n                   device=d, requires_grad=True)\ns.set_joint_configuration(th)\nsdf_val, sdf_grad = s(pts)\nsdf_val.sum().backward()\n# th.grad contains d(sdf_val)/d(th)\n```\n\n#### Performance\n\nUsing `CachedSDF` with `compute_grad=False` on a RTX 4070 (8-link Kuka):\n\n| Scenario | Time |\n|---|---|\n| Single config, 100K points | 3.1 ms |\n| 1000 configs × 10K points | 198 ms |\n| 5000 configs × 10K points | 997 ms |\n\nWith-grad queries are ~1.5-2x slower. `compute_grad=False` skips gradient computation entirely.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FUM-ARM-Lab%2Fpytorch_volumetric","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FUM-ARM-Lab%2Fpytorch_volumetric","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FUM-ARM-Lab%2Fpytorch_volumetric/lists"}