ROS2 Humble + UV: Set Up NeuPAN for Mapless Navigation
Intro
This walks through the whole process: setting up the environment, configuring NeuPAN, running a simulation, and deploying on a real robot.
arxiv.orgDon't follow the root README.md at https://github.com/KevinLADLee/neupan_ros2—the tutorial there hasn't been kept up to date! Read the subdirectory's https://github.com/KevinLADLee/neupan_ros2/blob/main/src/neupan_ros2/README.md instead. That outdated one really got me.
Tested environment
- Ubuntu 22.04
- ROS2 Humble
- UV
Set up the environment
Clone the NeuPAN ROS2 repo
Clone the neupan_ros2 repo and enter it. We don't need its commit history, so --depth 1 makes the clone faster.
git clone https://github.com/KevinLADLee/neupan_ros2 --depth 1
cd neupan_ros2
chmod +x *.shInstall the system dependencies
./setup.shSet up Python with UV
Create a Python virtual environment in the workspace and install the dependencies. I'm skipping pyproject here to keep installation simple.
Python 3.10 works best with NeuPAN. Also note that it requires numpy<2.0.
uv venv
uv pip install torch torchvision "numpy<2.0" --torch-backend=autoClone the main NeuPAN repo and install it
git clone https://github.com/hanruihua/NeuPAN --depth 1
uv pip install -e NeuPAN/Build the ROS2 workspace
./build.shSimulation
Once the build succeeds, you can run the simulation. Put this in sim.sh for a quick launch.
# Set up the environment
source .venv/bin/activate
export PYTHONPATH=$PYTHONPATH:$(pwd)/NeuPAN:$(pwd)/.venv/lib/python3.10/site-packages
source install/setup.bash
# Start the simulation
ros2 launch neupan_ros2 sim_complete.launch.pyHeads up: the command below from the root README is outdated 😅. The author removed sim_diff_launch.py ages ago. The up-to-date docs are in neupan_ros2/src/neupan_ros2 at main · KevinLADLee/neupan_ros2:
Launch files:
- Add robot-specific launch files: limo.launch.py, ranger.launch.py, simulation.launch.py
- Add sim_complete.launch.py for full simulation with ddr_minimal_sim
- Remove deprecated launch files (limo_diff_launch.py, neupan_launch.py, sim_diff_launch.py)
source install/setup.bash
ros2 launch neupan_ros2 sim_diff_launch.py sim_env_config:=scenario_corridor.yamlDeploy on a real robot
Create a robot configuration
Copy the template to create a config for your robot. I'll call mine my_robot.
You'll find the configuration guide at /src/neupan_ros2/config/robots/_template/README.md. I've also marked the fields you need to change with comments in the code blocks below.
For more detail on these configuration files, see the official repo: https://github.com/hanruihua/NeuPAN/blob/579e7afa239cd7ff61f7f63fbd4aaaecbb136d3b/README.md.
cd ./src/neupan_ros2/config/robots
cp -r _template my_robot
mv robot.yaml.template robot.yaml
mv planner.yaml.template planner.yamlEdit planner.yaml. My chassis has four-wheel drive, so I'm using the differential-drive model.
Set the maximum speed and acceleration to match your robot. Be sure to set length and width to its actual dimensions.
# mpc
receding: 8
step_time: 0.25
ref_speed: 0.5
device: 'cpu'
time_print: False
collision_threshold: 0.01
# robot
robot:
kinematics: 'diff' # Four-wheel drive: 'diff'; Ackermann: 'acker'; NeuPAN also recently added 'omni' for omnidirectional robots
max_speed: [0.5, 1.0] # [linear speed m/s, angular speed rad/s] -> adjust to your robot's actual capabilities
max_acce: [0.5, 1.0] # Maximum acceleration
length: 0.6 # IMPORTANT: measure your robot's actual length (m)
width: 0.55 # IMPORTANT: measure your robot's actual width (m)
# wheelbase: # Only needed for Ackermann steering
# initial path
ipath:
interval: 0.03
# waypoints: [[0, 0, 0], [1, 0, 0]]
curve_style: 'line' # Use 'line' for four-wheel drive; 'dubins' or reeds for Ackermann
min_radius: 0.05 # Differential-drive robots can turn in place, so the minimum radius is 0# robot
loop: False
arrive_threshold: 0.5
close_threshold: 0.05
arrive_index_threshold: 3
# proximal alternating minimization network
pan:
iter_num: 2
dune_max_num: 200
nrmp_max_num: 10
dune_checkpoint: None
iter_threshold: 0.1
# adjust parameters
adjust:
q_s: 1.0
p_u: 0.5
eta: 15.0
d_max: 0.1 # Maximum safety margin (m); too large increases computation
d_min: 0.01 # Minimum safety margin (m); too small may risk collisionsEdit robot.yaml
neupan_node:
ros__parameters:
# Robot identification
robot_type: 'my_robot' # Change this to your robot's name—yes, its name
robot_description: 'LIMO differential drive robot' # Robot description
# Configuration file paths (relative to robot directory)
planner_config_file: 'planner.yaml'
dune_checkpoint_file: 'models/dune_model_5000.pth' # Path to the DUNE model we'll train later. If your robot and LiDAR are similar, you can try this one first, but it may collide...
# TF Frame configuration
map_frame: 'map'
base_frame: 'livox_frame'
lidar_frame: 'livox_frame'
# Visualization control
enable_visualization: true # Master switch; turn this off to disable all visualizations
enable_dune_markers: true # DUNE point-cloud markers (turning these off saves 5–10% CPU; recommended on embedded hardware, but fine for testing)
enable_nrmp_markers: true # NRMP point-cloud markers
enable_robot_marker: true # Robot footprint marker
marker_size: 0.05
marker_z: 1.0
# Scan processing
scan_angle_max: 3.14 # Maximum LiDAR angle; change this if your scanner isn't 360°
scan_angle_min: -3.14 # Minimum LiDAR angle; change this if your scanner isn't 360°
scan_downsample: 1
scan_range_max: 30.0 # Maximum LiDAR range (m)
scan_range_min: 0.01 # Minimum LiDAR range (m)
flip_angle: false
refresh_initial_path: true
include_initial_path_direction: false
control_frequency: 50.0 # Planning and control loop frequency (Hz); recommended range: 10–100
# ========== Topic Configuration (Optional) ==========
# Uncomment and modify to customize topic names for your robot
# cmd_vel_topic: '/neupan_cmd_vel'
# scan_topic: '/scan'
# plan_input_topic: '/plan'
# goal_topic: '/goal_pose'Train a DUNE model
The best part of NeuPAN is that you only need to train an end-to-end neural network to output control commands based on your robot's dimensions and LiDAR scan range. It doesn't depend on the surrounding environment, so a single model can handle different settings.
Training the DUNE model is straightforward—just run the code from the official repo. Training does demand a lot from both the CPU and GPU (especially the CPU, since cvxpy can't use GPU acceleration). I'd recommend training on your own computer: even a server with a powerful GPU can take longer if its CPU is weak. In my tests, CPU training on a Jetson Orin Nano Super edge device took about eight hours.
You can also use the Colab Notebook I made to train online with a T4. Its CPU is weaker, so expect roughly two hours.
colab.research.google.comgit clone https://github.com/hanruihua/NeuPAN --depth 1
uv sync # Download Python dependencies
cd ./NeuPAN/example/dune_train/Edit dune_train_diff.yaml (or dune_train_acker.yaml for Ackermann steering).
Add device: 'cuda' to enable GPU acceleration.
On an i5-12600KF + RTX 4070 Ti, training with CUDA took about 30 minutes.
device: 'cuda' # IMPORTANT: enables CUDA acceleration; without this line, training runs on the CPU
robot:
kinematics: 'diff'
length: 0.6 # Robot length; same as in the earlier config
width: 0.55 # Robot width; same as in the earlier config
train:
direct_train: true
data_size: 100000
data_range: [-30, -30, 30, 30] # Rectangle based on the LiDAR radius; for a 30 m radius, use 30 for all four values
batch_size: 256
epoch: 5000
valid_freq: 250
save_freq: 500
lr: 5e-5
lr_decay: 0.5
decay_freq: 1500Start training
uv run dune_train_diff.pyIf you see ModuleNotFoundError: No module named 'tkinter', your system is missing tkinter. Install it with sudo apt install python3-tk, then rerun the training command.
When training finishes, it prints the saved model path:
Training... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
finish train, the model is saved in /data/neupan_ros2/NeuPAN/example/dune_train/model/diff_robot_default/model_5000.pth
Complete Training. The model is saved in /data/neupan_ros2/NeuPAN/example/dune_train/model/diff_robot_default/model_5000.pthCopy it into the config's models folder. The filename must exactly match the one set in robot.yaml.
cp /data/neupan_ros2/NeuPAN/example/dune_train/model/diff_robot_default/model_5000.pth /data/neupan_ros2/src/neupan_ros2/config/robots/my_robot/models/dune_model_5000.pthThat's the model training done.
Set up the launch script
Go back to neupan_ros2/src/neupan_ros2/launch and copy a launch script for your robot type.
cp limo.launch.py my_robot.launch.pyOpen it and change the path pointing to your robot's configuration.
# Configuration paths
pkg_share = get_package_share_directory('neupan_ros2')
robot_config_dir = os.path.join(pkg_share, 'config', 'robots', 'my_robot') # Replace 'limo' with your own robot config folder name
robot_config = os.path.join(robot_config_dir, 'robot.yaml')
rviz_config = os.path.join(pkg_share, 'rviz', 'neupan_sim.rviz')Back in the neupan_ros2 workspace directory, rebuild the neupan_ros2 package.
colcon build --packages-select neupan_ros2Deployment
Start the chassis with your robot's own configuration first, then run the launch script.
ros2 launch neupan_ros2 my_robot.launch.pyRefs:
github.comgithub.com