Humanoid Kinematics Fundamentals
Introductionβ
Humanoid robots represent the pinnacle of robotic engineering, requiring sophisticated control systems to mimic human motion. This chapter explores the mathematical and algorithmic foundations of humanoid locomotion.
1. Kinematic Chainsβ
Forward Kinematicsβ
Given joint angles ΞΈβ, ΞΈβ, ..., ΞΈβ, calculate the end-effector position and orientation.
Denavit-Hartenberg (DH) Parameters:
| Joint | ΞΈ | d | a | Ξ± |
|---|---|---|---|---|
| 1 (Hip) | ΞΈβ | 0 | 0 | 90Β° |
| 2 (Knee) | ΞΈβ | Lβ | 0 | 0Β° |
| 3 (Ankle) | ΞΈβ | Lβ | 0 | 0Β° |
Transformation Matrix:
T = Tβ Γ Tβ Γ Tβ
Inverse Kinematics (IK)β
Given desired end-effector pose, solve for joint angles.
Analytical IK (for simple chains):
import numpy as np
def solve_ik_2dof(x_target, y_target, L1, L2):
"""
Solve 2-DOF planar arm IK
"""
# Distance to target
D = np.sqrt(x_target**2 + y_target**2)
# Check reachability
if D > (L1 + L2) or D < abs(L1 - L2):
raise ValueError("Target unreachable")
# Elbow-up solution
cos_theta2 = (D**2 - L1**2 - L2**2) / (2 * L1 * L2)
theta2 = np.arccos(cos_theta2)
# Shoulder angle
k1 = L1 + L2 * np.cos(theta2)
k2 = L2 * np.sin(theta2)
theta1 = np.arctan2(y_target, x_target) - np.arctan2(k2, k1)
return theta1, theta2
Numerical IK (Jacobian-based):
def jacobian_ik(target_pose, current_angles, max_iter=100, tol=1e-3):
"""
Iterative IK using Jacobian pseudo-inverse
"""
q = current_angles.copy()
for i in range(max_iter):
# Current end-effector pose
current_pose = forward_kinematics(q)
# Error
error = target_pose - current_pose
if np.linalg.norm(error) < tol:
return q
# Jacobian
J = compute_jacobian(q)
# Update: Ξq = JβΊ Β· error
delta_q = np.linalg.pinv(J) @ error
q += 0.1 * delta_q # Learning rate
return q
2. Bipedal Locomotionβ
2.1 Zero Moment Point (ZMP)β
The ZMP is the point on the ground where the net moment from gravity and inertia is zero. For stable walking, ZMP must remain within the support polygon.
ZMP Equation:
ZMP_x = (Ξ£ m_i Β· (zΜ_i + g) Β· x_i) / (Ξ£ m_i Β· (zΜ_i + g))
Python Implementation:
def calculate_zmp(masses, positions, accelerations, g=9.81):
"""
Calculate Zero Moment Point
Args:
masses: List of link masses [m1, m2, ...]
positions: List of (x, y, z) positions
accelerations: List of (αΊ, ΓΏ, zΜ) accelerations
g: Gravity constant
"""
numerator_x = 0
numerator_y = 0
denominator = 0
for m, pos, acc in zip(masses, positions, accelerations):
force_z = m * (acc[2] + g)
numerator_x += force_z * pos[0]
numerator_y += force_z * pos[1]
denominator += force_z
zmp_x = numerator_x / denominator
zmp_y = numerator_y / denominator
return zmp_x, zmp_y
2.2 Gait Planningβ
Walking Cycle Phases:
- Double Support: Both feet on ground (10-20% of cycle)
- Single Support: One foot on ground (30-40%)
- Swing Phase: Foot in air (30-40%)
Trajectory Generation (Cubic Spline):
from scipy.interpolate import CubicSpline
def generate_foot_trajectory(start, end, height, num_points=50):
"""
Generate smooth foot swing trajectory
"""
# Waypoints: start β apex β end
x_waypoints = [start[0], (start[0] + end[0])/2, end[0]]
y_waypoints = [start[1], (start[1] + end[1])/2, end[1]]
z_waypoints = [0, height, 0]
t = [0, 0.5, 1.0]
cs_x = CubicSpline(t, x_waypoints)
cs_y = CubicSpline(t, y_waypoints)
cs_z = CubicSpline(t, z_waypoints)
t_interp = np.linspace(0, 1, num_points)
return np.array([cs_x(t_interp), cs_y(t_interp), cs_z(t_interp)]).T
3. Balance Controlβ
3.1 Center of Mass (CoM) Trackingβ
Maintain CoM within support polygon:
def com_controller(com_current, com_target, zmp_current, support_polygon, kp=5.0):
"""
Proportional controller for CoM
"""
# Error
error = com_target - com_current
# Check ZMP constraint
if not is_point_in_polygon(zmp_current, support_polygon):
# Emergency step adjustment
return adjust_foot_placement(zmp_current, support_polygon)
# Control output (desired CoM acceleration)
accel = kp * error
return accel
3.2 Ankle and Hip Strategiesβ
Ankle Strategy (for small disturbances):
def ankle_torque(com_error, ankle_stiffness=200):
"""
Generate corrective ankle torque
"""
return -ankle_stiffness * com_error
Hip Strategy (for large disturbances):
def hip_bend(com_error, max_bend=0.3):
"""
Bend hips to shift CoM
"""
bend_angle = np.clip(com_error * 0.5, -max_bend, max_bend)
return bend_angle
4. Jacobian and Velocity Controlβ
4.1 Differential Kinematicsβ
Relate joint velocities to end-effector velocities:
v = J(q) Β· qΜ
Example (3-DOF arm):
def compute_jacobian(q, L1, L2, L3):
"""
Compute Jacobian for 3-DOF planar arm
"""
theta1, theta2, theta3 = q
J = np.array([
[-L1*np.sin(theta1) - L2*np.sin(theta1+theta2) - L3*np.sin(theta1+theta2+theta3),
-L2*np.sin(theta1+theta2) - L3*np.sin(theta1+theta2+theta3),
-L3*np.sin(theta1+theta2+theta3)],
[L1*np.cos(theta1) + L2*np.cos(theta1+theta2) + L3*np.cos(theta1+theta2+theta3),
L2*np.cos(theta1+theta2) + L3*np.cos(theta1+theta2+theta3),
L3*np.cos(theta1+theta2+theta3)]
])
return J
4.2 Singularity Avoidanceβ
Detect and avoid kinematic singularities:
def detect_singularity(J, threshold=1e-3):
"""
Check if Jacobian is near-singular
"""
det = np.linalg.det(J @ J.T)
return abs(det) < threshold
def damped_pseudo_inverse(J, lambda_=0.01):
"""
Damped Least Squares (DLS) inverse
"""
return J.T @ np.linalg.inv(J @ J.T + lambda_**2 * np.eye(J.shape[0]))
5. Practical Implementation with ROS 2β
5.1 IK Service Nodeβ
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Pose
from sensor_msgs.msg import JointState
class HumanoidIKService(Node):
def __init__(self):
super().__init__('ik_service')
self.subscription = self.create_subscription(
Pose,
'/target_pose',
self.pose_callback,
10
)
self.joint_pub = self.create_publisher(JointState, '/joint_commands', 10)
def pose_callback(self, msg):
# Extract target position
x = msg.position.x
y = msg.position.y
z = msg.position.z
# Solve IK
joint_angles = self.solve_ik([x, y, z])
# Publish
joint_msg = JointState()
joint_msg.position = joint_angles
self.joint_pub.publish(joint_msg)
def solve_ik(self, target):
# Your IK solver here
pass
def main():
rclpy.init()
node = HumanoidIKService()
rclpy.spin(node)
6. Case Study: Walking Gaitβ
Objective: Generate a stable walking gait for a humanoid.
Steps:
- Plan ZMP trajectory within support polygon
- Compute CoM trajectory from ZMP using dynamics
- Solve whole-body IK for joint trajectories
- Execute with torque control
Code Skeleton:
def plan_walking_gait(step_length, step_height, num_steps):
"""
High-level walking planner
"""
trajectories = []
for i in range(num_steps):
# Swing foot trajectory
foot_traj = generate_foot_trajectory(
start=[i*step_length, 0, 0],
end=[(i+1)*step_length, 0, 0],
height=step_height
)
# ZMP trajectory (stays within support polygon)
zmp_traj = plan_zmp_trajectory(foot_traj)
# CoM trajectory
com_traj = zmp_to_com(zmp_traj)
# IK for all timesteps
joint_traj = whole_body_ik(foot_traj, com_traj)
trajectories.append(joint_traj)
return trajectories
Summaryβ
This chapter covered:
- β Forward and Inverse Kinematics
- β ZMP and Bipedal Stability
- β Gait Planning and Trajectory Generation
- β Balance Control (Ankle/Hip Strategies)
- β Jacobian-based Velocity Control
Next: Module 5.2 - Hands and Grasping Control