Zero Moment Point and Balance Criterion
The Zero Moment Point (ZMP) is the point on the ground where the resultant contact forces between the feet and the ground produce no moment about the horizontal axes. To maintain balance, the ZMP must remain within the robot’s support polygon, defined as the convex hull of the contact areas of the feet. Intuitively, this ensures that the ground reaction forces can generate a counteracting moment to keep the feet flat and prevent tipping, maintaining dynamic equilibrium. For a more thorough explanation I recommend this blog post by Stéphane Caron.
Linear Inverted Pendulum Model
The first step of the controller is to define a reference ZMP trajectory, alternating from one foot to the other at each step. This reference is generated using a cubic spline that interpolates the position of each foot. The objective is to establish a relationship between the position of this reference ZMP and the robot’s Center of Mass ( CoM). This relationship can be derived from a simplified model of the robot’s dynamics known as the Linear Inverted Pendulum Model (LIPM).
The LIPM is derived under the following assumptions:
- The mass of the body is concentrated at a single point, the Center of Mass (CoM).
- Legs are massless and do not contribute to the system dynamics.
- The CoM moves on a horizontal plane at a constant height, eliminating vertical motion coupling.
- No angular momentum is generated about the CoM, meaning the upper body remains still to avoid producing additional moments.
Under these assumptions and for small angles, the inverted pendulum dynamics can be linearized, leading to the following second-order linear equation:
where \(x_z\) denotes the ZMP, \(x_c\) the CoM projection, and \(z_c\) the constant CoM height.
Preview Control
In Kajita's paper, the idea is to use a preview control in order to track and anticipate the ZMP reference change. The control input minimizes a quadratic cost over a finite horizon:
yielding a feedback + integral + preview law.
The resulting controller anticipates future ZMP references, ensuring stable walking trajectories.
The result of the preview controller can be observed on the figure below. The upper-left figure shows the trajectory of the CoM in red over time, the generated reference ZMP in blue and the support polygon in green. The upper-right and lower-right figures show the trajectory of the ZMP and COM over time for x and y pos. Finally, the lower-left figure shows the preview gains that are computed.
Example
You can reproduce the example displayed on the figure by launching the script example_1_lipm_preview_control.py. We
recommend you to use Docker as explained in the installation part:
xhost +local:root
docker run --rm -it \
--env DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix:ro \
--device /dev/dri:/dev/dri \
ghcr.io/rdesarz/biped-walking-controller \
python examples/example_1_lipm_preview_control.py
The parameters used in this script are the following:
dt = 0.005 # Delta of time of the model simulation
t_preview = 1.6 # Time horizon used for the preview controller
t_ss = 0.6 # Single support phase time window
t_ds = 0.4 # Double support phase time window
t_init = 2.0 # Initialization phase (transition from still position to first step)
t_end = 1.0 # Final phase (transition from walking to standstill position)
foot_shape = Polygon(
((0.11, 0.05), (0.11, -0.05), (-0.11, -0.05), (-0.11, 0.05))) # Shape of the foot for support polygon computation
n_steps = 5 # Number of steps
l_stride = 0.3 # Length of the stride
References
-
Kajita, S., Kanehiro, F., Kaneko, K., Fujiwara, K., Harada, K., Yokoi, K., & Hirukawa, H.
Biped Walking Pattern Generation by Using Preview Control of Zero-Moment Point.
Proceedings of the IEEE International Conference on Robotics and Automation (ICRA), 2003. -
Katayama, T., Ohki, T., Inoue, T., & Kato, T.
Design of an Optimal Controller for a Discrete-Time System Subject to Previewable Demand.
International Journal of Control, vol. 41, no. 3, pp. 677–699, 1985. -
Caron, S.
Zero-tilting moment point.
Available online at https://scaron.info/robotics/zero-tilting-moment-point.html, accessed 2025.
(Detailed explanations and examples for Zero-tilting moment point)
Code API
PreviewControllerParams
dataclass
PreviewControllerParams(zc, g, Qe, Qx, R, n_preview_steps)
Hyperparameters for preview control gain synthesis.
Attributes:
| Name | Type | Description |
|---|---|---|
zc |
float
|
CoM height (meters). |
g |
float
|
Gravity magnitude (m/s^2). |
Qe |
(ndarray, shape(1, 1))
|
Weight on integrated ZMP tracking error. |
Qx |
(ndarray, shape(3, 3))
|
Weight on state [x, x_dot, x_ddot]. |
R |
(ndarray, shape(1, 1))
|
Weight on jerk input magnitude. |
n_preview_steps |
int
|
Number of preview items P used to build Gd (uses P-1 gains). |
PreviewControllerMatrices
dataclass
PreviewControllerMatrices(A, B, C, Gi, Gx, Gd)
Structure that contains the Discrete LIPM-with-jerk preview-control matrices. Serves as input for the update of the control command.
Attributes:
| Name | Type | Description |
|---|---|---|
A |
(ndarray, shape(3, 3))
|
State transition of [x, x_dot, x_ddot] (per-axis). |
B |
(ndarray, shape(3, 1))
|
Input matrix for jerk u. |
C |
(ndarray, shape(1, 3))
|
Output mapping from state to ZMP. |
Gi |
(float or ndarray, shape(1, 1))
|
Integral gain on ZMP tracking error. |
Gx |
(ndarray, shape(1, 4))
|
State-feedback gain on [e_int, x, x_dot, x_ddot]. |
Gd |
(ndarray, shape(P - 1))
|
Preview gains for future ZMP references over P-1 steps. |
compute_zmp_ref
compute_zmp_ref(t, com_initial_pose, steps, ss_t, ds_t, t_init, t_final, interp_fn=cubic_spline_interpolation)
Build a piecewise ZMP reference on the ground plane from footsteps. The ZMP reference starts at com_initial_pose. Then during t_init period of time, the ZMP shift to the right foot. It then goes from a step to another. Then it goes back between the final position of the feet during t_final.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
(array - like, shape(T))
|
Time samples (seconds), monotonically increasing. |
required |
com_initial_pose
|
(array - like, shape(2))
|
Initial ZMP target under the CoM at startup (x, y). |
required |
steps
|
(array - like, shape(N, 2))
|
Footstep sequence on the ground (x, y) per step. The last element is the final midpoint target. |
required |
ss_t
|
float
|
Duration of single support per step. |
required |
ds_t
|
float
|
Duration of double support between steps. |
required |
t_init
|
float
|
Ramp time from initial CoM ZMP to the first step. |
required |
t_final
|
float
|
Final blending time to bring ZMP to the midpoint of the last two feet. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
zmp_ref |
(ndarray, shape(T, 2))
|
ZMP reference trajectory on the ground plane. |
Notes
- For each step: hold current foot during SS, then linearly blend to the next foot during DS.
- Last phase: SS on the penultimate foot, then blend to the average of the
last two foot positions during
t_final.
compute_preview_control_matrices
compute_preview_control_matrices(params, dt)
Construct preview-control gains for the discrete LIPM with jerk input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
PreviewControllerParams
|
Model and cost weights. |
required |
dt
|
float
|
Discrete time step. |
required |
Returns:
| Type | Description |
|---|---|
PreviewControllerMatrices
|
Controller matrices (A, B, C, Gi, Gx, Gd) reused at runtime. |
Method
- Build augmented system with integral of ZMP error.
- Solve discrete-time algebraic Riccati equation for K.
- Derive integral gain Gi, state gain Gx, and preview gains Gd for P-1 future
reference samples using the closed-loop matrix
Ac.
References
- Kajita et al., 2003. Biped walking pattern generation by using preview control.
- Katayama et al., 1985. Design of an optimal preview controller.
update_control
update_control(ctrl_mat, current_zmp, zmp_ref, x, y)
One-step preview control update for x and y axes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctrl_mat
|
PreviewControllerMatrices
|
Precomputed controller matrices. |
required |
current_zmp
|
(array - like, shape(2))
|
Measured ZMP at current step [zx, zy]. |
required |
zmp_ref
|
(ndarray, shape(P - 1, 2))
|
Future ZMP references used with Gd for both axes. First element is the next sample after the current time. |
required |
x
|
(ndarray, shape(4))
|
Augmented states per axis: [e_int, pos, vel, acc]. |
required |
y
|
(ndarray, shape(4))
|
Augmented states per axis: [e_int, pos, vel, acc]. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
u |
(ndarray, shape(2))
|
Jerk command for x and y. |
x_next, y_next : ndarray, shape (4,)
|
Next augmented states after applying u. |
Notes
- Integral state is updated with ZMP output error, then state is propagated with (A, B) and jerk input.
- Uses the same scalar Gi and vector Gx for both axes.
build_zmp_horizon
build_zmp_horizon(com_initial_target, t_horizon, t_state, state, delta_t, current_step_idx, steps_sequence, steps_foot, ss_t, ds_t, t_init, t_end, interp_fn=cubic_spline_interpolation)
Build a ZMP reference over a preview horizon based on the current walking state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t_horizon
|
float
|
Length of the preview horizon [s]. |
required |
t_state
|
float
|
Time elapsed in the current state [s]. |
required |
state
|
WalkingState
|
Current walking state (INIT, DS, SS_LEFT, SS_RIGHT, END). |
required |
delta_t
|
float
|
Sampling period of the preview horizon [s]. |
required |
current_step_idx
|
int
|
Index of the current step in |
required |
steps_sequence
|
ndarray
|
Sequence of footsteps. Shape (N, 2) or (N, >=2). Only the (x, y) components are used as ZMP targets. |
required |
ss_t
|
float
|
Duration of a single-support phase [s]. |
required |
ds_t
|
float
|
Duration of a double-support phase [s]. |
required |
t_init
|
float
|
Duration of the INIT phase [s]. |
required |
t_end
|
float
|
Duration of the END phase [s]. |
required |
interp_fn
|
callable
|
Interpolation function used in double support. Expected signature: interp_fn(alpha, p0, p1) where alpha in [0, 1], p0, p1 are 2D numpy arrays. |
cubic_spline_interpolation
|
Returns:
| Name | Type | Description |
|---|---|---|
t_samples |
(ndarray, shape(N))
|
Relative time samples over the horizon, starting at 0. |
zmp_horizon |
(ndarray, shape(N, 2))
|
ZMP reference (x, y) at each time sample. |
Notes
- This function does NOT enforce continuity with the previous ZMP reference. At each call, the horizon is built from scratch from the current state.
- State-transition logic is a simple cyclic model: INIT -> DS -> SS -> DS -> SS -> ... -> END and the step index increments when leaving an SS state.
options: members_order: source heading_level: 2