← Back to Dashboard

Lab 9: Mapping 🗺️

MAE 4190 • Spring 2026 • Rajarshi Das

Approach

Scan Strategy

I chose orientation control (PID on integrated gyroscope yaw) for the mapping scan, reusing the PID tuning from Lab 6 (Kp = 8, Kd = 0.52). The robot performs incremental on-axis turns of 35°, stopping at each step to take a stationary ToF reading. Since 35 does not divide 360 evenly (GCD = 5), readings from successive rotations land at different angular positions. Over 41 readings (~4 full rotations), this produces 41 unique angles mod 360° with an average spacing of ~8.8° - well above the lab's minimum of ~25°.

The entire scan is executed autonomously on the Artemis via a state machine (mapping.cpp) that cycles through TURNING → SETTLING (500ms) → READING for each increment. Stopping before each ToF measurement ensures the sensor points at a fixed direction, avoiding the false readings that occur when distance changes during a measurement.


BLE Commands

Two new BLE commands were added for this lab:

CommandIDDescription
MAP_SCAN21Starts scan: MAP_SCAN:35|41 → 41 readings at 35° increments
GET_MAP_LOG22Sends logged (yaw, tof1, tof2) tuples over BLE

On the Python side, a run_scan() helper sets gains, triggers the scan, waits for completion, and retrieves data. Each location's data is saved as JSON for offline replotting.

angles, tof1, tof2 = run_scan(ble, increment=35, num_readings=41,
                                kp=8.0, ki=0.0, kd=0.52)

Orientation Control

PID Performance

The yaw check plots below compare actual gyro yaw against ideal 35° increments across all 41 readings. The error subplot shows per-step deviation. Across all scan locations, the PID achieves a mean error of ~1.3° with σ < 1°, consistent over 4 full rotations. The small negative bias (~-1.35°) is expected - the 2.5° brake zone in the PID causes the robot to stop just short of each target.

Yaw check at (-3,-2)
Point 1 (-3, -2): mean=-1.5°, std=0.5°
Yaw check at (0,3)
Point 2 (0, 3): mean=-1.1°, std=0.8°
Yaw check at (5,-3)
Point 3 (5, -3): mean=-1.3°, std=0.6°
Yaw check at (5,3)
Point 4 (5, 3): mean=-1.5°, std=0.6°

On-Axis Turn Video

I did not realize a video was required until writing this report, so this was recorded on a table at home rather than in the lab. The robot turns roughly on-axis with some visible drift. In the lab, the robot stayed within the 1ft × 1ft grid tiles with less drift - likely due to different surface friction and battery charge. The primary mapping error source is this on-axis drift, as the gyroscope yaw and stationary ToF readings are both very accurate.


Scan Results

Scans were collected at four marked locations, all with the same starting orientation (front sensor toward +y).

Point 1: (-3, -2)

Polar plot at (-3,-2)
Polar plot - front and side ToF
Rotation overlay at (-3,-2)
Multi-rotation overlay - readings overlap well

Point 2: (0, 3)

Polar plot at (0,3)
Polar plot - front and side ToF
Rotation overlay at (0,3)
Multi-rotation overlay

Point 3: (5, -3)

Polar plot at (5,-3)
Polar plot - front and side ToF
Rotation overlay at (5,-3)
Multi-rotation overlay

Point 4: (5, 3)

Polar plot at (5,3)
Polar plot - front and side ToF
Rotation overlay at (5,3)
Multi-rotation overlay

Merge & Transform

Transformation Matrices

Converting sensor measurements to world coordinates requires two chained transformations.

T1 - Sensor frame → Robot frame. The front ToF sensor is offset ~0.0635 ft (¾ inch) from the robot's center of rotation along its forward axis:

$$T_1 = \begin{bmatrix} 1 & 0 & 0 & 0.0635 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

T2 - Robot frame → World (inertial) frame. The robot is at known position $(x_r, y_r)$, rotated by $\theta$ (gyro yaw + initial heading $\theta_0 = 90°$ since the robot starts facing +y):

$$T_2 = \begin{bmatrix} \cos\theta & -\sin\theta & 0 & x_r \\ \sin\theta & \cos\theta & 0 & y_r \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

For a ToF measurement of distance $d$, the sensor-frame point vector is:

$$P = \begin{bmatrix} d \\ 0 \\ 0 \\ 1 \end{bmatrix}$$

The world-frame coordinates are then computed by chaining the transforms:

$$\begin{bmatrix} x_w \\ y_w \\ 0 \\ 1 \end{bmatrix} = T_2 \cdot T_1 \cdot P$$

Which expands to the closed-form equations used in my Python code:

for i in range(len(angles_radians)):
    angle = angles_radians[i]
    d = tof_mm[i] * 0.00328084  # mm to ft

    # T2: robot frame -> world frame
    T_i = np.array([[np.cos(angle), -np.sin(angle), 0, x_r],
                     [np.sin(angle),  np.cos(angle), 0, y_r],
                     [0, 0, 1, 0],
                     [0, 0, 0, 1]])

    # T1: sensor frame -> robot frame
    T_r = np.array([[1, 0, 0, 0.0635],
                     [0, 1, 0, 0],
                     [0, 0, 1, 0],
                     [0, 0, 0, 1]])

    p_tof = np.array([[d], [0], [0], [1]])

    coords = np.matmul(T_i, np.matmul(T_r, p_tof))
    x_coords.append(coords[0][0])
    y_coords.append(coords[1][0])

Since the ToF offset (0.0635 ft ≈ 19mm) is small relative to wall distances, the simplified form x = x_r + d·cos(θ₀ + α) produces nearly identical results.


Merged Plot

All scans merged into world-frame coordinates. Circles = front sensor, × = side sensor (offset 90°), squares = robot positions.

Merged map - all scan locations

Line-Based Map

Wall segments were manually estimated from the merged scatter plot. The outer boundary, a left-side notch, a bottom bump, and an interior box obstacle were traced. Wall endpoint lists are saved to line_map.json for Lab 10's simulator.

Line-based map

Error Analysis

For an on-axis turn in the center of a 4×4m empty room (2m to each wall):

Angular error: With a measured mean of ~1.3° and max of ~2.5°, positional error at 2m is 2000mm × sin(1.3°) ≈ 45mm average, and 2000mm × sin(2.5°) ≈ 87mm maximum.

ToF noise: From Lab 3, ±3-11mm depending on range. At 2m, ~11mm. Combined via RSS: √(45² + 11²) ≈ 46mm total error at 2m (~1.5 inches).

Dominant error source: On-axis drift during turns contributes more to mapping error than sensor noise or angular accuracy. The gyro yaw is precise (~1°), the ToF is accurate when stationary, but the robot's center shifting during rotation moves the effective scan origin by 1-2cm per rotation, causing slight smearing in the merged plot.

Acknowledgements

Claude AI was used for assistance with the mapping state machine implementation, BLE command design, transformation matrix code, plotting utilities, and structuring this lab report. All hardware testing, data collection, PID tuning, scan execution, and wall segment estimation were done by me.