做无人机开发这些年,我带过不少新人,也踩过数不清的坑。有个规律挺有意思:最后能独立扛起项目的人,往往不是一开始就会调参、会写控制律的,而是Linux功底扎实、能在命令行里行云流水解决问题的。无人机软件栈的底层是Linux,这句话不是口号,是真刀真枪的硬约束——PX4飞控固件要编译,ROS节点要跑,视觉SLAM要装CUDA,仿真要起Gazebo,哪一步都离不开一套稳定、干净的Linux开发环境。
这篇内容围绕“Module 3:Ubuntu 20.04 + Linux 工程基础——无人机软件开发环境”展开。目标很直接:从零开始搭出一套专门面向无人机软件开发的Ubuntu 20.04环境,说清楚为什么选这个版本、怎么规划磁盘和源、哪些Linux命令是真正高频的、Python和ROS环境怎么隔离,以及PX4仿真怎么跑起来。它适合两种人:一是刚接触无人机软件开发的学生或转行者,二是已经在用Ubuntu但总觉得环境“不舒服”、想系统性整理一遍的开发者。
既然标题已经点明了是Module 3,前面大概率已经有了无人机的基本概念或硬件认知。这个阶段的任务就是把“软件地基”打好——地基稳了,后面学路径规划、目标检测、飞控二次开发都顺。这篇文章没有废话,都是实操经验和踩坑记录。
1. 为什么无人机软件开发绕不开Ubuntu 20.04——先说版本选型
1.1 无人机软件栈对系统版本的真实约束
很多没接触过无人机软件栈的人会有一个错觉:Linux发行版那么多,随便装一个就行?实际不是这样。无人机软件链的核心成员——PX4 Autopilot、ROS、Gazebo、MAVSDK、OpenCV、CUDA——它们对操作系统版本有明确的依赖关系。其中约束最狠的是ROS和Gazebo的组合。
Ubuntu 20.04对应的是ROS Noetic,这是ROS1的最后一个长期支持版本,官方支持到2025年。Gazebo在20.04的官方源里是Gazebo 11,恰好是PX4官方仿真推荐搭配。CUDA这边,NVIDIA驱动520分支对应CUDA 12.x,在20.04上依然有官方支持。也就是说,20.04在“ROS1 Noetic + Gazebo 11 + PX4 SITL”这条经典链路上,是官方文档覆盖最全、社区踩坑记录最多的组合。
这里想说得直白一点:如果你跑的无人机项目是PX4 + ROS + Gazebo这条主流路线,选Ubuntu 20.04不是“老”,而是“成熟”。20.04是2014年Ubuntu 14.04之后又一个长期稳定普及度极高的版本,软件生态的兼容性在多年打磨后非常可靠。
1.2 20.04相比18.04、22.04的取舍
先说18.04。它的ROS对应是Melodic,Gazebo是9,PX4新版本对Gazebo 9的支持已经越来越少。很多新出的PX4模块或外部插件直接用Gazebo 11的API,Gazebo 9跑起来会报一堆找不到头文件的错。对新手来说,这种“版本不匹配”的问题非常消耗耐心。
22.04则是另一个方向的问题:ROS 2的Humble很新,但大量存量项目、教程、论文复现代码还是ROS 1 Noetic的。如果你跟着PX4官方文档走,很多示例代码默认ROS Noetic + MAVROS。22.04上跑ROS 1,要么用Docker容器,要么自己编译源码,这对新手不友好,对老手也麻烦。
所以我通常建议新人直接上20.04。它不是配置最“潮”的,但一定是踩坑成本最低的。等你哪天需要ROS 2或更新的Gazebo了,用Docker或双系统再切换也不迟。
1.3 一个反直觉的建议:先装双系统还是虚拟机
很多初学者问:我Windows用习惯了,能不能在虚拟机里装Ubuntu 20.04来做无人机开发?我的建议很明确:日常验证、跟教程敲命令,虚拟机可以;真要编译PX4固件、跑Gazebo仿真、连接飞控USB调参,请果断装双系统。
原因不复杂。虚拟机对USB设备透传的支持始终有限,尤其是飞控、GPS模块、数传模块这类串口设备。虽然VMware和VirtualBox都支持USB passthrough,但遇到驱动或权限问题时的排查路径,比双系统麻烦得多。更关键的是性能:Gazebo仿真本质是物理引擎实时计算加3D渲染,虚拟机的图形性能和CPU调度会有明显损耗,起飞悬停的仿真跑起来一卡一卡的,很难判断算法问题还是性能问题。
如果是NVIDIA显卡的机器,还有个坑:Host系统(Windows)和Guest系统(Ubuntu)抢GPU资源,CUDA在虚拟机里的性能衰减很严重,视觉SLAM训练和推理基本不用想。所以我把话放这:想在无人机软件方向认真走下去,双系统是首选。虚拟机可以作为“临时看一下文件”的辅助工具。
2. 构建可用开发环境:磁盘规划、换源、驱动与必备组件
2.1 磁盘分区与系统安装的工程化思路
装系统前先想清楚磁盘怎么分,这比装完系统再折腾省心得多。我个人的工程习惯是:如果一块SSD是500GB以上,给Ubuntu至少划出200GB。不要只给一个根分区就算了,要分出独立的home分区和swap分区。
具体来说:/给80~100GB,装系统和开发工具;/home给100GB以上,用来放ROS工作空间、PX4源码、数据集;swap给8~16GB,考虑到Gazebo和编译时内存可能吃紧。
这样一个好处是:以后系统崩了或者想换发行版,只要不格式化/home,代码和数据集都还在。我见过太多人把代码放在根目录下面,系统一升级全没了,痛不欲生。
分区表格里至少要有这几项:
| 挂载点 | 建议大小 | 文件系统 | 用途 |
|---|---|---|---|
/ | 80~100GB | ext4 | 系统与软件 |
/home | 剩余空间 | ext4 | 代码、数据集、工作空间 |
| swap | 8~16GB | swap | 内存交换 |
安装的时候如果用的是U盘启动盘,建议用Rufus制作启动盘,在写入模式上选择DD模式,这样引导兼容性最好。
2.2 软件源配置:别等到编译到一半才想起来
装完系统第一件事,不是装任何软件,而是换软件源。国内默认源是archive.ubuntu.com,下载速度会非常痛苦,尤其是apt安装依赖时一长串包列表。换成阿里云或清华的镜像源,速度立竿见影。
换源的操作如下:
编辑/etc/apt/sources.list:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo vim /etc/apt/sources.list把里面的archive.ubuntu.com全部替换成mirrors.aliyun.com(或者mirrors.tuna.tsinghua.edu.cn)。然后执行:
sudo apt update && sudo apt upgrade -y这里提醒一个细节:Ubuntu 20.04的源文件里可能还有security.ubuntu.com和ports.ubuntu.com,也要一并替换。很多同学只换主源,安全更新源还是国外,结果apt upgrade时又卡住。
2.3 NVIDIA驱动与CUDA:图形界面与计算环境的版本匹配
无人机视觉开发基本绕不开NVIDIA显卡。装驱动要遵循一个原则:用Ubuntu的附加驱动工具,不要从NVIDIA官网瞎下载。20.04的“软件和更新”应用里,切到“附加驱动”标签页,系统会自动检测合适的驱动版本。
不过有些场景下,官方源里的驱动版本不够新。比如你需要在Ubuntu 20.04上用较新的CUDA,可能需要手动安装特定分支的驱动。网上很多人会搜到“nvidia 520 linux 64-bit ubuntu 20.04”这类关键词,520分支对应的是CUDA 12.x时代,如果你要跑的是较新的PyTorch或TensorFlow,这个版本是合理的。
手动安装驱动的路径是:
sudo apt install build-essential dkms sudo add-apt-repository ppa:graphics-drivers/ppa sudo apt update sudo apt install nvidia-driver-520 sudo reboot装完用nvidia-smi确认驱动工作正常。有一点一定要记住:不要同时从官网下载.run驱动和用apt装驱动,两者会打架,装完经常出现登录界面循环或黑屏。如果已经踩了这个坑,可以在恢复模式下purge掉nvidia相关包再重装。
2.4 必备开发组件的批量安装
基础环境里有一批是所有项目都要用的,直接用一条命令装齐:
sudo apt install -y git vim curl wget htop tree net-tools cmake build-essential \ python3 python3-pip python3-venv python3-dev \ terminator openssh-server openssh-client \ libeigen3-dev libopencv-dev \ net-tools can-utils这里说明一下每个组件的角色:git是代码版本管理基础,PX4源码和ROS包都靠它拉取;eigen是无人机算法里频繁用到的线性代数库;opencv是视觉开发的基础,后面跑目标检测或光流都要用;can-utils是排查CAN总线设备问题用的,比如部分电调和传感器通过CAN连接飞控。
terminator是个被低估的工具。它支持分屏,一个终端跑仿真,一个终端跑ROS节点,一个终端跑日志监控,效率比开一堆窗口高太多。我建议所有新人第一天就装。
3. 真正干活的Linux基本功:无人机工程中的命令实战
3.1 文件与目录操作:日志、数据集、构建产物的分层管理
Linux命令大家都会背,但无人机项目里真正高频的是哪几条?我观察下来,不是ls、cd这种基础,而是跟“找东西”和“看占用”相关的命令。
无人机开发中经常要处理三类数据:飞控日志(.ulg文件或.bin文件)、相机数据集(一堆.jpg/png序列)、编译产物(build目录里的中间文件)。如果目录不分层,几天后你的home目录就是一锅粥。
我自己习惯的项目目录结构是:
~/drone_ws/ ├── src/ # 源码、ROS包 ├── data/ # 数据集、测试图片、日志 ├── build/ # 编译产物 每次删除不心疼 └── scripts/ # 自动化脚本在找文件时,du和df是最高频的硬盘检查工具:
df -h # 查看磁盘分区剩余空间 du -sh ~/drone_ws/* # 查看各目录占用很多人磁盘满了不知道是哪里占的,第一反应是用图形界面一个个点,效率极低。直接用du -sh //* 2>/dev/null | sort -rh | head -20列出来最大的一批目录,一目了然。
3.2 进程与资源监控:定位CPU占用和内存泄漏
无人机算法的调试中有一种经典场景:代码跑了一段后,飞机反应越来越迟钝,甚至掉线。极大概率是某个节点内存泄漏或CPU被打满。这时候top和htop是你的第一道防线。
htop比top直观很多,可以直接按CPU或内存排序,快速定位是哪个进程在捣鬼:
htop还有一种情况:明明某个程序退出了,但串口或网口还被占着,报“port already in use”。这是典型的僵尸进程问题。排查方法:
ps aux | grep 你的程序名 kill -9 对应的PID如果你监听的是UDP/TCP端口,可以用ss -tulpn查看哪个进程占了端口。这些都是无人机开发里每天都要用的底层能力,比死记命令重要得多。
3.3 串口与USB设备权限:飞控连接开发机的常见坑
这是新手最容易卡住的地方之一。飞控通过USB连接到电脑后,Linux下会表现为一个串口设备,通常是/dev/ttyACM0或/dev/ttyUSB0。但默认情况下,普通用户没有权限访问串口设备,于是QGroundControl或MAVROS一连接就报权限错误。
解决办法是把自己加入dialout用户组:
sudo usermod -aG dialout $USER执行完必须注销重新登录,组权限才会生效。之后用ls -l /dev/ttyACM0确认设备已可访问。
再补一个经验:如果设备插上后连/dev/ttyACM0都不出现,先别忙着重装驱动,用dmesg | tail -20看内核日志。很多情况是USB线质量问题或接口接触不良,这时候硬件层面换根线比折腾软件有效得多。
3.4 Shell脚本:编译、刷写、日志采集的自动化
以前见新人编译PX4固件,都是手动敲命令,然后看着一屏一屏的输出发呆。这种做法浪费生命。我建议把重复操作写成脚本,比如一个build_and_test.sh:
#!/bin/bash set -e source /opt/ros/noetic/setup.bash cd ~/PX4-Autopilot make px4_sitl gazeboset -e的作用是脚本中任何一条命令失败就停止执行,避免犯“编译失败了还在往下跑”的低级错误。日志采集也可以用类似思路:
#!/bin/bash mkdir -p ~/drone_ws/logs/$(date +%Y%m%d_%H%M%S) rosbag record -O ~/drone_ws/logs/$(date +%Y%m%d_%H%M%S)/flight.bag /mavros/state /mavros/local_position/pose用日期做目录名,以后回看日志时一目了然,不用猜是哪个架次的。
4. Python与ROS:无人机算法开发的软件基座
4.1 系统Python与虚拟环境的隔离
Ubuntu 20.04自带的Python 3.8是系统级Python,很多系统工具依赖它。如果你不管三七二十一用sudo pip install往里塞包,大概率会把系统搞坏。经典事故是把setuptools或pip升到不兼容版本,然后系统的软件中心、甚至apt工具链都崩了。
正确的做法是任何项目都开虚拟环境:
python3 -m venv ~/drone_ws/venv source ~/drone_ws/venv/bin/activate pip install numpy opencv-python matplotlib虚拟环境之间互不影响,而且是用户级的,不需要sudo。如果项目里需要不同的Python版本,建议装pyenv来管理多个版本,但暂时没有这个需求的就别折腾了,够用就好。
与Python配套的还需要注意:ROS Noetic默认Python 3.8,虚拟环境里的Python如果版本不同,ROS的python脚本会找不到rospy。所以虚拟环境和ROS混用时,必须在虚拟环境里手动安装ROS的Python模块,要么就不在虚拟环境里跑ROS节点,二选一,不要混着乱来。
4.2 ROS/ROS2安装思路与工作空间
Ubuntu 20.04上的ROS Noetic安装比较标准,流程是:
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 install curl curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - sudo apt update sudo apt install ros-noetic-desktop-full安装完成后要配置环境变量:
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc source ~/.bashrcROS的工作空间结构需要刻意养成习惯。后续编译自定义功能包时,创建workspace:
mkdir -p ~/catkin_ws/src cd ~/catkin_ws catkin_make(或者用catkin build)无人机项目里ROS永远是核心通信骨架:MAVROS把PX4的状态发给其他节点,视觉节点把检测结果发布出去,路径规划节点接收目标点并解算期望姿态。这一条消息流在搭环境时就要了然于胸。
4.3 时间同步与通信机制:无人机多进程协作的底层逻辑
无人机系统里时间同步是很多人忽略的坑。飞控内部有自己的时钟,ROS节点也有自己的时钟,如果它们是两台不同的电脑(比如机载电脑和地面站),时间不同步会导致数据时间戳错乱,日志分析时对不上号。
20.04上常用的方案是用chrony做NTP时间同步:
sudo apt install chrony sudo systemctl restart chrony对于需要更高精度时钟同步的场景,可以了解PTP(精确时间协议),但由于需要硬件支持,适合后面深入做多机编队时再研究。初学者先把chrony配好,保证机载电脑和地面站在同一个时间基准下,问题就解决了一大部分。
通信机制方面,ROS的话题(Topic)是异步的、一对多的,适合传感器数据和状态量;服务(Service)是同步请求/响应的,适合“查询参数”或“执行一次性动作”;动作(Action)适合需要持续反馈的任务,比如航点跟踪。理解这三者的区别,后面写无人机的任务节点才不会逻辑混乱。
5. 仿真先行:PX4 + Gazebo 环境搭建思路
5.1 为什么要先搭仿真
很多新人的想法是要去买一台真机再开干。我不反对真机,但强烈建议在仿真里先跑通整个流程。原因很现实:PX4在仿真里可以直接跑SITL(Software In The Loop)模式,飞控固件的逻辑和真机一模一样,主要区别只是没有真实传感器数据。
在Gazebo里你能做大量测试:验证起飞流程、测试避障算法的逻辑、调串级PID的初步参数——这些在真机上一旦炸机,轻则换桨叶,重则伤人或毁机架。而仿真里炸一百次也不心疼,还能随时重置。
此外,仿真环境方便采集数据。机器人在仿真世界里的ground truth位置、速度、姿态是完美的,没有传感器噪声。这对算法调试初期排除感知误差非常有帮助。
5.2 基础安装步骤与关键依赖
PX4官方推荐的依赖脚本能省很多事,但要注意:不要无脑跑官方脚本,先确认你已经装了前面提到的基础组件。然后按这个顺序:
git clone https://github.com/PX4/PX4-Autopilot.git ~/PX4-Autopilot --recursive cd ~/PX4-Autopilot ./Tools/setup/ubuntu.shubuntu.sh是一个自动化脚本,会把Gazebo、ROS Noetic相关依赖、MAVROS等一并装好。整个跑完大概需要20~40分钟,取决于网络和机器性能。
脚本跑完后再编一次确认环境:
make px4_sitl gazebo第一次编译会拉取很多子模块,时间会比较长。看到终端输出类似[100%] Built target gazebo就说明通过了。
这里有个经验:如果编译过程中报“缺少某个cmake包”,先试sudo apt install对应名字的-dev包,不要立刻自己在网上找一个源码来编。90%的依赖问题apt都能解决,源码编译引入的依赖树反而更乱。
5.3 第一次启动仿真后要检查什么
启动Gazebo后,画面里会出现一架多旋翼停在跑道旁。看起来很简单,但有几个东西一定要确认:
用QGroundControl(QGC)地面站连接仿真环境,选UDP,端口通常默认14550。连接成功后QGC会显示飞机状态为“MAVLink connected”。这时尝试发送一个起飞任务,观察飞机是否正常离地、悬停是否稳定。如果一切正常,恭喜,你的仿真链路已经通了。
另外要检查终端窗口的MAVLink输出是否持续打印INFO [commander] Takeoff detected之类的日志。这说明PX4状态机已经工作,通过MAVLink渠道和外部沟通正常。如果飞机没有反应,第一排查对象往往是UDP端口冲突,或者QGC没切到UDP模式。
仿真环境还有个容易被忽略的点:Gazebo默认跑得很慢时,可能是图形渲染拖了后腿。这时可以关掉Gazebo的GUI,只用无头模式跑仿真,在性能弱的电脑上会有明显改善。具体做法是在launch文件中设置headless:=true。
6. 从“能跑”到“能交付”:工程化习惯与常见坑
6.1 版本控制与SSH远程开发
无人机项目代码的迭代速度很快,特别是调PID、改控制逻辑时,一天内一个参数要反复试。没有版本控制,改乱了就只能靠记忆回滚,迟早出事。所有代码工作,第一步就是git init。
最基础的一套流程是:
git init git add . git commit -m "Initial commit: 完成仿真环境配置" git log --oneline如果看重历史清晰,可以学一下分支管理。比如以main作为稳定版本,每次开发新功能切新分支,测试稳定后再merge。这个习惯在团队合作中尤为重要,自己一个人开发时也能快速回溯“上周这个参数调整后的代码长什么样”。
远程开发方面,无人机常用的开发模式是SSH到机载电脑(比如树莓派或NVIDIA Jetson)上开发。这时候受益最大的不是图形界面,而是SSH端口转发和screen/tmux。在机载电脑上跑长时任务时,如果SSH断线导致任务中断就麻烦了。用screen或tmux把任务挂到后台,即使断线重连,任务还在跑:
screen -S px4_sim # 接着执行想长时间运行的命令 # 断线后重新连接 screen -r px4_sim6.2 实时性相关排查技巧
无人机是强实时系统,飞控的内环控制必须按时完成。虽然我们做的是Linux上层开发,不是飞控本身,但依然会遇到实时性问题,比如订阅MAVLink消息的频率不稳定、PX4姿态消息卡顿等。
排查时第一步看的是通信时延和丢包,最直接的工具是mavlink_status话题和rosnode info。用rostopic hz /mavros/state看消息频率是否稳定在设定值(通常是10Hz或30Hz)。如果频率忽高忽低,先排查是不是同一网络里有其他设备占用带宽,再看CPU负载曲线。
还有一点要牢记:不要在需要实时响应的代码回调里做复杂计算,比如在MAVLink订阅回调里跑YOLO检测,那必然会阻塞消息处理。正确做法是回调里只做数据拷贝,复杂计算放到独立线程去执行。
6.3 日志管理思路
日志是无人机开发和排错的生命线。出现过一种情况:飞控日志在,但ROS节点日志没有,传感器数据的时间戳对不上,最后只能“盲猜”问题出在哪。建议从一开始就建立起日志体制:
- 飞控日志:QGroundControl里设置«日志下载»,每次飞行后下载 .ulg 文件。
- ROS日志:用
rosbag record记录话题数据,至少包含/mavros/state、/mavros/local_position/pose、传感器话题。 - 终端日志:用
script命令录制终端输出:
script flight_log_$(date +%Y%m%d_%H%M%S).txt日志文件按日期或飞行架次命名,并集中放到一个目录,定期归档。不要相信自己的记忆力,写文档、写注释,这都是老生常谈但确实管用的。
6.4 几个值得养成的习惯
第一条:每次装完系统后,把安装过的所有软件和配置写成markdown笔记。半年后你换新电脑,照着笔记装环境,一晚上就搞定,而不是重新踩一遍所有坑。这个笔记就是你个人的环境备份。
第二条:使用别名简化高频命令。比如在~/.bashrc里加几行:
alias px4build='cd ~/PX4-Autopilot && make px4_sitl gazebo' alias ws='cd ~/catkin_ws && catkin_make' alias logs='cd ~/drone_ws/logs'别小看这些操作,每一条命令都少打半行,一天下来省下的时间可以多看几篇论文。
第三条:不要用root账户做日常开发。安装软件用sudo,开发过程全部用自己的普通用户。原因很实际:普通用户的权限隔离能在你手滑时救你一把,比如rm -rf打错路径,root环境下可能连整个系统都没了,普通用户至少有自己的home目录做缓存。
第四条:保持对“磁盘空间”的敏感。编译PX4时,build目录轻松吃掉几十GB。养成定期跑du -sh ~/PX4-Autopilot/build的习惯,隔一段时间就把build目录清理掉重新编译。这块本地空间的浪费最无谓。
最后说点实在的
从多年前第一次在Ubuntu上编译PX4固件,到后来在机载电脑上部署整套无人机感知系统,我发现在Linux这块花的时间从来不会白费。很多人在乎“学会了哪条命令”“装好了哪个环境”,但真正拉开差距的是“遇到问题了怎么排查”。环境坏了不可怕,可怕的是不知道从哪查起。这一模块把Linux工程基础打牢,后面学串级PID、路径规划、目标检测,你才有底气说“我来调”,而不是“代码能跑就行”。如果这篇文章能帮你少走一些弯路,那这个记录就值了。