Skip to the lesson
FixTheRobot Home / VLA / Phase 4 / 4.3

VLA · Phase 4 · Action Representation and Policy Learning

4.3 — Action Normalization and Decoding

Scaling actions for training and turning model outputs back into robot commands.

  • Roadmap phase 4
  • Action Representation and Policy Learning
  • About 11 minutes to read

Between the model and the motor sits a small piece of arithmetic that causes a surprising number of bugs. This lesson covers how actions are rescaled, why quantiles are popular, and how to decode safely.

Claims about papers, models and textbooks carry numbered references, listed with what each one is cited for in the References. Code marked “Toy model” is our own illustration, not the cited system.

Learning objectives

After this lesson, you should be able to:

  • Compare min-max, mean-std and quantile scaling.
  • Explain how an outlier harms min-max scaling.
  • Decode and clip a model output.
  • Explain why statistics must travel with the model.
  1. Compute statistics on the training actions
  2. Normalise actions for training
  3. The model predicts normalised actions
  4. Decode with the same statistics
  5. Clip to safe limits and send
Normalise, predict, decode, clip.

The one-sentence idea

Rescale for learning, then undo exactly the same rescaling for the robot.

The statistics belong to the model.

Why numbers get rescaled

A gripper opens by centimetres, a joint turns by radians, a wrist rolls by degrees. If those raw numbers went straight into a model, the largest would dominate learning, and a token scheme would waste bins on ranges nobody uses. So training pipelines rescale each action dimension to a standard range, and at run time the model’s output is scaled back. The scaling statistics become part of the model, and OpenVLA is explicit about it: bins for each action dimension lie between the 1st and 99th quantile of the training actions, and the predicted tokens are decoded to continuous values and de-normalised to the robot’s physical scale.[1][1]

Three common recipes: min–max (squeeze the smallest and largest values to −1 and 1), mean–std (subtract the mean, divide by the standard deviation, as in lesson 3.1), and quantile (use, say, the 1st and 99th percentiles as the ends).

Why quantiles: one glitch can ruin min–max

Real recordings contain glitches: a sensor spike, an operator jerk. With min–max scaling, one extreme value stretches the range and squeezes all normal motions into a few bins. Quantile scaling ignores the extremes:

import numpy as np
rng = np.random.default_rng(0)

actions = rng.normal(0.0, 0.02, 10000)                  # normal motions, a few centimetres
actions[0] = 1.0                                         # one glitch, 100 times larger

def bins_used(x, lo, hi, bins=256):
    idx = np.clip(((x - lo) / (hi - lo) * bins).astype(int), 0, bins - 1)
    return len(np.unique(idx))

lo, hi = actions.min(), actions.max()
q_lo, q_hi = np.quantile(actions, [0.01, 0.99])
print("min-max range:  ", round(float(lo), 3), "to", round(float(hi), 3), "-> distinct bins used:", bins_used(actions, lo, hi))
print("1%-99% range:   ", round(float(q_lo), 3), "to", round(float(q_hi), 3), "-> distinct bins used:", bins_used(actions, q_lo, q_hi))

Output:

min-max range:   -0.078 to 1.0 -> distinct bins used: 35
1%-99% range:    -0.047 to 0.046 -> distinct bins used: 256

With min–max, all the normal data is squeezed into just 35 of the 256 bins; with quantiles it uses all 256, spread over the motions that matter. The cost: genuinely extreme values (the top and bottom 1%) get clipped, so quantile scaling should be used when you trust that the extremes are rare or unimportant.

Decoding: from model output to a safe command

Decoding is the reverse trip, and it has two jobs: undo the scaling, and make sure the result is acceptable. A model can output a value slightly outside the training range, or, with text-style outputs, nothing sensible at all. Clip to the allowed range before anything reaches a motor:

import numpy as np

mean = np.array([0.0, 0.0, 0.04])                        # saved with the model: dataset statistics per dimension
std  = np.array([0.02, 0.02, 0.02])
limit = np.array([0.05, 0.05, 0.08])                     # the largest move this robot may be commanded to make

def decode(model_output):
    physical = model_output * std + mean                 # undo the normalisation
    return np.clip(physical, -limit, limit)              # never exceed the safe range

for out in (np.array([0.5, -1.0, 0.0]), np.array([4.0, 0.2, 3.0])):
    print("model output", out.tolist(), "-> command", np.round(decode(out), 3).tolist())

Output:

model output [0.5, -1.0, 0.0] -> command [0.01, -0.02, 0.04]
model output [4.0, 0.2, 3.0] -> command [0.05, 0.004, 0.08]

Toy model. The statistics and limits are made up. In a real system the limits come from the robot’s specifications and safety review (lesson 9.4).

The second output was unreasonable (4 standard deviations on the first axis) and the clip held it at the limit instead of passing it to the robot. Clipping is a last line of defence, not a substitute for a good model.

The classic bug: the wrong statistics

Statistics are specific to a dataset, usually to a robot. Use robot A’s statistics to decode for robot B and the commands are wrong by a constant factor, silently:

import numpy as np

true_command = 0.03                                      # what the model meant: 3 cm
output = (true_command - 0.0) / 0.01                     # the normalised value, using robot A's std of 0.01 m

for name, std in (("robot A statistics (correct)", 0.01), ("robot B statistics (wrong)", 0.03)):
    print(f"{name:<30} -> {output * std * 100:.1f} cm")

Output:

robot A statistics (correct)   -> 3.0 cm
robot B statistics (wrong)     -> 9.0 cm

Three times too far, with no error message. Store the statistics next to the model, version them, and check that the model name and the statistics file match before running. Datasets with several robots, as in Open X-Embodiment with 22 embodiments,[2] make this more important, not less.

Common misconceptions

Misconception 1 “Normalisation is a preprocessing detail.”

Correction It is part of the model: wrong statistics give wrong commands without any error.

Misconception 2 “Min-max is the safest choice.”

Correction One glitch can squeeze all normal data into a few bins.

Misconception 3 “Clipping makes a bad output safe.”

Correction It caps the size of the command. It does not make the action correct.

Engineering takeaways

  1. Three recipes: min-max, mean-std, quantile.
  2. Quantiles resist outliers.
  3. Decode, then clip.
  4. Ship statistics with the model.

Whatever you divide by in training, multiply by at run time.

Knowledge check

Try each question first, then reveal the explanation.

Question 1

Normalised value 2.0, mean 0.1, std 0.05. What is the physical value?

Explanation

0.2. 2.0 × 0.05 + 0.1.

Question 2

Why can quantile scaling beat min-max scaling?

Explanation

Extreme outliers do not stretch the range.

Question 3

What is the risk of using another robot’s statistics?

Explanation

Silently wrong command sizes.

References

Numbered notes in the text, like [1], point here. Each entry says what it is cited for and how that was checked. Papers and books can be revised or misread, so follow the links before relying on a detail. Code marked “Toy model” is our own illustration of an idea, not the code of the cited system.

  1. Kim, M. J., et al. (2024). OpenVLA: An open-source vision-language-action model. Conference on Robot Learning (CoRL) 2024. arXiv:2406.09246

    Cited for:

    • Each action dimension is split into 256 bins between the 1st and 99th quantile of the training actions; the 256 bins are written over the 256 least-used tokens of the Llama tokenizer, and the model is trained with next-token prediction on the action tokens. Checked against the paper, via summaries of its text.
    • At run time the predicted action tokens are decoded to continuous values and de-normalised to the robot’s physical scale. Checked against the paper, via summaries of its text.
  2. Open X-Embodiment Collaboration (2023). Open X-Embodiment: Robotic learning datasets and RT-X models. arXiv:2310.08864

    Cited for:

    • Pools more than one million robot trajectories from 22 robot embodiments, assembled by 21 institutions and covering 527 skills. Checked against the paper’s abstract.

What’s next

Next: predicting several future actions at once with action chunking.

Next lesson

4.4 — Temporal action prediction and action chunking

Predicting a short sequence of future actions at once instead of a single step.

Back to the VLA roadmap