首页/文章/ 详情

仿真测试入门参考(19):CARLA的车辆模型

11月前浏览5434

 经常有朋友问如何学习仿真测试,于是想着把自己的一些经验和理解分享出来,希望能有所帮助。不过视野和技术有限,所说不一定对,供大家批评和参考。这是第19篇,CARLA的车辆模型。

国庆前的一篇介绍了CARLA的基本概念,然后就是逐渐对CARLA中的各个部分进行介绍了。终于在国庆期间赶完了这篇,可能是全网最详细的CARLA车辆模型的说明,供大家参考。

不过还是有些地方没有搞明白,欢迎大家指教,不胜感激!

比如carla.WheelPhysicsControl中的tire_friction是什么作用?如何对carla.VehiclePhysicsControl中未列出的其他参数进行设置?如何获取SlipAngle/suspForce/normTireLatForce等内部参数?

这些信息可以通过Vehicle的show_debug_telemetry方法显示在渲染窗口,如下图所示:

下面是正文:

1.车辆模型

CARLA中的车辆模型包括3D模型和动力学模型两个方面。3D模型用于实现车辆的可视化渲染,可以在3D模型开发软件中设计、开发,并导入到虚幻引擎UE中使用。动力学模型用于实现车辆逼真的动态效果,CARLA中的车辆采用中的虚幻引擎UE中的AWheeledVehicle模型(核心是NVIDIA的PhysX模型)。下面对PhysX模型进行简要的说明。

参考了这些资料:

(1)Nvidia Physx文档:

https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/Manual/Vehicles.html#pxvehiclewheeldata

(2)Unreal Engine文档:

https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/Vehicles/VehicleUserGuide/

(3)carla文档:

https://carla.readthedocs.io/en/latest/core_actors/

PhysX中考虑了不同的车轮数量,但是为了说明的简单,本节将车辆简化为两轮模型,将车体(簧上质量)简化为前轴质量和后轴质量,通过并通过悬架与车轮(簧下质量)相连,如下图所示。

在静止状态下,由力平衡和力矩平衡可由下式得到:

式中M、Mf和Mr分别为总质量、前轴质量和后轴质量,Xcm、Xr和Xf分别为质心与几何中心的水平距离、前轴与几何中心的距离和后轴与几何中心的水平距离。

仿真运行进行更新时,首先采用光线投射方法(raycast),进行悬架位置的计算,如下图。将车轮与地面的接触位置简化为一个轮胎触点,光线投射从车轮最高点开始,并沿着悬架舒张方向向下投射到车轮最低点为止,计算出车辆与地面的接触点以及车轮的位置,进而计算悬架的压缩量和弹簧力,并结合驱动力和行驶阻力计算簧上质量和簧下质量所有受到的外力。然后便可建立车辆各部件的方程,对各种状态进行求解。  

2.车辆的添加和使用

车辆Vehicle作为交通参与者Actor的重要类型,其使用方式也按照交通参与者Actor的三个步骤,即:选取蓝图并生成(spawning)、操作(handling)、销毁(destruction)。

(1)选取蓝图并生成(spawning)

生成交通参与者使用World的try_spawn_actor(blueprint, spwan_point)方法,该方法需要指定准备生成的交通参与者的蓝图blueprint以及生成的位置spwan_point。

代码的第16行,从蓝图库中获取了vehicle.tesla.model3作为我们准备生成的车型。代码的第19~33行通过全局变量SPAWN_POINT可以配置两种不同的生成位置获取方法:

①SPAWN_POINT为“spectator”时,在spectator当前的位置生成。观察者spectator是一个特殊的actor,用于调整CARLA渲染窗口的视角,该视角可以通过键盘上的W/A/S/D和鼠标拖动进行调整,也可以设置其位置和朝向,代码的第58~64行,设置观察者spectator跟随ego车辆的位置和朝向;

②SPAWN_POINT为“random”时,在预先定义的生成点中随机选择。为方便交通参与者的生成,CARLA的地图中提供了很多预先定义的生成点,这些生成点在地图上的所有范围都有分布,可通过代码第28~31行进行可视化,效果下图所示:

(2)操作(handling)

操作包含仿真运行期间所有对交通参与者的处理,如参数设置、状态获取和控制等。车辆Vehicle的属性和方法既包括交通参与者Actors公有的内容,也包含车辆Vehicle特有的内容。

车辆模型的参数可参照第42~50行获取和设置。bounding_box是车辆的外接长方体,其bounding_box.extent属性中的x/y/z分别是几何中心距离三个面的距离,其实也就是长宽高的一半。get_physics_control()可获取车辆模型的物理参数,具体如下表所示:

CARLA中旋转部件(如发动机、车轮)在只受阻尼力矩时的,新角速度的计算公式为:

其中w_new和w_old分别为当前帧的新角速度与上一帧的旧角速度,u_damp为阻尼系数,M为转动惯量,dt为仿真步长。对于发动机,其阻尼系数根据油门开度的不同进行线性插值得到,如下式所示:

其中,u_damp为插值后的阻尼系数,离合器结合时u_zero为零油门开度离合器结合的阻尼系数damping_rate_zero_throttle_clutch_engaged,离合器分离时u_zero为零油门开度离合器分离的阻尼系数damping_rate_zero_throttle_clutch_disengaged,u_full为最大油门开度的阻尼系数damping_rate_full_throttle,a为油门开度。

轮胎的纵向力可由下式计算得到:

其中,K_X为纵向刚度long_stiff_value,s_X为弧度表示的纵向滑移角度,g为当前的重力加速度。

轮胎的侧向力可由下式计算得到:

其中,当轮胎的实际负载F_Z不小于饱和负载K_N*F_N时,K_Y为侧向刚度系数lat_stiff_value,当轮胎的实际负载小于饱和负载时,K_Y由平滑的插值得到(暂时没搞明白是怎么插值的)。一个插值的示例如下图所示,该示例中饱和负载时,K_N=3,K_Y=18。s_Y为弧度表示的侧向滑移角度,K_N为饱和负载系数lat_stiff_max_load,F_N为车辆完全静止时的车轮负载。

CARLA提供了不同的方法对车辆进行控制,如:

①采用Actor的方法进行控制,如通过set_target_velocity方法设置目标速度、通过set_transform方法设置位姿;

②采用Vehicle的apply_ackermann_control方法设置目标转向角、速度和加速度等;

③采用Vehicle的apply_control方法设置油门、制动和转向比例;

④设置车辆为自动驾驶模式,由Traffic Manager模块对车辆进行控制。

这四种方法在代码的66~83行进行了示例,并可通过全局变量CONTROL_MODE取值进行设置。

仿真运行中可获取车辆运行状态用于计算、显示等后续处理,代码85~91行对车辆的位姿、速度和加速度进行了获取和打印。

(3)销毁(destruction)

运行帧数大于10000后,跳出53行的while循环,在94行对车辆进行了销毁处理。

需要补充说明的是,本示例中采用了CARLA默认的异步变步长模式,仿真运行由CARLA的服务端Server自行触发,这样的话在客户端获取信息或者进行设置时需要等待触发才能生效,这就是代码中world.wait_for_tick()的用处所在。

代码如下:

    import carlaimport random#设置ego生成位置的选择模式,"spectator" OR "random"SPAWN_POINT = "random"#设置ego的控制模式,"set_transform" OR "ackermann_control" OR "control" OR "autopilot"CONTROL_MODE = "autopilot"def main():    #创建client,并获取world    client = carla.Client("localhost", 2000)    world = client.get_world()    #设置ego的车型    ego_bp = world.get_blueprint_library().find('vehicle.tesla.model3')    ego_bp.set_attribute('role_name','ego')    if SPAWN_POINT == "spectator":        #选择当前spectator位置为ego生成位置        spectator = world.get_spectator()        spectator_tf = spectator.get_transform()        spawn_point = spectator_tf    elif SPAWN_POINT == "random":        #随机选择预定义的生成点为ego生成位置        spawn_points = world.get_map().get_spawn_points()                # #生成点的可视化        # for i, spawn_point in enumerate(spawn_points):        #     world.debug.draw_string(spawn_point.location, str(i), life_time=100)        #     world.debug.draw_arrow(spawn_point.location, spawn_point.location + spawn_point.get_forward_vector(), life_time=100)        spawn_point = random.choice(spawn_points)        #生成ego车    ego_vehicle = world.try_spawn_actor(ego_bp, spawn_point)    snap = world.wait_for_tick()    init_frame = snap.frame    run_frame = 0    print(f"spawn ego vehicle at: {spawn_point}")    #获取和设置车辆属性    bbx = ego_vehicle.bounding_box    physics = ego_vehicle.get_physics_control()    print(f"bounding_box = {bbx}")    print(f"physics = {physics}")    physics.mass = 2000    ego_vehicle.apply_physics_control(physics)    ego_vehicle.set_light_state(carla.VehicleLightState.All)    #采用默认的异步变步长模式    while run_frame < 10000:        snap = world.wait_for_tick()        run_frame = snap.frame - init_frame        print(f"-- run_frame = {run_frame}")        #设置spectator跟随ego车        spectator = world.get_spectator()        ego_tf = ego_vehicle.get_transform()        spectator_tf = carla.Transform(ego_tf.location, ego_tf.rotation)        spectator_tf.location += ego_tf.get_forward_vector() * (-10)        spectator_tf.location += ego_tf.get_up_vector() * 3        spectator.set_transform(spectator_tf)        #控制车辆        if CONTROL_MODE == "set_transform":            new_ego_tf = carla.Transform(ego_tf.location, ego_tf.rotation)            new_ego_tf.location += ego_tf.get_forward_vector() * 0.1            ego_vehicle.set_transform(new_ego_tf)        elif CONTROL_MODE == "ackermann_control":            #ackermann_control            ackermann_control = carla.VehicleAckermannControl()            ackermann_control.speed = 5.0            ego_vehicle.apply_ackermann_control(ackermann_control)        elif CONTROL_MODE == "control":            #control            control = carla.VehicleControl()            control.throttle = 0.3            ego_vehicle.apply_control(control)        else:            #为ego车设置自动驾驶            ego_vehicle.set_autopilot(True)                #获取车辆状态        transform = ego_vehicle.get_transform()        velocity = ego_vehicle.get_velocity()        acceleration = ego_vehicle.get_acceleration()        print(f"-current transform = {transform}")        print(f"-current velocity = {velocity}")        print(f"-current acceleration = {acceleration}")    #销毁车辆    is_destroyed = ego_vehicle.destroy()    if is_destroyed:        print(f"ego_vehicle has been destroyed sucessfully")if __name__ == "__main__":    main()
    来源:孙工自动驾驶

    附件

    免费链接.txt
    自动驾驶控制渲染
    著作权归作者所有,欢迎分享,未经许可,不得转载
    首次发布时间:2023-10-08
    最近编辑:11月前
    孙工自动驾驶
    硕士 专注自动驾驶仿真测试
    获赞 16粉丝 22文章 78课程 0
    点赞
    收藏
    未登录
    还没有评论
    课程
    培训
    服务
    行家
    VIP会员 学习 福利任务 兑换礼品
    下载APP
    联系我们
    帮助与反馈