Skip to content

calculator

Odometry Calibration Calculator.

Pure mathematical functions for computing calibration parameters.

Calibrates three parameters for differential drive odometry: wheel distance, left wheel radius, and right wheel radius.

Calibration process

Phase 1 (Turn in Place): Spin N rotations to compute wheel distance from encoder arcs Phase 2 (Square Path): Drive squares to isolate linear motion and find wheel radius ratio (beta) Phase 3 (Straight Line): Drive known distance to solve for actual wheel radius using beta

compute_alpha_coefficients(turns, lticks_delta, rticks_delta, encoder_ticks) #

Compute alpha coefficients from turn-in-place data.

Alpha represents wheel rotations per robot rotation.

Parameters:

Name Type Description Default
turns int

Number of full rotations performed

required
lticks_delta int

Change in left encoder ticks

required
rticks_delta int

Change in right encoder ticks

required
encoder_ticks float

Encoder ticks per wheel revolution

required

Returns:

Type Description
tuple[float, float]

Tuple of (alpha_l, alpha_r)

Source code in cogip/tools/firmware_odometry_calibration/calculator.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def compute_alpha_coefficients(
    turns: int,
    lticks_delta: int,
    rticks_delta: int,
    encoder_ticks: float,
) -> tuple[float, float]:
    """
    Compute alpha coefficients from turn-in-place data.

    Alpha represents wheel rotations per robot rotation.

    Args:
        turns: Number of full rotations performed
        lticks_delta: Change in left encoder ticks
        rticks_delta: Change in right encoder ticks
        encoder_ticks: Encoder ticks per wheel revolution

    Returns:
        Tuple of (alpha_l, alpha_r)
    """
    alpha_l = lticks_delta / (encoder_ticks * turns)
    alpha_r = rticks_delta / (encoder_ticks * turns)
    return alpha_l, alpha_r

compute_beta_coefficient(lticks_linear, rticks_linear) #

Compute beta coefficient (radius ratio) from linear encoder components.

For straight-line motion, both wheels travel the same distance D: D = 2π * radius_l * (lticks / encoder_ticks) D = 2π * radius_r * (rticks / encoder_ticks)

Therefore: radius_r / radius_l = lticks / rticks

Beta = radius_r / radius_l = lticks_linear / rticks_linear

Parameters:

Name Type Description Default
lticks_linear float

Linear component of left encoder ticks

required
rticks_linear float

Linear component of right encoder ticks

required

Returns:

Type Description
float | None

Beta coefficient (radius_r / radius_l), or None if rticks_linear is too small

Source code in cogip/tools/firmware_odometry_calibration/calculator.py
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
def compute_beta_coefficient(
    lticks_linear: float,
    rticks_linear: float,
) -> float | None:
    """
    Compute beta coefficient (radius ratio) from linear encoder components.

    For straight-line motion, both wheels travel the same distance D:
        D = 2π * radius_l * (lticks / encoder_ticks)
        D = 2π * radius_r * (rticks / encoder_ticks)

    Therefore: radius_r / radius_l = lticks / rticks

    Beta = radius_r / radius_l = lticks_linear / rticks_linear

    Args:
        lticks_linear: Linear component of left encoder ticks
        rticks_linear: Linear component of right encoder ticks

    Returns:
        Beta coefficient (radius_r / radius_l), or None if rticks_linear is too small
    """
    if abs(rticks_linear) < 1:
        return None
    return lticks_linear / rticks_linear

compute_left_wheel_radius_result(distance_mm, lticks_delta, rticks_delta, state, encoder_ticks, left_polarity=1.0, right_polarity=1.0) #

Phase 3: Compute left wheel radius from straight line data.

For straight-line motion of distance D

D = 2π * radius_l * (lticks / encoder_ticks) D = 2π * radius_r * (rticks / encoder_ticks)

With radius_r = beta * radius_l, summing both equations: 2D = 2π * radius_l * (lticks + beta * rticks) / encoder_ticks

Formula: radius_l = D * encoder_ticks / (π * (lticks + beta * rticks))

Parameters:

Name Type Description Default
distance_mm int

Distance traveled in mm

required
lticks_delta int

Change in left encoder ticks (raw)

required
rticks_delta int

Change in right encoder ticks (raw)

required
state CalibrationState

Current calibration state with alpha and beta coefficients

required
encoder_ticks float

Encoder ticks per wheel revolution

required
left_polarity float

Left encoder polarity correction (+1 or -1)

1.0
right_polarity float

Right encoder polarity correction (+1 or -1)

1.0

Returns:

Type Description
tuple[CalibrationResult, CalibrationState] | None

Tuple of (CalibrationResult, CalibrationState), or None if computation fails

Source code in cogip/tools/firmware_odometry_calibration/calculator.py
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
def compute_left_wheel_radius_result(
    distance_mm: int,
    lticks_delta: int,
    rticks_delta: int,
    state: CalibrationState,
    encoder_ticks: float,
    left_polarity: float = 1.0,
    right_polarity: float = 1.0,
) -> tuple[CalibrationResult, CalibrationState] | None:
    """
    Phase 3: Compute left wheel radius from straight line data.

    For straight-line motion of distance D:
        D = 2π * radius_l * (lticks / encoder_ticks)
        D = 2π * radius_r * (rticks / encoder_ticks)

    With radius_r = beta * radius_l, summing both equations:
        2D = 2π * radius_l * (lticks + beta * rticks) / encoder_ticks

    Formula: radius_l = D * encoder_ticks / (π * (lticks + beta * rticks))

    Args:
        distance_mm: Distance traveled in mm
        lticks_delta: Change in left encoder ticks (raw)
        rticks_delta: Change in right encoder ticks (raw)
        state: Current calibration state with alpha and beta coefficients
        encoder_ticks: Encoder ticks per wheel revolution
        left_polarity: Left encoder polarity correction (+1 or -1)
        right_polarity: Right encoder polarity correction (+1 or -1)

    Returns:
        Tuple of (CalibrationResult, CalibrationState), or None if computation fails
    """
    # Apply polarity correction
    lticks_delta = int(lticks_delta * left_polarity)
    rticks_delta = int(rticks_delta * right_polarity)

    denominator = math.pi * (lticks_delta + state.beta * rticks_delta)

    if abs(denominator) < 1:
        return None

    new_left_wheel_radius = (distance_mm * encoder_ticks) / denominator
    new_right_wheel_radius = state.beta * new_left_wheel_radius
    new_wheels_distance = compute_wheel_distance(
        state.alpha_l, state.alpha_r, new_left_wheel_radius, new_right_wheel_radius
    )

    result = CalibrationResult(
        wheels_distance=new_wheels_distance,
        right_wheel_radius=new_right_wheel_radius,
        left_wheel_radius=new_left_wheel_radius,
    )

    # State unchanged in phase 3
    return result, state

compute_right_wheel_radius_result(squares, lticks_delta, rticks_delta, state, encoder_ticks, left_wheel_radius, left_polarity=1.0, right_polarity=1.0) #

Phase 2: Compute right wheel radius from square trajectory data.

Parameters:

Name Type Description Default
squares int

Number of square paths performed

required
lticks_delta int

Change in left encoder ticks (raw)

required
rticks_delta int

Change in right encoder ticks (raw)

required
state CalibrationState

Current calibration state with alpha coefficients

required
encoder_ticks float

Encoder ticks per wheel revolution

required
left_wheel_radius float

Current left wheel radius

required
left_polarity float

Left encoder polarity correction (+1 or -1)

1.0
right_polarity float

Right encoder polarity correction (+1 or -1)

1.0

Returns:

Type Description
tuple[CalibrationResult, CalibrationState] | None

Tuple of (CalibrationResult, updated CalibrationState), or None if computation fails

Source code in cogip/tools/firmware_odometry_calibration/calculator.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
def compute_right_wheel_radius_result(
    squares: int,
    lticks_delta: int,
    rticks_delta: int,
    state: CalibrationState,
    encoder_ticks: float,
    left_wheel_radius: float,
    left_polarity: float = 1.0,
    right_polarity: float = 1.0,
) -> tuple[CalibrationResult, CalibrationState] | None:
    """
    Phase 2: Compute right wheel radius from square trajectory data.

    Args:
        squares: Number of square paths performed
        lticks_delta: Change in left encoder ticks (raw)
        rticks_delta: Change in right encoder ticks (raw)
        state: Current calibration state with alpha coefficients
        encoder_ticks: Encoder ticks per wheel revolution
        left_wheel_radius: Current left wheel radius
        left_polarity: Left encoder polarity correction (+1 or -1)
        right_polarity: Right encoder polarity correction (+1 or -1)

    Returns:
        Tuple of (CalibrationResult, updated CalibrationState), or None if computation fails
    """
    # Apply polarity correction
    lticks_delta = int(lticks_delta * left_polarity)
    rticks_delta = int(rticks_delta * right_polarity)

    # Subtract rotation component to get linear component
    lticks_linear = lticks_delta - (state.alpha_l * encoder_ticks * squares)
    rticks_linear = rticks_delta - (state.alpha_r * encoder_ticks * squares)

    beta = compute_beta_coefficient(lticks_linear, rticks_linear)
    if beta is None:
        return None

    new_right_wheel_radius = beta * left_wheel_radius
    new_wheels_distance = compute_wheel_distance(
        state.alpha_l, state.alpha_r, left_wheel_radius, new_right_wheel_radius
    )

    result = CalibrationResult(
        wheels_distance=new_wheels_distance,
        right_wheel_radius=new_right_wheel_radius,
        left_wheel_radius=left_wheel_radius,
    )

    updated_state = CalibrationState(
        alpha_l=state.alpha_l,
        alpha_r=state.alpha_r,
        beta=beta,
    )

    return result, updated_state

compute_wheel_distance(alpha_l, alpha_r, left_wheel_radius, right_wheel_radius) #

Compute wheel distance from alpha coefficients and wheel radii.

Formula: axletrack = |alpha_l| * radius_l + |alpha_r| * radius_r

During rotation in place, wheels turn in opposite directions, so alpha coefficients have opposite signs. We use absolute values to sum their contributions.

Parameters:

Name Type Description Default
alpha_l float

Left wheel rotations per robot rotation (may be negative)

required
alpha_r float

Right wheel rotations per robot rotation (may be negative)

required
left_wheel_radius float

Current left wheel radius estimate

required
right_wheel_radius float

Current right wheel radius estimate

required

Returns:

Type Description
float

Computed wheel distance in mm

Source code in cogip/tools/firmware_odometry_calibration/calculator.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def compute_wheel_distance(
    alpha_l: float,
    alpha_r: float,
    left_wheel_radius: float,
    right_wheel_radius: float,
) -> float:
    """
    Compute wheel distance from alpha coefficients and wheel radii.

    Formula: axletrack = |alpha_l| * radius_l + |alpha_r| * radius_r

    During rotation in place, wheels turn in opposite directions, so alpha
    coefficients have opposite signs. We use absolute values to sum their
    contributions.

    Args:
        alpha_l: Left wheel rotations per robot rotation (may be negative)
        alpha_r: Right wheel rotations per robot rotation (may be negative)
        left_wheel_radius: Current left wheel radius estimate
        right_wheel_radius: Current right wheel radius estimate

    Returns:
        Computed wheel distance in mm
    """
    return abs(alpha_l) * left_wheel_radius + abs(alpha_r) * right_wheel_radius

compute_wheel_distance_result(turns, lticks_delta, rticks_delta, encoder_ticks, left_wheel_radius, right_wheel_radius, left_polarity=1.0, right_polarity=1.0) #

Phase 1: Compute wheel distance from turn-in-place data.

Parameters:

Name Type Description Default
turns int

Number of full rotations performed

required
lticks_delta int

Change in left encoder ticks (raw)

required
rticks_delta int

Change in right encoder ticks (raw)

required
encoder_ticks float

Encoder ticks per wheel revolution

required
left_wheel_radius float

Current left wheel radius

required
right_wheel_radius float

Current right wheel radius

required
left_polarity float

Left encoder polarity correction (+1 or -1)

1.0
right_polarity float

Right encoder polarity correction (+1 or -1)

1.0

Returns:

Type Description
tuple[CalibrationResult, CalibrationState]

Tuple of (CalibrationResult, updated CalibrationState)

Source code in cogip/tools/firmware_odometry_calibration/calculator.py
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def compute_wheel_distance_result(
    turns: int,
    lticks_delta: int,
    rticks_delta: int,
    encoder_ticks: float,
    left_wheel_radius: float,
    right_wheel_radius: float,
    left_polarity: float = 1.0,
    right_polarity: float = 1.0,
) -> tuple[CalibrationResult, CalibrationState]:
    """
    Phase 1: Compute wheel distance from turn-in-place data.

    Args:
        turns: Number of full rotations performed
        lticks_delta: Change in left encoder ticks (raw)
        rticks_delta: Change in right encoder ticks (raw)
        encoder_ticks: Encoder ticks per wheel revolution
        left_wheel_radius: Current left wheel radius
        right_wheel_radius: Current right wheel radius
        left_polarity: Left encoder polarity correction (+1 or -1)
        right_polarity: Right encoder polarity correction (+1 or -1)

    Returns:
        Tuple of (CalibrationResult, updated CalibrationState)
    """
    # Apply polarity correction
    lticks_delta = int(lticks_delta * left_polarity)
    rticks_delta = int(rticks_delta * right_polarity)

    alpha_l, alpha_r = compute_alpha_coefficients(turns, lticks_delta, rticks_delta, encoder_ticks)
    new_wheels_distance = compute_wheel_distance(alpha_l, alpha_r, left_wheel_radius, right_wheel_radius)

    result = CalibrationResult(
        wheels_distance=new_wheels_distance,
        right_wheel_radius=right_wheel_radius,
        left_wheel_radius=left_wheel_radius,
    )

    state = CalibrationState(
        alpha_l=alpha_l,
        alpha_r=alpha_r,
        beta=0.0,
    )

    return result, state