Skip to content

AI Driver and Recovery

noteMASTER11 edited this page Jul 24, 2026 · 10 revisions

AI Driver and Recovery

TaxiDriver AI Driver is an adapter around BeamNG's built-in vehicle AI. TaxiDriver selects and validates the route, starts ai.driveUsingPath, observes traffic, controls the pickup hand-off, and verifies that the gameplay target was actually reached. BeamNG remains responsible for steering, lane following, ordinary obstacle avoidance, traffic-light behavior, throttle, braking and gearbox operation.

AI Driver is experimental. Its behavior still depends on the active map graph, BeamNG traffic AI, vehicle controllers and community content. It is intended for assisted gameplay and testing, not as a guarantee of human-level autonomous driving.

The page name retains “Recovery” for compatibility with existing Wiki links. In the current implementation recovery means bounded native-route replanning. The previous local free-space planner, custom bypass controller, reverse-ray recovery and world decision visualizer are no longer part of the active AI runtime.

Runtime boundary

flowchart LR
  Trip["TaxiDriver trip target"] --> Supervisor["autopilot.lua"]
  GPS["BeamNG ground-marker route"] --> Supervisor
  Graph["BeamNG road graph"] --> Supervisor
  Supervisor --> Native["Vehicle ai.driveUsingPath"]
  Native --> Vehicle["Player vehicle"]
  Traffic["Tracked traffic vehicles"] --> Guard["taxiDriverStockAiObserver"]
  Vehicle --> Guard
  Guard --> Speed["Smooth temporary speed limit"]
  Speed --> Native
  Native --> Done["Native Route Done"]
  Done --> Supervisor
  Supervisor --> Check{"Inside physical target?"}
  Check -->|yes| Gameplay["Board / load / complete leg"]
  Check -->|no| Replan["Rebuild from current road segment"]
  Replan --> Native
Loading
Component Responsibility
autopilot.lua Player identity, route selection, native-AI lifecycle, physical arrival verification and bounded replanning
taxiDriverStockAiObserver.lua Following gap, steering-arc traffic prediction, target-aligned final braking and protected Route Done observation
physicalPickup.lua Physical passenger/cargo props, horn sequence, walking passenger, boarding completion and passenger-hit detection
taxiDriverTelemetry.lua Vehicle telemetry, horn control and observer diagnostics
fleetWorker.lua Reduced-cost native routing and bounded job recovery for one hired vehicle
aiLogger.lua Optional streaming JSONL records for route, traffic, drivetrain, damage and lifecycle events

Player-vehicle safety

Only the current primary player vehicle may receive the player AI route. The supervisor compares the requested object with be:getPlayerVehicleID(0) before every native route command.

This boundary prevents three earlier failure modes:

  • a stale callback cannot command a destroyed vehicle VM;
  • a Fleet or traffic vehicle ID cannot replace the player's active AI target;
  • the player vehicle cannot be processed through NPC traffic lifecycle logic.

ai.setRecoverOnCrash(false) is enforced for the player. BeamNG normally permits traffic AI to use safe teleport when an NPC is stuck. That behavior is unsuitable for the player because a deliberate stop for boarding may look like a stuck traffic vehicle and can relocate or reset the active trip.

Route modes

AI Driver has two route-selection modes. The mode is independent from the aggression preset.

Autonomous legal route

This is the default.

  1. TaxiDriver resolves the road edge that contains the pickup, stop, destination or fuel target.
  2. The target direction determines the legal approach node and departure node.
  3. The current vehicle heading determines the directed road edge on which the search begins.
  4. A shortest-path search runs in directed-edge state (previousNode, currentNode).
  5. Immediate reversal to previousNode is forbidden.
  6. One-way metadata is respected.
  7. The target departure node is appended so native AI crosses the target edge in the intended direction.

The directed-edge state is important. A node-only path can reach the correct junction from the wrong side and ask native AI to make an immediate U-turn across opposing traffic.

For a candidate transition from state (p, v) to (v, u), the accumulated cost is:

C(v, u) = C(p, v) + max(0.1, edgeLength(v, u))

The transition is rejected when u = p or when BeamNG marks (v, u) as the forbidden direction of a one-way edge.

Strict GPS route

When Strict GPS route is enabled, TaxiDriver reads the ordered road-node sequence from core_groundMarkers.routePlanner.path and sends that exact sequence to native AI.

Coordinate-only start/end entries are discarded. Only entries containing a valid waypoint ID are serialized. This prevents invalid strings such as table: 0x... from reaching BeamNG's ai.lua.

flowchart TD
  Toggle{"Strict GPS enabled?"} -->|no| Legal["Directed legal route"]
  Toggle -->|yes| Read["Read displayed ground-marker path"]
  Read --> Valid{"At least two waypoint IDs?"}
  Valid -->|yes| Exact["Send exact GPS node order"]
  Valid -->|no| Legal
  Reroute["BeamNG recalculates GPS"] --> Dirty["Mark AI route dirty"]
  Dirty --> Read
Loading

Strict GPS affects path choice, not steering. BeamNG AI may still move within the road geometry to avoid traffic or complete a turn. If the displayed route cannot be read during a transient recalculation, TaxiDriver falls back to the legal autonomous route rather than disabling AI.

Native route command

Before starting a route, TaxiDriver:

  • unloads any legacy custom recovery controller;
  • loads taxiDriverStockAiObserver;
  • enables native obstacle avoidance;
  • enables lane following where required;
  • applies the selected aggression, traffic-wait and legal-speed settings;
  • disables player safe-teleport recovery;
  • starts ai.driveUsingPath with the selected node list.

The native route remains authoritative for steering. The traffic guard never writes steering input.

Physical arrival and premature Route Done

BeamNG Route Done means that native AI considers its graph path complete. It does not prove that the TaxiDriver trigger was reached.

TaxiDriver therefore measures physical distance to the current target:

d = length(vehiclePosition - targetPosition)

If d exceeds the gameplay arrival radius, the completion is classified as premature. The route is immediately rebuilt from the current road segment. A maximum of three premature-completion retries prevents an infinite loop on an unreachable target.

stateDiagram-v2
  [*] --> Planning
  Planning --> Driving: Native route issued
  Driving --> Planning: GPS recalculated
  Driving --> RouteDone: BeamNG reports Route Done
  RouteDone --> Planning: Outside target and retries remain
  RouteDone --> Waiting: Outside target and retry limit reached
  RouteDone --> Arrived: Physical target reached
  Driving --> Paused: UI / Vehicle Config suspension
  Paused --> Planning: Resume
  Driving --> Off: Player takes control
  Arrived --> Off: Gameplay leg changes
Loading

Final-target speed limiting is enabled only when the vehicle heading agrees with the target-side road direction. The car is not asked to stop early on the opposite side of the road merely because its Euclidean distance to the target is small.

Traffic Guard

Native BeamNG AI handles ordinary avoidance, but it can follow too closely or miss a vehicle that enters the swept path during a turn. taxiDriverStockAiObserver adds a speed-only safety layer.

Straight following corridor

Nearby tracked vehicles are projected into the ego vehicle's forward and lateral axes. A vehicle becomes a lead candidate only when it is ahead and overlaps the current body-width corridor.

Let:

  • v be ego speed;
  • v_l be lead speed;
  • deltaV = max(0, v - v_l) be closing speed;
  • T be configured following time;
  • g_min be configured minimum bumper gap;
  • a_c be comfortable deceleration.

The desired gap is:

g_desired = g_min + v * T + deltaV^2 / (2 * a_c)

The observer starts limiting speed when the measured gap is below g_desired or the time to collision becomes short:

TTC = gap / deltaV

Smooth braking

The requested deceleration rises with the gap deficit, but its rate of change is limited. The active speed cap is integrated using a jerk limit of 2.5 m/s^3:

a_next = approach(a_current, a_requested, jerkLimit * dt)
v_limit_next = max(v_desired, v_limit - a_next * dt)

When the path clears, the temporary limit is released progressively. Full immediate braking is used only when TTC < 0.55 s or the required physical deceleration reaches 8.5 m/s^2.

This distinction prevents ordinary distant traffic from producing repeated full-brake pulses.

Steering-arc prediction

A straight corridor does not cover the outside corner of a turning vehicle. When steering magnitude is significant, the observer predicts the ego centre along the current turn radius and compares it with short-term motion of nearby vehicles.

The player observer samples twelve points at 100 ms intervals. For sample time t, the approximate ego position in vehicle coordinates is:

yaw(t) = min(1.45, v * t / turnRadius)
forward(t) = sin(yaw(t)) * turnRadius
lateral(t) = turnSide * (1 - cos(yaw(t))) * turnRadius

The closest predicted separation is reduced by the bounding radii of both vehicles. A small remaining clearance becomes a curved-path lead candidate and uses the same smooth braking model.

Passenger pickup sequence

AI pickup requires all of the following:

  • the vehicle is within seven metres of the passenger target;
  • target-side direction alignment is valid;
  • vehicle speed reaches a complete-stop threshold;
  • the physical passenger flow has not already completed.
sequenceDiagram
  participant AI as Native AI
  participant TD as TaxiDriver
  participant Car as Taxi vehicle
  participant Passenger as Physical passenger

  AI->>TD: Arrive within pickup radius
  TD->>Car: Hold vehicle stopped
  TD->>TD: Confirm stable full stop
  TD->>Car: Horn on for 600 ms
  TD->>Car: Horn off for 200 ms
  TD->>Car: Horn on for 600 ms
  TD->>Car: Horn off
  TD->>Passenger: Walk toward taxi
  Passenger-->>TD: Boarding complete
  TD->>Car: Release forced stop
  TD->>AI: Start destination route
Loading

The passenger prop is excluded from broad traffic prediction to avoid repeated all-vehicle work near pickup. A lightweight direct distance check still treats the passenger as a pickup obstacle. Hitting the passenger at speed cancels the order and applies a major fine.

Cargo uses BeamNG's small cardboard-box vehicle. Reaching the loading radius removes the prop and continues into the existing cargo-mass system. If either prop cannot be spawned, the order falls back to the logical loading sequence.

Presets and Custom settings

Setting Balanced default Runtime effect
Aggression 40% Native BeamNG AI aggression
Following time 2.3 s Time component of the desired traffic gap
Minimum distance 4 m Minimum bumper gap
Comfortable braking 3.5 m/s² Progressive following and arrival deceleration
Traffic wait 3 s Native AI traffic-wait parameter
Obey speed limits On Uses BeamNG legal route-speed mode
Lane discipline On Requests native lane following
Strict GPS route Off Uses displayed GPS nodes instead of autonomous legal routing
AI trip debug log Off Writes a streaming per-session JSONL journal

The complete presets are:

Preset Aggression Following time Minimum gap Braking Traffic wait Legal speed Lane discipline
Modest Novice 30% 3.4 s 7 m 2.5 m/s² 6 s Yes Yes
Cautious Driver 35% 2.8 s 5.5 m 3.0 m/s² 4 s Yes Yes
Balanced 40% 2.3 s 4 m 3.5 m/s² 3 s Yes Yes
Assertive 60% 1.8 s 3 m 4.5 m/s² 2 s No Yes
Mad Racer 85% 1.3 s 2 m 6.0 m/s² 1 s No No

Strict GPS is deliberately independent from these presets and is preserved when the player changes temperament.

Fleet AI

Every hired Fleet vehicle owns a separate native worker and a separate Vehicle Lua observer. It does not share target, route progress or traffic state with the player.

Capability Player AI Driver Fleet worker
Route source Legal directed route or strict GPS Worker assignment route
Traffic observer 100 ms 200 ms
Curved trajectory samples 12 6
Target-aligned final braking Yes No player-specific target hand-off
Premature Route Done handling Up to 3 immediate retries Current-segment replan after 1 s
Stuck recovery Native route reissue Minimum 45 s, bounded replans
Native safe teleport Disabled Enabled for non-player NPC recovery

Fleet route progress is checked every 250 ms and staggered across workers. Passed route nodes are removed before a replan. This keeps hired taxis persistent and traffic-aware without running the full player update cost for every vehicle.

Refueling

AI never creates a fuel detour automatically. The player must:

  1. press Refuel;
  2. let TaxiDriver create a real or Magic Fuel route;
  3. enable AI from the refueling map if automatic travel is desired.

The accepted passenger or cargo order remains intact. AI stops when the station route is complete and the player chooses the amount to refuel.

Diagnostics

Enable AI trip debug log to create:

taxidriver_ailog_<session-start-timestamp>.jsonl

The file is opened when AI control starts and is closed when the player disables AI or the game session ends. Records are flushed continuously so data before a crash remains readable.

Important records include:

  • selected route source and node preview;
  • target and physical distance;
  • premature native completion and retry count;
  • following gap, closing speed and time to collision;
  • curved-path risk;
  • temporary speed limit and applied deceleration;
  • pickup horn stages;
  • gearbox, ignition, pedals and vehicle damage;
  • route stop reason.

General runtime messages remain available in beamng.log with the [TaxiDriver] prefix when general debug logging is enabled.

Known limitations

  • Native AI may still make poor lane choices or hesitate on incomplete community-map graphs.
  • Strict GPS can guarantee the requested node order, not human-like steering within every junction.
  • Parked objects that BeamNG does not expose as tracked vehicles may be handled only by native obstacle avoidance.
  • The traffic guard controls speed only; it intentionally does not replace native steering.
  • Fleet protection is lower frequency by design and prioritizes bounded CPU cost over player-level responsiveness.
  • The player should take control whenever native AI cannot resolve a map, traffic or vehicle-controller edge case safely.

Clone this wiki locally