Skip to content

Commit e73041f

Browse files
committed
init
1 parent 5e97af5 commit e73041f

File tree

553 files changed

+71576
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

553 files changed

+71576
-1
lines changed

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
# Nexus
1+
# Nexus
2+
3+
## Getting Started
4+
- [Installation](docs/install.md)
5+
- [Prepare Dataset](docs/prepare_dataset.md)
6+
- [Train and Eval](docs/train_eval.md)
7+

docs/install.md

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Installation
2+
3+
## Setup conda environment
4+
5+
```python
6+
conda create -n nexus python=3.9
7+
conda activate nexus
8+
9+
git clone https://github.com/OpenDriveLab/Nexus.git && cd Nexus
10+
pip install -r requirements.txt
11+
```
12+
13+
## Install Nuplan Devkit
14+
15+
1. Navigate to the `third_party` directory:
16+
```bash
17+
cd Nexus/third_party
18+
```
19+
20+
2. Clone the Nuplan Devkit repository:
21+
```bash
22+
git clone https://github.com/motional/nuplan-devkit.git
23+
```
24+
25+
3. Install the Nuplan Devkit:
26+
```bash
27+
cd nuplan-devkit
28+
pip install -e .
29+
```
30+
31+
4. Set the `NUPLAN_DEVKIT_PATH` environment variable:
32+
```bash
33+
export NUPLAN_DEVKIT_PATH=$YOUR_PATH_TO_Nexus/Nexus/third_party/nuplan-devkit
34+
```
35+
5. install following packages:
36+
```bash
37+
pip install aiofiles aioboto3 flatdict adjustText loralib easydict einops_exts
38+
pip install waymo-open-dataset-tf-2-12-0==1.6.4
39+
```
40+
41+
## Install ALF
42+
43+
Follow these steps to install ALF while ignoring the dependencies of torch:
44+
45+
1. Navigate to the `third_party` directory:
46+
47+
```bash
48+
cd Nexus/third_party
49+
```
50+
51+
2. Clone the ALF repository:
52+
53+
```bash
54+
git clone https://github.com/HorizonRobotics/alf.git
55+
```
56+
57+
3. Edit the `setup.py` file to ignore the dependencies of torch:
58+
59+
Comment out lines 52, 53, and 54 in the `setup.py` file:
60+
61+
```python
62+
# 'torch==2.2.0',
63+
# 'torchvision==0.17.0',
64+
# 'torchtext==0.17.0',
65+
```
66+
67+
4. Install ALF:
68+
69+
```bash
70+
pip install -e .
71+
```

docs/prepare_dataset.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Dataset Preparation
2+
3+
## Reformatting the Original Dataset
4+
5+
### Waymo Dataset
6+
To reformat Waymo's scenario protocol buffer data, download the dataset and then specify the `--wod_path` argument to the local path of the Waymo Motion dataset. Finally, execute the following command to split the data:
7+
8+
```bash
9+
python scripts/preprocess/process_wod_data.py --wod_path your/path/to/waymo/motion/dataset
10+
```
11+
12+
### Nuplan Dataset
13+
For instructions on setting up the Nuplan dataset, refer to [Nuplan Devkit Documentation](https://nuplan-devkit.readthedocs.io/en/latest/dataset_setup.html).
14+
15+
## Caching the Dataset
16+
To train efficiently, it is essential to cache the data first. Follow these steps:
17+
18+
### Waymo Dataset
19+
Run the following script to cache the Waymo dataset:
20+
21+
```bash
22+
sh ./scripts/preprocess/cache_wod_data.sh
23+
```
24+
25+
### Nuplan Dataset
26+
Run the following script to cache the Nuplan dataset:
27+
28+
```bash
29+
sh ./scripts/preprocess/cache_nuplan_data.sh
30+
```

docs/train_eval.md

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Training and Evaluation Guide
2+
3+
## Training
4+
To train on the Nuplan Dataset, execute the following command:
5+
```bash
6+
sh ./scripts/training/train_nexus_nuplan.sh
7+
```
8+
9+
To train on the Waymo Open Dataset, execute the following command:
10+
```bash
11+
sh ./scripts/training/train_nexus_wod.sh
12+
```
13+
14+
## Evaluation
15+
16+
### Waymo Sim Agents Benchmark
17+
18+
#### Steps to Fix Bugs in Waymo Open Dataset Package
19+
20+
1. **Install the Waymo Open Dataset Package**
21+
Make sure you have installed the Waymo Open Dataset package by running:
22+
```bash
23+
pip install waymo-open-dataset-tf-2-12-0==1.6.4
24+
```
25+
26+
2. **Modify the File**
27+
Edit the file located at `/usr/local/lib/python3.9/dist-packages/waymo_open_dataset/wdl_limited/sim_agents_metrics/metrics.py`. Change the code at line 47 from:
28+
```python
29+
config_path = '{pyglib_resource}waymo_open_dataset/wdl_limited/sim_agents_metrics/challenge_2024_config.textproto'.format(pyglib_resource='')
30+
```
31+
to:
32+
```python
33+
import os
34+
# Get the resource path from an environment variable
35+
pyglib_resource = os.getenv('PYGLIB_RESOURCE_PATH', '')
36+
37+
# Construct the full config path
38+
config_path = f'{pyglib_resource}/waymo_open_dataset/wdl_limited/sim_agents_metrics/challenge_config.textproto'
39+
```
40+
41+
3. **Set Environment Variable**
42+
Set the environment variable for the resource path by running:
43+
```bash
44+
export PYGLIB_RESOURCE_PATH=/usr/local/lib/python3.9/dist-packages
45+
```
46+
47+
By following these steps, you will ensure that the configuration path is correctly set and the package functions properly.
48+
49+
50+
51+
#### Start Evaluation
52+
To evaluate on the Waymo Sim Agents Benchmark, follow these steps:
53+
54+
1. Running the testing script:
55+
```bash
56+
sh ./scripts/testing/test_nexus_wod_rollouts.sh
57+
```
58+
59+
2. Running the offline evaluation script with multiprocessing
60+
```bash
61+
python ./scripts/testing/offline_sim_agents.py --pkl_dir ${YOUR_PKL_DUMP_PATH} --nprocess 32 --output_dir ${OUTPUT_DIR}
62+
```
63+
64+
### Metrics(e.g. FDE, ADE, ...) Evaluation
65+
To evaluate specific metrics like FDE, ADE, Collision rate, etc., run the following command:
66+
```bash
67+
sh ./scripts/testing/test_nexus_metrics.sh
68+
```
69+
70+
## Checkpoints
71+
72+
We provide several checkpoints for model reproduction. To use a checkpoint, download it and replace the checkpoint path in the bash script:
73+
74+
```bash
75+
+checkpoint.ckpt_path=PATH_TO_CHECKPOINT \
76+
+checkpoint.strict=True \
77+
```
78+
79+
### Checkpoint List
80+
We provided the following CKPT:
81+
| Model | Dataset | Checkpoint |
82+
|---------------|---------|---------------------------------------------------------------------------------------------|
83+
| Nexus-nuplan | NuPlan | [Google Cloud](https://storage.googleapis.com/93935945854-us-central1-blueprint-config/epoch_llama_sm.ckpt)
84+
| Nexus-wod | Waymo Motion dataset | [Google Cloud](https://storage.googleapis.com/93935945854-us-central1-blueprint-config/epoch_llama_sm.ckpt) |

nuplan_extent/__init__.py

Whitespace-only changes.
149 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from nuplan.common.actor_state.vehicle_parameters import VehicleParameters
2+
3+
4+
def get_ideal_one_parameters() -> VehicleParameters:
5+
return VehicleParameters(
6+
vehicle_name="Leading Ideal One",
7+
vehicle_type="gen1",
8+
width=1.96,
9+
front_length=3.9,
10+
rear_length=1.13,
11+
wheel_base=2.935,
12+
cog_position_from_rear_axle=1.67,
13+
height=1.777,
14+
)
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import math
2+
3+
import numpy as np
4+
5+
6+
class EdgeComputer:
7+
"""
8+
Estimate the left and right road edges given a raster bev map and expert future waypoints.
9+
For each future waypoint, find its corresponding left and right road edge.
10+
"""
11+
12+
def __init__(
13+
self,
14+
bev_map: np.ndarray,
15+
trajectory: np.ndarray,
16+
resolution: float = 0.5,
17+
y_flip: bool = True,
18+
):
19+
"""
20+
:param bev_map: one channel bev map [H, W], with 1 stands for drivable area and 0 stands for
21+
non-drivable area (currently only supports H==W).
22+
:param trajectory: future waypoints with shape [N, 3], where N is the number of waypoints and
23+
for each waypoint it contains (x, y, yaw).
24+
:param resolution: resolution of the bev map, used to convert trajectory coords value into pixels.
25+
:param y_flip: whether to flip y coord value while converting, defaults to True for Carla data.
26+
"""
27+
assert len(
28+
bev_map.shape) == 2, "The bev map should only have one channel."
29+
h, w = bev_map.shape
30+
assert h == w, "The bev map should have same H and W."
31+
self.map_half_size = h / 2
32+
self.resolution = resolution
33+
self.y_flip = y_flip
34+
self.bev_map = bev_map
35+
36+
# convert trajectory xy to pixel value in bev map
37+
self.traj_in_pixel = self.to_pixel(trajectory[:, :2])
38+
self.angles = trajectory[:, 2]
39+
# number of waypoints
40+
self.num_waypoints = trajectory.shape[0]
41+
42+
def to_pixel(self, trajectory):
43+
"""Convert coords into pixels."""
44+
assert trajectory.shape[1] == 2
45+
k = -1 if self.y_flip else 1
46+
traj_in_pixel = np.zeros_like(trajectory)
47+
traj_in_pixel[:, 0] = trajectory[:, 0] / \
48+
self.resolution + self.map_half_size
49+
traj_in_pixel[:, 1] = k * trajectory[:, 1] / \
50+
self.resolution + self.map_half_size
51+
return traj_in_pixel
52+
53+
def to_coords(self, traj_in_pixel):
54+
"""Convert pixels back into coords."""
55+
assert traj_in_pixel.shape[1] == 2
56+
k = -1 if self.y_flip else 1
57+
trajectory = np.zeros_like(traj_in_pixel)
58+
trajectory[:, 0] = (
59+
traj_in_pixel[:, 0] - self.map_half_size) * self.resolution
60+
trajectory[:, 1] = (
61+
traj_in_pixel[:, 1] - self.map_half_size) * k * self.resolution
62+
return trajectory
63+
64+
def get_traj_in_pixel(self):
65+
return self.traj_in_pixel
66+
67+
@staticmethod
68+
def get_slope(angle):
69+
"""Get the slope given yaw angle."""
70+
tan_angle = math.tan(angle)
71+
return tan_angle
72+
73+
def get_edges(self,
74+
max_steps: int,
75+
delta_l: float = 0.5,
76+
tolerance: int = 0,
77+
ideal_steps: int = -1):
78+
"""Estimate left and right edges given the expert waypoints.
79+
80+
The basic idea is to sample along the line that passes through the waypoint
81+
and is perpendicular to the expert route continuesly until a non-drivable area
82+
is found.
83+
:param max_steps: maximum steps to sample along one direction.
84+
:param delta_l: the interval between each sample alone the line.
85+
:param tolerance: the 'delay' to stop sampling, if it's 0 then stop immediately after
86+
find the edge, otherwise keep sampling for {tolerance} steps after
87+
find the edge.
88+
:param ideal_steps: the most possible steps to sample that could find the edge, used
89+
when bev map is incomplete. In other words, this gives the bev map
90+
an 'imaginary' drivable boundary.
91+
"""
92+
left_edges = []
93+
right_edges = []
94+
# record the search steps for both direction in previous waypoints
95+
self.search_steps = np.zeros((self.num_waypoints, 2))
96+
self.max_steps = max_steps
97+
self.delta_l = delta_l
98+
self.tolerance = tolerance
99+
self.ideal_steps = ideal_steps if ideal_steps > 0 else max_steps
100+
101+
for i in range(self.num_waypoints):
102+
left_edge = self._find_points_one_direction(i, right=False)
103+
right_edge = self._find_points_one_direction(i, right=True)
104+
left_edges.append(left_edge)
105+
right_edges.append(right_edge)
106+
left_edges = np.array(left_edges)
107+
right_edges = np.array(right_edges)
108+
return left_edges, right_edges
109+
110+
def _find_points_one_direction(
111+
self,
112+
waypoint_step: int,
113+
right: bool,
114+
):
115+
"""Sample along one direction.
116+
:param waypoint_step: indicate the current searching waypoint.
117+
:param right: indicate the searching direction, whether it's left or right.
118+
"""
119+
x = self.traj_in_pixel[waypoint_step, 0]
120+
y = self.traj_in_pixel[waypoint_step, 1]
121+
slope = self.get_slope(self.angles[waypoint_step])
122+
delta_x = self.delta_l / (math.sqrt(1 + slope**2))
123+
road_edge = None
124+
# if direction is right, x value is incremented
125+
k = 1 if right else -1
126+
# column index, left is 0 and right is 1
127+
col_ind = 1 if right else 0
128+
129+
# get the sampling steps from the last waypoint for reference.
130+
if waypoint_step == 0:
131+
last_steps = self.ideal_steps
132+
else:
133+
last_steps = self.search_steps[waypoint_step - 1, col_ind]
134+
135+
# sample along x in one direction
136+
found = -1
137+
step = 1
138+
while (found != 0) and (step <= self.max_steps):
139+
if found > 0:
140+
found -= 1
141+
x_next = x + k * step * delta_x
142+
y_next = slope * x_next + (y - slope * x)
143+
if found == -1 and self.bev_map[round(y_next), round(x_next)] == 0:
144+
found = self.tolerance
145+
if found == 0:
146+
road_edge = (x_next, y_next)
147+
steps = step
148+
step += 1
149+
150+
# if edge is not found after reaching max sampling steps, use the sampling
151+
# steps from last waypoint as the imaginary edge.
152+
if road_edge is None:
153+
x_next = x + k * last_steps * delta_x
154+
y_next = slope * x_next + (y - slope * x)
155+
road_edge = (x_next, y_next)
156+
steps = last_steps
157+
158+
self.search_steps[waypoint_step, col_ind] = steps
159+
return road_edge

0 commit comments

Comments
 (0)