update robot: add centroidal dynamics and few minor updates

This commit is contained in:
Brian Delhaisse
2019-06-18 00:47:37 +02:00
parent 124ebdf5be
commit 28c3b7e187
3 changed files with 422 additions and 147 deletions
+416 -141
View File
@@ -25,7 +25,7 @@ from pyrobolearn.robots.base import ControllableBody
__author__ = "Brian Delhaisse"
__copyright__ = "Copyright 2018, PyRoboLearn"
__credits__ = ["Brian Delhaisse", "Leonel Rozo"]
__credits__ = ["Brian Delhaisse", "Leonel Rozo", "Songyan Xin"]
__license__ = "MIT"
__version__ = "1.0.0"
__maintainer__ = "Brian Delhaisse"
@@ -182,7 +182,16 @@ class Robot(ControllableBody):
@property
def num_dofs(self):
"""Return the number of degrees of freedom (i.e. the number of joints that are not fixed)"""
"""Return the number of degrees of freedom (DoFs); that is, if the base is not fixed, 6 (= 3 degrees for
translation + 3 degrees for orientation) + the joints that are not fixed.
"""
if self.fixed_base:
return len(self.joints)
return len(self.joints) + 6
@property
def num_free_joints(self):
"""Return the number of joints that are not fixed."""
return len(self.joints)
########
@@ -304,6 +313,9 @@ class Robot(ControllableBody):
self.com = self.sim.get_center_of_mass_position(self.id)
return self.com
# alias
get_com_position = get_center_of_mass_position
def get_center_of_mass_velocity(self):
"""
Return the center of mass velocity.
@@ -313,6 +325,9 @@ class Robot(ControllableBody):
"""
return self.sim.get_center_of_mass_velocity(self.id)
# alias
get_com_velocity = get_center_of_mass_velocity
# def get_linear_momentum(self):
# """
# Compute the linear momentum around the center of mass.
@@ -339,28 +354,6 @@ class Robot(ControllableBody):
# np.array[3]: angular momentum
# """
# pass
#
# def get_centroidal_dynamics(self, q=None, dq=None):
# """
# Compute the centroidal momentum dynamics based on [1]. "The centroidal momentum of a rigid-body system
# consists of its net linear momentum as well as its net angular momentum about its center of mass (CoM)" [1]
#
# Args:
# q (np.array[N], None): joint positions of size N, where N is the total number of DoFs. If None, it will
# get the current joint positions (but note that this could lead to a decrease of performance).
# dq (np.array[M], None): joint velocities of size M (with 0 < M <= N). If None, it will
# get the current joint velocities (but note that this could lead to a decrease of performance).
#
# Returns:
# np.array[6, N+6]: centroidal momentum matrix :math:`A_G`
# np.array[6]: the dot product between the derivative of the centroidal momentum matrix with the
# generalized velocities vector. That is, :math:`\dot{A}_G \dot{q}`
#
# References:
# [1] "Improved computation of the humanoid centroidal dynamics and application for whole-body control",
# Wensing and Orin, 2016
# """
# pass
########################
# Joints (joint space) #
@@ -1520,7 +1513,7 @@ class Robot(ControllableBody):
link_ids = list(range(self.num_links))
return np.array([self.sim.get_dynamics_info(self.id, link)[2] for link in link_ids])
def set_link_positions(self, link_ids, position, orientation=None):
def set_link_positions(self, link_ids, positions, orientations=None):
"""
Set the position(s) of the given link(s) using inverse kinematics (IK).
@@ -1529,9 +1522,22 @@ class Robot(ControllableBody):
Args:
link_ids (int, int[N]): link id, or list of desired link ids.
position (np.array[3], [np.array[3]], np.array[N,3]):
orientation (np.array[4], [np.array[4]], np.array[N,4]):
positions (np.array[3], [np.array[3]], np.array[N,3]): desired link position(s).
orientations (np.array[4], list of np.array[4], np.array[N,4]): desired link orientation(s) (expressed as
quaternions [x,y,z,w])
"""
# TODO: think when setting the position of multiple links where some joints are shared (need to use the
# null-space)
pass
def set_link_velocities(self, link_ids, velocities):
# TODO: think when setting the position of multiple links where some joints are shared (need to use the
# null-space)
pass
def set_link_forces(self, link_ids, forces):
# TODO: think when setting the position of multiple links where some joints are shared (need to use the
# null-space)
pass
#################
@@ -1869,10 +1875,10 @@ class Robot(ControllableBody):
robot_mass = 0
# initialize center of mass jacobian: J_com
if self.has_fixed_base():
Jcom = np.zeros((6, self.num_dofs))
else:
Jcom = np.zeros((6, self.num_dofs + 6))
# if self.has_fixed_base():
Jcom = np.zeros((6, self.num_dofs))
# else:
# Jcom = np.zeros((6, self.num_dofs + 6))
# calculate the CoM jacobian
for link_id in range(self.num_links):
@@ -1885,6 +1891,9 @@ class Robot(ControllableBody):
return Jcom
# alias
get_com_jacobian = get_center_of_mass_jacobian
def get_angular_velocities_from_derivative_rpy(self, rpy_angle, dRPY):
r"""
Return the angular velocities :math:`\omega` from the derivative of RPY Euler angles \math:`\dot{\phi}`.
@@ -2041,21 +2050,6 @@ class Robot(ControllableBody):
"""
return np.linalg.inv(self.get_JJT(jacobian))
@staticmethod
def compute_dynamic_manipulability_ellipsoid(jacobian, inertia):
r"""
Compute the dynamic manipulability ellipsoid (matrix) as `M = J(q)H(q)^{-1} (J(q)H(q)^{-1})^T`.
Args:
jacobian (np.array[D,N]): Jacobian matrix
inertia (np.array[N,N], np.array[6+N,6+N], np.array[M,M]): inertia matrix in joint space
Returns:
np.array[D,D]: dynamic manipulability
"""
epsilon = jacobian.dot(np.linalg.inv(inertia))
return epsilon.dot(epsilon.T)
@staticmethod
def in_singular_configuration(jacobian):
r"""
@@ -2194,44 +2188,6 @@ class Robot(ControllableBody):
return dq, np.min(S), distance
def calculate_inverse_differential_kinematics_dynamic_manipulability(self, jacobian, inertia,
target_dynamic_manipulability, Km):
"""
Compute the inverse differential kinematics for dynamic Manipulability; it will return a joint velocity for all
the actuated joints.
Args:
jacobian (np.array[D,N]): Jacobian matrix
target_dynamic_manipulability (np.array[D,D]): target dynamic manipulability
Km (float[,]): Proportional gain for manipulability error
Returns:
np.array[N]: joint velocities
float: minimum of eigenvalues of the dynamic manip. Jacobian
float: Distance between desired and current manip. ellipsoids
"""
num_task_vars = np.size(target_dynamic_manipulability, 0)
# Compute manipulability error
dynamic_manip = self.compute_dynamic_manipulability_ellipsoid(jacobian, inertia)
Me = logarithm_map([target_dynamic_manipulability[0:num_task_vars, 0:num_task_vars]],
dynamic_manip[0:num_task_vars, 0:num_task_vars])[0]
# print("Me: {}".format(Me))
distance = distance_spd(target_dynamic_manipulability[0:num_task_vars, 0:num_task_vars],
dynamic_manip[0:num_task_vars, 0:num_task_vars])
print("SPD dist: {}".format(distance))
Jm_red = self.compute_dynamic_manipulability_jacobian(jacobian, inertia, num_task_vars)
# print("Jm: {}".format(Jm_red))
# Matrix singularity robustness
U, S, Vh = np.linalg.svd(Jm_red)
damping = 1E-2 if np.min(S) < 1E-2 else 1E-8
dq = np.dot(self.get_damped_least_squares_inverse(Jm_red, damping), np.dot(Km, symmetric_matrix_to_vector(Me)))
return dq, np.min(S), distance
def compute_velocity_manipulability_jacobian(self, jacobian, num_task_vars):
r"""
Compute the velocity manipulability Jacobian [1].
@@ -2274,50 +2230,6 @@ class Robot(ControllableBody):
return Jm_red
def compute_dynamic_manipulability_jacobian(self, jacobian, inertia, num_task_vars):
r"""
Compute the dynamic manipulability Jacobian.
Args:
jacobian (np.array[D,N]): Jacobian matrix
inertia (np.array[N,N]): inertia matrix
num_task_vars (float): number of task variables (usually 3 or 6)
Returns:
np.array[(num_task_vars * num_task_vars + num_task_vars) / 2, N]: manipulability jacobian matrix
"""
num_dofs = jacobian.shape[1]
# Compute derivative of Jacobian wrt joint angles
J_grad = self.compute_jacobian_joint_derivative(jacobian)
# Compute derivative of Inertia matrix wrt joint angles
H_grad = self.compute_inertia_joint_derivative(jacobian)
#
L = np.dot(jacobian, np.linalg.inv(inertia))
# for i in range(J_grad.shape[2]):
# print("dJ/dq_{}: {}".format(i, J_grad[:, :, i]))
# for i in range(H_grad.shape[2]):
# print("dH/dq_{}: {}".format(i, H_grad[:, :, i]))
# Dynamic manipulability Jacobian
Lgrad = tensor_matrix_product(J_grad, np.linalg.inv(inertia), 1) - \
tensor_matrix_product(tensor_matrix_product(H_grad, L, 0), np.linalg.inv(inertia), 1)
Jm = tensor_matrix_product(np.transpose(Lgrad[:, :], [1, 0, 2]), L, 0) + tensor_matrix_product(Lgrad, L, 1)
# for i in range(Jm.shape[2]):
# print("Jm_{}: {}".format(i, Jm[:, :, i]))
# Jm = Jm[num_task_vars, num_task_vars, :]
# # Manipulability Jacobian in matrix form (Mandel notation)
# num_vars = len(num_task_vars)
Jm_red = np.zeros(((num_task_vars * num_task_vars + num_task_vars) / 2, np.sum(num_dofs)))
# print("Jm_red.shape: {}".format(Jm_red.shape))
for i in range(Jm.shape[2]):
Jm_red[:, i] = symmetric_matrix_to_vector(Jm[0:num_task_vars, 0:num_task_vars, i])
return Jm_red
def hard_priorities(self, jacobians, task_velocities, method='backtrack'):
r"""
Return dq.
@@ -2472,7 +2384,7 @@ class Robot(ControllableBody):
if q is None:
q = self.get_joint_positions()
else:
if len(q) != self.num_dofs:
if len(q) != self.num_free_joints: # self.num_dofs:
raise ValueError("All the joint positions need to be given to this method. You can then slice the"
"inertia matrix afterward.")
@@ -2485,6 +2397,9 @@ class Robot(ControllableBody):
return np.array(self.sim.calculate_mass_matrix(self.id, q_aug))
return np.array(self.sim.calculate_mass_matrix(self.id, q_aug))[q_idx, q_idx]
# alias
get_inertia_matrix = get_mass_matrix
def compute_inertia_joint_derivative(self, jacobian):
r"""
Compute the derivative of the Inertia matrix H(q) wrt joint values q.
@@ -2749,6 +2664,46 @@ class Robot(ControllableBody):
dq = np.zeros(len(q))
return self.get_coriolis_and_gravity_compensation_torques(q, dq, q_idx)
def get_coriolis_torques(self, q=None, dq=None, q_idx=None):
r"""
Return the torques that need to be applied to the robot joints such that it compensates for Coriolis effects
in the absence of gravity, i.e. :math:`\tau = C(q,\dot{q}) \dot{q}`
From the equations of motion:
.. math:: H(q) \ddot{q} + C(q,\dot{q}) \dot{q} + g(q) = \tau + J^T(q) F,
we can see that if we set :math:`F` and :math:`\ddot{q}` to 0, then we have:
.. math:: \tau_1 = C(q,\dot{q}) \dot{q} + g(q),
and if additionally, we set :math:`\dot{q}` to 0, then we have:
.. math:: \tau_2 = g(q).
We can then get :math:`C(q,\dot{q}) \dot{q} = \tau_1 - \tau_2`.
Args:
q (np.array[N], None): all the joint positions. If None, it will get the current joint positions of all the
joints. However, note that if you already got the joint positions in your code,
it is better to pass them to this method for performance.
dq (np.array[N], None): all the joint velocities. If None, it will get the current joint velocities of
all the joints.
q_idx (int[M], None): slice the torques at the given q indices (0 < M <= N).
Returns:
np.array[M]: joint torques to be applied [Nm]
"""
if q is None:
q = self.get_joint_positions()
if dq is None:
dq = self.get_joint_velocities()
tau1 = self.get_coriolis_and_gravity_compensation_torques(q=q, dq=dq, q_idx=q_idx)
tau2 = self.get_gravity_compensation_torques(q=q, q_idx=q_idx)
return tau1 - tau2
def apply_coriolis_and_gravity_compensation(self, q=None, dq=None, q_idx=None, external_torques=0.):
r"""
Apply Coriolis and Gravity Compensation; set the torques using torque control.
@@ -2772,7 +2727,7 @@ class Robot(ControllableBody):
# TODO: finish to implement the method + think about multiple links + think about dimensions
def get_active_compliant_torques(self, q=None, dq=None, q_idx=None, jacobian=None, link_velocity=None,
link_ids=None, kd=60):
link_id=None, kd=60):
r"""
Return the torques that need to be applied to enable active compliance. This is done by enabling Coriolis
and gravity compensation along with a damping force projected from the Cartesian space to the joint space.
@@ -2790,6 +2745,8 @@ class Robot(ControllableBody):
dq (np.array[N], None): all the joint velocities. If None, it will get the current joint velocities of
all the joints.
q_idx (int[M], None): slice the torques at the given q indices (0 < M <= N).
jacobian (np.array[6,N], np.array[6,6+N]): Jacobian matrix.
link_velocity (np.array[6]): linear and angular velocity of the link in the Cartesian world space
Returns:
np.array[M]: joint torques to be applied [Nm]
@@ -2822,33 +2779,341 @@ class Robot(ControllableBody):
dq (np.array[N], None): all the joint velocities. If None, it will get the current joint velocities of
all the joints.
q_idx (int[M], None): slice the torques at the given q indices (0 < M <= N).
external_torques (float, np.array[M]): external torques.
"""
joint_id = self.joints if q_idx is None else self.joints[q_idx]
torques = self.get_active_compliant_torques(q, dq, q_idx)
self.set_joint_torques(joint_id, torques + external_torques)
self.set_joint_torques(torques + external_torques, joint_id)
def get_impedance_torques(self, x=0, dx=0, ddx=0):
def get_impedance_torques(self, jacobian, xdes=0, x=0, dx=0, dxdes=0, ddx=0, ddxdes=0, Km=1, Dm=0.01):
r"""
Return the impedance torques.
.. math:: F_{a} = H_m (\ddot{x} - \ddot{x}_d) + D_m (\dot{x} - \dot{x}_d) + K_m (x - x_d)
Args:
Returns:
np.array[N]: impedance torques
References:
[1] Lecture on "Impedance Control" by Prof. De Luca, Universita di Roma,
http://www.diag.uniroma1.it/~deluca/rob2_en/15_ImpedanceControl.pdf
"""
pass
def apply_task_impedance_control(self):
def get_attractor_torques(self, x_des, jacobian, link_id=None, q=None, dq=None, x=None, dx=None, K=5, D=0.1):
r"""
.. math:: F_{a} = H_m (\ddot{x} - \ddot{x}_d) + D_m (\dot{x} - \dot{x}_d) + K_m (x - x_d)
"""
pass
def get_attractor_torques(self):
r"""
The torques to be applied are given by:
The torques to be applied (using impedance control with an attractor point) are given by:
.. math:: \tau = C(q,\dot{q}) \dot{q} + g(q) + J^T F
where :math:`F = K(x_d - x) - D v` with :math:`x` and :math:`v` are the Cartesian position and velocities,
and :math:`K` and :math:`D` are the stiffness and damping factor, respectively.
Args:
q (np.array[N]): joint positions
dq (np.array[N]): joint velocities
x_des (np.array[3]): desired position of the link
x (np.array[3]): cartesian world position of the link
dx (np.array[3]): cartesian world linear velocity of the link
jacobian (np.array[3,N]): linear jacobian associated to the link
K (float, np.array[3,3]): proportional gain scalar or matrix
D (float, np.array[3,3]): derivative gain scalar or matrix
Returns:
np.array[N]: torques to apply
References:
[1] Lecture on "Impedance Control" by Prof. De Luca, Universita di Roma,
http://www.diag.uniroma1.it/~deluca/rob2_en/15_ImpedanceControl.pdf
"""
pass
# check arguments
if q is None:
q = self.get_joint_positions()
if dq is None:
dq = self.get_joint_velocities()
if link_id is not None:
if x is None:
x = self.get_link_world_positions(link_id)
if dx is None:
dx = self.get_link_world_linear_velocities(link_id)
if jacobian is None:
jacobian = self.get_jacobian(link_id, q=q)
# coriolis and gravity torques
torques = self.get_coriolis_and_gravity_compensation_torques(q=q, dq=dq)
# impedance control: attractor point
forces = K.dot(x_des - x) - D.dot(dx)
torques += jacobian.T.dot(forces)
return torques
@staticmethod
def compute_dynamic_manipulability_ellipsoid(jacobian, inertia):
r"""
Compute the dynamic manipulability ellipsoid (matrix) as `M = J(q)H(q)^{-1} (J(q)H(q)^{-1})^T`.
Args:
jacobian (np.array[D,N]): Jacobian matrix
inertia (np.array[N,N]): inertia matrix in joint space
Returns:
np.array[D,D]: dynamic manipulability
"""
epsilon = jacobian.dot(np.linalg.inv(inertia))
return epsilon.dot(epsilon.T)
def calculate_inverse_differential_kinematics_dynamic_manipulability(self, jacobian, inertia,
target_dynamic_manipulability, Km):
"""
Compute the inverse differential kinematics for dynamic Manipulability; it will return a joint velocity for all
the actuated joints.
Args:
jacobian (np.array[D,N]): Jacobian matrix
inertia (np.array[N,N]): inertia matrix
target_dynamic_manipulability (np.array[D,D]): target dynamic manipulability
Km (float[,]): Proportional gain for manipulability error
Returns:
np.array[N]: joint velocities
float: minimum of eigenvalues of the dynamic manip. Jacobian
float: Distance between desired and current manip. ellipsoids
"""
num_task_vars = np.size(target_dynamic_manipulability, 0)
# Compute manipulability error
dynamic_manip = self.compute_dynamic_manipulability_ellipsoid(jacobian, inertia)
Me = logarithm_map([target_dynamic_manipulability[0:num_task_vars, 0:num_task_vars]],
dynamic_manip[0:num_task_vars, 0:num_task_vars])[0]
# print("Me: {}".format(Me))
distance = distance_spd(target_dynamic_manipulability[0:num_task_vars, 0:num_task_vars],
dynamic_manip[0:num_task_vars, 0:num_task_vars])
print("SPD dist: {}".format(distance))
Jm_red = self.compute_dynamic_manipulability_jacobian(jacobian, inertia, num_task_vars)
# print("Jm: {}".format(Jm_red))
# Matrix singularity robustness
U, S, Vh = np.linalg.svd(Jm_red)
damping = 1E-2 if np.min(S) < 1E-2 else 1E-8
dq = np.dot(self.get_damped_least_squares_inverse(Jm_red, damping), np.dot(Km, symmetric_matrix_to_vector(Me)))
return dq, np.min(S), distance
def compute_dynamic_manipulability_jacobian(self, jacobian, inertia, num_task_vars):
r"""
Compute the dynamic manipulability Jacobian.
Args:
jacobian (np.array[D,N]): Jacobian matrix
inertia (np.array[N,N]): inertia matrix
num_task_vars (float): number of task variables (usually 3 or 6)
Returns:
np.array[(num_task_vars * num_task_vars + num_task_vars) / 2, N]: manipulability jacobian matrix
"""
num_dofs = jacobian.shape[1]
# Compute derivative of Jacobian wrt joint angles
J_grad = self.compute_jacobian_joint_derivative(jacobian)
# Compute derivative of Inertia matrix wrt joint angles
H_grad = self.compute_inertia_joint_derivative(jacobian)
#
L = np.dot(jacobian, np.linalg.inv(inertia))
# for i in range(J_grad.shape[2]):
# print("dJ/dq_{}: {}".format(i, J_grad[:, :, i]))
# for i in range(H_grad.shape[2]):
# print("dH/dq_{}: {}".format(i, H_grad[:, :, i]))
# Dynamic manipulability Jacobian
Lgrad = tensor_matrix_product(J_grad, np.linalg.inv(inertia), 1) - \
tensor_matrix_product(tensor_matrix_product(H_grad, L, 0), np.linalg.inv(inertia), 1)
Jm = tensor_matrix_product(np.transpose(Lgrad[:, :], [1, 0, 2]), L, 0) + tensor_matrix_product(Lgrad, L, 1)
# for i in range(Jm.shape[2]):
# print("Jm_{}: {}".format(i, Jm[:, :, i]))
# Jm = Jm[num_task_vars, num_task_vars, :]
# # Manipulability Jacobian in matrix form (Mandel notation)
# num_vars = len(num_task_vars)
Jm_red = np.zeros(((num_task_vars * num_task_vars + num_task_vars) / 2, np.sum(num_dofs)))
# print("Jm_red.shape: {}".format(Jm_red.shape))
for i in range(Jm.shape[2]):
Jm_red[:, i] = symmetric_matrix_to_vector(Jm[0:num_task_vars, 0:num_task_vars, i])
return Jm_red
def get_centroidal_dynamics(self, q=None, dq=None, inertia=None):
r"""
Compute the centroidal momentum dynamics based on [1]. "The centroidal momentum of a rigid-body system
consists of its net linear momentum as well as its net angular momentum about its center of mass (CoM)" [1]
The centroidal momentum, which is the sum of all body spatial momenta computed with respect to the CoM, is
given by:
.. math:: h_G = A_G \dot{q},
where :math:`h_G \in \mathbb{R}^6` is the centroidal momentum, :math:`A_G \in \mathbb{R}^{6 \times (n+6)}` is
the centroidal momentum matrix (CMM), and :math:`\dot{q}` are the system's generalized velocities. The CMM is
related to the joint space inertia matrix (see code). This centroidal momentum collects the system linear and
angular momentum together.
The centroidal dynamics are then given by the equation:
.. math:: \dot{h}_G = A_G \ddot{q} + \dot{A}_G \dot{q}.
The centroidal dynamics :math:`\dot{h}_G` are then linked to external forces on the system by:
.. math:: \dot{h}_G = f_G^{net},
where :math:`f_G^{net}` is the net external wrench applied on the robot expressed at the CoM. This last term
includes for instance the gravity force and ground reaction forces.
Warnings: this currently does not work with a fixed base.
Args:
q (np.array[N], None): joint positions of size N, where N is the total number of free joints. If None, it
will get the current joint positions (but note that this could lead to a decrease of performance).
dq (np.array[N], None): joint velocities of size N, where N is the total number of free joints. If None, it
will get the current joint velocities (but note that this could lead to a decrease of performance).
inertia (np.array[6+N,6+N]): inertia matrix. If None, it will get the current inertia matrix
(but note that this could lead to a decrease of performance if you have already computed it).
Returns:
np.array[6, N+6]: the centroidal momentum matrix :math:`A_G`
np.array[6]: the centroidal dynamics velocity-dependent bias vector :math:`\dot{A}_G \dot{q}`
Raises:
RuntimeError: if the robot has not a floating base (i.e. it has a fixed base).
References:
[1] "Improved computation of the humanoid centroidal dynamics and application for whole-body control",
Wensing and Orin, 2016
[2] "Motion Planning and Control of Dynamic Humanoid Locomotion" (PhD thesis: sec 2.1.5 and 3.1.3), Xin,
2018
"""
# check if floating base
if self.fixed_base:
raise RuntimeError("You can not get the centroidal dynamics for a body with a fixed base; need to be a "
"floating base.")
# get number of DoFs
N, n = self.num_dofs, self.num_dofs - 6 # N=n+6
# check arguments
if q is None:
q = self.get_joint_positions() # shape = (N,)
if dq is None:
dq = self.get_joint_velocities() # shape = (N,)
if inertia is None:
inertia = self.get_mass_matrix(q) # shape = (N,N)
H = inertia
# Coriolis term: C(q, dq) * dq
C_dq = self.get_coriolis_torques(q=q, dq=dq) # shape = (N,)
# U_1
U_1 = np.hstack((np.identity(6), np.zeros((6, n)))) # shape = (6,n+6)
# R_01(q_1)
R_01 = get_matrix_from_quaternion(self.get_base_orientation()).T # shape = (3,3)
# Phi_1: this is a matrix transfer generalized velocity of floating base to spatial velocity defined in local
# frame
Phi_1 = np.vstack((np.hstack((np.zeros((3, 3)), np.identity(3))),
np.hstack((R_01.T, np.zeros((3, 3)))))) # shape = (6,6)
# Psi_1
Psi_1 = np.linalg.inv(Phi_1) # shape = (6,6)
# some other computations
H11 = U_1.dot(H).dot(U_1.T) # shape = (6,6)
I1C = Psi_1.T.dot(H11).dot(Psi_1) # shape = (6,6)
M = I1C[6 - 1, 6 - 1]
p1G = (1.0 / M) * np.array([I1C[3 - 1, 5 - 1], I1C[1 - 1, 6 - 1], I1C[2 - 1, 4 - 1]]) # shape = (3,)
X_iG_T = np.vstack((np.hstack((R_01, R_01.dot(skew_matrix(p1G).T))),
np.hstack((np.zeros((3, 3)), R_01)))) # shape = (6,6)
# compute centroidal momentum matrix and the dot product between the derivative of this centroidal momentum
# matrix with the generalized velocities vector
A_G = X_iG_T.dot(Psi_1.T).dot(U_1).dot(H) # shape = (6,n+6)
A_Gd_dq = X_iG_T.dot(Psi_1.T).dot(U_1).dot(C_dq) # shape = (6,)
return A_G, A_Gd_dq
def get_centroidal_momentum(self, q=None, dq=None, inertia=None):
r"""
Return the centroidal momentum which consists of the net linear and angular momentum about the rigid-body's
center of mass (CoM). This is thus the sum of all body spatial momenta computed with respect to the CoM,
given by:
.. math:: h_G = A_G \dot{q},
where :math:`h_G \in \mathbb{R}^6` is the centroidal momentum, :math:`A_G \in \mathbb{R}^{6 \times (n+6)}` is
the centroidal momentum matrix (CMM), and :math:`\dot{q}` are the system's generalized velocities. The CMM is
related to the joint space inertia matrix (see code). This centroidal momentum collects the system linear and
angular momentum together.
Args:
q (np.array[N], None): joint positions of size N, where N is the total number of free joints. If None, it
will get the current joint positions (but note that this could lead to a decrease of performance).
dq (np.array[N], None): joint velocities of size N, where N is the total number of free joints. If None, it
will get the current joint velocities (but note that this could lead to a decrease of performance).
inertia (np.array[6+N,6+N]): inertia matrix. If None, it will get the current inertia matrix
(but note that this could lead to a decrease of performance if you have already computed it).
Returns:
np.array[6]: the centroidal momentum
Raises:
RuntimeError: if the robot has not a floating base (i.e. it has a fixed base).
References:
[1] "Improved computation of the humanoid centroidal dynamics and application for whole-body control",
Wensing and Orin, 2016
[2] "Motion Planning and Control of Dynamic Humanoid Locomotion" (PhD thesis: sec 2.1.5 and 3.1.3), Xin,
2018
"""
if dq is None:
dq = self.get_joint_velocities() # shape = (N,)
A_G, A_Gd_dq = self.get_centroidal_dynamics(q, dq, inertia)
return A_G.dot(dq)
@staticmethod
def get_centroidal_momentum_singular_values(A_G):
r"""
Return the singular values of the centroidal momentum matrix.
Args:
A_G (np.array[6, N+6]): centroidal momentum matrix
Returns:
np.array[6]: singular values
"""
u, s, vh = np.linalg.svd(A_G, full_matrices=True)
return s
@staticmethod
def get_centroidal_momentum_orientation_and_scale(A_G):
r"""
Return the orientation and scale of the centroidal momentum matrix ellipsoid.
Args:
A_G (np.array[6, N+6]): centroidal momentum matrix
Returns:
np.array[4]: orientation (expressed as a quaternion [x,y,z,w])
float: scale
"""
u, scale, vh = np.linalg.svd(A_G, full_matrices=True)
quaternion = get_quaternion_from_matrix(u)
return quaternion, scale
######################
# Symbolic Equations #
@@ -3474,6 +3739,16 @@ class Robot(ControllableBody):
@staticmethod
def get_ellipsoid_orientation_and_scale(X):
r"""
Get ellipsoid's orientation and scale.
Args:
X (np.array): 2D matrix
Returns:
np.array[4]: orientation (expressed as a quaternion [x,y,z,w])
float: scale
"""
# compute evecs and singular values
_, S, V = np.linalg.svd(X)
+2 -2
View File
@@ -1948,7 +1948,7 @@ class Raisim(Simulator):
pass
def calculate_jacobian(self, body_id, link_id, local_position, q, dq, des_ddq):
"""
r"""
Return the full geometric Jacobian matrix :math:`J(q) = [J_{lin}(q), J_{ang}(q)]^T`, such that:
.. math:: v = [\dot{p}, \omega]^T = J(q) \dot{q}
@@ -1974,7 +1974,7 @@ class Raisim(Simulator):
pass
def calculate_mass_matrix(self, body_id, q):
"""
r"""
Return the mass/inertia matrix :math:`H(q)`, which is used in the rigid-body equation of motion (EoM) in joint
space given by (see [1]):
+4 -4
View File
@@ -1001,7 +1001,7 @@ class Simulator(object):
def set_joint_motor_control(self, body_id, joint_ids, control_mode=2, positions=None,
velocities=None, forces=None, kp=None, kd=None, max_velocity=None):
"""
r"""
Set the joint motor control.
In position control:
@@ -2083,7 +2083,7 @@ class Simulator(object):
pass
def calculate_jacobian(self, body_id, link_id, local_position, q, dq, des_ddq):
"""
r"""
Return the full geometric Jacobian matrix :math:`J(q) = [J_{lin}(q), J_{ang}(q)]^T`, such that:
.. math:: v = [\dot{p}, \omega]^T = J(q) \dot{q}
@@ -2109,7 +2109,7 @@ class Simulator(object):
pass
def calculate_mass_matrix(self, body_id, q):
"""
r"""
Return the mass/inertia matrix :math:`H(q)`, which is used in the rigid-body equation of motion (EoM) in joint
space given by (see [1]):
@@ -2134,7 +2134,7 @@ class Simulator(object):
def calculate_inverse_kinematics(self, body_id, link_id, position, orientation=None, lower_limits=None,
upper_limits=None, joint_ranges=None, rest_poses=None, joint_dampings=None,
solver=None, q_curr=None, max_iters=None, threshold=None):
"""
r"""
Compute the FULL Inverse kinematics; it will return a position for all the actuated joints.
"You can compute the joint angles that makes the end-effector reach a given target position in Cartesian world