Skip to content

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:

\[ \ddot{x}_c = \frac{g}{z_c} (x_c - x_z) \]

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:

\[ J = \sum_{k=0}^{\infty} \left( Q_e e_k^2 + x_k^T Q_x x_k + R \Delta u_k^2 \right) \]

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 steps_sequence. Interpreted as: - In SS: index of the current support foot. - In DS: index of the target foot (previous is current_step_idx - 1).

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