1. 环境搭建前的整体思路与方案选型
1.1 为什么选择WSL加ROS这套组合
Ego-Planner是浙大FAST实验室开源的一套基于梯度的局部轨迹规划算法,在无人机自主导航领域被广泛引用。它的核心优势在于不需要ESDF地图,直接用稀疏的AABB障碍物信息做梯度优化,计算效率比Fast-Planner那一代高出一大截。但很多人卡在第一步——环境搭不起来。
我前后在三台不同配置的机器上部署过Ego-Planner,踩过的坑基本覆盖了从系统层到编译层的所有环节。先说方案选型:为什么用WSL而不是纯Ubuntu双系统或者虚拟机?原因很直接——日常办公还得用Windows,双系统切换成本太高;虚拟机跑Gazebo这种3D仿真,显卡直通麻烦,帧率低到没法看。WSL2支持GPU加速(需要WSLg和对应的驱动),Gazebo能跑到接近原生的性能,同时Windows和Linux的文件系统可以互通,改代码用VSCode远程连接WSL,体验比纯Linux桌面还顺手。
ROS版本的选择上,Ego-Planner官方推荐Ubuntu 20.04加ROS Noetic。这个组合是经过验证最稳的,因为Ego-Planner依赖的Ceres Solver、PCL、Eigen这些库在Noetic下版本匹配度最好。有人尝试在Ubuntu 22.04加ROS2 Humble上跑,理论上可行,但需要改不少CMake配置和消息类型,对于第一次接触这个项目的人来说,没必要给自己增加难度。先把Noetic跑通,理解整个数据流之后再考虑迁移ROS2。
1.2 WSL版本选择与安装方式对比
WSL现在有两个主要版本:WSL1和WSL2。Ego-Planner必须用WSL2,因为WSL1不支持完整的系统调用,Gazebo的图形渲染和Ceres的某些底层计算会出问题。检查当前WSL版本很简单,在PowerShell里执行wsl -l -v就能看到每个发行版对应的版本号。
安装方式有两种:微软商店一键安装和命令行离线安装。商店安装最省事,但下载速度看网络脸色,有时候一个Ubuntu镜像能下半小时。命令行安装用wsl --install -d Ubuntu-20.04,同样受网络影响。如果你遇到wsl --install 太慢的情况,我的建议是直接去下载Ubuntu 20.04的WSL离线包(rootfs.tar.gz格式),然后用wsl --import命令导入。这个离线包大概几百MB,用下载工具拉比商店快得多。
导入命令是这样的:
wsl --import Ubuntu-20.04 D:\wsl\Ubuntu2004 D:\wsl\ubuntu2004.rootfs.tar.gz --version 2第一个参数是发行版名称,第二个是安装目标路径,第三个是离线包路径。导入完成后用wsl -d Ubuntu-20.04进入系统。这种方式的好处是安装位置可控,不会默认塞到C盘用户目录下,后期磁盘空间管理方便很多。
注意:导入的离线包如果是老版本,进去之后先执行
sudo apt update && sudo apt upgrade把基础包更新一遍,不然后面装ROS可能会遇到依赖版本冲突。
1.3 显卡驱动与GUI支持的准备
WSL2跑Gazebo需要图形界面支持。Windows 11自带WSLg,装好WSL2之后GUI应用可以直接显示。Windows 10的话需要额外配置X Server,比如VcXsrv或者Xming。我实测下来,Windows 11加WSLg的体验明显更好,窗口缩放、剪贴板共享、音频都正常,Gazebo的界面不会闪。
显卡驱动方面,NVIDIA显卡需要安装Windows端的Game Ready驱动(版本号470以上),WSL里面不需要单独装显卡驱动,系统会自动挂载/usr/lib/wsl/lib下的库。验证方法是在WSL里执行nvidia-smi,如果能正常输出显卡信息就说明GPU直通成功了。AMD显卡的支持相对麻烦一些,需要安装对应的ROCm驱动,而且不是所有型号都支持,这一点要提前确认。
如果nvidia-smi报错或者Gazebo界面一直在闪,大概率是驱动版本不匹配或者WSLg的渲染后端有问题。可以尝试在WSL里设置环境变量export LIBGL_ALWAYS_SOFTWARE=1强制软件渲染,虽然性能差一些,但至少能跑起来排查问题。
2. ROS与依赖库的安装细节
2.1 用鱼香ROS一键安装还是手动配置
ROS Noetic的安装方式有两种主流选择:手动添加源然后apt安装,或者用鱼香ROS一键安装脚本。手动安装的步骤是设置sources.list、添加密钥、apt update、apt install ros-noetic-desktop-full。这个过程本身不复杂,但国内网络环境下下载速度是个大问题,ros-noetic-desktop-full这个包加起来有好几个GB,中途断线就得重来。
鱼香ROS一键安装脚本(wget http://fishros.com/install -O fishros && bash fishros)的优势在于自动换源,它会根据你的系统版本选择最快的镜像源,而且把ROS、Gazebo、常用工具链都串起来装。我试过在全新WSL环境里用这个脚本,从零到能跑roscore大概十五分钟,比手动配置快不少。但要注意,这个脚本安装的Gazebo版本可能和Ego-Planner要求的版本有差异,装完之后需要确认一下Gazebo的版本号。
手动安装的话,关键命令是这些:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 sudo apt update sudo apt install ros-noetic-desktop-full装完之后别忘了echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc,不然每次开终端都要手动source。
2.2 Ceres Solver的编译与版本匹配
Ego-Planner依赖Ceres Solver做非线性优化。ROS Noetic自带的Ceres版本是1.14,但Ego-Planner的某些分支需要Ceres 2.0以上。如果你直接用apt安装的libceres-dev,编译Ego-Planner时可能会报找不到ceres::Manifold或者LocalParameterization相关的错误,这就是版本不匹配的典型症状。
我的建议是源码编译Ceres 2.1.0。步骤不复杂,但依赖项要装齐:
sudo apt install cmake libgoogle-glog-dev libgflags-dev libatlas-base-dev libsuitesparse-dev git clone https://ceres-solver.googlesource.com/ceres-solver cd ceres-solver git checkout 2.1.0 mkdir build && cd build cmake .. make -j4 sudo make install编译过程中如果报Eigen版本太低的错误,需要升级Eigen到3.3以上。Ubuntu 20.04自带的Eigen是3.3.7,一般够用,但如果遇到问题就去Eigen官网下载3.4.0源码编译安装。
实操心得:Ceres编译很吃内存,WSL默认分配的内存可能不够。建议在Windows用户目录下创建
.wslconfig文件,写入memory=8GB和processors=4,然后wsl --shutdown重启WSL,这样编译时不会因为OOM被kill。
2.3 PCL与Eigen的常见冲突处理
PCL(点云库)在Ego-Planner里主要用于地图可视化和点云处理。ROS Noetic自带的PCL是1.10,这个版本和Eigen 3.3.7配合没问题。但如果你手动升级了Eigen到3.4,PCL编译时可能会报alignment相关的运行时错误,这是因为Eigen 3.4对内存对齐的要求更严格,而老版本PCL的某些结构体没有加EIGEN_MAKE_ALIGNED_OPERATOR_NEW宏。
解决办法有两个:一是降级Eigen回3.3.7,二是给PCL打补丁重新编译。我推荐第一个方案,因为Ego-Planner对Eigen版本没有硬性要求,3.3.7完全够用。降级的方法是sudo apt install libeigen3-dev=3.3.7-2,如果apt里没有这个版本,就去Eigen的GitLab仓库下载3.3.7的源码编译安装。
另一个常见问题是PCL和ROS的pcl_ros包版本冲突。如果你在CMakeLists里同时find_package了PCL和pcl_ros,可能会遇到符号重复定义的链接错误。这时候需要确保find_package(PCL REQUIRED)在find_package(catkin REQUIRED COMPONENTS pcl_ros)之前,让CMake优先使用系统PCL。
3. Ego-Planner源码编译与仿真环境配置
3.1 源码下载与工作空间结构
Ego-Planner的源码在GitHub上,直接clone速度慢的话可以用gitee的镜像。整个仓库包含几个子模块:ego-planner(核心算法)、uav_simulator(仿真环境)、map_generator(地图生成)。clone的时候记得加--recursive把子模块一起拉下来。
mkdir -p ~/ego_ws/src cd ~/ego_ws/src git clone --recursive https://github.com/ZJU-FAST-Lab/ego-planner.git工作空间的目录结构建议保持默认,不要随意改动包名和路径,因为Ego-Planner的launch文件里有很多相对路径引用。如果你需要同时跑多个版本的Ego-Planner,可以用不同的工作空间隔离,比如~/ego_ws_v1和~/ego_ws_v2,每次编译前source对应的setup.bash。
编译之前先装依赖:
sudo apt install libarmadillo-dev libdw-devlibarmadillo-dev是线性代数库,libdw-dev用于堆栈回溯。这两个包不装的话编译会报错。
3.2 catkin_make编译参数调优
Ego-Planner的编译时间比较长,尤其是uav_simulator里的local_sensing包,包含大量模板代码。默认的catkin_make是单线程编译,在WSL里可能要跑十几分钟。可以用catkin_make -j4开四线程,但要注意内存占用,如果WSL只分配了4GB内存,开四线程可能会卡死。
编译命令:
cd ~/ego_ws catkin_make -j4 -DCMAKE_BUILD_TYPE=Release-DCMAKE_BUILD_TYPE=Release很重要,Debug模式下Ego-Planner的优化求解会慢到没法用。Release模式开启-O3优化,轨迹规划能跑到实时。
如果编译过程中报undefined reference to ceres::...,说明Ceres的链接有问题。检查CMakeLists.txt里有没有find_package(Ceres REQUIRED),以及target_link_libraries里有没有加${CERES_LIBRARIES}。有时候Ceres装到了/usr/local/lib,但链接器找不到,需要export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH。
注意:每次修改CMakeLists或者添加新文件后,最好先
catkin_make clean再重新编译,避免增量编译的缓存问题。我遇到过好几次改了代码但编译结果没变的情况,clean之后就好了。
3.3 Gazebo仿真环境的启动与调试
Ego-Planner的仿真启动文件在uav_simulator/launch目录下,常用的有single_run.launch和swarm.launch。单机仿真用single_run.launch就够了:
roslaunch ego_planner single_run.launch这个launch会启动Gazebo、加载地图、生成无人机模型、启动Ego-Planner节点。如果Gazebo界面一直在闪或者黑屏,先检查WSLg是否正常工作。可以在WSL里跑glxgears测试OpenGL渲染,如果glxgears也闪,那就是WSLg的渲染问题,尝试更新Windows端的显卡驱动,或者在WSL里设置export LIBGL_ALWAYS_INDIRECT=0。
Gazebo启动后,在RViz里应该能看到无人机模型和点云地图。用2D Nav Goal工具点击目标点,无人机就会开始规划轨迹并飞行。如果无人机不动,检查/goal话题有没有收到消息,以及Ego-Planner节点有没有正常输出规划结果。
仿真环境里的地图是map_generator生成的随机障碍物地图,如果你想用自己的地图,可以把点云文件放到map_generator/resource目录下,然后修改launch文件里的map_size和obstacle_num参数。
4. 常见问题排查与避坑经验
4.1 WSL相关问题的快速定位
WSL的问题主要集中在网络、文件系统和GUI三个方面。网络问题表现为apt下载慢、git clone超时,解决办法是换国内源,把/etc/apt/sources.list里的archive.ubuntu.com换成mirrors.tuna.tsinghua.edu.cn或者mirrors.aliyun.com。文件系统问题表现为编译时IO错误或者文件权限异常,这是因为WSL2的ext4虚拟磁盘和Windows的NTFS文件系统之间有转换开销,建议把代码放在WSL的~/目录下,不要放在/mnt/c/里。
GUI问题最常见的就是Gazebo闪屏。我遇到过三种情况:一是WSLg版本太老,wsl --update更新一下就好;二是显卡驱动不匹配,去NVIDIA官网下载最新驱动;三是Gazebo的渲染引擎和WSLg的Wayland协议不兼容,这时候可以试试export GDK_BACKEND=x11强制用X11后端。
还有一个坑是WSL的时钟漂移。WSL2的虚拟化时钟有时候会和Windows主机不同步,导致ROS的TF变换报时间戳错误。解决办法是在WSL里执行sudo hwclock -s同步硬件时钟,或者装一个ntpdate定时同步。
4.2 编译错误的分类与解决思路
Ego-Planner的编译错误大致分四类:找不到头文件、链接错误、模板实例化错误、C++标准不匹配。找不到头文件通常是依赖没装全,根据报错信息里的头文件名去apt搜索对应的dev包安装就行。链接错误一般是库路径问题,用ldd命令检查可执行文件依赖的库有没有解析到正确的路径。
模板实例化错误比较隐蔽,通常表现为一大堆instantiation of...的报错。这种错误往往是Eigen或者PCL的版本问题,降级到ROS Noetic自带的版本一般能解决。C++标准不匹配的话,在CMakeLists里加set(CMAKE_CXX_STANDARD 14)或者set(CMAKE_CXX_STANDARD 17),Ego-Planner用的是C++14,但某些依赖可能需要C++17。
下面这个表格整理了我遇到过的典型编译错误和对应的解决方法:
| 错误信息关键词 | 可能原因 | 解决方法 |
|---|---|---|
undefined reference to ceres:: | Ceres链接路径不对 | 检查LD_LIBRARY_PATH,重新编译Ceres |
Eigen::internal::plain_array | Eigen版本冲突 | 降级Eigen到3.3.7 |
pcl::PointCloud相关报错 | PCL版本不匹配 | 用ROS自带的PCL,不要手动升级 |
cannot find -larmadillo | 缺少armadillo库 | sudo apt install libarmadillo-dev |
C++17 required | 编译器标准太低 | CMakeLists里设置CXX_STANDARD 17 |
4.3 仿真运行时的性能优化
Ego-Planner在WSL里跑仿真,性能瓶颈主要在Gazebo的物理引擎和Ego-Planner的优化求解。Gazebo默认的物理更新频率是1000Hz,对于无人机仿真来说太高了,可以降到250Hz减少CPU占用。修改方法是在world文件里把<max_step_size>从0.001改成0.004。
Ego-Planner的优化求解频率默认是100Hz,如果CPU跟不上,可以降到50Hz。在advanced_param.xml里找到optimization_rate参数,改成50就行。另外,RViz的点云显示很吃GPU,如果帧率低,可以把点云的Decay Time调小,或者直接关掉点云显示只看轨迹。
WSL的内存分配也很关键。默认WSL2最多用主机内存的50%,如果主机是16GB内存,WSL最多用8GB。跑Gazebo加Ego-Planner,8GB勉强够用,但编译的时候可能会OOM。建议在.wslconfig里设置memory=12GB,给WSL多分一点。
实操心得:如果Gazebo跑起来后无人机抖动厉害或者直接穿模,检查一下
local_sensing的raycast参数。这个参数控制深度相机的射线投射数量,数值太大会导致计算量爆炸,太小则障碍物检测不全。我一般设成raycast_num=30,在精度和性能之间比较平衡。
4.4 从仿真到实机的过渡注意事项
仿真跑通之后,很多人想直接上实机。这里有几个关键差异要注意:仿真里的无人机模型是理想化的,没有电机延迟、没有传感器噪声、没有风扰。实机上Ego-Planner的轨迹可能会因为定位漂移而撞障碍物。建议先在仿真里加噪声测试,把local_sensing的noise_std参数调大,模拟真实传感器的噪声水平。
实机的状态估计器也很关键。仿真里用的是Gazebo的ground truth位姿,实机上需要用VINS或者FAST-LIO做状态估计。Ego-Planner订阅的是/odom话题,实机上要确保状态估计器发布的/odom频率稳定在50Hz以上,否则规划器会因为位姿跳变而输出抖动轨迹。
最后,实机的安全策略一定要做好。Ego-Planner本身没有紧急停止功能,需要在外部加一个安全节点,监测无人机和障碍物的距离,小于安全阈值时直接切offboard模式悬停。这个安全节点用简单的距离判断就行,不需要太复杂,但一定要有。
5. 工具链与辅助配置的补充说明
5.1 VSCode远程连接WSL的开发环境配置
用VSCode连WSL开发Ego-Planner,体验比在WSL终端里用vim好太多。装一个Remote - WSL扩展,然后在WSL里执行code .就能打开当前目录。VSCode会自动在WSL里装一个server端,代码补全、跳转、调试都在WSL环境里运行,和本地开发没区别。
C++的智能提示需要配置c_cpp_properties.json,把ROS的头文件路径加进去:
{ "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "/opt/ros/noetic/include/**", "/usr/include/eigen3/**", "/usr/local/include/**" ], "cppStandard": "c++14" } ], "version": 4 }调试的话,在launch.json里配置gdb,可以给Ego-Planner的核心节点打断点,单步跟踪轨迹优化的过程。不过Ego-Planner的优化求解在Ceres内部,断点打进去容易卡死,建议只在规划器的入口和出口打日志,不要深入Ceres内部。
5.2 常用调试工具与话题监控
ROS的调试工具链很成熟,rostopic echo看话题数据,rosbag record录包回放,rqt_graph看节点连接关系。Ego-Planner的关键话题有/goal(目标点)、/odom(位姿)、/planning/trajectory(规划轨迹)、/planning/pos_cmd(位置指令)。调试的时候先确认/odom有数据,再看/goal有没有收到,最后看/planning/trajectory有没有输出。
如果轨迹规划失败,Ego-Planner会在终端打印Failed to generate trajectory之类的日志。常见原因是目标点在障碍物内部,或者起点被障碍物包围。这时候可以用rviz的Publish Point工具在障碍物外面点一个目标点,看能不能规划成功。
rosbag录包的时候注意磁盘空间,Gazebo的点云数据量很大,录几分钟就好几个GB。可以只录关键话题:
rosbag record /odom /goal /planning/trajectory /planning/pos_cmd回放的时候用rosbag play --clock,注意加--clock参数,不然TF的时间戳会乱。
5.3 地图生成与自定义场景
Ego-Planner自带的map_generator可以生成随机地图,但如果你想测试特定场景,比如走廊、房间、树林,就需要自己建图。最简单的方法是用Gazebo的Building Editor画一个2D平面图,然后导出成点云。或者用Blender建一个3D模型,导出成.dae格式,在Gazebo里加载。
点云地图的格式要求是pcl::PointCloud<pcl::PointXYZ>,保存成.pcd文件。在launch文件里把map_generator的map_type改成point_cloud,然后指定pcd_file路径就行。注意点云的坐标系要和Gazebo的世界坐标系对齐,不然障碍物位置会偏。
如果点云地图太大导致加载慢,可以用pcl_filter做降采样,把点云分辨率降到0.1米左右。Ego-Planner的障碍物膨胀半径默认是0.3米,点云太密的话膨胀后会连成一片,无人机找不到可通行的缝隙。
6. 个人实操体会与后续扩展方向
6.1 我踩过的几个印象深刻的坑
第一个坑是WSL的磁盘空间。WSL2的虚拟磁盘默认是动态扩展的,但不会自动收缩。我编译了几次Ego-Planner之后,虚拟磁盘涨到了50GB,删了代码也不释放。后来用wsl --manage Ubuntu-20.04 --set-sparse true开启稀疏模式,再执行sudo fstrim -av才把空间收回来。
第二个坑是Gazebo的模型下载。第一次启动Gazebo的时候,它会从在线模型库下载无人机模型和场景模型,国内网络环境下经常卡住。解决办法是提前把模型库下载到~/.gazebo/models目录下,或者设置GAZEBO_MODEL_DATABASE_URI环境变量指向本地路径。
第三个坑是Ceres的线程数。Ceres默认用所有可用的CPU核心做并行优化,在WSL里会把CPU占满,导致Gazebo卡顿。可以在Ego-Planner的代码里设置ceres::Solver::Options::num_threads = 2,限制Ceres的线程数,给Gazebo留出CPU资源。
6.2 后续可以尝试的扩展
Ego-Planner跑通之后,可以尝试几个扩展方向。一是多机编队,swarm.launch支持多架无人机同时仿真,可以研究一下机间避障和编队保持的算法。二是动态障碍物,在Gazebo里加一些移动的障碍物,测试Ego-Planner的在线重规划能力。三是把Ego-Planner和SLAM结合,用FAST-LIO或者VINS做状态估计,实现无GPS环境下的自主导航。
如果想把Ego-Planner迁移到ROS2,工作量主要在消息类型和构建系统上。ROS2用ament_cmake替代catkin,消息类型从.msg改成.idl,TF变换用tf2替代tf。算法核心代码基本不用改,主要是接口层的适配。我试过在ROS2 Humble上编译Ego-Planner,改了两天能跑起来,但性能比Noetic下差一些,可能是ROS2的DDS通信开销更大。
最后说一个实际部署时的经验:Ego-Planner的轨迹优化对初始值很敏感,如果起点和终点的直线路径完全被障碍物挡住,优化器可能会陷入局部最优,规划出一条绕远路的轨迹。这时候可以在advanced_param.xml里调大max_iteration,或者换一个更激进的初始路径生成策略。我在实机上遇到过一次无人机在原地转圈的情况,就是因为初始路径太差,优化器找不到可行解。后来加了一个简单的A*做全局引导,问题就解决了。