点击下方卡片,关注“自动驾驶之心”公众号
今天自动驾驶之心邀请来了仿真专家张峻川!浅析自动驾驶端到端模型仿真测试。如果您有相关工作需要分享,请在文末联系我们!
自动驾驶课程学习与技术交流群事宜,也欢迎添加小助理微信AIDriver004做进一步咨询
>>点击进入→自动驾驶之心『仿真测试』技术交流群
作者 | 张峻川
编辑 | 自动驾驶之心
前言
“浅析”究竟有多浅——本文将围绕三个自动驾驶端到端模型闭环仿真测试项目的例子,分析不同的端到端模型仿真框架。主要分析的内容是:仿真系统产生的传感器数据是如何传递给端到端模型的,模型输出的控制命令又是如何反馈给仿真系统的。
本文的三个例子正好覆盖了基于游戏引擎的仿真系统、基于3DGS三维重建的仿真系统和基于生成式模型的仿真系统。
Bench2Drive
Website: https://thinklab-sjtu.github.io/Bench2Drive/
Github: https://github.com/Thinklab-SJTU/Bench2Drive/
Bench2Drive项目的详细介绍,可以参考自动驾驶之心的文章:上交ReThinkLab新作 | Bench2Drive:首个端到端自动驾驶综合能力闭环评估开放平台!
简言之,Bench2Drive是一个以开源自动驾驶仿真软件CARLA为基础的测试框架,而且重新实现了UniAD,VAD等方法的代码。我们将以此项目的代码为例分析CARLA的Leaderboard,一个常用的端到端仿真测试框架。
Bench2Drive闭环仿真程序总入口:Bench2Drive/leaderboard/leaderboard/leaderboard_evaluator.py:main(),其中主要创建了一个LeaderboardEvaluator的类的实例并调用了其run()方法。
LeaderboardEvaluator类主要职责包括管理CARLA服务器生命周期、加载路线配置和城镇环境、动态加载智能体模块、验证传感器配置有效性等。
核心组件:
_setup_simulation()
: Bootstraps CARLA server/client with retry logic_load_and_run_scenario()
: Core scenario loop with error handlingrun()
: Main execution flow with route iteration_cleanup()
: Ensures proper resource teardown
在创建LeaderboardEvaluator类实例的时候,在该类的构造函数中:
self.module_agent将从leaderboard/team_code/下获取被测端到端模型的python module,例如来自Bench2DriveZoo仓库的uniad_b2d_agent.py
会创建一个ScenarioManager类的实例self.manager。
接下来,在LeaderboardEvaluator的run()方法会调用def _load_and_run_scenario()
,在该方法中:
self.agent_instance会创建一个UniAD的UniadAgent类的实例,UniadAgent类继承自leaderboard/leaderboard/autoagents/autonomous_agent.py中的AutonomousAgent类。AutonomousAgent类在初始化时会给自己创建一个来自leaderboard/leaderboard/envs/sensor_interface.py的SensorInterface()的实例:
self.sensor_interface = SensorInterface()
self.manager.load_scenario(self.route_scenario, self.agent_instance, config.index, config.repetition_index)
进入ScenarioManager类的load_scenario()方法
在self.manager.load_scenario()方法中:
self._agent_wrapper = AgentWrapperFactory.get_wrapper(agent)
给agent(即上一步的agent_instance)实例选择一个AgentWrapper,对于本文中的UniAD,这将会是实例化一个leaderboard/leaderboard/autoagents/agent_wrapper.py的AgentWrapper类。self._agent_wrapper.setup_sensors(self.ego_vehicles[0])
执行AgentWrapper类的setup_sensors方法,在carla中的主车上附加上仿真所用的传感器。
在setup_sensors()方法的代码内部,有使用CARLA仿真经验的人就会发现一个熟悉的语句:sensor.listen(CallBack(id_, type_, sensor, self._agent.sensor_interface))
,这里的self._agent
就是前文的agent_instance。
其中,CallBack类也来自于leaderboard/leaderboard/envs/sensor_interface.py,该类的__call__方法会将CARLA仿真的传感器数据存入agent的sensor_interface中的data_buffers的Queue()队列中。因此,在仿真运行过程中,每一步的传感器数据就会通过这个callback存入UniadAgent类的实例的sensor_interface中。
我们再回到def _load_and_run_scenario()
方法,接下来会进入self.manager.run_scenario()
,run_scenario()的核心是ScenarioManager类的def _tick_scenario(self)
方法: 其中的ego_action = self._agent_wrapper()
,AgentWrapper的__call__方法会直接返回UniadAgent类的实例,调用该类的__call__方法。
在UniadAgent类的父类AutonomousAgent类的__call__方法中,首先就获取了该步仿真下的传感器数据:
input_data = self.sensor_interface.get_data(GameTime.get_frame())
get_data会把数据从sensor_interface的data_buffers中取出。
control = self.run_step(input_data, timestamp)
UniadAgent类覆写的run_setup会被调用。就像Leaderboard官方文档给出的agent编写建议creating-your-own-autonomous-agent写的那样,这个函数的输入input_data就包含了一个端到端模型所需要的传感器数据输入。
在UniadAgent类实例的run_setup中,传感器输入经过处理,最终会输入给UniAD模型进行推理:output_data_batch = self.model(input_data_batch, return_loss=False, rescale=True)
,而推理结果经过一些列的PID调制等步骤,最终会作为control输出。
我们再回到def _tick_scenario(self)
方法,上文提到的ego_action = self._agent_wrapper()
,ego_action承接了UniAD模型输出的control,并接下来通过self.ego_vehicles[0].apply_control(ego_action)
控制仿真中的主车。
至此,我们通过Bench2Drive项目简要分析了CARLA Leaderboard在端到端模型闭环仿真中的输入输出形式。
HUGSIM
Github: https://github.com/hyzhou404/HUGSIM/
HUGSIM项目的详细介绍,可以参考自动驾驶之心的文章:真实闭环拉满!浙大&华为发布全新闭环仿真工具HUGSIM
简言之,HUGSIM不仅研究了一种新的基于3DGS的自动驾驶场景三维重建方法,而且在该方法的基础上开发了一个可闭环测试端到端自动驾驶模型的仿真系统。
HUGSIM使用Gymnasium作为框架,以完成仿真运行。Gymnasium是一个强化学习环境库,它提供了一套标准化的接口来构建和测试强化学习算法。以下是一些基本用法,重点介绍 gymnasium.make()
, gymnasium.reset()
, gymnasium.step()
等方法的使用:
1. 创建环境 gymnasium.make()
gymnasium.make()
函数用于创建或加载一个环境。你需要传入环境的ID,例如 "CartPole-v1"。
2. 重置环境 gymnasium.reset()
gymnasium.reset()
方法用于重置环境到初始状态。在每次新的episode开始时,都需要调用这个方法。它返回环境的初始观察值。
3. 与环境交互 gymnasium.step()
gymnasium.step(action)
方法用于执行一个动作,并返回环境的反馈。具体返回值包括:
observation
:环境的新状态。reward
:执行动作后获得的即时奖励。terminated
:布尔值,表示环境是否终止(例如,杆倒下或达到最大步数)。truncated
:布尔值,表示环境是否被截断(例如,达到最大步数)。info
:额外的信息,如调试信息等。
在HUGSIM中,仿真环境和被测对象是两个单独的进程,进程间数据的交换采用的是FIFO管道的形式。FIFO管道是一种特殊的文件,允许不相关的进程通过文件系统进行通信。FIFO管道在文件系统中有一个路径名,任何有权限的进程都可以打开它进行读写。
HUGSIM闭环仿真程序总入口:HUGSIM/closed_loop.py:main()。下面我们仍以UniAD为例。
首先,process = launch(ad_path, args.ad_cuda, output)
在另一进程中启动了UniAD_SIM/tools/e2e.sh脚本,通过UniAD_SIM/tools/closeloop/e2e.py运行起了UniAD模型。
然后进入create_gym_env函数中:
env = gymnasium.make('hugsim_env/HUGSim-v0', cfg=cfg, output=output)
创建仿真环境,进入逐步仿真过程:pipe.write(pickle.dumps((obs, info)))
将三维重建的渲染结果等信息obs写入obs_pipe,这将作为传感器信息给到UniAD。plan_traj = pickle.loads(pipe.read())
从plan_pipe中读取UniAD输出的规划路径。acc, steer_rate = traj2control(plan_traj, info)
将规划的路径转化成控制命令。obs, reward, terminated, truncated, info = env.step(action)
通过gymnasium框架的step方法,将控制命令给到仿真车辆,运行仿真环境向前计算一步,获得新的obs。重复2-5步骤
HUGSIM仿真环境hugsim_env代码位于sim/hugsim_env/envs/hug_sim.py中,核心是HUGSimEnv类。
在HUGSimEnv类的step方法中,控制命令action改变ego车动作self.ego,在step中会进行测评指标判断以决定仿真是否中止,然后进行到observation = self._get_obs()
:
在self._get_obs()
中:
c2w = self.ego @ c2front
viewpoint = create_cam(intrinsic, c2w)
render_pkg = self.render_fn(viewpoint=viewpoint, prev_viewpoint=None, **self.render_kwargs)
新的self.ego会通过render_fn产生新的渲染结果。
下面来看UniAD_SIM/tools/closeloop/e2e.py中对于UniAD模型的调用,关键步骤有:
raw_data = pipe.read()
从obs_pipe读取传感器数据data = parse_raw(raw_data, cameras, img_norm_cfg, history)
对传感器数据做预处理results = model(return_loss=False, rescale=True, **data)
调用UniAD模型进行推理pipe.write(pickle.dumps(plan_traj))
将后处理过的推理结果写入plan_pip
至此,我们通过简要分析了HUGSIM工程在端到端模型闭环仿真中的输入输出形式。
DriveArena
Website: https://pjlab-adg.github.io/DriveArena/
Github: https://github.com/PJLab-ADG/DriveArena/
DriveArena项目的详细介绍,可以参考自动驾驶之心的文章:首个为高保真度闭环模拟设计!DriveArena:自动驾驶闭环生成仿真平台(上海AI Lab等)
DriveArena项目中的闭环仿真系统,主要由World Dreamer,Traffic Manager和被测的Agent组成。其中World Dreamer通过扩散模型和上游输入,提供新的图像信息,作为传感器信息输入给Agent。Traffic Manager接受Agent输出的规划轨迹,生成交通流信息,提供给World Dreamer。
工程中三者间的信息交互是通过FastAPI实现调度的。FastAPI是一个现代、快速(高性能)的Web框架,用于构建API,FastAPI的性能与Node.js和Go相当,是目前最快的Python Web框架之一,基于ASGI(异步服务器网关接口),支持异步编程。
简言之,DriveArena项目中的World Dreamer, Traffic Manager和Agent是通过Web网络通讯实现信息交互的。
Traffic Manager
Traffic Manager启动的入口在TrafficManager/sim_manager.py:main(),创建了一个SimulationManager类的实例并执行了run_simulation()方法。
下面进入run_simulation()的核心:self.process_frame()。在process_frame()中:
limsim_trajectories = self.planner.plan(self.model.timeStep * 0.1, self.roadgraph, self.vehicles)
是根据路网和车辆信息来进行交通流仿真,生成的轨迹准备传输给World Dreamer作为图像生成的依据。diffusion_data = limsim2diffusion(...)
将轨迹转换成扩散模型的输入格式gen_images = self.send_request_diffusion(diffusion_data)
在send_request_diffusion函数中,向"dreamer-api/"发送网络请求response = requests.post(self.DIFFUSION_SERVER + "dreamer-api/", json=serialized_data)
,扩散模型的输入包含在了serialized_data中,response则包含了生成的图像response = requests.get(self.DRIVER_SERVER + "driver-get/")
向"driver-get/"发送网络请求,response中获取Agent规划输出的ego车轨迹(生成的图像向Agent发送的过程在World Dreamer部分将有介绍)limsim_trajectories[self.EGO_ID] = ego_traj
闭环仿真中,代表Agent的规划出的ego车轨迹参与到下一轮的交通流仿真中。
World Dreamer
World Dreamer启动的入口在WorldDreamer/tools/dreamer_fast_api.py:main()。
在主函数中,主要进行了以下操作:
image_queue = Queue()
定义了一个队列用来存放渲染的图像pipe, weight_dtype= load_model(cfg)
加载了StableDiffusion模型为后续生成图像做准备uvicorn.run(app, host=args.host, port=args.port, log_level="info")
uvicorn是一个ASGI(Asynchronous Server Gateway Interface)服务器,用于运行异步Python Web应用,这行代码的作用是启动一个ASGI服务器,并将FastAPI应用 (app) 部署到指定的主机和端口上。
而在本py文件的app中,主要定义了以下功能:
@app.post("/dreamer-api/")
async def process(request: Request, background_tasks: BackgroundTasks)
在FastAPI中,process函数是由框架自动调用的,@app.post("/dreamer-api/")装饰器会在FastAPI应用启动时自动将URL路径/dreamer-api/与POST请求方法绑定到process函数,这个注册过程发生在应用初始化阶段,无需开发者显式调用。当Traffic Manager发送POST请求到/dreamer-api/端点时,该函数会自动调用。
在process()中,request传入了Traffic Manager送来的交通流信息,在此:
return_tuples = run_one_batch(cfg, pipe, val_input, weight_dtype,
transparent_bg=transparent_bg,
map_size=target_map_size)
调用了扩散模型生成图像,并在接下来image_queue.put({'param': param, 'img': gen_imgs_list[0]})
将图像存入image_queue队列。
background_tasks.add_task(send2agent)
向Agent传输生成的图像被放入了一个后台任务。
在async def send2agent()
中,图像从image_queue中取出:
response = requests.post("http://localhost:{}/driver-api".format(args.agent_port), json=send_data)
向"driver-api/"发送网络请求,执行端到端的自动驾驶规划。生成的图像都在send_data里。
Agent
我们再次以UniAD为例,Agent的启动入口在:DrivingAgents/UniAD/demo/uniad_fast_api.py:main(),main函数的前部都在做加载UniAD模型的工作,最后的:
uvicorn.run(app, host=args.host, port=args.port, log_level="info")
与前述类似。
@app.post("/driver-api/")
async def process(request: Request, background_tasks: BackgroundTasks):
在process()中,request传入了来自World Dreamer的生成图像等信息,作为端到端模型的输入,在此:
outputs = custom_single_gpu_test(model, dataset, args.tmpdir, args.gpu_collect, eval_occ=False, eval_planning=False)
调用了UniAD模型进行推理,在outputs中输出了规划出的ego轨迹。
@app.get("/driver-get/")
async def get_output()
当Traffic Manager向"driver-get/"发送网络请求时,get_output()会将outputs中的ego轨迹返回。
至此,我们通过简要分析了DriveArena工程在端到端模型闭环仿真中的输入输出形式。
结语
自动驾驶端到端模型的研究是时下十分前沿热门的内容,因此模型在环闭环仿真框架也是百花齐放:使用worldsim软件如CARLA的就沿用成熟的Leaderboard框架,使用新兴方法如3DGS,Diffusion的就视项目情况而定选择一种方便的实现形式。在这种端到端“模型在环”的不考虑硬件实现和仿真实时性的前提下,这些框架都已经能够成功地供研究者展开闭环测试。当然,在端到端模型落地成为可部署的软件形式或域控制器硬件形式时,其闭环仿真系统的输入输出接口形式应该就和目前差异不大了。
① 自动驾驶论文辅导来啦
② 国内首个自动驾驶学习社区
『自动驾驶之心知识星球』近4000人的交流社区,已得到大多数自动驾驶公司的认可!涉及30+自动驾驶技术栈学习路线,从0到一带你入门自动驾驶感知(端到端自动驾驶、世界模型、仿真闭环、2D/3D检测、语义分割、车道线、BEV感知、Occupancy、多传感器融合、多传感器标定、目标跟踪)、自动驾驶定位建图(SLAM、高精地图、局部在线地图)、自动驾驶规划控制/轨迹预测等领域技术方案、大模型,更有行业动态和岗位发布!欢迎扫描加入

③全网独家视频课程
端到端自动驾驶、仿真测试、自动驾驶C++、BEV感知、BEV模型部署、BEV目标跟踪、毫米波雷达视觉融合、多传感器标定、多传感器融合、多模态3D目标检测、车道线检测、轨迹预测、在线高精地图、世界模型、点云3D目标检测、目标跟踪、Occupancy、CUDA与TensorRT模型部署、大模型与自动驾驶、NeRF、语义分割、自动驾驶仿真、传感器部署、决策规划、轨迹预测等多个方向学习视频(扫码即可学习)
④【自动驾驶之心】全平台矩阵