首页/文章/ 详情

Carla传感器模型深度解析——激光雷达篇

8月前浏览772

文 | 峻川

出品 | 车路漫漫

正文共: 9232字 7

预计阅读时间: 24分钟


写在前面

大家好,我是峻川。

之前给大家分享了鱼眼摄像头的仿真:《Carla传感器模型深度解析——鱼眼相机篇》,本期继续分享激光雷达的仿真。

本文的激光雷达代码完全源自github上的开源项目PCSim: LiDAR Point Cloud Simulation and Sensor Placement,可以实现在Carla中模拟多种激光射线扫描类型的激光雷达和多次回波等效果。正文中作为示例的是本文作者基于此改编的适配最新版Carla"Unreal Engine 5.5 version of CARLA"的激光雷达代码。文末会附上改编后的代码。

具体这些区别于Carla自带激光雷达的Lidar特殊效果的原理请参考以上链接中的论文,本文只聚焦于代码的适配。

创作不易,如果对你有所帮助,还请帮忙点个赞。

激光雷达模型代码解析

在Carla的文档中,有如何建立新传感器模型的教程:how-to-add-a-new-sensor,建议首先阅读链接中的文档以熟悉这一过程。

Carla中激光雷达代码的核心,是Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Sensor/RayCastSemanticLidar中的ARayCastSemanticLidar这个Sensor Actor。该ARayCastSemanticLidar的代码实现了Carla中的语义激光雷达传感器,支持多种扫描模式。以下是关键部分的解读:

1. 核心结构

  • 继承关系:继承自Carla的传感器基类,实现语义激光雷达功能
  • 主要组件
    • FLidarDescription:存储激光雷达参数(通道数、FOV、扫描频率等)
    • FSemanticLidarData:存储采集的点云数据(包含语义信息)
    • LaserAngles:存储各激光束的垂直角度

2. 核心功能流程

  1. 初始化阶段

    • 通过Set()方法解析蓝图参数
    • 根据类型(Risley_pris m/Solid_state/Surround)初始化对应参数
    • CreateLasers()创建激光束角度配置
  2. 每帧处理(PostPhysTick):

3. 扫描模式实现差异

扫描模式      
角度生成方式      
典型应用场景      
default      
均匀分布的垂直角度      
传统机械式雷达      
Surround      
预设的垂直角度数组      
多线机械雷达      
Solid_state      
预定义的水平和垂直角度数组      
MEMS/Flash雷达      
Risley_pris m      
从CSV文件读取角度数据      
旋转棱镜式雷达      

4. 关键算法

射线投射核心逻辑

bool ShootLaser(...) {
   1. 计算激光发射方向
   2. 执行物理射线检测
   3. 处理反射(EnableGhost时):
      if(击中高反射物体 && 随机数>0.5){
          计算反射向量
          发射二次射线
          合并反射点数据
      }
   4. 返回检测结果
}

并行处理

ParallelFor(ChannelCount, [&](int32 idxChannel) {
   // 每个通道独立进行射线检测
   for(每个扫描点){
      计算垂直/水平角度
      执行ShootLaser
      记录命中点
   }
});

5. 特殊功能

  • 语义信息采集

    Detection.object_tag = HitInfo.Component->CustomDepthStencilValue;
    // 通过UE的CustomDepthStencil获取物体语义标签
  • 多路径反射模拟(EnableGhost):

    • 对高反射率物体(如交通标志)进行二次射线检测
    • 通过反射率数组控制不同材质的反射行为
    • 生成虚拟反射点增强点云真实性

6. 性能优化

  • 并行计算:使用UE的ParallelFor并行处理多个通道

  • 内存预分配

    PointsPerChannel.resize(Description.Channels);
    RecordedHits.reserve(MaxPointsPerChannel);
  • 物理场景锁

    GetPxScene()->lockRead();
    // 物理计算...
    GetPxScene()->unlockRead();

7. ROS2集成

#if WITH_ROS2
ROS2->ProcessDataFromSemanticLidar(...);
// 转换坐标系并发布ROS2消息
#endif

实现与ROS2的数据对接,支持自动驾驶系统集成。

其中的重点函数:

void ARayCastSemanticLidar::SimulateLidar(const float DeltaTime) 是语义激光雷达的核心模拟函数,负责不同扫描模式的点云生成逻辑。以下是对该函数的详细解析:


1. 函数流程图

2. 代码分段解析

  1. 公共预处理
TRACE_CPUPROFILER_EVENT_SCOPE(ARayCastSemanticLidar::SimulateLidar);
if((Description.LidarType == "default") || (Description.LidarType == "Surround")) 
{
   // 机械式扫描逻辑
}
else if (Description.LidarType == "Solid_state")
{
   // 固态扫描逻辑
}
else if (Description.LidarType == "Risley_pris m")
{
   // 棱镜式扫描逻辑
}
  • 关键参数Description 结构体包含雷达配置参数
  • 设计特点:通过雷达类型分发处理逻辑,实现多模式支持

  1. 机械式扫描模式(default/Surround)
// 计算每通道点数
const uint32 PointsToScanWithOneLaser = 
    FMath::RoundHalfFromZero((Description.PointsPerSecond * DeltaTime)/ChannelCount);

// 角度计算
constfloat AngleDistanceOfTick = Description.RotationFrequency * HorizontalFov * DeltaTime;
constfloat AngleDistanceOfLaserMeasure = AngleDistanceOfTick / PointsToScanWithOneLaser;

// 并行射线检测
ParallelFor(ChannelCount, [&](int32 idxChannel) {
   for (auto idxPts = 0u; idxPts < PointsToScanWithOneLaser; idxPts++) {
       constfloat VertAngle = LaserAngles[idxChannel];
       constfloat HorizAngle = ...;
       if (ShootLaser(...)) {
           WritePointAsync(...);
       }
   }
});

// 保存数据
ComputeAndSaveDetections(ActorTransf);
  • 核心算法

    • 旋转速度控制AngleDistanceOfTick 计算每帧水平旋转角度
    • 角度插值AngleDistanceOfLaserMeasure 实现角度细分
    • 并行优化ParallelFor 多线程处理不同通道
  • 典型参数值(示例):

    PointsPerSecond = 1,000,000  // 每秒百万点
    RotationFrequency = 10       // 10Hz旋转
    HorizontalFov = 360          // 水平视场角

  1. 固态扫描模式(Solid_state)
// 环形缓冲区管理
const int Currentidx = SemanticLidarData.GetidxPtsOneLaser();
const int Lastidx = (Currentidx + PointsToScan) % (TotalPoints/ChannelCount);

// 角度获取
float VertAngle = Description.vfov[(idxPts+Currentidx) % TotalPoints];
float HorizAngle= Description.hfov[(idxPts+Currentidx) % TotalPoints];

// 数据持久化
SemanticLidarData.SetidxPtsOneLaser(Lastidx);
  • 核心特点

    • 预定义模式:使用预设的hfovvfov数组定义扫描路径
    • 增量式扫描:通过环形缓冲区实现连续扫描
    • 内存优化:避免重复存储角度数据
  • 扫描模式示例

    hfov = [012, ..., 359]    // 水平角度序列
    vfov = [5,5,5,...,5]          // 固定垂直角度(模拟Flash雷达)

  1. 棱镜式扫描模式(Risley_pris m)
// CSV数据读取
float Azimuth = Description.livox_csv_info[i][1]; 
float Height = Description.livox_csv_info[i][2] - 90;

// 时间戳生成
float l_timestamp = Livox_loop_count * 4.0 + simTime;

// 反射模拟
if(Reflectivity[objtag] > 0.5 && random > 0.5) {
   FVector Exitvector = FMath::GetReflectionVector(...);
   // 二次射线检测
}
  • 关键技术

    • 运动补偿Livox_loop_count 记录扫描周期数
    • 反射建模:通过材质反射率数组控制二次反射
    • 非均匀采样:从CSV读取真实扫描模式数据
  • 典型CSV结构

    Time(s), Azimuth(deg), Elevation(deg)
    0.0, 45.2, 30.1
    0.1, 46.8, 29.8
    ...

  1. 通用处理逻辑
// 物理场景锁定
GetWorld()->GetPhysicsScene()->GetPxScene()->lockRead();
// 并行处理...
GetWorld()->GetPhysicsScene()->GetPxScene()->unlockRead();

// 数据转换
FTransform ActorTransf = GetTransform();
ComputeAndSaveDetections(ActorTransf);
  • 关键机制
    • 物理引擎同步:通过lockRead/unlockRead保证物理查询线程安全
    • 坐标系转换:将命中点转换到传感器本地坐标系
    • 数据打包:将原始命中数据转换为标准点云格式

3. 性能关键点分析

  1. 并行优化

    ParallelFor(ChannelCount, [&](int32 idxChannel) {
       // 通道级并行
    });
    • 利用UE的Task Graph系统
  2. 内存管理

    RecordedHits.reserve(MaxPointsPerChannel); // 预分配内存
    SemanticLidarData.ResetMemory(...);        // 内存复用
    • 避免动态内存分配带来的性能抖动
  3. 射线检测优化

    GetWorld()->ParallelLineTraceSingleByChannel(...); // 批量射线检测
    • 利用PhysX的SIMD优化

4. 扩展性设计

  1. 扫描模式扩展

    else if (Description.LidarType == "Custom") {
       // 实现新的扫描模式
    }
    • 通过新增类型分支支持新扫描方式
  2. 噪声注入

    // 在ShootLaser中添加噪声
    detection.point += FMath::RandRange(-0.1f0.1f); 
    • 可扩展实现雨雾等环境干扰
  3. 自定义材质系统

    Reflectivity[objtag] = GetMaterialReflectivity(HitResult);
    • 通过扩展材质系统实现更真实的反射行为

5. 典型调用时序

// 每帧调用时序
World.Tick() 
   → PostPhysTick() 
      → SimulateLidar() 
         → ShootLaser() x N 
         → ComputeAndSaveDetections()
      → DataStream.SerializeAndSend()
      → ROS2.ProcessData()

该函数通过精细的扫描模式分派、高效的并行计算和灵活的数据处理,实现了多模式语义激光雷达的实时仿真,是Carla传感器系统的核心组件之一。

另一个关键函数:bool ARayCastSemanticLidar::ShootLaser(...)


1. 函数功能概述

这是激光雷达的核心射线投射函数,负责:

  1. 发射单条激光射线
  2. 检测物体碰撞
  3. 处理反射(二次射线)
  4. 记录语义信息
  5. 添加时间戳

2. 函数流程图


3. 关键代码段解析

  1. 基础射线检测
// 计算射线方向
FTransform ActorTransf = GetTransform();
FVector LidarBodyLoc = ActorTransf.GetLocation();
FRotator LidarBodyRot = ActorTransf.Rotator();
FRotator LaserRot(VerticalAngle, HorizontalAngle, 0);
FRotator ResultRot = UKis metMathLibrary::ComposeRotators(LaserRot, LidarBodyRot);
FVector EndTrace = Description.Range * UKis metMathLibrary::GetForwardVector(ResultRot) + LidarBodyLoc;

// 执行射线检测
GetWorld()->ParallelLineTraceSingleByChannel(
    HitInfo,
    LidarBodyLoc,
    EndTrace,
    ECC_GameTraceChannel2, // 自定义碰撞通道
    TraceParams
);
  • 坐标系转换:将激光的本地旋转(VerticalAngle/HorizontalAngle)转换为世界坐标系
  • 性能优化:使用ParallelLineTraceSingleByChannel并行射线检测
  • 碰撞通道ECC_GameTraceChannel2通常用于激光雷达专用检测

  1. 反射处理(幽灵模式)
// 反射率检查
std::vector<float> Reflectivity = {0.0,...,0.80,...};
if(Reflectivity[objtag] > 0.5) {
    // 50%概率触发反射
    float random = std::rand()%(999+1)/(float)(999+1);
    if(random > 0.5) {
        // 计算反射向量
        FVector Exitvector = FMath::GetReflectionVector(UnitPoint, HitResult.ImpactNormal);
        
        // 二次射线检测
        FVector EndTrace1 = ReflectRange * Exitvector + ExitPoint;
        GetWorld()->ParallelLineTraceSingleByChannel(...);
        
        // 修正反射点坐标
        HitPoint = FirstHitPoint + Incidencevector * distance/Incidencedistance;
    }
}
  • 材质反射率表:索引对应UE的CustomDepthStencilValue
  • 随机反射:通过50%概率模拟反射的不确定性
  • 反射点修正:保持反射点沿原射线方向分布(模拟雷达信号传播特性)

  1. 语义信息采集
// 获取语义标签
Detection.object_tag = HitInfo.Component->CustomDepthStencilValue;

// 获取ActorID
const FActorRegistry &Registry = GetEpisode().GetActorRegistry();
const FCarlaActor* view = Registry.FindCarlaActor(HitInfo.Actor.Get());
Detection.object_idx = view ? view->GetActorId() : 0;
  • 数据映射:通过UE的CustomDepthStencilValue实现语义标签与游戏对象的绑定
  • 对象追踪:使用Carla的Actor注册系统维护全局对象ID

4. 关键算法

反射向量计算
  • 使用FMath::GetReflectionVector实现
  • 符合光学反射定律
二次射线范围计算
ReflectRange = (Range - Incidencedistance) * 0.8f;
  • 限制反射射线的最大距离为剩余射程的80%
  • 防止无限反射循环

5. 性能优化策略

优化手段      
实现方式      
性能收益      
并行射线检测      
ParallelLineTraceSingleByChannel
提升3-5倍检测速度      
快速随机数生成      
std::rand()      
简单实现      
减少分支预测开销      
预计算反射率表      
静态数组访问      
避免运行时计算      
短距离二次射线      
80%剩余距离限制      
减少无效检测      

6. 典型参数值

参数      
示例值      
说明      
Description.Range
10000 (单位:cm)      
100米探测范围      
Reflectivity[12]
0.80      
交通标志的高反射率设置      
ReflectRange
800-2000      
根据首次命中距离动态变化      
random      
范围      
0.0-1.0      
均匀分布随机数      

7. 调试与验证

  1. 可视化调试

    // 绘制原始射线(红色)
    DrawDebugLine(GetWorld(), LidarBodyLoc, EndTrace, FColor::Red, false0.1f);

    // 绘制反射射线(绿色)
    DrawDebugLine(GetWorld(), ExitPoint, EndTrace1, FColor::Green, false0.1f);
  2. 验证反射逻辑

    • 在镜面材质表面应出现对称反射点
    • 低反射材质(如沥青)不应产生二次反射
  3. 性能测试

    TRACE_CPUPROFILER_EVENT_SCOPE(ShootLaser);
    // 统计单次调用耗时

8. 扩展性设计

  1. 动态反射率配置

    // 从材质系统获取实时反射率
    float Reflectivity = HitResult.Material->GetReflectivity(); 
  2. 多级反射

    // 递归实现多次反射
    void ProcessReflection(int depth) {
        if(depth > MAX_REFLECT_DEPTH) return;
        // ...
    }
  3. 噪声注入

    // 添加角度噪声
    float NoisyVertAngle = VerticalAngle + FMath::RandRange(-0.1f0.1f);

该函数通过精确的物理模拟和高效的算法实现,为自动驾驶仿真提供了高真实度的激光雷达点云数据,同时保持实时性能。

根据文档how-to-add-a-new-sensor,接下来是编制Sensor data serializer,Sensor data object,在SensorRegistry.h中注册你的新传感器等环节,本文提供的代码在这些部分中基本都是参照Carla原有传感器的代码进行编写的,读者可自行对照文末提供的代码进行分析。

代码验证

本文适配的CarlaUE5和UnrealEngine5_carla的确切版本是:

CarlaUE5:
commit 0bfb05bfdde8435bfecfccdef8a0e05041b22aef (origin/ue5-dev, origin/HEAD)
Author: MarcelPiNacy-CVC <169088301+MarcelPiNacy-CVC@users.noreply.github.com>
Date:   Mon Mar 10 14:24:37 2025 +0100
    Remove MakeFile. (#8739)

UnrealEngine5_carla:
commit eea6a8e677e3509a3e56122d69e3d380bb624bbf (HEAD -> ue5-dev-carla, origin/ue5-dev-carla, origin/HEAD)
Author: Marcel Pi <169088301+MarcelPiNacy-CVC@users.noreply.github.com>
Date:   Fri Feb 28 12:53:51 2025 +0100
    Add missing chrono include.

编译CarlaUE5和UnrealEngine5_carla的方法在官方文档中很清晰了。在CarlaUE的编译脚本中,会下载Unreal\CarlaUnreal\Content目录下的大文件(约70G),本文附上文件Carla_UE5.5.zip的百度云链接:

https://pan.baidu.com/s/10RzEAvGs_Ie6PTZq0jwkEA

提取码: h9fb

读者在编译前可提前下载,并放置在对应位置。

首次编译成功,可以运行Carla后,如果对UE和Carla的代码进行了修改,需要对UnrealEngine5_carla和CarlaUE5分别再次进行编译:

# UE5:
./Setup.sh
./GenerateProjectFiles.sh
make

# CarlaUE5:
./CarlaSetup.sh --interactive --python-root=/home/vipuser/.conda/envs/carla/bin

最新版本的CarlaUE5可以通过--python-root参数指定需要的python虚拟环境。CarlaSetup.sh中的参数launch修改为1可以在运行脚本后打开UE窗口。

在合入本文代码后,还需要修改Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Sensor/Risley_pris m.cpp代码中的filename,指向您本地的RisleyPris mCsv文件夹。

点击UE窗口中的Run后,运行PythonAPI\lidar_lib_example\lidar_display.py,会在窗口中显示激光雷达效果。

关于lidar_display.py的一些说明:

在脚本中切换ego_horizon,ego_rs和ego_pandar64可以使用不同类型的激光雷达进行仿真。使用LidarMotonDistortion("./11_horizon/", 10)功能,会将加入了motion distortion的点云保存成ply文件,效果如下图:

最后附上本文代码:Lidar_CarlaUE5.zip,链接: 

https://pan.baidu.com/s/1tX9OEZrC_gIeJiFSc9akwQ 

提取码: cxgv

如果您需要适配UE 4.26版本的激光雷达代码,还可以参考以下链接carla中的PCSim branch:

https://github.com/GimpelZhang/carla/tree/PCSim

本文完。


来源:车路漫漫

附件

免费链接.txt
ACTMarcTosca碰撞光学通用UGpython自动驾驶MEMS游戏控制
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2025-12-10
最近编辑:8月前
李慢慢
硕士 自动驾驶仿真工程师一枚
获赞 14粉丝 93文章 320课程 0
点赞
收藏
作者推荐

上海车展高光时刻-仿真测试助力解锁智能底盘新未来

正文共: 1024字 7图预计阅读时间: 3分钟封面来源:上海车展现场近期,浙江天行健智能科技有限公司携手比博斯特汽车电子有限公司、杭州沃镭智能科技股份有限公司强势亮相上海车展,共同为国内外参展嘉宾带来智能底盘控制的“最强大脑”——“羲和”智能底盘融合系统!重构智能底盘,引领行业新风尚“羲和”是对智能底盘制动、转向、悬架等产品从产品定义、系统架构、开发流程到匹配测试的全面重构。它打破传统技术桎梏,深度整合前沿科技,以更高效、更智能、更安全的姿态,为汽车底盘的发展开辟全新道路。 而在“羲和”系统背后,是浙江天行健核心产品的强力支撑!汽车智能驾驶一体化仿真测试平台 PanoSim可搭建各类复杂驾驶场景,实现对智能驾驶系统的全链路测试;高精度车辆动力学仿真软件 PanoCar以高精度的动力学模型,精准还原车辆动态特性。两款软件凭借深厚的技术积累与创新能力,共同赋能“羲和”系统。 冬季测试训练场,还原极端场景 此次车展,通过PanoSim搭建冬季测试训练场,模拟冰雪道路的极端路况,包含了冰雪圆环,雪面路双变道,冰雪面抗俯仰,蛇形绕桩,棋盘格路面,冰雪坡道起步等各种典型测试场景,展现了软件强大且丰富的场景搭建能力。无论是湿滑冰面的制动测试,还是极致空间下的的四轮转向,都能精准反馈,为各类极端路况测试提供更真实的仿真效果。专业台架测试,精准打磨细节 专业的台架测试是本次展会的另一亮点。依托高精度动力学模型和台架系统,全方面支持对双移线、对开路面、坡道起步、极限出库工况、原地调头、蟹行驾驶、高速增稳等多个功能进行测试,大幅提升开发测试效率、测试安全性与测试准确度。多视角多维度,可视化信息一目了然 丰富的可视化信息展示,让科技不再“高冷”。通过多视角、多维度的数据展现,用户可以从各方面直观地观察车辆的运行状态。无论是工程师进行技术分析,还是普通观众了解产品原理,都能轻松看懂,真正实现科技的透明化与亲民化。 灵活接口,激发无限可能 标准化接口,有利于大幅降低综合成本,更可以快速响应客户的各类需求,灵活支持功能上移;进一步支持与驱动域、座舱域、智驾域的跨域融合。 上海车展的惊艳亮相,只是浙江天行健智能科技探索智能底盘领域的一个新成果。未来,我们将继续深耕技术创新,不断优化产品性能,携手更多合作伙伴,为全球用户带来更智能、更高效的产品体验! 以上,本文完。来源:车路漫漫

未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习计划 福利任务
下载APP
联系我们
帮助与反馈