Module 2: Chassis & Drivetrain Assembly
Level: 🟢 Beginner Board: None (mechanical build) Prerequisites: Module 1 Estimated time: 60–90 minutes Goal: Assemble the rolling chassis so it passes the 1-metre straight-line push test with less than 5 cm of lateral drift.
What You'll Learn
By the end of this module you will understand differential drive geometry, be able to mount motors with correct bracket alignment, install the ball caster at the right height, and verify the chassis mechanically before any electronics are connected.
The chassis is the foundation everything else mounts to. A motor that is 2 mm out of square will make your robot drift under power. A caster set at the wrong height shifts weight off the drive wheels and kills traction. Fifteen minutes of understanding the geometry now prevents an hour of confused debugging later.
2.1 Differential drive geometry
Your robot steers by running its two wheels at different speeds. There is no steering axle and no servo turning a front wheel. The left and right motors each drive one wheel independently. Speed one up relative to the other and the robot curves toward the slower side. Run them in opposite directions and it spins on the spot. This is called differential drive, and it is the most common locomotion system in small robotics because it has no extra moving parts to fail and gives you complete control over both speed and heading through software alone.
The three diagrams below show every fundamental movement case. Study them before you touch hardware. This mental model is the one you will reach for constantly when writing motion code in Modules 4 through 6.
<svg viewBox="0 0 740 290" width="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="aB" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#1a1a18" stroke-width="1.5" stroke-linecap="round"/></marker>
<marker id="aG" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#1a6b4a" stroke-width="1.5" stroke-linecap="round"/></marker>
<marker id="aR" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#c8420a" stroke-width="1.5" stroke-linecap="round"/></marker>
<marker id="aRot" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M2 2L8 5L2 8" fill="none" stroke="#1f4d8c" stroke-width="1.5" stroke-linecap="round"/></marker>
</defs>
<!-- col labels -->
<text x="110" y="28" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" letter-spacing="1" fill="#6b6a65">CASE 1 — STRAIGHT</text>
<text x="370" y="28" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" letter-spacing="1" fill="#6b6a65">CASE 2 — ARC TURN</text>
<text x="620" y="28" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" letter-spacing="1" fill="#6b6a65">CASE 3 — PIVOT</text>
<!-- ── CASE 1 ── -->
<rect x="60" y="95" width="100" height="110" rx="8" fill="#e2ded3" stroke="#b0aba0" stroke-width="1.5"/>
<rect x="42" y="113" width="20" height="74" rx="6" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.5"/>
<rect x="158" y="113" width="20" height="74" rx="6" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.5"/>
<circle cx="110" cy="91" r="8" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.2"/>
<rect x="46" y="80" width="7" height="26" rx="2" fill="#1a6b4a"/>
<rect x="55" y="80" width="7" height="26" rx="2" fill="#1a6b4a"/>
<rect x="161" y="80" width="7" height="26" rx="2" fill="#1a6b4a"/>
<rect x="170" y="80" width="7" height="26" rx="2" fill="#1a6b4a"/>
<line x1="110" y1="86" x2="110" y2="52" stroke="#1a1a18" stroke-width="2" marker-end="url(#aB)"/>
<text x="110" y="228" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="11" font-weight="500" fill="#1a6b4a">L = R speed</text>
<text x="110" y="245" text-anchor="middle" font-family="Manrope,sans-serif" font-size="12" fill="#3d3c38">drives straight</text>
<line x1="248" y1="44" x2="248" y2="270" stroke="#c4bfb0" stroke-width="1" stroke-dasharray="4 3"/>
<line x1="498" y1="44" x2="498" y2="270" stroke="#c4bfb0" stroke-width="1" stroke-dasharray="4 3"/>
<!-- ── CASE 2 ── -->
<rect x="320" y="95" width="100" height="110" rx="8" fill="#e2ded3" stroke="#b0aba0" stroke-width="1.5"/>
<rect x="302" y="113" width="20" height="74" rx="6" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.5"/>
<rect x="418" y="113" width="20" height="74" rx="6" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.5"/>
<circle cx="370" cy="91" r="8" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.2"/>
<rect x="307" y="96" width="7" height="12" rx="2" fill="#c8aa60"/>
<rect x="422" y="80" width="7" height="28" rx="2" fill="#1a6b4a"/>
<rect x="431" y="80" width="7" height="28" rx="2" fill="#1a6b4a"/>
<path d="M370 86 Q328 58 296 44" fill="none" stroke="#1a1a18" stroke-width="2" marker-end="url(#aB)"/>
<circle cx="302" cy="150" r="5" fill="#c8420a" stroke="#a03000" stroke-width="1"/>
<text x="285" y="160" text-anchor="end" font-family="IBM Plex Mono,monospace" font-size="9" fill="#c8420a">ICR</text>
<text x="370" y="228" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="11" font-weight="500" fill="#c8420a">L < R speed</text>
<text x="370" y="245" text-anchor="middle" font-family="Manrope,sans-serif" font-size="12" fill="#3d3c38">curves left</text>
<!-- ── CASE 3 ── -->
<rect x="570" y="95" width="100" height="110" rx="8" fill="#e2ded3" stroke="#b0aba0" stroke-width="1.5"/>
<rect x="552" y="113" width="20" height="74" rx="6" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.5"/>
<rect x="668" y="113" width="20" height="74" rx="6" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.5"/>
<circle cx="620" cy="91" r="8" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.2"/>
<line x1="562" y1="108" x2="562" y2="82" stroke="#1a6b4a" stroke-width="2.5" marker-end="url(#aG)"/>
<line x1="678" y1="82" x2="678" y2="108" stroke="#c8420a" stroke-width="2.5" marker-end="url(#aR)"/>
<path d="M606 86 A18 18 0 1 1 634 86" fill="none" stroke="#1f4d8c" stroke-width="1.8" stroke-dasharray="5 3" marker-end="url(#aRot)"/>
<circle cx="620" cy="150" r="5" fill="#1f4d8c"/>
<text x="620" y="228" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="11" font-weight="500" fill="#1f4d8c">L fwd / R rev</text>
<text x="620" y="245" text-anchor="middle" font-family="Manrope,sans-serif" font-size="12" fill="#3d3c38">pivots in place</text>
<!-- legend -->
<rect x="60" y="264" width="10" height="10" rx="2" fill="#1a6b4a"/>
<text x="76" y="273" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">fast / forward</text>
<rect x="196" y="264" width="10" height="10" rx="2" fill="#c8aa60"/>
<text x="212" y="273" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">slow</text>
<rect x="300" y="264" width="10" height="10" rx="2" fill="#c8420a"/>
<text x="316" y="273" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">reverse</text>
<circle cx="424" cy="269" r="5" fill="#c8420a"/>
<text x="436" y="273" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">ICR — center of rotation</text>
</svg>
Fig 2.1: Three differential drive movement cases. The speed bars on each wheel show relative velocity. The robot's heading is controlled entirely by the ratio of left to right wheel speed, not the absolute values. The red dot in Case 2 marks the instantaneous center of rotation (ICR).
The wheelbase
The distance between the centerlines of the two drive wheels is called the wheelbase. A wider wheelbase gives more turning stability but demands more torque to pivot. Your kit has a fixed wheelbase. Measure it now (center of left tread to center of right tread) and write it down. You will need it in Module 5 when calculating turn angles mathematically.
The turn radius is set by the speed ratio between wheels, not the absolute speed. A 2:1 ratio traces the same arc at 20% power as at 80%; only the time to complete it changes.
Advanced path In Case 2, the red dot marks the instantaneous center of rotation (ICR), which is the point the robot pivots around at any given moment. When both wheels run at equal speed the ICR is infinitely far ahead, which gives a straight line. When one wheel stops, the ICR sits at that wheel's contact patch. When one wheel reverses, the ICR falls between the two wheels. In Module 5 you will use the ICR concept to calculate the exact arc radius for a given speed ratio, which is how you code a precise 90-degree turn without guessing and adjusting by hand.
2.2 Inside the DC gear motor
Before bolting the motors in, you need to understand what you are working with: specifically why the gearbox exists and what the two wire leads actually control.
<svg viewBox="0 0 740 300" width="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="aS" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#6b6a65" stroke-width="1.5" stroke-linecap="round"/></marker>
<marker id="aGrn" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#1a6b4a" stroke-width="1.5" stroke-linecap="round"/></marker>
</defs>
<!-- MOTOR CAN -->
<rect x="42" y="78" width="226" height="136" rx="10" fill="#5a5a50" stroke="#3a3a30" stroke-width="2"/>
<rect x="42" y="78" width="20" height="136" rx="5" fill="#3a3a30"/>
<ellipse cx="190" cy="146" rx="68" ry="56" fill="#c8a060" stroke="#a07840" stroke-width="1.5"/>
<ellipse cx="190" cy="146" rx="46" ry="38" fill="#e0b870" stroke="#a07840" stroke-width="1"/>
<ellipse cx="78" cy="146" rx="13" ry="38" fill="#b08040" stroke="#806020" stroke-width="1.5"/>
<line x1="78" y1="116" x2="78" y2="134" stroke="#604020" stroke-width="2"/>
<line x1="78" y1="158" x2="78" y2="176" stroke="#604020" stroke-width="2"/>
<rect x="56" y="130" width="18" height="14" rx="2" fill="#999" stroke="#666" stroke-width="1"/>
<rect x="56" y="149" width="18" height="14" rx="2" fill="#999" stroke="#666" stroke-width="1"/>
<line x1="42" y1="137" x2="56" y2="137" stroke="#bbb" stroke-width="2"/>
<line x1="42" y1="156" x2="56" y2="156" stroke="#bbb" stroke-width="2"/>
<rect x="42" y="141" width="236" height="10" rx="3" fill="#999" stroke="#777" stroke-width="1.5"/>
<!-- GEARBOX housing -->
<rect x="268" y="90" width="150" height="112" rx="6" fill="#2a3a4a" stroke="#1a2a3a" stroke-width="2"/>
<circle cx="305" cy="132" r="34" fill="none" stroke="#4a8ab0" stroke-width="2"/>
<circle cx="305" cy="132" r="8" fill="#4a8ab0"/>
<circle cx="305" cy="97" r="5" fill="#4a8ab0"/>
<circle cx="305" cy="167" r="5" fill="#4a8ab0"/>
<circle cx="270" cy="132" r="5" fill="#4a8ab0"/>
<circle cx="340" cy="132" r="5" fill="#4a8ab0"/>
<circle cx="281" cy="108" r="4" fill="#4a8ab0"/>
<circle cx="281" cy="156" r="4" fill="#4a8ab0"/>
<circle cx="329" cy="108" r="4" fill="#4a8ab0"/>
<circle cx="329" cy="156" r="4" fill="#4a8ab0"/>
<circle cx="368" cy="154" r="18" fill="none" stroke="#6ab0d0" stroke-width="2"/>
<circle cx="368" cy="154" r="6" fill="#6ab0d0"/>
<line x1="339" y1="150" x2="350" y2="154" stroke="#6ab0d0" stroke-width="1.5" stroke-dasharray="3 2"/>
<rect x="386" y="141" width="52" height="10" rx="3" fill="#aaa" stroke="#777" stroke-width="1.5"/>
<rect x="406" y="141" width="26" height="5" rx="1" fill="#888"/>
<line x1="438" y1="146" x2="462" y2="146" stroke="#1a6b4a" stroke-width="2" marker-end="url(#aGrn)"/>
<text x="468" y="143" font-family="IBM Plex Mono,monospace" font-size="10" fill="#1a6b4a">output</text>
<text x="468" y="156" font-family="IBM Plex Mono,monospace" font-size="10" fill="#1a6b4a">shaft</text>
<!-- WIRE LEADS -->
<line x1="42" y1="137" x2="18" y2="122" stroke="#c8420a" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="12" cy="119" r="6" fill="#c8420a"/>
<text x="8" y="108" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" fill="#c8420a">+</text>
<line x1="42" y1="156" x2="18" y2="171" stroke="#222" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="12" cy="174" r="6" fill="#222" stroke="#555" stroke-width="1"/>
<text x="8" y="190" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" fill="#6b6a65">−</text>
<!-- RPM BAR CHART right side -->
<rect x="548" y="92" width="32" height="108" rx="3" fill="#c8aa60" opacity="0.35"/>
<rect x="548" y="92" width="32" height="108" rx="3" fill="none" stroke="#c8aa60" stroke-width="1.5"/>
<text x="564" y="88" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="9" fill="#c8aa60">9,600 rpm</text>
<text x="564" y="214" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">motor</text>
<text x="608" y="152" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="11" fill="#3d3c38">÷80</text>
<rect x="634" y="174" width="32" height="26" rx="3" fill="#1a6b4a" opacity="0.45"/>
<rect x="634" y="174" width="32" height="26" rx="3" fill="none" stroke="#1a6b4a" stroke-width="1.5"/>
<text x="650" y="170" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="9" fill="#1a6b4a">~120 rpm</text>
<text x="650" y="214" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">output</text>
<line x1="540" y1="200" x2="680" y2="200" stroke="#c4bfb0" stroke-width="1"/>
<text x="155" y="248" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" letter-spacing="1" fill="#888">MOTOR CAN</text>
<text x="155" y="264" text-anchor="middle" font-family="Manrope,sans-serif" font-size="12" fill="#6b6a65">Fast spin, almost no torque</text>
<text x="343" y="248" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" letter-spacing="1" fill="#888">GEARBOX</text>
<text x="343" y="264" text-anchor="middle" font-family="Manrope,sans-serif" font-size="12" fill="#6b6a65">Slow output, 80× more torque</text>
</svg>
Fig 2.2: DC gear motor cross-section. The bare motor shaft runs at ~9,600 RPM with almost no usable torque. The gearbox reduces this to ~120 RPM at the output shaft while multiplying the available force by the gear ratio. Without the gearbox, the motor cannot move a loaded robot.
The two wire leads control direction. Connect positive to red and negative to black and the shaft turns one way. Swap them and it reverses. The H-bridge motor driver does this swap electronically, so you never have to rewire. But you do need to know which lead is which before assembly.
Check polarity before you mount anything
Touch a 1.5V AA cell directly to the motor leads. Watch which direction the output shaft spins. Mark the lead that produces forward wheel rotation with a small piece of tape. Do both motors. If you mix up polarity after the motors are bolted in, the robot will reverse or spin instead of going straight, and diagnosing that through a tangle of wires is not fun.
Bare motor wires. Some chassis kits ship with motor leads that have no connector — just bare wire ends. If yours do: strip about 5 mm of insulation from each lead, twist the strands tightly together so no loose strands splay outward, then insert into the L298N's screw terminal and tighten the terminal screw firmly. Do not solder the wires directly to the terminal block; heat from soldering can crack the plastic housing. Twisted strands held by the screw clamp are reliable at the current levels this motor draws.
Advanced path Gear Ratio = Motor RPM ÷ Output RPM. For a motor at 9,600 RPM with 120 RPM output: GR = 80:1. Torque scales inversely: if the motor produces 0.5 mN·m and the gearbox is 75% efficient, output torque = 0.5 × 80 × 0.75 = 30 mN·m. Top speed: wheel circumference (π × 0.065 m for a 65 mm wheel) × 120 RPM = ~0.41 m/s. You will use this in Module 8 when calculating how much reaction time the robot has before hitting a wall at full speed.
2.3 Motor mounting: alignment and bolt sequence
A motor bracket that is even slightly canted will make the robot drift consistently to one side under power. You can compensate for drift in code later, but you should not have to. Fixing it mechanically is faster and more reliable than chasing it through PWM adjustments.
<svg viewBox="0 0 340 290" width="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="aRed2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#c8420a" stroke-width="1.5" stroke-linecap="round"/></marker>
</defs>
<!-- CORRECT section -->
<rect x="8" y="8" width="72" height="20" rx="3" fill="#1a6b4a"/>
<text x="44" y="22" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" letter-spacing="1" fill="#fff">CORRECT</text>
<rect x="8" y="38" width="324" height="84" rx="6" fill="#e2ded3" stroke="#b0aba0" stroke-width="1.5"/>
<rect x="20" y="50" width="54" height="60" rx="4" fill="#5a5a50" stroke="#3a3a30" stroke-width="1.5"/>
<circle cx="47" cy="80" r="7" fill="#aaa" stroke="#777" stroke-width="1.5"/>
<rect x="266" y="50" width="54" height="60" rx="4" fill="#5a5a50" stroke="#3a3a30" stroke-width="1.5"/>
<circle cx="293" cy="80" r="7" fill="#aaa" stroke="#777" stroke-width="1.5"/>
<line x1="47" y1="80" x2="293" y2="80" stroke="#1a6b4a" stroke-width="1.5" stroke-dasharray="6 3"/>
<text x="170" y="75" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="9" fill="#1a6b4a">shared wheel axis</text>
<!-- WRONG section -->
<rect x="8" y="148" width="60" height="20" rx="3" fill="#c8420a"/>
<text x="38" y="162" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" letter-spacing="1" fill="#fff">WRONG</text>
<rect x="8" y="178" width="324" height="84" rx="6" fill="#e2ded3" stroke="#b0aba0" stroke-width="1.5"/>
<g transform="translate(20,188) rotate(10) translate(0,0)">
<rect x="0" y="0" width="54" height="60" rx="4" fill="#5a5a50" stroke="#c8420a" stroke-width="1.5"/>
<circle cx="27" cy="30" r="7" fill="#c8420a" stroke="#a03000" stroke-width="1.5"/>
</g>
<rect x="266" y="190" width="54" height="60" rx="4" fill="#5a5a50" stroke="#3a3a30" stroke-width="1.5"/>
<circle cx="293" cy="220" r="7" fill="#aaa" stroke="#777" stroke-width="1.5"/>
<line x1="46" y1="208" x2="293" y2="220" stroke="#c8420a" stroke-width="1" stroke-dasharray="4 3"/>
<path d="M170 272 Q140 264 110 258" fill="none" stroke="#c8420a" stroke-width="1.5" marker-end="url(#aRed2)"/>
<text x="200" y="275" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#c8420a">robot drifts left</text>
</svg>
Fig 2.3a: Top view of chassis. Correct: both shaft centers share one straight axis (green). Wrong: canted bracket breaks the axis and the robot drifts under power.
<svg viewBox="0 0 340 290" width="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="aGrn2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#1a6b4a" stroke-width="1.5" stroke-linecap="round"/></marker>
<marker id="aRed3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#c8420a" stroke-width="1.5" stroke-linecap="round"/></marker>
</defs>
<text x="170" y="22" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="11" letter-spacing="1" fill="#3d3c38">BOLT TIGHTENING SEQUENCE</text>
<rect x="70" y="40" width="200" height="200" rx="8" fill="#5a5a50" stroke="#3a3a30" stroke-width="2"/>
<circle cx="170" cy="140" r="66" fill="#888" stroke="#666" stroke-width="1.5"/>
<circle cx="170" cy="140" r="20" fill="#444" stroke="#333" stroke-width="1.5"/>
<circle cx="170" cy="140" r="5" fill="#aaa"/>
<circle cx="96" cy="64" r="12" fill="#1a6b4a" stroke="#0e4030" stroke-width="1.5"/>
<text x="96" y="69" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="12" font-weight="500" fill="#fff">1</text>
<circle cx="96" cy="216" r="12" fill="#1a6b4a" stroke="#0e4030" stroke-width="1.5"/>
<text x="96" y="221" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="12" font-weight="500" fill="#fff">2</text>
<circle cx="244" cy="64" r="12" fill="#c8aa60" stroke="#a08030" stroke-width="1.5"/>
<text x="244" y="69" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="12" font-weight="500" fill="#fff">3</text>
<circle cx="244" cy="216" r="12" fill="#c8aa60" stroke="#a08030" stroke-width="1.5"/>
<text x="244" y="221" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="12" font-weight="500" fill="#fff">4</text>
<path d="M96 76 Q96 140 96 204" fill="none" stroke="#1a6b4a" stroke-width="1.5" stroke-dasharray="4 3" marker-end="url(#aGrn2)"/>
<path d="M108 216 Q170 234 232 216" fill="none" stroke="#c8420a" stroke-width="1.5" stroke-dasharray="4 3" marker-end="url(#aRed3)"/>
<path d="M244 204 Q244 140 244 76" fill="none" stroke="#c8420a" stroke-width="1.5" stroke-dasharray="4 3" marker-end="url(#aRed3)"/>
<path d="M232 64 Q170 46 108 64" fill="none" stroke="#c8420a" stroke-width="1.5" stroke-dasharray="4 3" marker-end="url(#aRed3)"/>
<text x="170" y="258" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#3d3c38">Snug 1→2. Check bracket flat.</text>
<text x="170" y="274" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#3d3c38">Then 3→4→1→2 to final torque.</text>
</svg>
Fig 2.3b: Bolt tightening sequence. Start on one side (1 and 2), confirm the bracket sits flush, then cross to the opposite side. Never fully seat any single bolt before the others are started.
After both motors are loosely mounted, hold the chassis at eye level and sight down the wheel axis from above. Both wheel surfaces should sit in the same vertical plane. If one is visibly canted, loosen that bracket, re-seat it flat, and retighten. You get one good chance to correct this before the screw holes bite into the plastic and form a memory.
A single skewed motor costs 10–15% of forward range per battery charge and makes every autonomous behavior harder to tune. Two extra minutes here are not optional.
2.4 Ball caster: position and height
The ball caster has no motor and carries no electronics. Its only job is to provide a stable third ground contact point so the robot does not tip forward. But its mounting position determines turning behavior, and its stem height controls how weight is distributed across all three contact points.
<svg viewBox="0 0 340 270" width="100%" xmlns="http://www.w3.org/2000/svg">
<text x="170" y="22" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="11" letter-spacing="1" fill="#3d3c38">BALL CASTER — CROSS-SECTION</text>
<rect x="105" y="36" width="130" height="50" rx="6" fill="#8a8880" stroke="#6a6860" stroke-width="1.5"/>
<circle cx="126" cy="56" r="6" fill="#555" stroke="#444" stroke-width="1"/>
<circle cx="214" cy="56" r="6" fill="#555" stroke="#444" stroke-width="1"/>
<rect x="153" y="86" width="34" height="36" rx="3" fill="#6a6860" stroke="#4a4840" stroke-width="1.5"/>
<rect x="136" y="122" width="68" height="54" rx="12" fill="#5a5850" stroke="#4a4840" stroke-width="1.5"/>
<circle cx="170" cy="154" r="24" fill="#d0d0d0" stroke="#aaa" stroke-width="2"/>
<ellipse cx="162" cy="146" rx="8" ry="6" fill="white" opacity="0.35"/>
<line x1="50" y1="178" x2="290" y2="178" stroke="#b0aba0" stroke-width="2"/>
<circle cx="170" cy="178" r="4" fill="#c8420a"/>
<line x1="235" y1="56" x2="284" y2="56" stroke="#c4bfb0" stroke-width="0.75" stroke-dasharray="3 3"/>
<text x="287" y="60" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">mount plate</text>
<line x1="187" y1="104" x2="284" y2="104" stroke="#c4bfb0" stroke-width="0.75" stroke-dasharray="3 3"/>
<text x="287" y="108" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">stem / spacers</text>
<line x1="204" y1="146" x2="284" y2="146" stroke="#c4bfb0" stroke-width="0.75" stroke-dasharray="3 3"/>
<text x="287" y="150" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">cage</text>
<line x1="148" y1="162" x2="56" y2="200" stroke="#c4bfb0" stroke-width="0.75" stroke-dasharray="3 3"/>
<text x="50" y="215" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">ball — rolls</text>
<text x="50" y="229" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#6b6a65">any direction</text>
<text x="170" y="200" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" fill="#c8420a">contact point</text>
</svg>
Fig 2.4a: Ball caster cross-section. The stem height is adjustable with spacers. All three contact points (two wheel treads + caster ball) must touch a flat surface simultaneously for the chassis to sit level.
<svg viewBox="0 0 340 270" width="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="aGrn3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#1a6b4a" stroke-width="1.5" stroke-linecap="round"/></marker>
<marker id="aRed4" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#c8420a" stroke-width="1.5" stroke-linecap="round"/></marker>
</defs>
<text x="170" y="22" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="11" letter-spacing="1" fill="#3d3c38">CASTER POSITION — SIDE VIEW</text>
<line x1="14" y1="152" x2="162" y2="152" stroke="#b0aba0" stroke-width="1.5"/>
<line x1="178" y1="152" x2="326" y2="152" stroke="#b0aba0" stroke-width="1.5"/>
<!-- LEFT: caster front (correct) -->
<text x="84" y="46" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" fill="#1a6b4a">FRONT ✓</text>
<rect x="32" y="88" width="114" height="46" rx="6" fill="#e2ded3" stroke="#b0aba0" stroke-width="1.5"/>
<rect x="132" y="94" width="16" height="56" rx="5" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.5"/>
<circle cx="48" cy="143" r="11" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.5"/>
<path d="M90 86 Q52 48 28 86" fill="none" stroke="#1a6b4a" stroke-width="1.8" stroke-dasharray="5 3" marker-end="url(#aGrn3)"/>
<text x="84" y="178" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#1a6b4a">tight, controlled</text>
<text x="84" y="194" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#1a6b4a">turning arc</text>
<line x1="170" y1="38" x2="170" y2="220" stroke="#c4bfb0" stroke-width="1" stroke-dasharray="3 3"/>
<!-- RIGHT: caster rear (not recommended) -->
<text x="256" y="46" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" fill="#c8420a">REAR ✗</text>
<rect x="194" y="88" width="114" height="46" rx="6" fill="#e2ded3" stroke="#b0aba0" stroke-width="1.5"/>
<rect x="196" y="94" width="16" height="56" rx="5" fill="#3d3c38" stroke="#1a1a18" stroke-width="1.5"/>
<circle cx="294" cy="143" r="11" fill="#888" stroke="#666" stroke-width="1.5"/>
<path d="M254 86 Q298 40 322 86" fill="none" stroke="#c8420a" stroke-width="1.8" stroke-dasharray="5 3" marker-end="url(#aRed4)"/>
<text x="256" y="178" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#c8420a">rear swings wide</text>
<text x="256" y="194" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#c8420a">on every turn</text>
</svg>
Fig 2.4b: Caster position affects turning geometry. Front-mounted: robot pivots around the drive axle and the front swings inward for a tight arc. Rear-mounted: the back end swings wide on every turn, making close-quarters navigation harder.
Set the assembled robot on a flat desk after mounting the caster. All three contact points should touch simultaneously with zero rocking. Front dips: add a spacer to the caster stem. Front rises and robot rocks on wheels: remove a spacer. A tilted chassis shifts weight onto the caster and off the drive wheels, reducing traction and pointing the ultrasonic sensor at the floor instead of forward.
2.5 Wheel attachment and the straight-line test
Wheel hubs on these kit motors use a D-shaft, which is a round shaft with one flat face cut into it. The hub has a matching D-bore and a grub screw that locks it in position. Getting this wrong is the most common cause of wheels that wobble at speed or slip under motor load.
<svg viewBox="0 0 340 270" width="100%" xmlns="http://www.w3.org/2000/svg">
<text x="170" y="22" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="11" letter-spacing="1" fill="#3d3c38">D-SHAFT — END-ON VIEW</text>
<!-- LEFT: correct -->
<text x="80" y="50" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" fill="#1a6b4a">CORRECT</text>
<circle cx="80" cy="118" r="46" fill="#8a8880" stroke="#5a5850" stroke-width="2"/>
<rect x="34" y="72" width="92" height="22" rx="2" fill="#e2ded3"/>
<circle cx="80" cy="118" r="46" fill="none" stroke="#1a6b4a" stroke-width="2.5" stroke-dasharray="6 3"/>
<rect x="98" y="72" width="14" height="22" rx="3" fill="#1a6b4a" stroke="#0e4030" stroke-width="1.5"/>
<text x="80" y="186" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#1a6b4a">Screw on flat — locks</text>
<line x1="170" y1="38" x2="170" y2="230" stroke="#c4bfb0" stroke-width="1" stroke-dasharray="3 3"/>
<!-- RIGHT: wrong -->
<text x="258" y="50" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" fill="#c8420a">WRONG</text>
<circle cx="258" cy="118" r="46" fill="#8a8880" stroke="#5a5850" stroke-width="2"/>
<rect x="212" y="72" width="92" height="22" rx="2" fill="#e2ded3"/>
<rect x="278" y="97" width="14" height="22" rx="3" fill="#c8420a" stroke="#a03000" stroke-width="1.5"/>
<line x1="246" y1="172" x2="270" y2="196" stroke="#c8420a" stroke-width="2.5"/>
<line x1="270" y1="172" x2="246" y2="196" stroke="#c8420a" stroke-width="2.5"/>
<text x="258" y="220" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#c8420a">Screw on round — slips</text>
</svg>
Fig 2.5a: D-shaft end-on. The grub screw must bear against the flat face. On the round section it compresses against a curve and will back out under motor load, causing the wheel to slip.
<svg viewBox="0 0 340 270" width="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="aBk2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto-start-reverse"><path d="M2 2L8 5L2 8" fill="none" stroke="#3d3c38" stroke-width="1.5" stroke-linecap="round"/></marker>
</defs>
<text x="170" y="22" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="11" letter-spacing="1" fill="#3d3c38">STRAIGHT-LINE PUSH TEST</text>
<rect x="24" y="124" width="296" height="7" rx="2" fill="#c8aa60" opacity="0.7"/>
<text x="170" y="116" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="10" fill="#c8aa60">tape — 1 metre</text>
<rect x="24" y="94" width="50" height="34" rx="4" fill="#e2ded3" stroke="#b0aba0" stroke-width="1.5"/>
<rect x="14" y="98" width="12" height="26" rx="4" fill="#3d3c38"/>
<rect x="74" y="98" width="12" height="26" rx="4" fill="#3d3c38"/>
<circle cx="49" cy="92" r="6" fill="#3d3c38"/>
<text x="49" y="146" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="9" fill="#6b6a65">start</text>
<line x1="84" y1="111" x2="256" y2="111" stroke="#3d3c38" stroke-width="1.5" stroke-dasharray="5 3" marker-end="url(#aBk2)"/>
<text x="170" y="104" text-anchor="middle" font-family="Manrope,sans-serif" font-size="11" fill="#3d3c38">push by hand, no power</text>
<rect x="264" y="94" width="50" height="34" rx="4" fill="#e2ded3" stroke="#1a6b4a" stroke-width="2"/>
<rect x="254" y="98" width="12" height="26" rx="4" fill="#3d3c38"/>
<rect x="314" y="98" width="12" height="26" rx="4" fill="#3d3c38"/>
<circle cx="289" cy="92" r="6" fill="#3d3c38"/>
<text x="289" y="146" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="9" fill="#1a6b4a">PASS</text>
<rect x="248" y="158" width="50" height="34" rx="4" fill="#e2ded3" stroke="#c8420a" stroke-width="2"/>
<rect x="238" y="162" width="12" height="26" rx="4" fill="#3d3c38"/>
<rect x="298" y="162" width="12" height="26" rx="4" fill="#3d3c38"/>
<circle cx="273" cy="156" r="6" fill="#3d3c38"/>
<text x="273" y="206" text-anchor="middle" font-family="IBM Plex Mono,monospace" font-size="9" fill="#c8420a">FAIL — drift > 5 cm</text>
</svg>
Fig 2.5b: The straight-line push test. Place the robot so the left wheel is on a tape line, push 1 metre by hand with no power. More than 5 cm of drift means a mechanical problem; inspect bracket angles, wheel seating, and caster height before continuing.
Run this test before connecting any wire. Drift you see here is purely mechanical. If you skip it, program the robot, and then discover it pulls left, you will spend time adjusting PWM values in code trying to compensate for a problem with a two-minute physical fix.
2.6 Common assembly mistakes
These are the five problems that appear most often on a first chassis build. Every one is faster to prevent than to trace back through a finished assembly.
| Mistake | What you observe | The fix |
|---|---|---|
| Motor bracket over-tightened on one side | Bracket cants, shaft skews, robot drifts consistently to one side under power | Loosen all four bracket screws, re-seat bracket flush, retighten in diagonal pairs |
| Wheel hub not fully seated on shaft | Wheel wobbles, slips under load, turn distances are inconsistent | Push hub flush to shaft shoulder, confirm grub screw bears on the flat face |
| Caster stem too tall | Front of chassis is elevated, drive wheels carry less weight, traction is poor | Remove one spacer, retest on flat surface, repeat until chassis sits level |
| Caster stem too short | Chassis tilts forward, ultrasonic sensor points at the floor rather than forward | Add a spacer to the caster stem until all three contact points touch simultaneously |
| Motor polarity not verified before mounting | Robot drives backward, or spins in place when both motors are commanded forward | Test shaft rotation with a 1.5V cell before assembly, mark positive lead with tape |
Instructor note The most useful thing you can do before this lab is to pre-build one chassis with a deliberately canted motor bracket, and a second with the caster one spacer too short. Push both robots along a tape line and let students feel the difference. The drift from a skewed motor is obvious and immediate, and students who experience the failure before building their own make far fewer bracket errors.
If time is short, the caster height demo is quicker to set up. A robot that tips forward when pushed at speed makes the weight-on-wheels connection visceral in a way a diagram cannot fully replicate.
Lab 02: Build the rolling chassis
No motors powered. No wiring. Mechanical assembly only, ending in one physical pass/fail test. The milestone is simple: the robot rolls 1 metre along a straight tape line under hand-push with less than 5 cm of lateral drift.
You need: chassis plate, 2× DC gear motors with brackets, 2× drive wheels, 1× ball caster with spacers, M3 hardware, hex key or flat screwdriver, 1 m of masking tape.
- Lay the chassis flat. Orient it so the sensor mounting holes face away from you; that is the front. Find the four motor mounting holes on the left and right edges.
- Attach motor brackets to both motors before mounting either to the chassis. Finger-tight only.
- Mount the left motor bracket to the chassis. Finger-tighten all four screws. Check from above that the motor body is parallel to the chassis edge. Tighten in diagonal pairs using the Fig 2.3b sequence.
- Repeat for the right motor. Hold the chassis at eye level and sight down the wheel axis; both motor shafts should align on a single straight line across the chassis.
- Check polarity: touch a 1.5V AA cell to each motor's leads. Mark the lead that makes the output shaft rotate in the forward direction with a small piece of tape.
- Attach both wheels. Align the hub D-bore with the shaft D-flat. Push fully home until flush. Tighten the grub screw firmly against the flat face.
- Spin each wheel by hand. It should turn freely with light, consistent resistance from the gearbox. Grinding or binding means a bracket screw is distorting the motor housing; loosen and re-check.
- Install the ball caster at the front. Add or remove spacers so all three contact points touch a flat desk simultaneously with no rocking.
- Lay 1 m of masking tape on the floor in a straight line. Place the robot so the left drive wheel sits on the tape. Push forward at a steady walking pace. Observe where the wheel is at the end of the run.
- Drift over 5 cm: inspect bracket angles first, then wheel seating, then caster height. Fix the most likely cause, retest. Do not move to Module 3 until the test passes.
Module milestone Rolling chassis travels 1 metre along a straight tape line under hand-push with less than 5 cm of lateral drift. No electronics required; this is a pure mechanical pass/fail. When it passes, move to Module 3.
Self-Check: Module 2
- I can explain in one sentence why differential drive needs no steering axle.
- I can name the three drive cases (straight, arc turn, pivot) and describe the wheel speed condition for each.
- I measured and recorded the wheelbase of my chassis.
- I verified motor polarity before mounting and marked the positive lead on each motor.
- I tightened both motor brackets using the diagonal bolt sequence.
- I adjusted the ball caster height until all three contact points touch a flat surface simultaneously.
- My chassis passed the straight-line push test with less than 5 cm of drift over 1 metre.
Key Terms Glossary
| Term | Definition |
|---|---|
| differential drive | A locomotion system where two independently powered wheels control both speed and direction. No steering axle; heading is determined by the ratio of left to right wheel speed. |
| wheelbase | The distance between the centerlines of the two drive wheels, measured tread center to tread center. Sets the turn radius at any given speed ratio. |
| gear ratio | Motor input RPM divided by output shaft RPM. An 80:1 ratio means the motor turns 80 times per one output shaft revolution. Higher ratio means slower speed but more torque at the wheel. |
| D-shaft | A motor output shaft with one flat face. The flat prevents wheel hubs from rotating on the shaft independently of the motor under load. |
| grub screw | A headless set-screw that clamps a hub to a shaft. Must bear against the flat face of a D-shaft; on the round section it provides almost no holding force. |
| instantaneous center of rotation | The point around which the robot pivots at any moment. Its position is entirely determined by the ratio of left to right wheel speed. |
| ball caster | A passive support that uses a freely rolling ball instead of a fixed axle. Allows movement in any direction and provides the robot's third ground contact point. |
| traction | The friction force between drive wheel and floor. Reduced by low normal force, smooth wheels, or a smooth floor. Directly affects turning repeatability. |
Previous: ← Module 01: How Robots Think and Move · Next: Module 03: Power & Wiring Basics →