Skip to content

Commit 1d0bd84

Browse files
committed
[WIP] Implement TSID example
1 parent 264b492 commit 1d0bd84

2 files changed

Lines changed: 472 additions & 1 deletion

File tree

biped_walking_controller/simulation.py

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,44 @@ def _apply_velocity_to_pybullet(robot_id, v_des, pb_to_pin_joint_vel):
220220
)
221221

222222

223+
def _disable_pybullet_joint_motors(robot_id, pb_to_pin_joint_vel):
224+
"""
225+
Disable Bullet's built-in motors before using explicit torque control.
226+
227+
Args:
228+
robot_id (int): PyBullet body id.
229+
pb_to_pin_joint_vel (Dict[int,int]): Bullet joint id -> Pinocchio velocity index map.
230+
"""
231+
for j_id in pb_to_pin_joint_vel:
232+
pb.setJointMotorControl2(robot_id, j_id, pb.VELOCITY_CONTROL, force=0.0)
233+
234+
235+
def _apply_torque_to_pybullet(robot_id, tau, pb_to_pin_joint_vel):
236+
"""
237+
Apply actuator torques to Bullet joints using Pinocchio/TSID ordering.
238+
239+
Args:
240+
robot_id (int): PyBullet body id.
241+
tau (np.ndarray): Actuated joint torques, ordered like Pinocchio velocities after free-flyer.
242+
pb_to_pin_joint_vel (Dict[int,int]): Bullet joint id -> Pinocchio velocity index map.
243+
244+
Notes:
245+
TSID returns only actuated torques, while Pinocchio velocity indices include
246+
the 6D floating base first. The torque index is therefore ``idx_v - 6``.
247+
"""
248+
for j_id, v_id in pb_to_pin_joint_vel.items():
249+
tau_id = v_id - 6
250+
if tau_id < 0 or tau_id >= len(tau):
251+
continue
252+
253+
torque = float(tau[tau_id])
254+
joint_max = pb.getJointInfo(robot_id, j_id)[10]
255+
if joint_max > 0.0:
256+
torque = float(np.clip(torque, -joint_max, joint_max))
257+
258+
pb.setJointMotorControl2(robot_id, j_id, pb.TORQUE_CONTROL, force=torque)
259+
260+
223261
def _reset_pybullet_position(robot_id, q_des, j_to_q_idx):
224262
"""
225263
Hard reset of Bullet joint positions.
@@ -273,6 +311,44 @@ def _get_q_from_pybullet(robot_id, nq, map_joint_idx_to_q_idx):
273311
return q
274312

275313

314+
def _get_v_from_pybullet(robot_id, nv, pb_to_pin_joint_vel):
315+
"""
316+
Read a Pinocchio-style generalized velocity vector from PyBullet.
317+
318+
Args:
319+
robot_id (int): PyBullet body id.
320+
nv (int): size of the generalized velocity vector.
321+
pb_to_pin_joint_vel (Dict[int,int]): Bullet joint id -> Pinocchio velocity index map.
322+
323+
Returns:
324+
np.ndarray: generalized velocity with the free-flyer first, then joints.
325+
"""
326+
v = np.zeros(nv)
327+
328+
com_pos, com_quat = pb.getBasePositionAndOrientation(robot_id)
329+
lin_com_world, ang_world = pb.getBaseVelocity(robot_id)
330+
331+
# Convert Bullet's inertial/CoM base velocity to the base_link velocity.
332+
_, _, _, p_li, q_li, *_ = pb.getDynamicsInfo(robot_id, -1)
333+
p_ib, q_ib = pb.invertTransform(p_li, q_li)
334+
_, q_base = pb.multiplyTransforms(com_pos, com_quat, p_ib, q_ib)
335+
336+
base_rot = np.array(pb.getMatrixFromQuaternion(q_base)).reshape(3, 3)
337+
com_offset_world = base_rot @ np.array(p_li)
338+
lin_base_world = np.array(lin_com_world) - np.cross(np.array(ang_world), com_offset_world)
339+
340+
# Pinocchio free-flyer velocities are expressed in the local base frame.
341+
v[:3] = base_rot.T @ lin_base_world
342+
v[3:6] = base_rot.T @ np.array(ang_world)
343+
344+
for j_id, v_id in pb_to_pin_joint_vel.items():
345+
if v_id < 0:
346+
continue
347+
v[v_id] = pb.getJointState(robot_id, j_id)[1]
348+
349+
return v
350+
351+
276352
def _build_pb_to_pin_joints_map(robot_id, model):
277353
"""
278354
Build Bullet joint id -> Pinocchio q index map using joint names.
@@ -526,6 +602,25 @@ def apply_joints_vel_to_robot(self, v: np.ndarray):
526602
robot_id=self.robot_id, v_des=v, pb_to_pin_joint_vel=self.pb_to_pin_joint_vel
527603
)
528604

605+
def disable_joint_motors(self):
606+
"""
607+
Disable PyBullet's default joint motors before explicit torque control.
608+
"""
609+
_disable_pybullet_joint_motors(self.robot_id, self.pb_to_pin_joint_vel)
610+
611+
def apply_joint_torques(self, tau: np.ndarray):
612+
"""
613+
Send joint torques to PyBullet in TSID/Pinocchio actuator ordering.
614+
615+
Parameters
616+
----------
617+
tau : np.ndarray, shape (nv - 6,)
618+
Actuated joint torques returned by TSID.
619+
"""
620+
_apply_torque_to_pybullet(
621+
robot_id=self.robot_id, tau=tau, pb_to_pin_joint_vel=self.pb_to_pin_joint_vel
622+
)
623+
529624
def get_q(self, nq: int) -> np.ndarray:
530625
"""
531626
Read the current configuration vector from PyBullet.
@@ -538,10 +633,26 @@ def get_q(self, nq: int) -> np.ndarray:
538633
Returns
539634
-------
540635
np.ndarray, shape (nq,)
541-
Configuration with base pose first (x,y,z, qw,qx,qy,qz), then joints.
636+
Configuration with base pose first (x,y,z, qx,qy,qz,qw), then joints.
542637
"""
543638
return _get_q_from_pybullet(self.robot_id, nq, self.pb_to_pin_joints_pos)
544639

640+
def get_v(self, nv: int) -> np.ndarray:
641+
"""
642+
Read the current generalized velocity vector from PyBullet.
643+
644+
Parameters
645+
----------
646+
nv : int
647+
Total velocity size expected by Pinocchio.
648+
649+
Returns
650+
-------
651+
np.ndarray, shape (nv,)
652+
Floating-base and joint velocities in Pinocchio ordering.
653+
"""
654+
return _get_v_from_pybullet(self.robot_id, nv, self.pb_to_pin_joint_vel)
655+
545656
def update_camera_to_follow_pos(self, x: float, y: float, z: float):
546657
"""
547658
Aim the debug camera at a world position.

0 commit comments

Comments
 (0)