#4809·bullet3

PyBullet inverse-dynamics cache is stale after mass changes and body reuse

Author: peachtree0222Created Sep 15, 2026Updated Sep 15, 2026

Environment

  • OS: Ubuntu 24.04.5 LTS, Linux 7.0.0-28-generic x86_64
  • Compiler: GCC 11.5.0
  • Python: 3.12.3
  • Source: bulletphysics/bullet3 master at 63c4d67e337017f9d8b298c900e9aabdb69296e7
  • PyBullet: built locally from that exact checkout

Description

calculateInverseDynamics, calculateMassMatrix, and Jacobian calculation share an inverse-dynamics model cached by raw btMultiBody*. changeDynamics can change mass or inertia without invalidating the model, and removeBody deletes a multibody without erasing its cache entry. A replacement can reuse both body ID and allocation, so calculations for the replacement use the deleted body's parameters.

Expected behavior: derived dynamics calculations reflect the current body's mass and inertia after a dynamics mutation or body replacement.

Actual behavior: for the reproducer's one-DOF fixed-base model, a fresh 10 kg link returns inverse torque -90 and mass matrix [[10]]. If a cached 1 kg body is changed to 10 kg, or removed and replaced by a 10 kg body at reused ID 0, both operations still return the 1 kg results -9 and [[1]].

Sources:

Reproduction package:

inverse-dynamics-cache-invalidation-poc.zip

Steps to reproduce

  1. Build PyBullet from the commit above using poc/README.md.
  2. Run python poc/reproducer.py fresh.
  3. Run python poc/reproducer.py reuse.
  4. Run python poc/reproducer.py mutate.

The exact baseline and fixed outputs are in poc/observed-output.txt.