<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.bespokerobotsociety.org/index.php?action=history&amp;feed=atom&amp;title=Behavior%3ADead_Reckoning</id>
	<title>Behavior:Dead Reckoning - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.bespokerobotsociety.org/index.php?action=history&amp;feed=atom&amp;title=Behavior%3ADead_Reckoning"/>
	<link rel="alternate" type="text/html" href="https://wiki.bespokerobotsociety.org/index.php?title=Behavior:Dead_Reckoning&amp;action=history"/>
	<updated>2026-04-25T11:25:49Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://wiki.bespokerobotsociety.org/index.php?title=Behavior:Dead_Reckoning&amp;diff=48&amp;oldid=prev</id>
		<title>John: Claude edited based on my notes, prompt, and SimpleBot code repository</title>
		<link rel="alternate" type="text/html" href="https://wiki.bespokerobotsociety.org/index.php?title=Behavior:Dead_Reckoning&amp;diff=48&amp;oldid=prev"/>
		<updated>2025-10-11T16:47:59Z</updated>

		<summary type="html">&lt;p&gt;Claude edited based on my notes, prompt, and SimpleBot code repository&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Behavior&lt;br /&gt;
|name=Dead Reckoning&lt;br /&gt;
|requires=[[Capability:Optical Odometry]] or [[Capability:IMU Sensing]], [[Capability:Differential Drive]]&lt;br /&gt;
|enables=[[Activity:Dead Reckoning Navigation]]&lt;br /&gt;
|difficulty=Beginner to Intermediate&lt;br /&gt;
|implementations=[[SimpleBot:Dead Reckoning Implementation]]&lt;br /&gt;
|status=Fully Documented&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Dead reckoning&amp;#039;&amp;#039;&amp;#039; is a navigation technique where a robot estimates its current position by tracking all motion from a known starting position. The term comes from maritime navigation (&amp;quot;deduced reckoning&amp;quot;) and involves continuously calculating position based on direction and distance traveled, without external reference points.&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
Dead reckoning works by:&lt;br /&gt;
# Starting from a known position (typically x=0, y=0, heading=0)&lt;br /&gt;
# Measuring all motion (linear and angular)&lt;br /&gt;
# Continuously updating the estimated position based on motion measurements&lt;br /&gt;
# Maintaining a pose estimate (x position, y position, heading angle θ)&lt;br /&gt;
&lt;br /&gt;
Unlike GPS or beacon-based navigation, dead reckoning is completely self-contained and works in any environment. However, small measurement errors accumulate over time, causing position estimates to drift from the true position.&lt;br /&gt;
&lt;br /&gt;
== Odometry-Based Dead Reckoning ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Odometry-based dead reckoning&amp;#039;&amp;#039;&amp;#039; uses wheel encoder measurements to track motion. This is the most common approach for ground robots and is the method used by SimpleBot.&lt;br /&gt;
&lt;br /&gt;
=== Principle of Operation ===&lt;br /&gt;
&lt;br /&gt;
For a differential drive robot:&lt;br /&gt;
# Count encoder pulses from left and right wheels independently&lt;br /&gt;
# Calculate distance traveled by each wheel using known wheel circumference and encoder resolution&lt;br /&gt;
# Determine robot motion based on wheel motion differences:&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Equal wheel distances&amp;#039;&amp;#039;&amp;#039; → Robot moves straight&lt;br /&gt;
#* &amp;#039;&amp;#039;&amp;#039;Different wheel distances&amp;#039;&amp;#039;&amp;#039; → Robot follows a curved path (arc)&lt;br /&gt;
# Update robot pose (x, y, θ) based on calculated motion&lt;br /&gt;
&lt;br /&gt;
=== Mathematical Foundation ===&lt;br /&gt;
&lt;br /&gt;
The basic calculations for odometry-based dead reckoning:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 1: Calculate wheel distances&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
 distance_left = left_pulses × distance_per_pulse&lt;br /&gt;
 distance_right = right_pulses × distance_per_pulse&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
 distance_per_pulse = (wheel_circumference) / (pulses_per_revolution)&lt;br /&gt;
 wheel_circumference = π × wheel_diameter&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 2: Calculate motion parameters&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
 distance_traveled = (distance_left + distance_right) / 2&lt;br /&gt;
 angle_turned = (distance_right - distance_left) / wheel_separation&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Step 3: Update pose&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
For straight motion (distance_left ≈ distance_right):&lt;br /&gt;
 x_new = x_old + distance_traveled × cos(θ_old)&lt;br /&gt;
 y_new = y_old + distance_traveled × sin(θ_old)&lt;br /&gt;
 θ_new = θ_old&lt;br /&gt;
&lt;br /&gt;
For curved motion (general case):&lt;br /&gt;
 θ_new = θ_old + angle_turned&lt;br /&gt;
 x_new = x_old + distance_traveled × cos(θ_old + angle_turned/2)&lt;br /&gt;
 y_new = y_old + distance_traveled × sin(θ_old + angle_turned/2)&lt;br /&gt;
&lt;br /&gt;
=== Pseudocode Example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# Initialize robot pose&lt;br /&gt;
pose_x = 0.0&lt;br /&gt;
pose_y = 0.0&lt;br /&gt;
pose_theta = 0.0&lt;br /&gt;
&lt;br /&gt;
# Robot physical parameters&lt;br /&gt;
WHEEL_DIAMETER = 0.065  # meters&lt;br /&gt;
WHEEL_SEPARATION = 0.15  # meters&lt;br /&gt;
PULSES_PER_REV = 20&lt;br /&gt;
&lt;br /&gt;
# Calculate distance per pulse&lt;br /&gt;
WHEEL_CIRCUMFERENCE = math.pi * WHEEL_DIAMETER&lt;br /&gt;
DISTANCE_PER_PULSE = WHEEL_CIRCUMFERENCE / PULSES_PER_REV&lt;br /&gt;
&lt;br /&gt;
def update_odometry(left_pulses, right_pulses):&lt;br /&gt;
    global pose_x, pose_y, pose_theta&lt;br /&gt;
&lt;br /&gt;
    # Calculate distances traveled by each wheel&lt;br /&gt;
    dist_left = left_pulses * DISTANCE_PER_PULSE&lt;br /&gt;
    dist_right = right_pulses * DISTANCE_PER_PULSE&lt;br /&gt;
&lt;br /&gt;
    # Calculate center distance and rotation&lt;br /&gt;
    dist_center = (dist_left + dist_right) / 2.0&lt;br /&gt;
    delta_theta = (dist_right - dist_left) / WHEEL_SEPARATION&lt;br /&gt;
&lt;br /&gt;
    # Update pose using midpoint method&lt;br /&gt;
    if abs(delta_theta) &amp;lt; 0.001:  # Straight motion&lt;br /&gt;
        pose_x += dist_center * math.cos(pose_theta)&lt;br /&gt;
        pose_y += dist_center * math.sin(pose_theta)&lt;br /&gt;
    else:  # Curved motion&lt;br /&gt;
        # Use heading at midpoint of arc&lt;br /&gt;
        mid_theta = pose_theta + delta_theta / 2.0&lt;br /&gt;
        pose_x += dist_center * math.cos(mid_theta)&lt;br /&gt;
        pose_y += dist_center * math.sin(mid_theta)&lt;br /&gt;
        pose_theta += delta_theta&lt;br /&gt;
&lt;br /&gt;
    # Normalize angle to [-π, π]&lt;br /&gt;
    pose_theta = math.atan2(math.sin(pose_theta), math.cos(pose_theta))&lt;br /&gt;
&lt;br /&gt;
    return (pose_x, pose_y, pose_theta)&lt;br /&gt;
&lt;br /&gt;
# Main loop&lt;br /&gt;
while robot_running:&lt;br /&gt;
    # Read encoder increments since last update&lt;br /&gt;
    left_pulses = read_left_encoder_delta()&lt;br /&gt;
    right_pulses = read_right_encoder_delta()&lt;br /&gt;
&lt;br /&gt;
    # Update position estimate&lt;br /&gt;
    position = update_odometry(left_pulses, right_pulses)&lt;br /&gt;
&lt;br /&gt;
    # Use position for navigation decisions&lt;br /&gt;
    navigate_using_position(position)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Odometry Error Sources ===&lt;br /&gt;
&lt;br /&gt;
Common error sources in odometry-based dead reckoning:&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Wheel slippage&amp;#039;&amp;#039;&amp;#039;: Wheels slip on smooth surfaces or during rapid acceleration&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Uneven wheel diameters&amp;#039;&amp;#039;&amp;#039;: Manufacturing variations or uneven wear&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Mechanical play&amp;#039;&amp;#039;&amp;#039;: Backlash in gears and drivetrain&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Encoder resolution&amp;#039;&amp;#039;&amp;#039;: Limited pulse count causes quantization errors&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Surface irregularities&amp;#039;&amp;#039;&amp;#039;: Bumps, cracks, and slopes affect wheel rotation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Wheel alignment&amp;#039;&amp;#039;&amp;#039;: Misaligned wheels cause systematic drift&lt;br /&gt;
&lt;br /&gt;
== IMU-Based Dead Reckoning ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;IMU-based dead reckoning&amp;#039;&amp;#039;&amp;#039; uses inertial measurement unit sensors (accelerometers and gyroscopes) to track motion. This approach is more complex than odometry and requires sophisticated sensor fusion.&lt;br /&gt;
&lt;br /&gt;
=== Principle of Operation ===&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Measure acceleration&amp;#039;&amp;#039;&amp;#039; using 3-axis accelerometer&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Remove gravity component&amp;#039;&amp;#039;&amp;#039; from acceleration readings&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrate acceleration&amp;#039;&amp;#039;&amp;#039; to obtain velocity&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrate velocity&amp;#039;&amp;#039;&amp;#039; to obtain position&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Measure angular velocity&amp;#039;&amp;#039;&amp;#039; using 3-axis gyroscope&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Integrate gyroscope readings&amp;#039;&amp;#039;&amp;#039; to obtain heading angle&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Apply sensor fusion&amp;#039;&amp;#039;&amp;#039; to combine all measurements and reduce drift&lt;br /&gt;
&lt;br /&gt;
=== Mathematical Foundation ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;For position (simplified 2D case):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
 # Measure acceleration in body frame&lt;br /&gt;
 accel_body = read_accelerometer()&lt;br /&gt;
&lt;br /&gt;
 # Rotate to world frame using current heading&lt;br /&gt;
 accel_world_x = accel_body_x × cos(θ) - accel_body_y × sin(θ)&lt;br /&gt;
 accel_world_y = accel_body_x × sin(θ) + accel_body_y × cos(θ)&lt;br /&gt;
&lt;br /&gt;
 # Remove gravity (9.81 m/s² in z-axis)&lt;br /&gt;
 accel_world_z = accel_body_z - 9.81&lt;br /&gt;
&lt;br /&gt;
 # Integrate to get velocity&lt;br /&gt;
 velocity_x = velocity_x_prev + accel_world_x × dt&lt;br /&gt;
 velocity_y = velocity_y_prev + accel_world_y × dt&lt;br /&gt;
&lt;br /&gt;
 # Integrate to get position&lt;br /&gt;
 position_x = position_x_prev + velocity_x × dt&lt;br /&gt;
 position_y = position_y_prev + velocity_y × dt&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;For heading:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
 # Measure angular velocity&lt;br /&gt;
 gyro_z = read_gyroscope_z()&lt;br /&gt;
&lt;br /&gt;
 # Integrate to get heading&lt;br /&gt;
 θ = θ_prev + gyro_z × dt&lt;br /&gt;
&lt;br /&gt;
=== Pseudocode Example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
# Initialize state&lt;br /&gt;
position = [0.0, 0.0, 0.0]  # x, y, z&lt;br /&gt;
velocity = [0.0, 0.0, 0.0]&lt;br /&gt;
theta = 0.0  # heading angle&lt;br /&gt;
prev_time = time.now()&lt;br /&gt;
&lt;br /&gt;
# IMU calibration offsets (determined during calibration)&lt;br /&gt;
ACCEL_BIAS = [0.05, -0.03, 0.02]  # m/s²&lt;br /&gt;
GYRO_BIAS = [0.01, 0.02, -0.01]   # rad/s&lt;br /&gt;
GRAVITY = 9.81  # m/s²&lt;br /&gt;
&lt;br /&gt;
def rotation_matrix_2d(angle):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Create 2D rotation matrix&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    return [[math.cos(angle), -math.sin(angle)],&lt;br /&gt;
            [math.sin(angle),  math.cos(angle)]]&lt;br /&gt;
&lt;br /&gt;
def update_imu_dead_reckoning():&lt;br /&gt;
    global position, velocity, theta, prev_time&lt;br /&gt;
&lt;br /&gt;
    # Calculate time step&lt;br /&gt;
    current_time = time.now()&lt;br /&gt;
    dt = current_time - prev_time&lt;br /&gt;
    prev_time = current_time&lt;br /&gt;
&lt;br /&gt;
    # Read IMU sensors&lt;br /&gt;
    accel_body = read_accelerometer()  # [ax, ay, az] in body frame&lt;br /&gt;
    gyro_body = read_gyroscope()       # [gx, gy, gz] in body frame&lt;br /&gt;
&lt;br /&gt;
    # Apply calibration corrections&lt;br /&gt;
    accel_body = [accel_body[i] - ACCEL_BIAS[i] for i in range(3)]&lt;br /&gt;
    gyro_body = [gyro_body[i] - GYRO_BIAS[i] for i in range(3)]&lt;br /&gt;
&lt;br /&gt;
    # Update heading from gyroscope&lt;br /&gt;
    theta += gyro_body[2] * dt  # z-axis gyro&lt;br /&gt;
&lt;br /&gt;
    # Rotate acceleration to world frame&lt;br /&gt;
    rot_mat = rotation_matrix_2d(theta)&lt;br /&gt;
    accel_world_x = rot_mat[0][0] * accel_body[0] + rot_mat[0][1] * accel_body[1]&lt;br /&gt;
    accel_world_y = rot_mat[1][0] * accel_body[0] + rot_mat[1][1] * accel_body[1]&lt;br /&gt;
    accel_world_z = accel_body[2]&lt;br /&gt;
&lt;br /&gt;
    # Remove gravity from z-axis&lt;br /&gt;
    accel_world_z -= GRAVITY&lt;br /&gt;
&lt;br /&gt;
    # Integrate acceleration to velocity&lt;br /&gt;
    velocity[0] += accel_world_x * dt&lt;br /&gt;
    velocity[1] += accel_world_y * dt&lt;br /&gt;
    velocity[2] += accel_world_z * dt&lt;br /&gt;
&lt;br /&gt;
    # Integrate velocity to position&lt;br /&gt;
    position[0] += velocity[0] * dt&lt;br /&gt;
    position[1] += velocity[1] * dt&lt;br /&gt;
    position[2] += velocity[2] * dt&lt;br /&gt;
&lt;br /&gt;
    return (position, velocity, theta)&lt;br /&gt;
&lt;br /&gt;
# Main loop&lt;br /&gt;
while robot_running:&lt;br /&gt;
    pose = update_imu_dead_reckoning()&lt;br /&gt;
&lt;br /&gt;
    # Use position for navigation&lt;br /&gt;
    navigate_using_position(pose)&lt;br /&gt;
&lt;br /&gt;
    # Sleep to maintain consistent update rate&lt;br /&gt;
    time.sleep(0.01)  # 100 Hz update rate&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== IMU Error Sources ===&lt;br /&gt;
&lt;br /&gt;
IMU-based dead reckoning faces significant challenges:&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gyroscope drift&amp;#039;&amp;#039;&amp;#039;: Bias errors accumulate rapidly when integrated&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Accelerometer bias&amp;#039;&amp;#039;&amp;#039;: Small constant errors become large position errors after double integration&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sensor noise&amp;#039;&amp;#039;&amp;#039;: Random noise accumulates through integration&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gravity vector estimation&amp;#039;&amp;#039;&amp;#039;: Errors in removing gravity cause acceleration measurement errors&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Temperature effects&amp;#039;&amp;#039;&amp;#039;: Sensor characteristics change with temperature&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vibration&amp;#039;&amp;#039;&amp;#039;: High-frequency vibration can bias accelerometer readings&lt;br /&gt;
&lt;br /&gt;
IMU-only dead reckoning typically exhibits much higher drift rates than odometry-based systems. Most practical systems combine IMU data with other sensors (sensor fusion).&lt;br /&gt;
&lt;br /&gt;
== Error Accumulation ==&lt;br /&gt;
&lt;br /&gt;
All dead reckoning systems suffer from &amp;#039;&amp;#039;&amp;#039;unbounded error growth&amp;#039;&amp;#039;&amp;#039; because small measurement errors are integrated over time:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Odometry drift&amp;#039;&amp;#039;&amp;#039;: Typically 5-10% of distance traveled under good conditions&lt;br /&gt;
** Example: After driving 10 meters, position error might be 0.5-1.0 meters&lt;br /&gt;
** Error grows approximately linearly with distance&lt;br /&gt;
** Systematic errors (like unequal wheel diameters) cause circular drift patterns&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IMU drift&amp;#039;&amp;#039;&amp;#039;: Much worse due to double integration of accelerometer noise&lt;br /&gt;
** Position error grows quadratically with time&lt;br /&gt;
** Without corrections, IMU-only systems become unusable within seconds&lt;br /&gt;
** Gyro drift causes heading errors that compound position errors&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Combined effects&amp;#039;&amp;#039;&amp;#039;: Heading errors particularly problematic&lt;br /&gt;
** A 1-degree heading error causes 1.7% lateral drift&lt;br /&gt;
** After driving 10 meters with 1° heading error, lateral position error is 0.17 meters&lt;br /&gt;
&lt;br /&gt;
== Coordinate Systems ==&lt;br /&gt;
&lt;br /&gt;
Dead reckoning requires careful management of coordinate frames:&lt;br /&gt;
&lt;br /&gt;
=== Robot Frame (Body Frame) ===&lt;br /&gt;
* Origin at robot center&lt;br /&gt;
* X-axis points forward&lt;br /&gt;
* Y-axis points left&lt;br /&gt;
* Rotates with the robot&lt;br /&gt;
&lt;br /&gt;
=== World Frame (Global Frame) ===&lt;br /&gt;
* Fixed reference frame&lt;br /&gt;
* Origin at starting position&lt;br /&gt;
* X and Y axes aligned with environment&lt;br /&gt;
* Does not move&lt;br /&gt;
&lt;br /&gt;
=== Pose Representation ===&lt;br /&gt;
&lt;br /&gt;
Robot pose in world frame: &amp;#039;&amp;#039;&amp;#039;(x, y, θ)&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;x&amp;#039;&amp;#039;&amp;#039;: Position along world X-axis&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;y&amp;#039;&amp;#039;&amp;#039;: Position along world Y-axis&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;θ&amp;#039;&amp;#039;&amp;#039;: Heading angle (orientation) relative to world X-axis&lt;br /&gt;
&lt;br /&gt;
=== Coordinate Transformation ===&lt;br /&gt;
&lt;br /&gt;
To transform a point from robot frame to world frame:&lt;br /&gt;
&lt;br /&gt;
 x_world = x_robot × cos(θ) - y_robot × sin(θ) + pose_x&lt;br /&gt;
 y_world = x_robot × sin(θ) + y_robot × cos(θ) + pose_y&lt;br /&gt;
&lt;br /&gt;
Using rotation matrix form:&lt;br /&gt;
 [x_world]   [cos(θ)  -sin(θ)] [x_robot]   [pose_x]&lt;br /&gt;
 [y_world] = [sin(θ)   cos(θ)] [y_robot] + [pose_y]&lt;br /&gt;
&lt;br /&gt;
== Improving Accuracy ==&lt;br /&gt;
&lt;br /&gt;
Several techniques can reduce dead reckoning errors:&lt;br /&gt;
&lt;br /&gt;
=== Sensor Fusion ===&lt;br /&gt;
Combine multiple sensor types to leverage their complementary characteristics:&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Odometry + IMU&amp;#039;&amp;#039;&amp;#039;: Odometry for position, IMU for rapid orientation updates&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Complementary filtering&amp;#039;&amp;#039;&amp;#039;: Use IMU for short-term (fast response), odometry for long-term (low drift)&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kalman filtering&amp;#039;&amp;#039;&amp;#039;: Optimal fusion of multiple noisy sensors&lt;br /&gt;
&lt;br /&gt;
=== External Reference Points ===&lt;br /&gt;
Reset or correct position estimates when passing known landmarks:&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Beacon detection&amp;#039;&amp;#039;&amp;#039;: Reset position when detecting a marker at known location&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Wall following&amp;#039;&amp;#039;&amp;#039;: Use distance sensors to maintain known offset from wall&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Line detection&amp;#039;&amp;#039;&amp;#039;: Reset position when crossing known lines or boundaries&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Visual landmarks&amp;#039;&amp;#039;&amp;#039;: Use camera to identify known features&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
Reduce systematic errors through careful calibration:&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Wheel diameter&amp;#039;&amp;#039;&amp;#039;: Measure actual effective diameter under load&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Wheel separation&amp;#039;&amp;#039;&amp;#039;: Measure actual track width between wheel contact points&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Encoder resolution&amp;#039;&amp;#039;&amp;#039;: Verify actual pulses per revolution&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;IMU bias&amp;#039;&amp;#039;&amp;#039;: Measure sensor offsets while stationary&lt;br /&gt;
&lt;br /&gt;
=== Closed-Loop Verification ===&lt;br /&gt;
Test dead reckoning accuracy by:&lt;br /&gt;
* Driving a closed path (square, circle) and measuring final position error&lt;br /&gt;
* Comparing estimated position to known checkpoints&lt;br /&gt;
* Recording error statistics over multiple runs&lt;br /&gt;
* Adjusting calibration parameters to minimize systematic errors&lt;br /&gt;
&lt;br /&gt;
=== Advanced Techniques ===&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Kalman filters&amp;#039;&amp;#039;&amp;#039;: Optimal state estimation with uncertainty quantification&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Particle filters&amp;#039;&amp;#039;&amp;#039;: Handle non-Gaussian errors and multiple hypotheses&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Extended Kalman Filter (EKF)&amp;#039;&amp;#039;&amp;#039;: Handle non-linear motion models&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Unscented Kalman Filter (UKF)&amp;#039;&amp;#039;&amp;#039;: Better handling of severe non-linearities&lt;br /&gt;
&lt;br /&gt;
== SimpleBot Implementation ==&lt;br /&gt;
&lt;br /&gt;
SimpleBot implements odometry-based dead reckoning using optical wheel encoders:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sensor type&amp;#039;&amp;#039;&amp;#039;: Optical encoders on each wheel&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Pulse counting&amp;#039;&amp;#039;&amp;#039;: Interrupt-driven pulse accumulation&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Update rate&amp;#039;&amp;#039;&amp;#039;: Real-time position calculation on each encoder pulse&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Drivetrain&amp;#039;&amp;#039;&amp;#039;: Differential drive with two independent motors&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Capabilities&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
** Track current (x, y, θ) pose continuously&lt;br /&gt;
** Execute motion patterns (squares, circles, arbitrary paths)&lt;br /&gt;
** Return to starting position via reverse path&lt;br /&gt;
** Report position estimates for navigation decisions&lt;br /&gt;
&lt;br /&gt;
See [[SimpleBot:Dead Reckoning Implementation]] for complete implementation details including:&lt;br /&gt;
* Hardware setup (encoder mounting and wiring)&lt;br /&gt;
* Interrupt service routines for pulse counting&lt;br /&gt;
* Position calculation algorithms&lt;br /&gt;
* Calibration procedures&lt;br /&gt;
* Example programs and test patterns&lt;br /&gt;
&lt;br /&gt;
== Practical Challenges ==&lt;br /&gt;
&lt;br /&gt;
=== Challenge 1: Drive and Return ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective&amp;#039;&amp;#039;&amp;#039;: Test basic dead reckoning accuracy&lt;br /&gt;
&lt;br /&gt;
# Program robot to drive a pattern (square, circle, or arbitrary path)&lt;br /&gt;
# Record the motion commands&lt;br /&gt;
# Execute the reverse motion sequence to return to start&lt;br /&gt;
# Measure final position error&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Success criteria&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
* Position error &amp;lt; 5% of total distance traveled&lt;br /&gt;
* Heading error &amp;lt; 5 degrees&lt;br /&gt;
&lt;br /&gt;
=== Challenge 2: Right Triangle Test ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective&amp;#039;&amp;#039;&amp;#039;: Test position accuracy across different motion types&lt;br /&gt;
&lt;br /&gt;
# Drive forward distance D&lt;br /&gt;
# Turn 90 degrees right&lt;br /&gt;
# Drive forward distance D (now at far corner of square)&lt;br /&gt;
# Calculate and turn toward starting position&lt;br /&gt;
# Drive directly back to start (hypotenuse of right triangle)&lt;br /&gt;
# Measure final position error&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Success criteria&amp;#039;&amp;#039;&amp;#039;:&lt;br /&gt;
* Return to within 10 cm of starting position&lt;br /&gt;
* Demonstrates both distance and angle accuracy&lt;br /&gt;
&lt;br /&gt;
This challenge tests:&lt;br /&gt;
* Forward distance measurement accuracy&lt;br /&gt;
* Turn angle accuracy&lt;br /&gt;
* Integration of distance and angle to calculate return path&lt;br /&gt;
* Cumulative error over multi-segment path&lt;br /&gt;
&lt;br /&gt;
=== Challenge 3: Long Distance Drift ===&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objective&amp;#039;&amp;#039;&amp;#039;: Characterize error growth over distance&lt;br /&gt;
&lt;br /&gt;
# Drive straight for increasing distances: 1m, 2m, 5m, 10m&lt;br /&gt;
# At each distance, measure lateral drift and distance error&lt;br /&gt;
# Calculate drift as percentage of distance traveled&lt;br /&gt;
# Plot error versus distance to characterize error growth&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&lt;br /&gt;
* [[Capability:Optical Odometry]] - Wheel encoder hardware and pulse counting&lt;br /&gt;
* [[Capability:IMU Sensing]] - Inertial measurement unit sensors&lt;br /&gt;
* [[Capability:Differential Drive]] - Two-wheeled drivetrain fundamentals&lt;br /&gt;
* [[Activity:Dead Reckoning Navigation]] - Using dead reckoning for autonomous navigation&lt;br /&gt;
* [[SimpleBot:Dead Reckoning Implementation]] - Complete implementation on SimpleBot&lt;br /&gt;
* [[Behavior:Wall Following]] - Navigation behavior that can correct dead reckoning drift&lt;br /&gt;
&lt;br /&gt;
[[Category:Behavior]]&lt;br /&gt;
[[Category:Beginner Behavior]]&lt;br /&gt;
[[Category:Intermediate Behavior]]&lt;/div&gt;</summary>
		<author><name>John</name></author>
	</entry>
</feed>