首页/文章/ 详情

深入探索Carla-SUMO联合仿真

8月前浏览513

一、介绍

1.1 CARLA

CARLA是由西班牙巴塞罗那自治大学计算机视觉中心指导开发的开源仿真模拟器,目前被广泛用于自动驾驶系统的开发、训练与验证。模块化和灵活的API,可以用于解决涉及自动驾驶问题的一系列任务。CARLA 的主要目标之一是为自动驾驶研发提供易用和可定制化的工具。可以满足自动驾驶中学习驾驶策略、训练感知算法等各方面需求。

CARLA基于Unreal Engine虚幻引擎来运行仿真,并使用OpenDRIVE (目前为1.4)高精地图来定义道路和城市设置,支持高渲染的三维模型。可以通过 Python和C++ 的API 来控制和自定义仿真,API的功能也在不断丰富和完善。

CARLA 模拟器是可伸缩的客户机-服务器架构。服务器端负责与仿真本身相关的所有任务: 传感器渲染、物理计算、世界状态及角色的更新等。为了更接近真实世界,推荐使用搭载 GPU 的服务器来运行,尤其是机器学习相关任务。客户端由场景中的角色控制逻辑和模拟世界的设置模块组成,通过API实现。

1.2 SUMO

SUMO是一款由德国航空航天中心交通运输研究所开发的开源的、多模式微观交通仿真软件。SUMO提供了具备一个完善的 Python 接口:Traffic Control Interface(TraCI),通过与仿真进程通讯,TraCI 可以获取仿真 主体的所有信息并在线调控,以控制仿真内个体的行为(包括机动车、路侧设施等) 。TraCI 接口提供了良好的二次开发基础与条件,可以灵活地与现有人工智能相关的 Python 库结合,为跨平台的联合仿真提供基础接口。这也是与Carla联合仿真的基础。

1.3 为什么需要联合仿真?

在目前的自动驾驶仿真测试领域中主要聚焦关键场景生成的测试方法,该方法用于针对性地构建容易诱发自动驾驶车辆发生事故的场景,并评估自动驾驶车辆在这些边角案例中的表现,从而对自动驾驶车辆进行测试。在仿真交通流环境中对自动驾驶车辆进行测试有不少优点,在仿真中可以全面构建各种与现实世界的驾驶条件非常相似交通环境。此类测试方法可以有效评估自动驾驶汽车在不同真实交通场景下的性能。

CARLA与SUMO在仿真结构上,均采用客户端-服务器的框架,这也为两者的联合仿真创造了条件。其中,服务器负责展示仿真界面,而客户端与服务器的连接,TCP/IP端口获取仿真内的信息,通过不同的端口,实现多客户端同步运行。通过构建桥接器,将SUMO和CARLA中不同格式的信息进行匹配,包括车辆类型、车辆位置信息、基础设施信息等。基于该桥接器,SUMO生成的背景车流可以在CARLA的环境中产生实时同步的映射车辆,而CARLA中接入的自动驾驶等控制算法 会对此产生反馈,也同样会在二维场景中进行同步。


二、安装

本节主要讲解安装Carla和SUMO。

孙工的视频详细讲解了Ubuntu系统上Carla的安装,习惯用linux的小伙伴们有福了。唯一提醒的地方是ubuntu20在升级pip时,加入环境变量时记得带上bin目录,否则pip -V还是20版本。

我这里就简单说说Windows平台(以Win10为例)的安装,并不涉及源码编译,使用官方编译好的压缩包CARLA_0.9.14.zip,解压后第一级目录只有一个WindowsNoEditor文件夹。进入下一级目录,如下图。

可以看到令人激动的CarlaUE4.exe服务端啦,点击就能运行(系统没有DirectX组件则需要先去下载安装好),效果如图。后续主要是安装Python的carla包和依赖。

说明一下,本例使用Python3.8(在另一台机器测试Python3.7也是可以的,其余版本未测试,不行的话建议创建虚拟环境)。

2.1、安装Python carla包

使用pip install carla安装,最好加上国内的源,速度超快,否则就难说了,我这里和后续的包安装都使用清华源,加上-i https://pypi.tuna.tsinghua.edu.cn/simple,下图是安装示例。

2.2、安装依赖

2.2.1 PythonAPI

进入到PythonAPI/util目录,安装requirements.txt中的依赖,执行

pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

很快就装完,过程如下图。

2.2.2 联合仿真依赖

要想进行Carla-SUMO联合仿真,还需要进入到Co-Simulation/Sumo安装requirements.txt中的依赖(虽然只有一个处理XML的包,因为SUMO是的输入基本都是XML文件),安装命令同上,确认当前目录是Sumo即可,过程如下图。

到此,Carla的开发环境就准备好了,接下来就是安装SUMO。

2.3、安装SUMO

在官网下载安装包,地址如下:

https://sumo.dlr.de/docs/Downloads.php

当前Windows平台安装包为sumo-win64-1.18.0.msi,可以根据引导完成安装,过程中需要注意自动把设置SUMO_HOME、PYTHONPATH环境变量选项勾上(省去后面手动配置)。

安装完SUMO后,测试环境变量是否正常,在cmd输入sumo-gui,如果能自动弹出SUMO界面,如下图,就说明SUMO安装成功、环境变量配置正确。


三、CARLA-SUMO联合仿真

3.1 案例文件

官方的案例库只提供了三个场景Town01, Town04, Town05,如下三图,用sumo-gui打开可以配置文件.sumocfg,查看路网和运行仿真。

Town01
 
Town04
   
Town05
     

3.2 运行联合仿真

运行联合仿真的基本逻辑是:

先启动CarlaUE4.exe服务端,使用Carla的PythonAPI加载场景地图,再启动基于Carla PythonAPI和SUMO TraCI Python API编写的run_synchronization.py脚本进行联合同步仿真。前面提到,直接点击CarlaUE4.exe就启动了服务端,也就是场景渲染的地方。

接下来就是加载场景和运行联合仿真脚本:

3.2.1 加载场景

也就是使用PythonAPI/uitl下的config.py加上map参数指定相应地图,本例就使用Town01,命令为

python config.py –-map Town01

运行后出现的视角如图。


3.2.2 错误处理

进入到Co-Simulation/Sumo目录,运行命令

python run_synchronization.py ./examples/Town01.sumocfg --sumo-gui

关于各个参数的详细解释下一节有专门解释,运行后就会出现弹出下图的sumo-gui,本例使用Town01场景。

朋友们别激动,不出意外的话点击sumo-gui上的绿色三角图标开始仿真就会报错,原因如下。首次运行联合仿真脚本出现错误的原因是在官方提供的sumo_integration包有个小问题,sumo_simulation.py文件中的第304行的traci模块错用,原代码为

sumo_net = traci.sumolib.net.readNet(net_file)

但实际上sumolib并不是traci的子模块,也就是下图所示的错误,只要去掉traci.即可。

3.2.3 运行同步仿真脚本

再次运行脚本就可以顺利地跑起来Carla和SUMO的联合同步仿真了,在Carla端界面,可以跟玩游戏一样用WASD键+鼠标调整视角。下面是Carla和SUMO并行的一个视角,便于观察双边的车辆是否同步。

到此就算跑通了Carla和SUMO的联合同步仿真。随后是之前的那个预告片,即Town01场景的运行记录。下一节将介绍联合仿真的核心要点,将涉及一些比较重要的概念和代码逻辑,帮助大家理解联合仿真。


四、核心要点

4.1 车辆类型映射

在Carla和SUMO中有着不同的车辆类型定义,在进行联合仿真前需要确定车辆类型的映射关系,实际是从Carla映射到SUMO,即基于Carla中车辆的blueprint来生成SUMO中的vType。

官方提供的脚本create_sumo_vtypes.py就是做这件事的(在Co-Simulation/Sumo/util/目录),我们来简单分析这个脚本。

main(args)函数开始,可以看到上来就是创建客户端,显然需要提前将Carla服务端运行起来。然后从世界中筛选出车辆的蓝图存到vehicle_blueprints变量,随后一个for循环遍历vehicle_blueprints,调用generate_vtype()方法来创建SUMO中对应的vType,最后调用write_vtype_xml()方法把结果写到一个xml文件供SUMO使用。

其中还用到一个本地vtypes.json文件(在/Co-Simulation/Sumo/data目录下),这个文件是真正的车辆类型映射关系。

4.2 创建SUMO路网

仿真路网是车辆运行的基础,Carla官方是假定你已经有一个Carla的仿真场景,再去创建SUMO的场景,本处也就是生成SUMO的路网.net.xml文件。该过程并不需要Carla服务端正在运行。

Carla官方提供的脚本Co-Simulation/Sumo/util/netconvert_carla.py的功能就是基于Carla场景生成SUMO路网,同样也简单对其进行分析。基本逻辑是首先使用SUMO的netconvert工具,将Carla中的OpenDRIVE地图转为一个初步的SUMO路网文件,然后对该路网文件进行修正。脚本定了两个重要的类SumoTopology和SumoTrafficLight,用于描述中间过程的SUMO路网结构和信号灯。两个重要的方法:

  • build_topology(),是将由OpenDRIVE路网转换得到的初步SUMO路网作为输入然后构造成一个SumoTopology对象,在后续步骤使用。
  • _netconvert_carla_impl(),会使用到SumoTrafficLight类,是后续修正SUMO路网的具体实现,作为netconvert_carla()方法只是类似接口的一个包装。

其中还用到OpenDRIVE和SUMO道路类型的映射关系文件opendrive_netconvert.typ.xml,存放在/Co-Simulation/Sumo/util/data目录下。

4.3 生成SUMO交通需求

该部分可以通过netedit的demand模式手动编辑,但这仅限于简单和少量的路径和需求,所以这里不展开如何进行操作。

更好的办法是使用SUMO官方提供的工具脚本randomTrips.py来自动生成随机的交通需求,即.rou.xml文件,具体操作可以看往期内容【省心省力 | SUMO交通需求自动创建工具 | randomTrips.py】,该文还介绍了其他几种创建需求的方法。

但需要注意一点,使用该脚本生成联合仿真的交通需求时,要对SUMO中车辆或流量的type参数进行匹配,也就是上面提到的create_sumo_vtypes.py脚本生成的vType集 合。

4.4 Carla & SUMO同步仿真

4.4.1 准备仿真

运行SUMO仿真需要将此前转换的路网.net.xml文件和生产的交通需求文件.rou.xml组装在一起作为仿真配置文件.sumocfg文件,设置交通需求时,需要将carlavtypes.rou.xml文件提供的车辆类型文件也加载进去,所以route-files对应的值是逗号分隔的两个rou.xml文件,其中一个就是carlavtypes.rou.xml

准备好SUMO的仿真配置文件.sumocfg后,运行脚本run_synchronization.py加上相应的参数即可与Carla进行同步仿真,SUMO侧使用TraCI接口来实现数据交换和同步。比如下面的命令即可启动Town01的同步仿真(控制台的当前目录在Co-Simulation/Sumo,即脚本run_synchronization.py所在目录):

python run_synchronization.py ./examples/Town01.sumocfg --sumo-gui

由于带上了参数sumo-gui,所以也能同时弹出sumo-gui界面,效果如案例视频。

4.4.2 参数说明

目前的版本0.9.14 中run_synchronization.py脚本除了必须的SUMO配置文件外,还有12个参数,其中前4个分别指定Carla和SUMO的ip和端口,

  1. --carla-host (default: 127.0.0.1) ,IP of the carla host server
  2. --carla-port (default: 2000) , TCP port to listen to
  3. --sumo-host (default: 127.0.0.1) , IP of the SUMO host server.
  4. --sumo-port (default: 8813) , TCP port to listen to.

其余8个参数包括:

  1. --sumo-gui,开启sumo-gui界面;
  2. --step-length (default: 0.05s) , Carla和SUMO联合仿真的时间步长,这里需要说明,该参数也会通过TraCI接口传给SUMO作为其仿真步长;
  3. --client-order,联合仿真在SUMO侧的TraCI客户端编号,默认为1;
  4. --sync-vehicle-lights (default: False) , 是否同步车辆灯态,默认不同步;
  5. --sync-vehicle-color (default: False) , 是否同步车辆颜色,默认不同步;
  6. --sync-vehicle-all (default: False) ,是否同步所有的车辆属性,默认不同步,笔者提醒,同步车辆属性在SUMO侧调用TraCI接口实现,比较耗时,所以都默认不同步;
  7. --tls-manager (default: none) , 指定管理信号灯运行的依据,如果为sumo则会根据SUMO仿真中的信号灯同步到Carla显示;如果选择carla则Carla自行管理,还会同步给SUMO;默认的none则双侧的信号灯不会同步,由哪一侧产生的车辆就遵守其对应的信号灯,而不会理会另一侧的设置。可以根据具体情形和需要来设置;
  8. --debug,调试模式,在测试过程中输出更多的日志。

4.4.3 同步仿真原理

接下来将简要分析一下run_synchronization.py脚本的逻辑,如果想深入了解CarlaSUMO的联合仿真,可以继续阅读本节内容,如不感兴趣可以直接跳过到4.5节。

该脚本的准备工作除了引入必要的carla相关包和SUMO_HOME环境变量设置外,还需要引入sumo_integration包,包括其中的bridge_helper、carla_simulation、sumo_simulation以及存放常量的constants。脚本中设计了一个SimulationSynchronization类,其中实现同步仿真的是tick()方法,同步仿真结束时释放各种资源的方法close()

synchronization_loop()是同步仿真运行的逻辑,首先分别初始化Carla和SUMO仿真(分别在carla_simulation和sumo_simulation中实现),再创建同步仿真类SimulationSynchronization实例,接下来就是一个while死循环,按固定的时间步长step-length推进仿真(即Carla的tick,这里也就是同步仿真类实例调用tick()方法),同步仿真结束后释放资源。相信到这里,大家已经完全理解Carla和SUMO的同步仿真原理了。

4.5 SUMO管理NPC车辆

得益于Carla和SUMO同步仿真实现,可以使用SUMO来管理在Carla生成的车辆(SUMO实现了Carla中Traffic Manager功能)。官方提供的spawn_npc_sumo.py脚本直接引入了run_synchronization.py中的SimulationSynchronization类,当然也有sumo_integration包中的carla_simulation和sumo_simulation。

spawn_npc_sumo.py脚本首先根据Carla服务端运行的场景在临时目录下自动生成SUMO路网,准备好.sumocfg配置文件。然后根据输入的NPC数量和参数生成车辆,在每次同步仿真推进一步后更新车辆在SUMO中的路径,下一步同步到Carla中。

该脚本的参数可以理解为run_synchronization.py和PythonAPI/examples目录下generate_traffic.py两个脚本参数的部分组合,TraCI相关只多了一个additional-traci-clients额外客户端参数,其余的参数包括:

  • 车辆数(-n);
  • 行人数(-w);
  • 车辆过滤(--filterv);
  • 行人过滤(--filterw);
  • 安全设置(--safe)

都来自于generate_traffic。比如服务端加载Town04,使用如下命令可以加入由SUMO控制的10辆车和20个行人,并在sumo-gui界面可以观察到相应的对象。

python spawn_sumo_npc.py -n 10 -w 20 --tls-manager carla --sumo-gui

五、结语

本文从安装到调试细致地讲解了如何进行CarlaSUMO的联合同步仿真,详细地阐述了同步仿真中的几个重要问题,并配合代码逻辑的梳理,相信读完本文,会对大家的工作和科研有所帮助。


来源:车路漫漫
二次开发航空航天汽车ADSUGpythonUM自动驾驶游戏控制渲染人工智能
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2025-12-10
最近编辑:8月前
李慢慢
硕士 自动驾驶仿真工程师一枚
获赞 14粉丝 93文章 320课程 0
点赞
收藏
作者推荐

《大话自动驾驶》,小人物笔下的自动驾驶江湖

作者| 11号线人01 诞生的背景 那是2022年3月份的最后一天,我已经居家办工有一周了,但午睡节奏还是没能掌握好,一不小心又睡到下午3点,愧疚与不安下,我赶紧打开手机,看到微 信多条未读消息时,大腿根子一紧,但定睛一看后,旋即又放下心来,均非工作上紧急任务消息,而是一位因文章而认识的前辈——苏清涛老师。苏老师问我:电子工业出版社计划出版一批自动驾驶相关书籍,你写的科普内容我觉得挺合适的,要是有整理成书的想法,我把出版社老师介绍给你。那天,距我在公众 号上发表科普文章仅过去7个月,我也仅写了23篇文章,对于能否写出一本像样的科普书,我心里其实是没底的,但男人奇怪的“我能行”自信心作祟,又让我冒着违约风险,义无反顾地接下了这个脑力和体力都充满挑战的任务。02 诞生的经过 作为一名具有纯正血统的工科生(如何证明呢,自上大学以来,班里男女比例一直维持在1:10),文学修养几乎全部来自经典名著《西游记》,表达技巧几乎全部来自电视剧《乡村爱情》,这也是为什么公众 号的文章总给人一种理性中充满感性、一本正经中夹杂着吊儿郎当。自身基本功的不足也成为我决定写科普书籍后遇到的第一个难题,如何用更合理的逻辑设计全书架构、如何用更书面的表达阐述原理内容、如何用更理性的态度表达行业观点,成为我不得不认真思考的问题。为了解决上面问题,我开始了对标,没错,是汽车工程师入门必掌握的对标大 法,对标虽然不能让你成为领头羊,但足以让你快速接近平均水平。我倾尽三年零花钱,买了近三年市面上出版的几乎所有关于智能网联汽车相关的书籍,学习他们的架构,学习他们的逻辑、学习他们的表达方式。拿着对标成果,并在电子工业出版社张爽张老师的指导下,开始了写作大纲的整理,并最终确定了三个大的篇章:硬件那些事儿、软件那些事儿和行业那些事儿。(1)硬件那些事儿主要科普激光雷达、毫米波雷达、相机、芯片、线控底盘等自动驾驶“肉体”的原理及作用。(2)软件那些事儿主要从算法、系统、功能等角度解释“肉体”是如何被赋予孤独而高贵的“灵魂”。 (3)行业那些儿主要选取我听到、看到、经历过的一些行业内有意思的事件进行妄议和反思。写作大纲的成型,也意味着此书顶层设计的完成,接下来就要进入到底层土方施工的阶段了,施工的精髓就是:时间就是金钱、效率就是生命,而就在我热火朝天准备大干一场的时候,遇到了此书诞生路上的第二个障碍:时间。作为一个角色丰富的多细胞生物,上班时我要扮演好员工、同事的角色,下班后我要扮演好家人、话搭子的身份,周末偶尔还要扮演好狐朋狗 友酒搭子的身份,所以我可用的时间理论上只剩下工作日深夜和充满不确定性的周末。为了最大程度克服这个障碍,我这个热情的多细胞生物在接下来的一年时间里启用了“地道战”战术:用善意谎言隐蔽自己。所以在那段时间,多数邀请我下班后交流下感情的供应商朋友,总是能收到我出差的推辞;多数苦劝我周末出来撸个串喝个酒的狐朋狗 友,总能收到我家里有事的推辞;所有劝我花钱去听“行业吹牛大会”的卖会朋友,总能收到我家里米缸见底的推辞。你还别说,“两耳不闻窗外事,一心只读圣贤书”之后,不仅时间和效率实现双增长,还意外收获了内心的宁静。当一年后,供应商朋友不再联系我,狐朋狗 友不再叫我,卖会朋友转行卖茶时,我也“筑基”完成,书籍初现。我眼含热泪将完成的初稿发给出版社老师,以为后面就是等待“三审三校”的结果,基于修订意见修修改改就好了,未曾想,一口大气还未长舒完成,出版社老师的一句话,让我遭遇此书诞生过程的第三个障碍:准备“推荐语”和“序”。这个时候,我又对标起之前买的那些书籍的“推荐语”和“序”,全是行业巨佬锐平和学术巨佬真情流露,而我要做的就是找到自动驾驶行业和学术上的巨佬,说服他们为我“服务”。推荐语还好说,各种机缘巧合,认识了几位在自动驾驶行业奉献的巨佬,微 信将书稿发给他们,并试着邀请他们写个100字左右推荐语,未曾想都欣然答应。但“序”是个难题,出版社老师建议是学术上有造诣的老师,我的第一反应是我研究生导师,这样省时、省力还省心,但我导师研究方向既不是自动驾驶,也不是人工智能,所以只能作罢。自动驾驶领域学术上有造诣的老师,我在论文里、讲座上倒是已经认识了他们,但是奈何他们不认识我,我将自己准备序过程遇到的困难告诉了出版社老师,出版社老师答曰:之前有个作者联系到了不认识的院士为书写序,认不认识不重要,找到他们的联系方式,联系他们,事在人为。冲着出版社老师的这碗鸡汤,我开启了半年求“爱”一般的求序经历,中间有失落、有感动、有疲惫、有惊喜。求序第一阶段策略定为重点击破,从熟人朋友圈下手,看看其上学时导师或所在课题组有没有合适的人选。厚着脸皮问了一圈,未曾想,还真觅得一位,在北理工读博的一位学长,旁边课题组的一位老师就是自动驾驶领域耳熟能详的大牛,我入行时就拜读过他书籍的封面及目录。在学长的帮助下,我先拿到了这位老师的邮箱,洋洋洒洒写了一百来字情真意切求序信后,如图1,信心满满地发了出去,一天、三天、五天,一周后我忍不住问学长,这位老师是不是忙得没时间看看邮箱呢。学长爽快地说:没事,我去给你要个手机号码。拿到手机号码后,我又将一百多字的求序宣言提炼成三十字左右发了出去,一天、两天、五天……,浓烈的爱石沉大海,这是让人失落的时刻。图1 发给北理工老师的求序邮件短暂恢复下心情后,迅速调整了求序策略:重点击破转为广撒网。提到汽车强校,行业内最先想到的就是同济大学和吉林大学(其实还有清华大学,只是那时我觉得一个末流985硕士实在是高攀不上,所以没敢打什么主意),我就去他们的研究生院网站,找到导师列表,凡是研究方向和自动驾驶沾亲带故的,我都记下他们的邮箱,经过初步筛选,找到了两所学校大概八位合适的老师。而后,每隔两天分别给两位老师发求序邮件,直到有老师回应。出人意料而又意料之中的是,全都石沉大海,那段时间,提到老师,我的脑海里已经不再是谆谆教导、和蔼可亲的儒雅长者,而是一脸严肃、面目狰狞的坏老头。念念不忘、没有回响的等待是令人疲惫的。带着失落与疲惫,我选择打开清华大学车辆与运载学院的网站,看到醒目位置的五位院士姓名,虽然渺小感骤升,但无路可退让我继续去物色院士外的其他老师。第一位就相中了名声在外的赵福全赵院长,抱着“有枣没枣打一杆子”的心态发了邮件,但没想到赵院长在五天后竟然回复了,如图2,虽然也是婉拒,但措辞确是让人感动的。图2 清华大学赵院长回信这久违的感动也让给我重拾了信心,国内顶级学府的老师也许就是不一样。我开始认真浏览老师列表,随即又看到了一个熟悉的名字——李升波,我写决策规划系列科普文章时,多次引用李老师论文及论坛演讲的内容。我正襟危坐,打起十二分精神,重新起草了邮件,在晚上10:01将邮件发出,一万个没想到,10:59分收到李老师的回复。李老师期望我能发一个个人简历先介绍下自己,这一刻我突然感觉有戏了。用心制作了一份个人简历发出之后,2个小时后便收到了李老师的回复:愿意作序,但在这项工作正式启动之前,期望你下次出差北京可以顺带来清华大学一叙,认识你,了解你在行业内所在的工作。这一刻,我为李老师严谨认真的态度所折服,并追忆起了具有同样风格的高中物理老师。准备前往北京的过程,我复盘了一下邀请同济大学、吉林大学老师们写序失败的经过,虽然最终也没总结出什么有用的信息,但意外发现发给同济大学一位老师的邮箱写错了,抱着严谨的态度,我重新发了一次,未曾想,竟然也收到了回复,真是好事成双,这位老师便是黄岩军,一位年轻、充满干劲、醉心科研的老师,这也是本书为何会有两个序的原因,并不是我得寸进尺、不知满足。与李老师面对面的交流相当愉快,交流中得知李老师也在繁忙的科研工作中也在努力做着中小学生的人工智能科普工作,其为小学生录制的《自动驾驶汽车》科普视频,生动、有趣且细腻,家中有小朋友的家长可以去B站看一看。临告别时,李老师送我一句话:小事看能力,大事看情怀,这句话如今不断在我脑海中复现,影响着我在岔路口前的抉择。03 诞生的期许 这本书历经二年,羞答答的像个姑娘,如今终于可以和大家打招呼了,惟愿此书内容能给买她的人带来一丝精进与思考,而不枉费自己花出去的大几十真金白银。最后,送上感谢,所有为此书直接、间接贡献过力量,认识的、不认识的朋友道一声祝福:谁谓河广?一苇杭之。初心 | 记录生而为人的证据,分享工农阶层原创作品,聚焦智能网联与人情冷暖。声明 | 本文部分文字及图片资料取自网络,如有侵权,请联系平台进行修改或删除;文章属于作者本人,仅代表个人观点,不代表平台立场,如有不妥,也请联系平台修改或删除;本文不作任何商业用途。来源:车路漫漫

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