Omnidirectional drives: move sideways with three omniwheels

Derive the wheel speeds for a three-wheel Kiwi drive. Command forward, sideways, and turning motion, preserve direction when motors saturate, and integrate the resulting world path.

By 12 min read

What you will learn

  • Derive each wheel speed from its mount position and driven direction.
  • Recover forward, lateral, and yaw velocity from three wheel rates.
  • Distinguish this Kiwi layout from a four-wheel mecanum drive.
  • Scale wheel commands together when a motor reaches its speed limit.
  • Convert constant body motion into a world-frame path.

Before you start

A robot faces a shelf but sits 30 centimeters too far to the right. An omnidirectional base can shift left while keeping its heading. The wheel commands must account for every wheel's mounting geometry.

This lesson derives a three-wheel Kiwi drive from its geometry. You can check a sideways command by hand, combine translation with rotation, and see what happens when a wheel reaches its speed limit.

Move sideways while keeping the same heading

Use a body frame with x_b forward, y_b left, and positive yaw counterclockwise. The requested body motion is (v_x, v_y, ω): two linear velocities in meters per second and one angular velocity in radians per second. This three-component velocity is a planar body twist.

An ideal differential-drive robot has zero sideways velocity at its axle midpoint. It can reach a position to the side by turning and driving. The omniwheel arrangement here can instead command v_y directly, including while v_x and ω are zero.

That ability concerns instantaneous motion. It does not mean every speed is possible. Wheel limits still bound the available combinations.

Specify the wheel layout first

Place three identical omniwheels at angles φ₁ = 0°, φ₂ = 120°, φ₃ = 240° around the body center. Their mount radius is a; each wheel's rolling radius is r. These are different lengths.

p_i = a (cos φ_i, sin φ_i)
t_i = (−sin φ_i, cos φ_i)

Each wheel drives along the counterclockwise tangent t_i. Its passive rollers allow motion in the radial direction. Positive wheel rate q_i means motion along the chosen tangent; motor wiring must be mapped to this convention.

An omniwheel's free rolling direction is perpendicular to its driven direction. A typical mecanum wheel uses angled rollers, often 45°, and needs a different velocity equation. Modern Robotics, Section 13.2 derives the general wheel model.

Do not reuse the following three-wheel matrix for a four-wheel mecanum base. For example, WPILib's mecanum kinematics takes four wheel locations and returns four wheel speeds. Its geometry and wheel ordering are part of that interface.

Project the velocity at one wheel

The body center translates, and yaw adds a velocity at each wheel mount. For a wheel at (p_x, p_y), rigid-body motion gives:

u_i = (v_x − ω p_y, v_y + ω p_x)
r q_i = t_i · u_i
q_i = (−sin φ_i v_x + cos φ_i v_y + aω) / r

The dot product selects the velocity along the driven tangent. The rollers accommodate the remaining radial component. Assuming no slip in the driven direction makes that tangent velocity equal to r q_i.

Every wheel gets the same aω contribution because its positive tangent points along counterclockwise rotation. A pure positive yaw command therefore gives three positive wheel rates in this convention.

Assemble the three wheel equations

Insert the three mount angles:

q₁ = (v_y + aω) / r
q₂ = (−√3 v_x/2 − v_y/2 + aω) / r
q₃ = (+√3 v_x/2 − v_y/2 + aω) / r

These equations form a linear map q = H v, where v = (v_x, v_y, ω). Its rows are [0, 1, a], [−√3/2, −1/2, a], and [√3/2, −1/2, a], each divided by r.

For this chosen layout, expanding the determinant gives det(H) = 3√3 a / (2r³). It is nonzero for positive a and r, so H has rank three. The three motor rates can specify all three planar velocity components under this ideal model.

Changing the wheel angles, tangent directions, or roller geometry changes H. Three motors alone do not guarantee the same rank or equations.

Recover body motion from wheel rates

Subtract the second wheel equation from the third to isolate v_x. Combine twice the first equation minus the other two to isolate v_y. Add all three to isolate yaw:

v_x = r (q₃ − q₂) / √3
v_y = r (2q₁ − q₂ − q₃) / 3
ω = r (q₁ + q₂ + q₃) / (3a)

Equivalently, symmetry gives v_x = −(2r/3) Σ sin φ_i q_i and v_y = (2r/3) Σ cos φ_i q_i. The expanded equations above make the signs easier to check.

The lab applies this inverse to its limited wheel rates. With measured encoder rates, the same kinematic inverse could provide an estimated body twist. Wheel odometry explains why measurements, calibration, and slip still affect the resulting pose estimate.

Calculate a sideways command

Use r = 0.1 m and a = 0.3 m. Ask for (v_x, v_y, ω) = (0, 0.3, 0):

q₁ = 0.3 / 0.1 = 3 rad/s
q₂ = −0.15 / 0.1 = −1.5 rad/s
q₃ = −0.15 / 0.1 = −1.5 rad/s

The inverse confirms the command. The difference q₃ − q₂ is zero, so v_x = 0. The sum of all rates is zero, so ω = 0. The lateral equation gives 0.1 × (6 + 1.5 + 1.5) / 3 = 0.3 m/s.

Starting at world pose (0, 0, 0), four seconds of this motion ends at (0, 1.2, 0). The robot moves left while continuing to face the positive world x direction.

Try forward, sideways, and turning motion

Three omniwheels, three motion components

Drive a Kiwi base in any planar direction

Request a body velocity, calculate each wheel rate, and scale the whole command if a wheel reaches its speed limit.

0.00
0.30
0.00
0.10
0.30
6.00

Every command lasts 4 seconds from world pose (0, 0, 0). Body x points forward, body y points left, and positive yaw turns counterclockwise. Wheel centers sit at 0°, 120°, and 240° around the chassis.

Wheel motion in the body frame

Kiwi wheel layout: scaling factor 1.000000Three tangent drive directions surround the body center. Brown arrows show signed applied wheel motion; their lengths use the wheel-rate limit as a reference. Dashed radial lines show free roller directions. The blue arrow shows applied chassis translation in the body frame.123xᵦyᵦ
Brown arrows: applied signed wheel rates. Dashed lines: passive roller motion. Blue arrow: applied body translation. The chassis radius shows wheel mounting distance; wheel rectangles are schematic.

Center route in the world frame

Omnidirectional motion after 4 secondsSolid blue is the applied center route. Dashed gray is the requested route without wheel limits. The applied final pose is x 0.000000 meters, y 1.200000 meters, heading 0.000000 radians. Both axes use the same scale.-1.11.1-0.51.7x (m)y (m)
Solid blue: applied motion. Dashed gray: requested motion. The open circle marks the start; the final arrow shows body heading. Only the center route appears, with no footprint or obstacles.
Wheel angular rates, in radians per second
WheelRequestedApplied
13.0000003.000000
2-1.500000-1.500000
3-1.500000-1.500000
Wheel scaling factor
1.000000
Peak requested wheel rate (rad/s)
3.000000
Applied body x velocity (m/s)
0.000000
Applied body y velocity (m/s)
0.300000
Applied yaw rate (rad/s)
0.000000
Final world x (m)
0.000000
Final world y (m)
1.200000
Final heading (rad)
0.000000
Center travel (m)
1.200000

The requested motion fits within every wheel speed limit. No scaling is needed.

The initial view reproduces that sideways calculation. Every preset starts at the same zero pose and runs a constant applied body twist for four seconds. Changing a control recomputes the whole experiment; reset restores Sideways and the original geometry.

The wheel diagram shows the mount layout, signed drive arrows, and free radial directions. Wheel drawings are schematic. Arrow length shows the applied rate as a fraction of the wheel limit. A stopped drive motor can still accompany radial motion through its rollers.

The world plot compares the requested and applied center paths on equal x and y scales. A heading arrow shows where the body faces, which can differ from its direction of travel. Overlapping routes appear as one solid line.

Choose Combined motion for (0.3, 0.2, 0.5). The rates are approximately (3.500000, −2.098076, 3.098076) rad/s. None exceeds the default 6 rad/s limit. After four seconds, the final pose is (−0.020880, 1.213407, 2.000000). Constant body velocities curve in the world as the heading changes.

Respect the wheel speed limits

Let Q be the common maximum absolute wheel rate. Each wheel must satisfy |q_i| ≤ Q. The collection of wheel limits bounds the body twist; Modern Robotics, Section 13.2, Part 2 develops this feasible velocity region.

The lab uses a single scaling factor:

α = min(1, Q / max_i |q_i|)
q_applied = α q_requested
v_applied = α v_requested

For a zero request, define α = 1. Linearity of H and its inverse gives the last equality. Scaling preserves the ratios of the requested velocity components. Clipping individual wheel rates generally changes those ratios and therefore changes the intended motion.

Choose Limited wheel speeds. The requested twist is (0.6, 0.4, 1), producing wheel rates approximately (7, −4.196152, 6.196152). With Q = 4 rad/s, α = 4/7 ≈ 0.571429. The applied twist becomes (0.342857, 0.228571, 0.571429).

The robot follows the same geometric motion more slowly. At four seconds, it has reached the requested motion's pose at 16/7 seconds: approximately (−0.209134, 1.295392, 2.285714). It would need seven seconds to reach the originally requested four-second pose under these constant commands.

Uniform scaling is one command policy. A controller that prioritizes yaw or translation may choose a different feasible twist. This example makes its policy explicit so the change is predictable.

Integrate motion in the world frame

The coordinate-frame rotation converts body velocities to world velocities:

ẋ = v_x cos θ − v_y sin θ
ẏ = v_x sin θ + v_y cos θ
θ̇ = ω

For constant applied body motion over duration T, define h = ωT/2 and sinc(h) = sin(h)/h, with sinc(0) = 1. Integrating the sine and cosine terms gives:

Δx = T sinc(h) [v_x cos(θ + h) − v_y sin(θ + h)]
Δy = T sinc(h) [v_x sin(θ + h) + v_y cos(θ + h)]
θ′ = θ + ωT

This handles straight translation at zero yaw and keeps heading unwrapped. A full turn reads 2π even though its final orientation matches zero. Center travel is T√(v_x² + v_y²); it can exceed the straight distance between endpoints.

The experiment assumes ideal rolling, free rollers, accurate geometry, and immediate wheel-rate tracking. It models neither acceleration nor forces, traction loss, floor unevenness, or motor dynamics. The plot omits obstacles and swept-footprint checks. A physical planner and controller must account for those effects.

Reproduce the calculations in Python

This Python 3 example uses only the standard library. It implements this exact three-wheel layout, applies the shared wheel limit, and integrates the sideways and limited-motion examples.

from math import cos, sin, sqrt


def run(vx, vy, yaw, limit, radius=0.1, mount=0.3, duration=4):
    rates = [(vy + mount * yaw) / radius,
             (-sqrt(3) * vx / 2 - vy / 2 + mount * yaw) / radius,
             (sqrt(3) * vx / 2 - vy / 2 + mount * yaw) / radius]
    peak = max(abs(q) for q in rates)
    scale = min(1, limit / peak) if peak else 1
    q1, q2, q3 = [q * scale for q in rates]
    ax = radius * (q3 - q2) / sqrt(3)
    ay = radius * (2 * q1 - q2 - q3) / 3
    aw = radius * (q1 + q2 + q3) / (3 * mount)
    half = aw * duration / 2
    sinc = 1 - half**2 / 6 + half**4 / 120 if abs(half) < 1e-4 else sin(half) / half
    x = duration * sinc * (ax * cos(half) - ay * sin(half))
    y = duration * sinc * (ax * sin(half) + ay * cos(half))
    return rates, scale, (ax, ay, aw), (x, y, aw * duration)


def numbers(values):
    return ", ".join(f"{0.0 if abs(v) < 1e-10 else v:.6f}" for v in values)


for name, command in [("Sideways", (0, 0.3, 0, 6)),
                      ("Limited", (0.6, 0.4, 1, 4))]:
    rates, scale, applied, pose = run(*command)
    print(f"{name} requested wheels (rad/s): {numbers(rates)}")
    print(f"{name} scale: {scale:.6f}; applied twist: {numbers(applied)}")
    print(f"{name} final pose (m, m, rad): {numbers(pose)}")

Expected output:

Sideways requested wheels (rad/s): 3.000000, -1.500000, -1.500000
Sideways scale: 1.000000; applied twist: 0.000000, 0.300000, 0.000000
Sideways final pose (m, m, rad): 0.000000, 1.200000, 0.000000
Limited requested wheels (rad/s): 7.000000, -4.196152, 6.196152
Limited scale: 0.571429; applied twist: 0.342857, 0.228571, 0.571429
Limited final pose (m, m, rad): -0.209134, 1.295392, 2.285714

The first two applied-twist components use m/s; the third uses rad/s. Change the wheel radius and watch the requested rates. A larger rolling radius needs fewer wheel revolutions for the same driven ground speed.

Try it yourself

Exercise 1. Drive forward with one motor stopped. Use r = 0.1 m and a = 0.3 m. Request (0.3, 0, 0). Find all three wheel rates. Why can wheel 1 have zero motor speed while its mount moves?

Check the forward calculation

The rates are (0, −1.5√3, +1.5√3), approximately (0, −2.598076, 2.598076) rad/s. Their sum is zero, so yaw is zero. The inverse gives v_x = 0.3 m/s and v_y = 0.

Wheel 1's driven direction is body y. Its mount travels in body x, which is its free radial direction. The passive rollers accommodate that motion. Choose Forward in the lab: after four seconds, the center is at (1.2, 0) with heading zero.

Exercise 2. Limit the sideways command. Reset to Sideways, then set the wheel rate limit to 2 rad/s. What are the scaling factor, applied wheel rates, lateral velocity, and four-second endpoint?

Check the shared scaling factor

The requested peak is 3 rad/s, so α = 2/3. Multiplying (3, −1.5, −1.5) by that factor gives (2, −1, −1) rad/s. The applied twist is (0, 0.2, 0).

The robot reaches (0, 0.8, 0) after four seconds. It needs six seconds to reach the original 1.2 m lateral displacement. Its heading stays unchanged throughout.

Sources and further study

Continue with differential-drive kinematics to compare the velocity constraints. The instantaneous center of rotation connects combined translation and yaw to a turning center. Skid steering explains a different mechanism for lateral motion at wheel contacts.