All work
2026 to present/ Research · RL/ AUB · Prof. Naseem Daher

Driver-Aggressiveness Index

Undergraduate research, running since January 2026, on context-aware autonomous ground vehicles for developing countries. An autonomous vehicle that assumes every surrounding driver follows the rules will not survive Beirut traffic. So the work is teaching it to read how aggressively the cars around it are actually behaving.

Interactive demo

Drive the inputs · the index and the traffic respond live

sumo · aggressiveness_index.py (live)
Aggressiveness index 0
35
1.8
3
2
40

Weighted index over five behavioural features, the same feature set the model consumes per tracked vehicle.

At a glance

My role
Features + RL reward reformulation
Stack
PyTorch · SUMO · ROS2
Supervisor
Prof. Naseem Daher, AUB
Status
Ongoing since Jan 2026

Research pipeline

Simulation is the only place ground truth exists

SUMO labelled profiles Trajectories per vehicle Features 5, windowed Estimator PyTorch Index 0-100 Policy RL ego published over ROS2 · same path in sim and on hardware true profile known → supervised labels
Real dashcam data has no ground-truth aggressiveness label; simulated traffic does.

Code

The reformulation, and the features behind the slider above

features/extract.py behaviour only lives across a window

def features(track, window=50):
    seg = track[-window:]
    return {
        "speed_var":  np.std(seg.speed),
        "gap_min":    np.min(seg.headway),          # seconds to lead vehicle
        "lane_rate":  count_changes(seg.lane) / seg.duration_min,
        "harsh_brake": np.sum(seg.accel < -3.0),    # m/s^2
        "throttle":   np.mean(np.clip(seg.accel, 0, None)),
    }
rl/exploration.py the bug: novelty that was an encoding artefact

def novelty_bonus(state, visits, beta=0.1):
    # BEFORE: counted raw encoded states, so re-scaling an observation
    # made an already-visited state look brand new. The agent learned to
    # farm that instead of exploring.
    #     return beta / sqrt(visits[encode(state)])

    # AFTER: count over behaviourally meaningful features, not the encoding
    key = discretise(behaviour_features(state))
    return beta / math.sqrt(visits[key] + 1)

Citations for the replacement are kept in rl/NOTES.md next to the change.

Inside the repo

Structure

  • sim/SUMO scenarios
  • network.net.xmlroad network
  • driver_profiles.xmlparameterised aggressiveness
  • run_scenarios.pybatch trajectory generation
  • features/trajectory → features
  • extract.pythe five behavioural features
  • windows.pyobservation windowing
  • models/PyTorch
  • aggressiveness_net.pyindex estimator
  • train.pytraining + validation split
  • rl/ego-vehicle policy
  • rewards.pyreward weighting
  • exploration.pyreformulated novelty bonus
  • NOTES.mdcitations for the reformulation
  • ros2_ws/ROS2 integration
  • index_publisher.pypublishes per-vehicle scores

Skills, in context

Where each one actually showed up

PyTorch Sequence model over windows: one harsh brake is an event, a pattern is a driver.
SUMO Labelled traffic generation; the only source of ground-truth aggressiveness.
Reinforcement learning Diagnosing and replacing the novelty bonus in exploration.py above.
ROS2 Publishing per-vehicle scores so sim and hardware share one code path.
Next project Acoustic Water-Leak Localizer