{"slug": "from-dnf-to-sub-2-00-how-i-used-ibm-bob-to-fix-my-ai-racing-car", "title": "From DNF to Sub-2:00 — How I Used IBM Bob to Fix My AI Racing Car", "summary": "A developer used IBM's Bob assistant to iteratively rewrite the baseline Python driver script from the IBM Bob TORCS University Challenge, turning an AI racing car nicknamed \"Max Verstoppin'\" from a zero-lap, 1,863-damage run into a clean 02:04.90 lap on the Corkscrew track. The key fix split the forward sensor cone into separate straight-line (max) and corner-guard (min) signals, letting the car hold pace on open straights while braking only when the road ahead genuinely closed in.", "body_md": "I spent 1.5 days getting TORCS installed, named my AI driver **Max Verstoppin'**,\n\nand watched him immediately drive full speed into a wall. This is the story of how\n\nIBM Bob and I turned him into Max Verstappen.\n\nThe [IBM Bob TORCS University Challenge](https://skills.yourlearning.ibm.com/activity/PLAN-6FA860C53B3A)\n\ngives you a base Python script — `snakeoil3_gym.py` — that controls an AI car in\n\n[TORCS](https://ibm.box.com/v/TORCSdownloadzip), an open-source racing simulator.\n\nThe goal: use IBM Bob as your crew, improve the script, and post the fastest clean\n\nlap on the **Corkscrew** track from a standing start with zero damage.\n\nSimple brief. Brutal execution.\n\nThe baseline `drive_example()` function looked like this:\n\n```\ntarget_speed = 300\n\nR['steer'] = S['angle'] * 15 / PI\nR['steer'] -= S['trackPos'] * .10\n\nif S['speedX'] < target_speed - (R['steer'] * 50):\n    R['accel'] += .01\nelse:\n    R['accel'] -= .01\n```\n\nNo brakes. No corner awareness. A steering multiplier of 15 that caused the wheel\n\nto thrash violently. A target speed of 300 km/h regardless of what's ahead.\n\nMax hit **237 km/h**, missed the first corner entirely, and sat pinned against the\n\nbarrier wall until the server hit the 100,000 step limit.\n\n**Laps: 0. Damage: 1,863. Top speed: 237 km/h.**\n\nI pointed IBM Bob at the script and asked it to analyse what was wrong. It broke\n\ndown `drive_example()` and flagged three core issues immediately:\n\n`brake` key in the action dictionary was never touched`* 15` caused severe oscillation\nOnce I understood the problems, the approach was straightforward: fix one or two\n\nthings at a time, run a lap, read the telemetry, feed it back to Bob, identify the\n\nnext bottleneck. Eight iterations. Alpha through Iota.\n\nThe first set of changes:\n\n`15` to `10`\n`track[8], track[9], track[10]` (the forward-facing sensors) to calculate a\ncorner-aware target speed instead of a hardcoded 300\n\n```\nforward_dist = min(track[8], track[9], track[10])\ntarget_speed = max(60, min(220, forward_dist * 1.1))\n\nspeed_excess = S['speedX'] - target_speed\nif speed_excess > 20:\n    R['brake'] = min(1.0, (speed_excess - 20) / 80.0)\n    R['accel'] = 0.0\n```\n\nMax finished his first ever clean lap. **02:24.17. Zero damage.**\n\nHe was slow — the `1.1` multiplier meant a 100m straight-line sensor reading only\n\nset a target of 110 km/h — but he was clean. Foundation laid.\n\nOver the next three runs, the multiplier was progressively tuned upward:\n\n| Run | Multiplier | Lap Time | Top Speed | \n|---|---|---|---|\n| Gamma | `× 2.0` | 02:15.40 | 217 km/h | \n| Epsilon | `× 2.5` | 02:06.71 | 225 km/h | \n\nEach step brought more straight-line speed while the braking system kept damage\n\nat zero. The brake ramp divisor was also tightened to make the car scrub speed\n\nfaster when approaching corners at higher speeds.\n\nThis was the most important change of the whole project.\n\nThe sensor logic was using `min()` across a five-sensor forward cone to set target\n\nspeed. The problem: when Max drifted toward the track edge, the diagonal sensors\n\nin that cone were pointing at the boundary wall and returning short readings —\n\nsometimes 20–30m — on a completely open straight. The car thought a corner was\n\ncoming and backed off the throttle for no reason.\n\nThe fix was to split the sensor reading into two separate signals:\n\n```\n# MAX of the cone → straight-line pace (one clear sensor is enough)\nstraight_dist = max(track[7], track[8], track[9], track[10], track[11])\n\n# MIN of the tightest 3 → corner guard (only fires when road ahead closes in)\ncorner_dist = min(track[8], track[9], track[10])\n\ntarget_speed = max(50, min(280, straight_dist * 2.8))\nif corner_dist < 55:\n    corner_cap = max(50, corner_dist * 2.8)\n    target_speed = min(target_speed, corner_cap)\n```\n\nAs long as *any* forward sensor sees clear road, Max pushes. Corner braking only\n\ntriggers when the road directly ahead is genuinely closing in.\n\n**02:04.90. 228 km/h. 0 damage.**\n\nNot every run went forward. Lowering the corner detection threshold to `< 45m`\n\n(from `< 60m`) delayed braking too long into the final tight turn. Max ran wide\n\nonto the grass, lost grip, and the lap time went *up* by 6 seconds despite zero\n\ndamage.\n\nThe off-track excursion didn't register as damage in TORCS — grass just kills\n\nspeed. A good reminder that the metrics don't always tell the full story.\n\n**Theta** split the difference: threshold `< 55m`, multiplier `2.8`. New PB at\n\n**02:02.42**.\n\n**Iota** fixed the last remaining issue — Max was loitering along the track edge\n\nafter corner exits, not punching the throttle onto the straight. Two causes:\n\n`straight_dist`. Fix: fall back to dead-ahead sensor only when `|trackPos| > 0.5`\n`|steer| < 0.1` — too tight. The shallow\ncorrection Max held while returning to centre never qualified. Relaxed to `0.2`.` 0.15` → `0.30`) so he snapped back\nto the racing line faster.\n\n```\ntrack_pos = S.get('trackPos', 0)\nif abs(track_pos) > 0.5:\n    straight_dist = track[9]  # dead-ahead only — ignore lying diagonal sensors\nelse:\n    straight_dist = max(track[7], track[8], track[9], track[10], track[11])\n\ncentering_gain = 0.3 if abs(track_pos) > 0.5 else 0.15\nR['steer'] -= track_pos * centering_gain\n```\n\n**01:58.15. 235 km/h. 0 damage. Sub-2:00.**\n\n| Run | Lap Time | Top Speed | Damage | Key Change | \n|---|---|---|---|---|\n| Alpha | DNF | 237 km/h | 1,863 | Baseline — blind throttle, no brakes | \n| Beta | 02:24.17 | 196 km/h | 0 | Braking, corner-aware speed | \n| Gamma | 02:15.40 | 217 km/h | 0 | `dist × 2.0` multiplier | \n| Epsilon | 02:06.71 | 225 km/h | 0 | `dist × 2.5` , tighter brake ramp | \n| Zeta | 02:04.90 | 228 km/h | 0 | Dual `max` /`min` sensor logic | \n| Eta | 02:11.21 | 228 km/h | 0 | ⚠️ Regression — threshold `< 45` too aggressive | \n| Theta | 02:02.42 | 229 km/h | 0 | Threshold `< 55` , multiplier`2.8` | \n| **Iota** | **01:58.15** | **235 km/h** | **0** | Edge sensor fix, adaptive centering | \n\nThe most useful thing IBM Bob did wasn't writing code — it was helping me\n\n**understand what the code was actually doing** before I changed anything. Once I\n\ncould see exactly why Max was crashing (no brakes, blind speed) or slowing down\n\n(sensor reading the wall on a straight), each fix became obvious.\n\nThe iterative loop — run, observe, diagnose, fix — is also just good engineering.\n\nEvery regression (Eta) taught something. Every metric mattered: lap time, damage,\n\n*and* top speed together told a more complete story than any one number alone.\n\nI also completed the **IBM Granite Models for Software Development** course on\n\nIBM SkillsBuild during this project and earned the official badge, which gave me\n\nsolid context for how these models reason about code — useful when you're having\n\ntechnical back-and-forth with Bob about sensor arrays and braking logic.\n\n", "url": "https://wpnews.pro/news/from-dnf-to-sub-2-00-how-i-used-ibm-bob-to-fix-my-ai-racing-car", "canonical_source": "https://dev.to/mackleegitonga/from-dnf-to-sub-200-how-i-used-ibm-bob-to-fix-my-ai-racing-car-513", "published_at": "2026-10-11 11:18:43+00:00", "updated_at": "2026-10-11 11:21:51.802374+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-tools", "autonomous-vehicles", "robotics"], "entities": ["IBM", "IBM Bob", "TORCS", "Max Verstoppin'", "Corkscrew", "snakeoil3_gym.py"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/from-dnf-to-sub-2-00-how-i-used-ibm-bob-to-fix-my-ai-racing-car", "markdown": "https://wpnews.pro/news/from-dnf-to-sub-2-00-how-i-used-ibm-bob-to-fix-my-ai-racing-car.md", "text": "https://wpnews.pro/news/from-dnf-to-sub-2-00-how-i-used-ibm-bob-to-fix-my-ai-racing-car.txt", "jsonld": "https://wpnews.pro/news/from-dnf-to-sub-2-00-how-i-used-ibm-bob-to-fix-my-ai-racing-car.jsonld"}}