Repository: bulletphysics/bullet3

Issue:
Pybullet: calculateInverseDynamics ignores some joint rotations but not others
I have been running into an issue where calculateInverseDynamics works on the first joint attached to the body, but not the second joint. Specifically, the angular position of the second joint is ignored. 



I put together this example to show the issue:
This is a floating multi-body consisting of 3 links and 2 revolute joints. The base link is the cube in the center.
![image](https://github.com/bulletphysics/bullet3/assets/50556748/99228c38-9de0-4b9b-8330-25fbfc05a815)

When the first joint rotates the torque required to accelerate the multibody about the z axis changes, which makes sense because this rotation has changed the moment of inertia in the z direction.
![image](https://github.com/bulletphysics/bullet3/assets/50556748/64b2ff8a-bf25-4d58-86aa-89b3645ff272)

Despite the links being identical and symmetric, the second joint's rotation has 0 impact on the torque required to accelerate the multibody about the z axis.
![image](https://github.com/bulletphysics/bullet3/assets/50556748/9032f6f1-4022-4219-b813-bb246f0f5286)


Here is the code:
```
import pybullet as p
import time


def TestTorques(bodyID, joint0, joint1):
	p.resetJointState(bodyID, 0, joint0,0)
	p.resetJointState(bodyID, 1, joint1,0)
	return p.calculateInverseDynamics(
		bodyID,
		[0,0,0,1] + [0,0,0] + [joint0, joint1],
		[0]*6 + [0,0], #No angular or linear velocity on the body + no joint velocity
		[0,0,1] + [0,0,0] + [0,0] #Angular acceleration about the z axis of the body. No other accelerations
	)



p.connect(p.GUI)
p.setTimeStep(1/100)
p.setGravity(0, 0, 0)


cubeVis = p.createVisualShape(p.GEOM_BOX, halfExtents = [0.1, 0.1, 0.1])
cubeCol = p.createCollisionShape(p.GEOM_BOX, halfExtents = [0.1, 0.1, 0.1])

brickVis = p.createVisualShape(p.GEOM_BOX, halfExtents = [0.05, 0.2, 0.05])
brickCol = p.createCollisionShape(p.GEOM_BOX, halfExtents = [0.05, 0.2, 0.05])

#Creates a multi-body made up of a cube with 2 rectangular prisms attached on either side. The rectangular prisims rotate with revolute joints
bodyID = p.createMultiBody(baseMass=1,
	baseInertialFramePosition=[0, 0, 0],
	baseCollisionShapeIndex=cubeCol,
	baseVisualShapeIndex=cubeVis,
	basePosition=[0, 0, 2],
	linkMasses = [1,1],
	linkCollisionShapeIndices = [brickCol,brickCol],
	linkVisualShapeIndices = [brickVis,brickVis],
	linkPositions = [[0.15,0,0],[-0.15,0,0]],
	linkOrientations = [[0,0,0,1],[0,0,0,1]],
	linkInertialFramePositions = [[0,0,0],[0,0,0]],
	linkInertialFrameOrientations = [[0,0,0,1],[0,0,0,1]],
	linkParentIndices = [0,0],
	linkJointTypes = [p.JOINT_REVOLUTE,p.JOINT_REVOLUTE],
	linkJointAxis = [[1,0,0],[-1,0,0]]
)

#To accelerate the body about the z axis by 1 rad/s^2
print(TestTorques(bodyID, 0, 0))
#Returns (0.0, 0.0, 0.0, 0.0, 0.0, 0.08, 0.0, 0.0)

time.sleep(3)

#If the first link is rotated by 90 degrees:
print(TestTorques(bodyID, 1.57079632679, 0))
#Returns (0.0, 0.0, 0.0, 0.0, 6.120736075183436e-14, 0.0675, 0.0, 0.0)
#As expected, rotating this joint changes the moment of inertia about the z-axis, changing the required torque

time.sleep(3)

#If the second link is rotated by 90 degrees:
print(TestTorques(bodyID, 0, 1.57079632679))
#Returns (0.0, 0.0, 0.0, 0.0, 0.0, 0.08, 0.0, 0.0)
#Despite the links being identical to each-other and symmetrically positioned, they have different effects on the moment of inertia
#Rotating the second link does not change the inertia (the response is the same as the [0, 0] case)

time.sleep(3)


#Any random rotation of the second link has 0 impact on the response:
print(TestTorques(bodyID, 0, 0.4884))
#Returns (0.0, 0.0, 0.0, 0.0, 0.0, 0.08, 0.0, 0.0)
#For some reason calculateInverseDynamics is ignoring the rotation of the second link

time.sleep(3)
```





Discussion:

Patch:
diff --git a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp
index 1d691c550c..b8fc344337 100644
--- a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp
+++ b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp
@@ -12360,9 +12360,10 @@ bool PhysicsServerCommandProcessor::processInverseDynamicsCommand(const struct S
 					q[4] = pos[1];
 					q[5] = pos[2];
 				}
+				int quatToEulerCorrection =  baseDofQ ? -1 : 0; // The conversion to Euler angles drops the DOF by one
 				for (int i = 0; i < num_dofs; i++)
 				{
-					q[i + baseDofQ] = clientCmd.m_calculateInverseDynamicsArguments.m_jointPositionsQ[i + baseDofQ];
+					q[i + baseDofQ + quatToEulerCorrection] = clientCmd.m_calculateInverseDynamicsArguments.m_jointPositionsQ[i + baseDofQ];
 				}
 				for (int i = 0; i < num_dofs + baseDofQdot; i++)
 				{