CarSim VS Solver API与Python:强化学习路径跟踪仿真环境实战复盘
做自动驾驶控制,特别是强化学习路径跟踪,最难的一步往往不是算法本身,而是搞一个能用的仿真环境。我试过直接用Python写车辆运动学模型,用自行车模型加个PID也能跑,但一旦涉及轮胎非线性、侧偏特性、悬架耦合这些真实工况,结果就会非常"玩具"。CarSim加上VS Solver API这套路子,算是把"动力学精度"和"算法开发效率"两头都占住了。
这里说的VS Solver API,是CarSim提供的一套求解器接口,直接面向车辆动力学仿真核心。配合Python调用,你可以在不依赖Simulink的情况下,把CarSim当成一个"动力学引擎"嵌进自己的强化学习训练流程里,做路径跟踪、横向控制、避障这些任务。对于在强化学习方向摸爬滚打的工程师和研究僧来说,这套方案能省掉大量造轮子的时间,把精力真正花在算法迭代上。
这篇文章是我基于实际项目经验的完整复盘,从环境搭建、接口原理、Python代码封装,到强化学习状态动作奖励设计、训练踩坑,一路写到底。无论你是刚接触CarSim的小白,还是已经在搞RL控制但被仿真环境卡住的老手,都值得花几分钟看完。
1. 为什么我选了VS Solver API这条技术路线
1.1 CarSim凭什么当这个"动力学裁判"
先聊一个核心问题:强化学习路径跟踪,仿真环境到底需要什么?
路径跟踪本质上是让车辆沿着期望轨迹行驶,控制量通常是前轮转角。如果你只是验证算法能不能收敛,那最简单的运动学模型就够用。但问题在于,强化学习的策略是通过跟环境大量交互学出来的。你在仿真里教给它的那一套"手感",到了真车上必须还能用——这就对仿真环境的保真度提出了硬性要求。
CarSim在这条链路里的地位,相当于一个"动力学裁判"。它内置了高精度的整车模型,包括车身、悬架、转向、轮胎、制动、空气动力学等多个子系统。相比简单的解析模型,CarSim能提供更接近实车的动态响应:比如高速过弯时轮胎进入非线性区,侧偏角增大后横摆力矩的衰减特征,这些是两自由度自行车模型根本描述不了的。
而且CarSim的输出覆盖非常全:车辆位置、速度、横摆角速度、质心侧偏角、四个轮子的垂向载荷、滑移率、侧偏角,全都可以按需取用。对强化学习来说,这意味着你可以从丰富的状态空间里挑最合适的量来构成观测,而不必像用简化模型那样将就。
注意:很多人上来就把CarSim当"高精度仿真器"用,但其实它的价值更多体现在"可控的复杂性"。它允许你只启用你关心的自由度,其他子系统用预设模式近似。这套"按需取用"的灵活性,在算法开发早期特别重要。
1.2 VS Solver API和Simulink联合仿真到底差在哪
说到CarSim,大部分人第一反应是"跟Simulink联合仿真"。这确实是最常见的用法,网上教程也多。但如果你做的是强化学习,这条路有几个麻烦的地方。
Simulink联合仿真的本质是:CarSim作为S-Function嵌入MATLAB/Simulink环境,每个仿真步长通过Simulink的求解器接口交换数据。这套机制在跑确定性控制(比如PID、MPC)时没什么问题,因为控制律是解析的,仿真步长固定,数据交换节奏稳定。
但强化学习不一样。训练过程中,策略网络每步都要和环境交互,交互频率取决于你的控制周期和服务器的计算资源。如果用Simulink联合仿真,每次训练启动都要加载整个Simulink模型,而且MATLAB/Simulink的启动开销、模型编译时间,都会变成训练循环里的额外负担。
VS Solver API呢?它本质上就是CarSim求解器的动态库封装,你直接在Python进程里加载,初始化之后按需步进仿真,像个"发动机"一样被你随时点火。没有图形界面,没有Simulink的中间层,数据交换走的是内存接口,速度不在一个量级上。
还有一个很现实的原因:Simulink联合仿真需要并行许可证,多人共用时经常卡授权。VS Solver API在授权允许的情况下可以脱离MATLAB运行,对服务器训练场景更友好。
1.3 这套方案适合什么场景
基于我的实践经验,VS Solver API + Python这套组合最适合下面三类场景:
- 强化学习路径跟踪研究:需要快速迭代、频繁做对比实验,训练环境和算法代码都希望尽量轻量化、可复用。
- 批量化参数扫描实验:比如你要研究不同车速、不同路面附着系数对控制效果的影响,需要跑几十上百组仿真,VS Solver API比Simulink灵活得多。
- 部署到Linux服务器的RL训练集群:CarSim提供了Linux版本的求解器库,你可以在无图形环境下跑大规模并行训练。
至于那些传统控制算法的验证,特别是需要跟Simulink里其他模块(比如复杂的Simulink控制逻辑)做联合验证的场景,Simulink联合仿真依然更合适。术业有专攻,选型不是"哪个好",而是"哪个适合你当前的处境"。
2. CarSim协同仿真环境的搭建与配置
2.1 安装与版本搭配:不踩坑的选型建议
这环节看起来简单,实际坑不少。CarSim的版本选择,直接决定了后面Python接口的可用性和稳定性。
目前常见的主流版本大致是CarSim 2019、2020和2021这几个。以我的经验,2020以上版本对VS Solver API的支持更完善,错误提示也更友好。如果你是为了这个项目新装CarSim,建议直接用新版,别拿旧版折腾。
Python这边需要注意的只有一个大原则:VS Solver API给的示例代码通常是基于Python 3.6~3.8写的,你不需要刻意装老版本Python,只要保证ctypes标准库可用就行,Python 3.8以上完全没问题。
另外有一个容易忽略的细节:CarSim的VS Solver API在不同操作系统上有不同的主程序形态。Windows上一般是SimSim.dll(有的版本叫carSim.dll),Linux上是SimSim.so。如果你的强化学习训练在Linux服务器上进行,需要在Linux环境下单独安装一套CarSim的Linux版本,并且确保许可证服务器能被服务器访问到。
提示:许可证授权是很多人卡住的第一个坎。CarSim默认的授权模式可能是本地锁或者网络锁。如果在服务器上跑,务必确认网络许可证配置正确,否则初始化求解器时大概率报license related error。
2.2 VS Solver API核心文件与目录结构
安装完成后,重点关注几个关键位置。
CarSim的安装目录下会有一个Simulation Model文件夹,里面是你可以调用的主模型库文件。我之前用的结构大致是:
SimSim.dll或SimSim.so:求解器主库,运行时加载VehicleSim头文件或文档:供C语言调用时参考,Python里用ctypes也能间接用上bin目录:主程序和执行工具Run_Control相关文件夹:存放仿真运行的中间文件
然后是CarSim的图形化界面(GUI)。这里要强调一点:虽然我们的训练通过Python直接调用,但车体参数、路面模型、仿真初始条件这些,还是要先在GUI里配好,然后导出一个.sim文件(或者叫simfile)。
GUI里的配置流程大致是:
- 选择或新建车辆模型,设置整车参数(质量、轴距、轮胎型号等)。
- 设置仿真工况:初始车速、档位、转向控制输入模式。
- 设置路面模型:附着系数、坡度。
- 在"仿真配置"里勾选"使用外部接口/求解器模式"。
- 导出
.sim文件到你的工作目录。
这个.sim文件就是Python端初始化求解器时的"钥匙"。它把整车参数、路面信息、初始条件全部打包了。你在Python里只需要告诉求解器"加载这个.sim文件",它就自动把整车模型建立起来。
2.3 Python端依赖与基本环境准备
Python端的依赖其实非常少。核心库只有几个:
ctypes:标准库,不用装numpy:数据处理必用matplotlib:可视化轨迹和曲线torch或tensorflow:强化学习策略网络用
装完这些,把CarSim安装目录下的求解器库文件路径记下来,后面要用ctypes加载。
还有一个容易被忽略的准备:工作目录的路径权限。CarSim在仿真过程中会在当前目录生成一些临时文件,如果路径含中文或者没有写权限,初始化就会莫名其妙失败。建议专门建立一个纯英文的工程目录,比如D:\VehicleSim\RL_Project,所有中间文件都放那里。
到这里,环境就算搭好了。但环境搭好只是第一步,真正复杂的是搞清楚VS Solver API的调用原理和数据流。
3. Python调用VS Solver的核心接口与数据流
3.1 VS Solver API的工作原理:把求解器当成一个"黑盒状态机"
第一次看VS Solver API的文档,很多人会被里面一堆VS_开头的函数搞晕。我在熟悉之前也有这种感觉,但后来想明白一个类比就好办了:把CarSim求解器想象成一个"黑盒状态机"。
你向它发送"前进到下一个时间步"的指令,它内部就基于车辆动力学方程向前推进一步,然后把这一时刻所有的车辆状态量从一个内存缓冲区读出来。你不需要关心里面怎么积分、怎么解算,只需要控制"走一步"的节奏和"进"、"出"的数据内容。
这个"状态机"有几个关键状态:
- 初始化:加载模型、分配内存、设置初始条件
- 运行中:每个步长推进一次,更新内部状态
- 暂停/重启动:支持中间改变输入或重置
- 关闭:释放资源,退出
API的工作流程就是在这几个状态之间切换。核心函数大致有:
VS_Initialize:初始化求解器,传入simfile路径VS_Advance:推进一个或多个时间步VS_Set:设置某个变量/输入量VS_Get:读取某个变量/输出量VS_Terminate:关闭求解器
这套设计思路非常像操作系统的API——你不用管底层驱动,只需要按协议调用就行。理解了这一点,后面的Python封装思路就顺理成章了。
3.2 关键数据结构与ID映射规则
使用VS Solver API最琐碎的地方在于变量ID的映射。CarSim里每个输入输出量都对应一个数字ID,这些ID定义在CarSim文档的变量列表里。
比如:
- 前轮转角输入,常见ID对应的是
Steer_L1或Steer_R1 - 车辆纵向速度输出,对应
Vx - 横摆角速度输出,对应
AVZ - 质心侧偏角,对应
Beta - 横向位置和纵向位置,对应
X和Y(注意坐标系定义)
问题来了:你在Python里并不知道这些ID的数值。怎么办?答案是靠CarSim自带的变量映射表。
在CarSim GUI里,有一个用于外部接口的"变量选择器",你可以挑出想要输出的变量,也可以选择允许外部输入的变量,然后在导出.sim文件时把这些变量配置一起打包。之后在Python里,你可以通过变量名称的字符串去查询实际ID,API提供类似的函数:
# 伪代码示意 vehicle_sim = ctypes.CDLL("SimSim.dll") vs_id = ctypes.c_void_p() # 初始化 vehicle_sim.VS_Initialize(ctypes.byref(vs_id), b"path/to/your.sim") # 查询变量ID solver_id_code = ctypes.c_int() vehicle_sim.VS_GetVarID(vs_id, b"Vx", ctypes.byref(solver_id_code))每一个输出变量查一次ID,之后在循环里就拿着这个ID去读取值,非常高效。
经验之谈:变量ID只在初始化后有效,不同版本可能ID不同,不要把硬编码的数字写死到仓库里。每次初始化后动态查询,才是稳妥做法。
3.3 最简单的Python调用框架:从初始化到步进
我给出一个基本可用的Python骨架,你在实际项目中可以直接参照扩展:
import ctypes import numpy as np # 1. 加载动态库 vs_lib = ctypes.CDLL(r"D:\VehicleSim\SimSim.dll") # 2. 定义函数原型(ctypes需要显式声明参数和返回类型) vs_lib.VS_Initialize.argtypes = [ctypes.POINTER(ctypes.c_void_p), ctypes.c_char_p] vs_lib.VS_Initialize.restype = ctypes.c_int vs_lib.VS_Advance.argtypes = [ctypes.c_void_p, ctypes.c_double, ctypes.c_double, ctypes.c_double, ctypes.c_double] vs_lib.VS_Advance.restype = ctypes.c_int vs_lib.VS_Get.argtypes = [ctypes.c_void_p, ctypes.c_int] vs_lib.VS_Get.restype = ctypes.c_double vs_lib.VS_Set.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_double] vs_lib.VS_Set.restype = ctypes.c_int vs_lib.VS_Terminate.argtypes = [ctypes.c_void_p] vs_lib.VS_Terminate.restype = ctypes.c_int # 3. 初始化 solver = ctypes.c_void_p() ret = vs_lib.VS_Initialize(ctypes.byref(solver), b"path/to/model.sim") if ret != 0: raise RuntimeError("初始化失败,返回码: {}".format(ret)) # 4. 查询关键变量ID vx_id = ctypes.c_int() vs_lib.VS_GetVarID(solver, b"Vx", ctypes.byref(vx_id)) steer_id = ctypes.c_int() vs_lib.VS_GetVarID(solver, b"Steer_L1", ctypes.byref(steer_id)) # 5. 仿真循环 dt = 0.001 # 内部仿真步长 t_end = 10.0 t = 0.0 while t < t_end: # 读取当前车速 vx = vs_lib.VS_Get(solver, vx_id.value) # 计算控制量(这里先用固定值) steer_angle = 0.01 * np.sin(t) # 设置前轮转角 vs_lib.VS_Set(solver, steer_id.value, steer_angle) # 推进一个步长 vs_lib.VS_Advance(solver, dt, 0.0, 0.0, 0.0) t += dt # 6. 关闭 vs_lib.VS_Terminate(solver)代码里有三处需要注意:
一,VS_Advance的参数含义。前两个是时间步长和仿真时间,后几个一般跟输出请求有关,你暂时照葫芦画瓢填0就行。
二,VS_Get和VS_Set的调用频率。理论上每一步都可以调用,但绘制曲线时你不需要每毫秒采样,可以在循环里每隔N步记录一次状态。
三,控制周期不等于仿真步长。强化学习环境里,决策频率通常10~50Hz,但CarSim内部求解步长往往是0.001~0.0005秒。这意味着你在一个控制周期内需要内部步进20~50次,然后把状态量反馈给策略网络。
3.4 数据流向与同步:避免"读一半旧数据"
做强化学习环境封装时,最大的坑之一是数据不同步。
我举一个实际场景:策略网络输入当前状态,输出一个转向指令。你拿着这个指令去VS_Set设置输入,然后立刻VS_Advance步进,接着VS_Get读取状态。看似没问题,但如果你在VS_Advance之前先VS_Get了一次状态,再去VS_Set、VS_Advance,那么策略拿到的状态就是"上一个时刻"的旧数据。
正确的时间序应该是:
- 在控制周期开始时,先
VS_Get读取当前状态,作为策略输入 - 策略网络计算输出(前轮转角/加速度)
- 调用
VS_Set设置控制量 - 调用
VS_Advance推进一个控制周期的时间(内部含多个求解步长) - 循环回到第1步
这个顺序不能乱。特别是如果你把CarSim封装成一个step()接口,一定要把"先读后设再推"这三步固化在同一个方法里,否则训练过程中状态和动作的时序完全错乱,策略学出来的东西毫无意义。
4. 强化学习路径跟踪:从环境封装到训练实战
4.1 把CarSim改造成一个标准的Gym环境
不管用什么强化学习框架,它要求的接口范式基本类似:一个step()接收动作,返回(state, reward, done, info)。所以第一步就是把VS Solver API的裸调用,封装成一个符合Gym规范的环境类。
import gym from gym import spaces import numpy as np class CarSimTrackingEnv(gym.Env): def __init__(self, simfile_path, dt_control=0.05): super().__init__() self.dt = dt_control # 控制周期,50Hz self.ts = 0.001 # 求解步长 self.solver = ctypes.c_void_p() self.load_solver(simfile_path) # 动作空间:前轮转角,范围假设正负0.5弧度 self.action_space = spaces.Box(low=-0.5, high=0.5, shape=(1,), dtype=np.float32) # 状态空间:横向偏差、航向偏差、纵向速度、横摆角速度、曲率 n_state = 5 self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(n_state,), dtype=np.float32) self.query_var_ids() def load_solver(self, simfile_path): # 见前文初始化代码 pass def query_var_ids(self): # 查询需要的所有变量ID:X, Y, Vx, AVZ, Beta, Psi pass def get_state(self): # 读取车辆当前位置和姿态,计算相对目标轨迹的偏差 # 返回numpy数组 pass def step(self, action): # 1. 先读当前状态(其实state已在get_state中给出) # 2. 设置前轮转角 # 3. 推进控制周期 # 4. 计算奖励和终止条件 pass def reset(self): # 重新初始化求解器,或者使用CarSim的重置机制 pass需要注意的地方是dt_control和self.ts的关系。CarSim的求解步长是针对动力学积分的,控制周期里需要步进int(self.dt / self.ts)次内部步长。如果你设置的控制周期是0.05秒(20Hz),求解步长0.001秒,那么每次step()要循环步进50次。这个循环内只做VS_Advance,不做多余的数据查询,性能才能上去。
状态空间的设计上,我推荐下面这组组合,实际效果很稳:
- 横向位置偏差
e_y:车辆当前位置到期望轨迹最近点的横向距离 - 航向偏差
e_psi:车辆当前航向角与轨迹切线方向的差值 - 纵向速度
Vx:让策略感知车速 - 横摆角速度
avz:反映车辆旋转动态 - 当前轨迹曲率
kappa:用于预测前方弯道情况
坐标变换建议:你在三维空间里拿到的是全局坐标,需要先做坐标系变换,把全局坐标映射到Frenet坐标系。简单做法是找到当前轨迹上离车辆最近的点,以此为参考计算横向和航向偏差。这个"找最近点"的步骤是性能关键,轨迹长的时候注意用KDTree加速,不要写个全量遍历。
4.2 强化学习状态、动作与奖励设计的关键细节
奖励函数的设计,对强化学习路径跟踪的效果起决定性作用。我见过太多人直接设成"横向偏差的负值",导致训练不收敛或者策略变得极其保守。
一组比较好的基线设置是:
reward = - (w1 * e_y^2 + w2 * e_psi^2 + w3 * a_steer^2) + bonus其中e_y是横向偏差,e_psi是航向偏差,a_steer是转向角速度(平滑性惩罚),bonus是到达终点或者有效跟踪到一定距离的正奖励。
权重设置经验值:w1=0.5,w2=0.3,w3=0.05左右起步。注意e_psi的单位是弧度,如果直接用数值会和米量纲的e_y差挺多,建议先归一化或者用平方权重统筹。大偏差的时候需要给一个硬惩罚或提前终止(done=True),避免策略陷入"远离回不来"的无效探索。
动作设计上,从连续动作空间开始直接学,不要先去离散化转向角。离散化的转向角在标准路径上勉强能看,但遇到连续弯道组合时,策略要么转向过猛,要么在离散点之间犹豫摆动,很难收敛到平滑的控制效果。直接上连续动作空间配合TD3或SAC这类算法,效率反而是更高的。
终止条件设置三件套:
- 车辆横向偏差绝对值超过阈值(比如3米)时,判定失控,终止
- 车辆速度超出合理区间时,终止
- 成功跟踪完整个轨迹,终止并给正奖励
4.3 训练过程中的三个实用技巧
技巧一,固定随机种子时注意连CarSim的初始种子一起固定。很多人在PyTorch里设了随机种子,但CarSim每次reset的微小差异可能打乱实验的可对比性。如果你要做基线对比,尽量用CarSim本身提供的"确定性重置"机制,一样的状态初始条件才能保证同一起跑线。
技巧二,训练初期别用太复杂的轨迹。我见过有人一上来就把"麋鹿测试""发卡弯"这种地狱难度的轨迹塞给强化学习模型,结果算法在探索期根本找不到正反馈,奖励曲线死水一潭。正确做法:用直线+缓弯起步,等策略稳定了,再渐进式增加曲率。
技巧三,用多个并行的CarSim实例加速数据收集。强化学习训练需要海量交互样本,单个CarSim实例是瓶颈。如果你的机器配置够,可以同时启动多个独立的CarSim求解器进程,每个进程跑一条随机轨迹,把经验回收到同一个replay buffer里。实测下来,4个并行实例能带来接近4倍的训练速度提升,瓶颈会转移到GPU的推理上。
4.4 一次训练效果评估:从奖励曲线到真实验证
训练结束后别只看奖励曲线。奖励高不代表控制效果好,可能的坑是策略学会"刷分",比如选了一条"省转向但偏离大"的路子。
我的评估习惯是做两件事。
第一,跑一组确定性轨迹跟跟踪误差统计。选择一组没有在训练集中出现过的路径(比如S形弯道、带坡度的曲线),记录横向偏差的均值、最大值、RMS值。如果横向偏差RMS小于0.3米,说明策略泛化能力不错。
第二,和传统MPC控制做对比实验。注意别拿同一组参数直接对比——MPC有模型参数整定的空间,强化学习也有超参数调优的空间,公平对比的意义在于看两者在极端工况下的表现差异。
个人经验:纯强化学习策略在中低速(60km/h以下)、中等曲率路径上,效果已经能接近MPC了;但在高速大曲率场景,MPC依然有明显优势——毕竟基于模型的优化器在预测时域内的计算天然有全局视野。不过RL的策略网络一旦训练好,推理速度毫秒级,这一点比MPC在线求解快一个数量级,未来做自适应控制里动态换工况的潜力非常大。
5. 踩坑记录:常见问题与排查技巧
5.1 连接类问题:初始化报错、加载库失败、授权异常
- 动态库加载报错:先检查路径。Windows上如果用的是相对路径或者带空格路径,ctypes经常会失败。解决办法:使用绝对路径,路径不要带中文。
- 初始化返回非0错误码:这时候最常见原因是
.sim文件里的配置和求解器库不兼容。检查一下.sim文件是用哪个版本的CarSim导出的,和求解器库版本是否对应——这就好比你用Office 2019打开一个2003的doc文件,可能内容能看但排版乱一个道理。 - License错误:如果报license相关错误,用CarSim自带的License管理工具重新激活;在服务器上跑的话,重点检查网络许可证服务器的端口是否可达。
5.2 数据异常类问题:读出NaN、状态跳变、数据没有更新
- 读出NaN:优先怀疑初始化没成功,或者变量ID查询结果。一个排查技巧是:初始化后用
VS_Get读取车速Vx和位置X,如果返回值是NaN,说明模型数据根本没载入成功。 - 状态跳变:大概率是因为控制周期和求解步长不匹配。比如你设置了
dt_control=0.05,但VS_Advance实际只推进了0.001秒,那么你拿到的状态和预期差了50倍。检查VS_Advance的返回值,确认实际推进的时间步是否正确。 - 数据没有更新:如果你读了好几次状态,发现完全没变化,八成是
VS_Get的变量ID是错的,或者读取位置在VS_Advance之前。把读到数值和CarSim GUI里的仿真曲线对照一下,能快速定位问题。
5.3 性能相关问题:仿真速度慢、训练时卡顿
- 仿真速度慢:把求解步长适度调大,比如从0.001调到0.002或0.005,观察仿真结果是否还在可接受范围。动力学模型步长太大确实会失真,但很多工况下0.002的误差已经可接受,换来的是速度翻倍。
- 训练卡顿:检查有没有在循环内反复查询变量ID——查询一次即可,存到变量外面;也不要每次
VS_Get都做ctypes类型转换这样的重复开销。更省的做法是把多变量打包成批量查询接口。
5.4 强化学习专用坑:环境reset不干净、奖励数值爆炸
- 重置不干净:我遇到过最隐蔽的问题是,
reset()里虽然重新初始化了求解器,但replay buffer里的旧经验还残留着上一轮的信息。对于路径跟踪这种任务,每轮的初始位置变化不大,旧经验严重拖慢新策略学习。解决方案是,当轨迹难度升级或者工况改变时,直接清空replay buffer。 - 奖励数值爆炸:横向偏差的平方在大偏差时数值可能极大,导致loss直接飞出。处理方案是截断:比如
e_y超过2米后不再增加惩罚,改用终止条件兜底。
提示:如果你在调参时发现策略总是"撞墙一样冲出去",检查奖励里有没有给"速度惩罚"。很多路径跟踪任务,策略会学会快速冲过弯道减少累积惩罚——这是典型的"刷分"行为,和真实驾驶经验相悖。
6. 一点额外的技术扩展思考
这套CarSim + Python的方案,不止能做路径跟踪。VS Solver API的可扩展性,其实足够支撑更多方向的强化学习课题:
- 换用更丰富的状态观测:比如加入车道线距离传感器的模拟数据,可以处理更复杂的感知-控制一体任务。
- 跟其他仿真器互联:VS Solver API的通用性较好,你可以把它嵌入到仿真平台里,让车辆动力学计算走CarSim,环境感知和场景生成走其他系统,形成一个完整闭环。
- 多车交互场景:理论上你可以启动多个CarSim实例,模拟多车博弈下的路径规划和跟踪控制。这对研究交互决策类算法特别有吸引力,单靠单车模型是撑不起来的。
我个人一直觉得,仿真器选型的本质,是对"保真度"和"开发效率"之间权衡取舍的判断。CarSim这套方案的优势在于,它在两者之间找到了一个相对舒服的位置。但无论你选什么环境,都要心中有数:仿真里能跑通,不代表实车就能上,最后的验收还是得靠真实道路。
写这篇文章,也是希望把我在这个方向上绕过的弯路、花过的时间,变成后来人可以少踩的坑。如果你正在搭这套环境,有任何问题欢迎在评论区一起讨论。