This is something that i have looked for in standard textbooks and have not found much information on it. I am not sure if it is because they are geared towards industrial arms which are typically not backdrivable. (meaning once you reach a position, you can essentially turn off your motors and the arm will continue to hold position). Or if they don't care about this level of control. (meaning they have low-level controllers that handle it and the arm will "know what to do"). Or if it is too simple of a problem to worry about.
nevertheless, i wrote some code to calculate these torques for a simulated robot. the output seems correct, although it was never tested. This is python code for OpenRAVE.
# Calculate the center of mass past the current joint.
# This assumes the mass is specified properly for each link.
mass = 0.0
com = np.array([0.0, 0.0, 0.0])
for i in range(joint.GetDOFIndex(), robot.GetDOF()):
j = robot.GetJointFromDOFIndex(i)
link = j.GetHierarchyChildLink()
mass += link.GetMass()
com += link.GetMass() * link.GetGlobalCOM()
# todo: add mass in robot's hand
com /= mass
return (mass, com)
# Calculate the torque required to hold joints in current configuration.
# 1. calculate the center of mass "past" the joint
# 2. find the closest perpendicular distance between the COM and joint axis
# 3. cross the joint axis with gravity vector
# 4. cross the perpendicular distance vector with gravity vector
# 5. return mass * direction * magnitude of both crosses
(mass, com) = get_COM_past_joint(joint)
anchor = joint.GetAnchor()
axis = joint.GetAxis()
joint_to_com_closest = (anchor - com) - np.dot((anchor - com), axis) * axis
axis_cross_k = np.cross(axis, np.array([0, 0, -1]))
joint_to_com_closest_cross_k = np.cross(joint_to_com_closest, np.array([0, 0, -1]))
direction = np.sign(np.dot(axis, joint_to_com_closest_cross_k))
EDIT: I recently found out that OpenRAVE does a more advanced form of this calculation already. (And their units are correct too).
torques = robot.ComputeInverseDynamics(dofaccelerations)
torqueconfiguration, torquecoriolis, torquegravity = robot.ComputeInverseDynamics(dofaccelerations,None,returncomponents=True)