基于Unity与开源工具链的Autoware矢量地图制作实战指南

发布时间:2026/7/24 9:05:19
基于Unity与开源工具链的Autoware矢量地图制作实战指南 1. 项目概述为什么我们需要一套完整的工具链如果你正在尝试将Autoware部署到实车上并且已经走过了编译、标定这些基础步骤那么你大概率会卡在“地图”这一关。Autoware的官方文档和社区教程对于矢量地图Vector Map的生成和使用往往语焉不详或者直接指向一些已经过时、依赖复杂商业软件的工具。很多团队在实车测试前会发现没有一套可靠、可控、可复现的地图制作流程整个项目就无从谈起。这正是我写下这篇实战指南的初衷——分享我们从零开始搭建一套完全开源、可视化、且能与Autoware无缝对接的矢量地图与Unity工具链的完整过程。这套工具链的核心价值在于“闭环”。它不仅仅是为了生成一个.csv格式的矢量地图文件而是构建一个从原始数据如点云、图像、GPS轨迹到可视化编辑再到最终用于Autoware定位与规划的完整工作流。我们选择Unity作为核心编辑器并非要用它做仿真那是另一个话题而是看中了其强大的3D场景编辑能力、友好的可视化界面以及灵活的脚本扩展性。相比于命令行工具或者简陋的二维编辑器在Unity里拖拽道路边界、设置交通信号灯、检查车道连接关系效率要高得多也直观得多。本指南将完全聚焦于实战避开理论空谈。我们将从最原始的数据比如一段用RTK采集的车辆轨迹开始一步步教你如何搭建环境、处理数据、在Unity中创建地图元素、导出为Autoware标准格式并解决在这个过程中必然会遇到的各种“坑”。无论你是自动驾驶算法工程师、系统集成工程师还是相关领域的学生这篇指南都将为你提供一个清晰、可操作的路径。2. 环境准备与核心工具选型工欲善其事必先利其器。在开始动手之前我们需要明确整个工具链的构成并准备好相应的软件环境。我们的目标是搭建一个在Ubuntu 22.04上运行的工具链但其中部分可视化编辑工作会在Windows下的Unity中进行这是一种兼顾开发效率与系统稳定性的常见选择。2.1 操作系统与基础依赖我们的工具链横跨Linux和Windows核心数据处理和Autoware运行在Ubuntu上地图编辑则在Windows的Unity中进行。Ubuntu 22.04 侧准备ROS 2 HumbleAutoware.universe当前主流版本基于ROS 2 Humble构建。请务必通过ROS官方源安装避免使用snap等版本。sudo apt update sudo apt install curl gnupg lsb-release sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release echo $UBUNTU_CODENAME) main | sudo tee /etc/apt/sources.list.d/ros2.list /dev/null sudo apt update sudo apt install ros-humble-desktop python3-colcon-common-extensionsAutoware.universe按照官方GitHub仓库的README进行源码编译。这里有一个关键坑点务必确保你的网络环境能稳定访问GitHub并提前安装所有依赖。编译过程可能长达数小时建议使用-j参数指定并行编译任务数如-j6并留足至少30GB的磁盘空间。Python 3.8 及相关库我们将使用Python脚本进行中间数据格式的转换和处理。需要安装numpy,pandas,open3d用于点云处理pyproj用于坐标转换等。pip3 install numpy pandas open3d pyprojWindows 10/11 侧准备Unity Hub Unity Editor下载并安装Unity Hub然后通过它安装一个长期支持LTS版本如2022.3.x。不推荐使用最新的技术预览版以避免不必要的兼容性问题。在安装时务必勾选“Windows Build Support”和“Linux Build Support”可选为未来跨平台考虑。Visual Studio 2022Unity默认的C#编辑器安装时记得勾选“.NET桌面开发”和“使用C的桌面开发”工作负载后者对某些本地插件可能有帮助。2.2 核心工具链组件详解我们的工具链主要由以下几个环环相扣的组件构成数据采集与预处理模块Ubuntu输入RTK-GPS轨迹文件.nmea或.csv、3D激光点云.pcd或.bag、同步的时间戳。工具ros2 bag命令、pcl_tools用于点云处理、自定义Python脚本。输出一个经过时间同步和粗略对齐的“数据包”包含参考轨迹和对应的点云快照。地图元素生成模块Python功能这是承上启下的关键。它将上一步的原始数据转换为Unity能够理解的、包含初步语义信息的中间格式。例如从RTK轨迹点生成一条初步的“车道中心线”折线从点云中通过地面分割和聚类识别出潜在的“道路边界点”和“路缘石点”。输出一个自定义的.json或.csv文件其中包含了点、线、面的基本几何信息和类型标签。Unity可视化编辑模块Windows核心我们将在Unity中开发一套编辑器工具Editor Tools。功能导入上一步生成的中间数据并在3D场景中渲染出来。提供图形化界面GUI来创建、编辑、删除Autoware矢量地图所需的所有元素点Node、线Lane、路面区域Area、停车线Stop Line、交通信号灯Traffic Light等。实现元素间的逻辑连接如车道的前后继承关系、停车线与信号灯的关联。提供测量、对齐、吸附等辅助编辑功能。输出一个在Unity场景中保存的、包含完整矢量地图信息的项目文件。Autoware格式导出模块Unity/C#功能将Unity场景中编辑好的地图按照Autoware官方定义的矢量地图格式一组特定的.csv文件如point.csv,lane.csv,dtlane.csv等编写导出脚本。关键必须严格遵守Autoware的格式规范包括坐标系通常为局部坐标系原点自定、单位米、ID编码规则和关联关系。地图验证与调试模块Ubuntu工具Autoware自带的map_tools如lanelet2相关工具、rviz2。流程将导出的矢量地图文件放入Autoware的指定地图包中启动Autoware和rviz2加载地图检查车道线显示是否正确、连接关系是否完整、与原始点云是否对齐。这是确保地图可用的最后一步。实操心得工具链选型的“为什么”为什么不用现成的lanelet2的josm编辑器因为它学习曲线陡峭且对于复杂的三维调整如立交桥不直观。为什么不用纯Web方案如Three.js因为Unity的编辑器生态和即时渲染能力更强方便我们快速开发自定义的编辑逻辑和可视化效果。这套Unity方案的本质是构建一个专属于我们自身数据格式和作业习惯的“地图IDE”长期来看效率提升是巨大的。3. 从原始数据到Unity可编辑格式的实战转换拥有了清晰的工具链蓝图后我们开始第一步也是最基础的一步将野外的原始传感器数据变成Unity里那些可以拖拽的图形元素。很多人失败就在这里因为数据不对齐后面所有编辑都是徒劳。3.1 RTK轨迹处理与车道中心线生成假设我们拥有一个RTK设备记录的轨迹文件rtk_trajectory.csv包含时间戳、经纬度、海拔高度、速度、朝向等信息。步骤1坐标转换WGS84 to UTM or LocalAutoware的矢量地图使用局部笛卡尔坐标系。我们需要将经纬度WGS84转换为平面坐标。UTM投影是一个不错的选择它能将地球曲面投影到平面在局部区域变形很小。import pandas as pd from pyproj import Proj, transform # 读取RTK数据 df pd.read_csv(rtk_trajectory.csv) # 假设经纬度列为 lon, lat 高度为 alt wgs84 Proj(initepsg:4326) # WGS84坐标系 # 根据轨迹中心点确定UTM分区例如第50带 utm Proj(projutm, zone50, ellpsWGS84) # 批量转换 df[x], df[y] transform(wgs84, utm, df[lon].values, df[lat].values) # 将原点平移到轨迹起点简化后续坐标 df[x] - df[x].iloc[0] df[y] - df[y].iloc[0] df[z] df[alt] - df[alt].iloc[0] # 高度也做平移现在df[[x, y, z]]就是相对于轨迹起点的局部坐标了。我们将这个处理后的数据保存为processed_trajectory.csv。步骤2生成初步车道中心线RTK轨迹通常位于车道中心。我们可以直接将这些有序的点连接起来作为车道的“脊柱”。但原始轨迹点可能太密集或含有噪声。降采样每隔0.5米或1米取一个点减少数据量。平滑使用滑动平均或样条插值如scipy.interpolate.splprep让曲线更光滑。生成Unity数据创建一个新的centerlines.json文件结构如下。每个Line对象代表一条车道中心线points是三维坐标数组。{ centerlines: [ { id: 1001, type: lane_center, points: [ [0.0, 0.0, 0.0], [1.2, 0.1, 0.0], [2.5, 0.3, 0.01], ... ] } ] }3.2 激光点云处理与道路结构提取点云数据.pcd或.bag中的点云话题提供了丰富的环境几何信息。我们的目标是从中提取道路边界、路缘石、停止线等要素的参考点。步骤1点云预处理与地面分割使用Open3D或PCL库。读取与滤波读取点云使用体素网格滤波下采样减少计算量。地面分割采用经典的法向量或RANSAC平面拟合方法分离出地面点云和非地面点云。道路要素基本都在地面上。import open3d as o3d pcd o3d.io.read_point_cloud(scene.pcd) # 下采样 downpcd pcd.voxel_down_sample(voxel_size0.05) # 使用RANSAC分割地面 plane_model, inliers downpcd.segment_plane(distance_threshold0.1, ransac_n3, num_iterations100) ground_cloud downpcd.select_by_index(inliers) obstacle_cloud downpcd.select_by_index(inliers, invertTrue)步骤2道路边界点提取这是难点完全自动化的提取在复杂场景下不可靠。我们采用“半自动”思路为Unity编辑提供“候选点”。对地面点云沿着轨迹的垂直方向法线方向做切片分析。在每个切片内寻找距离轨迹中心线两侧一定范围内如±5米的点的Y坐标横向距离的突变点这些突变点可能是路缘石。将这些候选点的3D坐标收集起来并打上标签如left_boundary,right_boundary。同样在轨迹停止的位置附近寻找点云中具有线性特征且高度与地面有微小差异的区域作为停止线的候选点。步骤3生成Unity点云参考文件将提取出的带有语义标签的点边界候选点、停止线候选点等保存为一个简单的文件供Unity导入并显示为不同颜色的点云作为编辑时的视觉参考。{ reference_points: [ {pos: [1.5, -2.1, 0.0], type: boundary_left}, {pos: [1.6, -2.2, 0.0], type: boundary_left}, {pos: [1.5, 2.3, 0.0], type: boundary_right}, {pos: [15.0, 0.0, 0.01], type: stop_line_candidate}, ... ] }注意事项数据同步是生命线以上所有处理的前提是RTK轨迹和激光点云的时间戳已经精确同步。如果数据来自ROS bag务必在录制时确保时钟同步或在后期处理时使用message_filters进行时间对齐。如果轨迹和点云根本对不上后续所有编辑都将失去意义。在生成中间文件后务必在Unity中同时导入中心线和参考点云检查它们是否在空间上吻合。不吻合就需要回溯检查时间同步和坐标转换过程。4. Unity编辑器工具链的开发与核心功能实现这是整个工具链的“大脑”和“操作台”。我们需要在Unity中创建一个专门的编辑器窗口并实现一系列地图元素编辑功能。我们将使用Unity的EditorWindow和Handle场景视图操控手柄系统。4.1 项目设置与基础架构创建新的Unity项目选择3D核心模板命名为AutowareMapEditor。导入必要资源创建Editor、Scripts、Data、Prefabs等文件夹。设计数据模型在Scripts下创建C#类定义与Autoware矢量地图元素对应的数据结构。这是后续所有操作的基础。// 示例定义矢量地图点 [System.Serializable] public class VectorMapPoint { public int id; // PID public Vector3 position; // 局部坐标 (x, y, z) public string type; // 类型如 white_line, stop_line } // 定义车道 [System.Serializable] public class VectorMapLane { public int id; // LID public int startPointId; // 起始点PID public int endPointId; // 结束点PID public Listint pointIds; // 构成车道的所有点PID列表用于曲线车道 public int leftBoundId; // 左边界线ID public int rightBoundId; // 右边界线ID public string laneType; // 车道类型 // ... 其他属性如限速、转向等 }创建主编辑器窗口在Editor文件夹下创建MapEditorWindow.cs继承自EditorWindow。这个窗口将是我们所有操作的指挥中心。4.2 核心编辑功能的实现功能1数据导入与可视化在编辑器窗口中添加按钮用于导入我们之前生成的centerlines.json和reference_points.json。将这些数据实例化为场景中的GameObject如用LineRenderer显示中心线用粒子系统或简单的Cube显示参考点并赋予不同的颜色以便区分。功能2地图元素的创建与编辑这是工具的核心。我们需要为每种元素点、线、区域实现创建和编辑逻辑。创建点在场景视图中按住Ctrl鼠标点击在鼠标点击的3D位置考虑与地面碰撞创建一个新的VectorMapPoint并生成一个可交互的Handle如一个可拖动的小球。创建车道线在编辑器窗口选择“创建车道”模式。在场景中连续点击生成一系列的点或选择已有的点按回车键完成。系统自动用贝塞尔曲线或折线连接这些点生成一个VectorMapLane对象。为这条车道线生成一个LineRenderer并允许用户通过拖拽线上的控制点来修改其形状。设置车道属性选中一条车道在编辑器窗口的检视面板Inspector中可以设置其laneType如drivingshoulder、speedLimit等。建立连接关系通过拖拽连线或在下拉菜单选择的方式将车道与它的左、右边界线也是由点构成的线元素关联起来。将停车线与信号灯关联。功能3场景视图交互与Handle使用UnityEditor.HandlesAPI来绘制和交互。// 在场景视图中绘制一个可拖动的点 void OnSceneGUI() { VectorMapPoint point target as VectorMapPoint; // target是选中的对象 EditorGUI.BeginChangeCheck(); // 绘制一个可拖动的定位手柄 Vector3 newPosition Handles.PositionHandle(point.position, Quaternion.identity); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(point, Move Point); point.position newPosition; // 通知所有依赖此点的元素如车道线更新 EditorUtility.SetDirty(point); } }通过自定义Editor类如VectorMapPointEditor并重写OnSceneGUI方法我们可以为每种地图元素类型定制其在场景视图中的操控行为。功能4地图管理与持久化我们需要一个顶层的VectorMapManager单例类来管理场景中所有的地图元素点、线、车道、信号灯等。它负责分配唯一的ID维护元素间的引用关系以及将整个地图序列化保存到一个自定义的二进制或JSON格式文件.vmap中方便下次打开Unity项目时直接加载。实操心得Undo系统的集成在编辑器工具开发中务必集成Unity的Undo系统Undo.RecordObject,Undo.RegisterCompleteObjectUndo。地图编辑是一个反复调整的过程没有撤销功能会让人崩溃。在每次修改地图元素属性的操作前后都要正确记录Undo这是专业编辑器工具的基本素养。5. Autoware矢量地图格式导出详解在Unity中精心编辑好地图后我们必须将其转换为Autoware能够识别的格式。Autoware的矢量地图由一系列具有固定名称和格式的CSV文件组成每个文件定义一类元素。5.1 文件格式规范解析你需要仔细阅读Autoware官方文档中关于vector_map的部分。以下是一些核心文件及其关键字段的解读point.csv定义所有点。PID是唯一标识B,L,H对应局部坐标的y, x, z注意顺序这是一个历史遗留的坐标系差异B为北向L为东向。vector.csv定义点之间的向量可选某些版本需要。line.csv定义由两个点BPID,FPID构成的线段。LID是唯一标识。dtlane.csv定义车道的详细轨迹信息这是关键中的关键。包含DID以及Dist,D横向偏移,Dyaw航向角等字段用于描述车道中心线的几何形状。通常由lane.csv引用。lane.csv定义车道。LnID是唯一标识BLID和FLID指向前驱和后继车道IDLaneNo是车道编号JCT表示是否是路口BLID2,BLID3,BLID4等用于描述路口处的多条前驱车道。stopline.csv定义停止线。ID关联的LID所在的线以及TLID关联的信号灯ID。trafficlight.csv定义交通信号灯。IDLinkID关联的线ID以及SubID用于区分同一组灯的不同灯色。5.2 从Unity数据到CSV的转换脚本在Unity的Editor文件夹下创建一个VectorMapExporter.cs脚本。这个脚本将遍历VectorMapManager中所有的数据按照上述规则生成CSV文件。关键步骤与坑点ID映射表在导出开始时为Unity中每个元素的唯一ID可能是GUID或自增整数建立一个到Autoware要求格式的ID从1开始连续递增的映射字典。这是确保所有文件间引用关系正确的基石。坐标系转换Unity是左手系Y向上而Autoware的局部地图坐标系通常是右手系Z向上或ENU东-北-天。必须进行坐标轴转换。通常的转换是Unity (X, Y, Z) - Autoware (L, B, H) (X, Z, Y)不这里极易出错你需要根据你的数据源头RTK转换后的坐标和Unity中的摆放方式来最终确定。一个稳妥的方法是在Unity中创建一个已知坐标的测试点如(1,0,0)导出后看它在Autoware的rviz中显示在哪里然后调整转换公式。常见的可能是Autoware_B Unity_Z,Autoware_L Unity_X,Autoware_H Unity_Y。生成dtlane.csv这是最复杂的部分。你需要将一条车道的中心线一系列点转换为Dist,D,Dyaw的序列。Dist是沿车道的累计距离D和Dyaw通常可以设为0如果你使用的是简单的中心线表示。更精确的做法需要根据道路曲率计算横向偏移和航向。建立连接关系在生成lane.csv时需要正确填写BLID前驱车道ID和FLID后继车道ID。这依赖于你在Unity编辑器中建立的车道连接关系。对于路口需要处理多个前驱BLID2-4和后继FLID2-4的情况。文件编码与格式确保CSV文件以UTF-8编码保存字段间用逗号分隔字符串字段用双引号括起来。可以使用StreamWriter配合CsvHelper库来规范生成。// 示例导出point.csv的简化代码片段 using System.IO; using UnityEngine; using UnityEditor; public static void ExportPoints(VectorMapManager manager, string exportPath) { StringBuilder sb new StringBuilder(); sb.AppendLine(PID,B,L,H,Bx,Ly,ReF,MCODE1,MCODE2,MCODE3); foreach (var point in manager.allPoints) { // 1. 坐标转换 (假设转换关系为Bz, Lx, Hy) float autowareB point.position.z; float autowareL point.position.x; float autowareH point.position.y; // 2. 获取映射后的PID int pid idMappingDict[point.id]; // 3. 拼接行数据未使用的字段填0或空 sb.AppendLine(${pid},{autowareB},{autowareL},{autowareH},0,0,0,0,0,0); } File.WriteAllText(Path.Combine(exportPath, point.csv), sb.ToString(), Encoding.UTF8); }导出后的目录结构应类似于your_map_directory/ ├── point.csv ├── line.csv ├── dtlane.csv ├── lane.csv ├── stopline.csv ├── trafficsign.csv (如果有) └── ... 其他必要的csv文件6. 地图验证、调试与实车部署前的最后检查导出一堆CSV文件并不意味着大功告成。必须在Autoware环境中进行严格的验证才能确保地图可用。6.1 在Autoware中加载与可视化创建地图包在Autoware工作空间的src目录下创建一个新的ROS 2包或者将你的CSV文件放入Autoware标准地图包结构的指定文件夹通常是map/vector_map。修改启动配置确保你的Autoware启动文件如planning.launch.xml或map.launch.py正确指向了新地图的路径。启动与检查source install/setup.bash ros2 launch autoware_launch planning.launch.xml map_path:/path/to/your_map_directory启动rviz2添加Lanelet2Map或VectorMap相关的显示插件。可视化检查项车道线是否显示检查所有车道是否以不同颜色根据类型正确显示。连接关系是否正确观察车道之间的连接箭头是否连贯有无断头路。路口处的车道连接是否复杂但正确。元素对齐如果可能在rviz2中同时加载录制好的点云bag数据检查车道线、停止线是否与点云中的实际道路特征对齐。坐标系检查地图是否出现在正确的位置原点附近没有发生奇怪的旋转或缩放。6.2 常见问题排查速查表在验证阶段你几乎一定会遇到以下问题。这里提供一个快速排查指南问题现象可能原因排查步骤与解决方案地图在rviz中完全不显示1. 地图文件路径错误。2. CSV文件格式错误编码、分隔符、列数不对。3. 坐标系设置错误。1. 检查launch文件中的map_path参数。2. 用文本编辑器或cat命令检查CSV文件前几行确保格式正确。尝试用ros2 run map_loader vector_map_loader手动加载看报错。3. 检查tf树确保map到base_link或world的坐标系变换存在。车道线显示为杂乱线条或位置错误1.point.csv中的坐标转换公式错误。2.lane.csv或dtlane.csv中的点ID引用错误。1.这是最可能的原因回顾并验证你的坐标转换逻辑。在Unity中输出几个特征点的坐标与导出的CSV文件对比再与在rviz中显示的坐标对比。2. 检查lane.csv中的BPID/FPID或pointIds是否都指向有效的PID。检查dtlane.csv的DID是否被lane.csv正确引用。车道连接线缺失或指向错误1.lane.csv中的BLID/FLID填写错误。2. 连接的车道方向相反。1. 核对Unity中建立的车道连接关系与导出的ID映射表。2. 确保车道的方向从BPID到FPID是符合交通行驶方向的。停止线/信号灯不显示或关联错误1. 对应的CSV文件缺失。2.stopline.csv中的TLID与trafficlight.csv中的ID不匹配。3. 关联的LID不存在。1. 确认所有必要的CSV文件都已导出并放在同一目录。2. 仔细检查stopline.csv和trafficlight.csv中的ID关联关系。3. 确认停止线所关联的LID在line.csv中存在。Autoware规划模块报错“找不到车道”1. 地图中存在孤立的车道无前驱或后继。2. 车道类型(LaneType)设置错误规划器过滤掉了。1. 检查每条车道确保在道路网中都是连通的起点和终点除外。2. 确认车道的LaneType字段是规划器可识别的类型如driving。6.3 实车部署前的闭环测试在电脑上验证无误后在实车上线前强烈建议进行一次“软件在环”的闭环测试。录制数据回放使用你制作地图时所用的同一段数据包ROS bag。启动完整系统在仿真环境下启动Autoware的所有模块感知、定位、规划、控制。设置定位使用ndt_localizer或ekf_localizer初始位置设在地图原点附近。观察规划轨迹发布目标点观察全局规划器如behavior_planner,mission_planner是否能在你的新地图上规划出合理的路径。局部规划器如obstacle_avoidance_planner生成的轨迹是否平滑地跟随车道线。检查逻辑设置一个目标点在某个停止线后观察车辆是否能在正确的位置停下。如果以上步骤全部通过恭喜你这份自制的高精度矢量地图已经具备了上车测试的资格。这套从数据到编辑再到导出的完整工具链不仅解决了你当前的项目需求更成为了团队未来快速迭代不同测试场地地图的宝贵资产。地图的质量直接决定了自动驾驶系统性能的上限投入时间打磨这套工具链绝对是值得的。

相关新闻

最新新闻

日新闻

周新闻

月新闻