自定义训练循环与Simulink动作噪声金宝app
这个例子展示了如何使用自定义强化学习(RL)训练循环来优化车辆队列应用程序的控制器。对于这个应用,动作噪声是在Simulink®模型中产生的,以促进训练期间的探索。金宝app
有关调优基于pid的车辆队列系统的示例,请参见<一个href="//www.tatmou.com/es/es/help/slcontrol/ug/control-design-for-platooning.html" data-docid="slcontrol_ug#mw_28870ef5-32d3-4817-82f5-f93ef7fca6a8" class="a">车辆队列控制器设计(金宝appSimulink控制设计).
队列有以下控制目标[1]。
单个车辆稳定性-如果前面的车辆以恒定速度行驶,每个后面车辆的间距误差收敛为零。
管柱稳定性-间距误差在传播到管柱尾部时不会放大。
队列环境模型
在这个例子中,队列中有5辆车。每辆车都被建模为具有以下参数的卡车-拖车系统。所有长度都以米为单位。
L1 = 6;<年代pan style="color:#228B22">卡车长度%L2 = 10;<年代pan style="color:#228B22">拖车长度%M1 = 1;<年代pan style="color:#228B22">%结长L = l1 + l2 + m1 + 5;<年代pan style="color:#228B22">%期望的车头间距
前车按照给定的加速度曲线行驶。每辆尾随车辆都有一个控制器来控制其加速度。
打开Simulin金宝appk®模型。
mdl =<年代pan style="color:#A020F0">“fiveVehiclePlatoonEnv”;open_system (mdl)
该模型包含一个RL代理块及其<年代tr在g class="emphasis bold">最后的动作输入端口开启。这个输入端口允许在Simulink模型中为非策略RL代理(如深度确定性策略梯度(DDPG)代理)定制噪声。金宝app
指定RL代理块的路径。
agentBlk = mdl +<年代pan style="color:#A020F0">“/ RL代理”;
控制器结构
在本例中,每个尾随车辆(自我车辆)具有相同的连续时间控制器结构和参数化。
在这里:
,<年代pan class="inlineequation"> ,<年代pan class="inlineequation"> 分别是自我飞行器的加速度,速度和位置。
,<年代pan class="inlineequation"> ,<年代pan class="inlineequation"> 分别是加速度,速度,和直接在前面的车辆的位置。
每辆车都可以完全获取自己的速度和位置状态,但只能通过无线通信获取前面车辆的加速度、速度和位置。
控制器使速度误差最小化<年代pan class="inlineequation"> 使用速度增益<年代pan class="inlineequation"> 并使间距误差最小化<年代pan class="inlineequation"> 使用间距增益<年代pan class="inlineequation"> .前馈增益<年代pan class="inlineequation"> 用于改善前车的跟踪。
前车的加速度被假设为正弦波
在这里:
是前车加速度(m/s^2)
为正弦波的振幅(m/s)。
为正弦波的频率(rad/s)。
为模拟时间,单位为s。
强化学习代理设计
该智能体的目标是计算自适应增益,以便每辆车都能相对于前面的车辆跟踪所需的间距。因此,模型的配置如下:
动作信号由增益组成<年代pan class="inlineequation"> 除领头车外,所有车辆共用。每个增益的下界分别为0,上界分别为1、20和20。代理每秒钟计算一次新的增益。为了鼓励在训练期间进行探索,增益受到具有零均值正态分布的随机噪声的扰动:<年代pan class="inlineequation"> 其中方差<年代pan class="inlineequation"> .
观测信号由车辆间距(<年代pan class="inlineequation"> 减去目标间距(<年代pan class="inlineequation"> ,车辆速度(<年代pan class="inlineequation"> ),以及车辆加速度(<年代pan class="inlineequation"> ).
每个时间步计算的奖励<年代pan class="inlineequation"> 是
地点:
是车辆间距(<年代pan class="inlineequation"> )时间步长<年代pan class="inlineequation"> .
根据实际车辆间距计算所有车辆的最大超调量<年代pan class="inlineequation"> 以及期望的间距<年代pan class="inlineequation"> .在这种情况下,超调被定义为当车辆间距小于所需间距时<年代pan class="inlineequation"> .
指示是否发生车辆碰撞。如果,模拟将终止<年代pan class="inlineequation"> 是真的。
奖励函数的第一项鼓励车辆间距匹配<年代pan class="inlineequation"> .第二项惩罚时间步长之间增益的巨大变化。第三项惩罚超出目标间距(太接近前面的车辆)。最后,第四项惩罚碰撞。
对于本例,为了适应模型中指定的自定义噪声,您实现了一个自定义DDPG训练循环。
定义模型参数
定义训练和模拟参数,在训练期间保持固定。
Ts = 1;<年代pan style="color:#228B22">%采样时间(秒)Tf = 100;<年代pan style="color:#228B22">%模拟长度(秒)AccelNoiseV = ones(1,5)*0.01;<年代pan style="color:#228B22">%加速度输入噪声方差VelNoiseV = ones(1,5)*0.01;<年代pan style="color:#228B22">%速度传感器噪声方差PosNoiseV = ones(1,5)*0.01;<年代pan style="color:#228B22">%位置传感器噪声方差ParamLowerLimit = [0 0 0]';<年代pan style="color:#228B22">控制器增益的下限ParamUpperLimit = [1 20 20]';<年代pan style="color:#228B22">%控制器增益上限UseParamNoise = 1;<年代pan style="color:#228B22">%选项,用于指示是否注入噪声
定义改变每个训练集的参数。这些参数的值在环境重置函数中更新resetFunction
.
LeadA = 2;<年代pan style="color:#228B22">牵引车辆加速度幅值LeadF = 1;<年代pan style="color:#228B22">引导车辆加速频率ParamNoiseV = [0.02 0.1 0.1];<年代pan style="color:#228B22">%控制器增益的方差%随机噪声种子ParamNoiseSeed = 1:3;<年代pan style="color:#228B22">控制器增益噪声种子AccelNoiseSeed = 1:5 + 100;<年代pan style="color:#228B22">%加速度输入噪声种子VelNoiseSeed = 1:5 + 200;<年代pan style="color:#228B22">%速度传感器噪声种子PosNoiseSeed = 1:5 + 300;<年代pan style="color:#228B22">位置传感器噪声种子每辆车的初始位置和速度InitialPositions = [200 150 100 50 0] + 50;<年代pan style="color:#228B22">%的位置initialvelocity = [10 10 10 10 10 10];<年代pan style="color:#228B22">%的速度
创建环境
使用以下命令创建环境<一个href="//www.tatmou.com/es/es/help/reinforcement-learning/ref/rlsimulinkenv.html" data-docid="rl_ref#mw_12cc40dc-3191-4a23-af71-1625696c1a14" class="a">rl金宝appSimulinkEnv
.
为此,首先定义环境的观察和操作规范。
obsInfo = rlNumericSpec([14 1]);actInfo = rlNumericSpec([3 1],<年代pan style="color:#0000FF">...LowerLimit = ParamLowerLimit,<年代pan style="color:#0000FF">...UpperLimit = ParamUpperLimit);obsInfo。Name =<年代pan style="color:#A020F0">“测量”;actInfo。Name =<年代pan style="color:#A020F0">“control_gains”;
接下来,创建环境对象。
env = rl金宝appSimulinkEnv(mdl,agentBlk,obsInfo,actInfo);
设置环境重置功能为本地功能resetFunction
包含在本例中。这个函数改变每个插曲的训练条件。
env。ResetFcn = @resetFunction;
模型中的噪声是使用<一个href="//www.tatmou.com/es/es/help/simulink/slref/randomnumber.html" data-docid="simulink_ref#f7-892059" class="a">随机数(金宝app模型)块。每个块都有自己的随机数生成器,因此也有自己的起始种子参数。为了确保噪声流在不同的集之间是不同的,使用更新种子变量resetFunction
.
创建参与者、评论家和政策
使用局部函数为代理创建参与者和评论家函数逼近器createNetworks
包含在本例中。
[批评家,演员]= createNetworks(obsInfo,actInfo);
创建用于更新参与者和评论家的优化器对象。对两个优化器使用相同的选项。
optimizerOpt = rlOptimizerOptions(<年代pan style="color:#0000FF">...LearnRate = 1 e - 3,<年代pan style="color:#0000FF">...GradientThreshold = 1,<年代pan style="color:#0000FF">...L2RegularizationFactor = 1 e - 3);criticOptimizer = rlooptimizer (optimizerOpt);actorOptimizer = rlooptimizer (optimizerOpt);
为参与者近似器创建确定性策略。
policy = rlDeterministicActorPolicy(actor);
指定策略采样时间。
政策。SampleTime = Ts;
创造经验缓冲
为代理创建一个最大长度为的经验缓冲区1 e6
.
replayMemory = rlReplayMemory(obsInfo,actInfo,1e6);
学习所需数据
为了在训练期间更新演员和评论家的情况runEpisode
功能处理从环境接收到的每个体验。对于本例,处理函数是processExperienceFcn
本地函数。
这个函数需要额外的数据来执行它的处理。创建一个结构来存储这些额外的数据。
processExpData。批评家=批评家;processExpData。TargetCritic =批评家;processExpData。演员=演员;processExpData。TargetActor = actor;processExpData。ReplayMemory = ReplayMemory;processExpData。CriticOptimizer = criticOptimizer; processExpData.ActorOptimizer = actorOptimizer; processExpData.MiniBatchSize = 128; processExpData.DiscountFactor = 0.99; processExpData.TargetSmoothFactor = 1e-3;
每一集,processExperienceFcn
函数更新评论家、演员、重放内存和优化器。更新后的数据被用作下一集的输入。
循环训练
为了训练代理,自定义训练循环在环境中模拟代理,最多为maxEpisodes
集。
maxEpisodes = 1000;
使用模拟时间和采样时间计算每集的最大步数。
maxSteps = cell (Tf/Ts);
对于这个自定义训练循环:
的
runEpisode
函数模拟一集环境中的代理。方法从环境中接收经验时进行处理
processExperienceFcn
函数。经验不会被记录下来
runEpisode
因为经验是在接受时被处理的。训练时要加快速度
runEpisode
,CleanupPostSim
选项设置为假
.这样做可以使模型在集之间进行编译。的
PlatooningTrainingCurvePlotter
对象是一个辅助对象,用于在训练运行时绘制训练数据。你可以停止训练使用<年代tr在g class="emphasis bold">停止按钮在训练图。
在所有的情节完成后,
清理
函数将清理环境并终止模型编译。
训练策略是一个计算密集型的过程,可能需要几分钟到几个小时才能完成。为了在运行此示例时节省时间,请通过设置加载预训练的代理doTraining
来假
.要自己训练策略,请设置doTraining
来真正的
.
doTraining = false;<年代pan style="color:#0000FF">如果doTraining<年代pan style="color:#228B22">创建绘图辅助对象。plotObj = PlatooningTrainingCurvePlotter();<年代pan style="color:#228B22">%训练循环为i = 1:maxEpisodes<年代pan style="color:#228B22">运行插曲。out = run (<年代pan style="color:#0000FF">...env、政策<年代pan style="color:#0000FF">...MaxSteps = MaxSteps,<年代pan style="color:#0000FF">...ProcessExperienceFcn = @processExperienceFcn,<年代pan style="color:#0000FF">...ProcessExperienceData = processExpData,<年代pan style="color:#0000FF">...LogExperiences = false,<年代pan style="color:#0000FF">...CleanupPostSim = false);<年代pan style="color:#228B22">提取插曲信息,用于更新训练曲线。episodeInfo = out.AgentData.EpisodeInfo;<年代pan style="color:#228B22">为下一集提取更新的processExpData。processExpData = out.AgentData.ProcessExperienceData;<年代pan style="color:#228B22">为下一集提取更新的策略。policy = out.AgentData.Agent;<年代pan style="color:#228B22">从processExpData中提取评论家和参与者网络。批评家= processexpdata .批评家;actor = processExpData.Actor;<年代pan style="color:#228B22">提取累积奖励,计算平均奖励%为本集的每一步。cumulative verwd = episodeinfo . cumulative verwd;avgRwdPerStep = cumulative verwd /episodeInfo.StepsTaken;<年代pan style="color:#228B22">%根据发病的最初观察评估q0。obs0 = episodeInfo.InitialObservation;Q0 =评价(评论家,[obs0,评价(演员,obs0)]);Q0 = double(Q0 {1});<年代pan style="color:#228B22">%更新情节。更新(plotObj, avgRwdPerStep、cumulativeRwd q0);<年代pan style="color:#228B22">如果按下按钮,%退出训练。如果plotObj。停止Training<年代pan style="color:#0000FF">打破;<年代pan style="color:#0000FF">结束结束清理环境。清理(env);<年代pan style="color:#228B22">%保存策略。保存(<年代pan style="color:#A020F0">“PlatooningDDPGPolicy.mat”,<年代pan style="color:#A020F0">“政策”);<年代pan style="color:#0000FF">其他的加载预训练的策略。负载(<年代pan style="color:#A020F0">“PlatooningDDPGPolicy.mat”);<年代pan style="color:#0000FF">结束
验证培训策略
通过使用reset函数指定的随机初始条件运行五次模拟来验证学习到的策略。
首先,关闭模型中的参数噪声。
UseParamNoise = 0;
根据训练好的策略对模型进行五次模拟。
N = 5;simOpts = rlSimulationOptions(<年代pan style="color:#0000FF">...MaxSteps = MaxSteps,<年代pan style="color:#0000FF">...NumSimulations = N);experiences = sim(env,policy,simOpts);
绘制车辆间距误差、增益和奖励经历
输出结构。
f = figure(Position=[100 100 1024 768]);tiledlayout (f (N 3);<年代pan style="color:#0000FF">为i = 1:N<年代pan style="color:#228B22">%得到空格tspacing =经验(i).观察。测量。时间;空格=挤压(经验(i).观察.测量.数据(1:4,:,:));<年代pan style="color:#228B22">%获得收益(i). action .control_gain . time;增益=挤压(经验(i). action . control_增益.数据);<年代pan style="color:#228B22">%得到奖励trwd =经验(i).奖励。时间;rwd =经验(i).Reward.Data;<年代pan style="color:#228B22">绘制间距nexttile楼梯(tspacing,间距);标题(sprintf (<年代pan style="color:#A020F0">“车辆间距误差模拟%u”,我)网格<年代pan style="color:#A020F0">在%图收益nexttile楼梯(tgains,收益”);标题(sprintf (<年代pan style="color:#A020F0">“车辆增益模拟%u”,我)网格<年代pan style="color:#A020F0">在%绘制奖励nexttile楼梯(trwd rwd);标题(sprintf (<年代pan style="color:#A020F0">“车辆奖励模拟%u”,我)网格<年代pan style="color:#A020F0">在结束
从图中,您可以看到经过训练的策略产生了自适应增益,可以充分跟踪所有车辆的所需间距。
本地函数
流程经验函数在RL代理块每次处理一个经验时被调用。在这里,processExperienceFcn
将体验追加到重放内存中,从重放内存中采样一小批体验,并更新评论家、演员和目标网络。
函数[policy,processExpData] = processExperienceFcn(exp,episodeInfo,policy,processExpData)<年代pan style="color:#228B22">附加经验到重放内存缓冲区。追加(processExpData.ReplayMemory exp);<年代pan style="color:#228B22">从重放记忆中抽取一小批经验。miniBatch = sample(processExpData.)ReplayMemory,<年代pan style="color:#0000FF">...processExpData。MiniBatchSize,<年代pan style="color:#0000FF">...DiscountFactor = processExpData.DiscountFactor);<年代pan style="color:#0000FF">如果~ isempty (miniBatch)<年代pan style="color:#228B22">使用迷你批处理更新网络参数。[processExpData,actorParams] = learnFcn(processExpData,miniBatch);<年代pan style="color:#228B22">使用参与者参数更新策略参数。policy = setLearnableParameters(policy,actorParams);<年代pan style="color:#0000FF">结束结束
的learnFcn
函数更新给定的采样小批的评论家、参与者和目标网络
函数[processExpData,actorParams] = learnFcn(processExpData,miniBatch)<年代pan style="color:#228B22">查找终端体验。doneidx = (miniBatch.)IsDone == 1);<年代pan style="color:#228B22">针对下一个观察结果计算目标下一个操作。nextAction = evaluate(processExpData.TargetActor, minibatch . nextoberation);<年代pan style="color:#228B22">% compute qtarget = reward + gamma*Q(nexobservation,nextAction)% =奖励+ gamma*预期未来回报targetq = miniBatch.Reward;<年代pan style="color:#228B22">引导目标在非终结体验。expectedFutureReturn =<年代pan style="color:#0000FF">...getValue (processExpData.TargetCritic miniBatch.NextObservation nextAction);Targetq (~doneidx) = Targetq (~doneidx) +<年代pan style="color:#0000FF">...processExpData.DiscountFactor。* expectedFutureReturn (~ doneidx);<年代pan style="color:#228B22">使用deepCriticLoss函数计算临界梯度。criticGradient = gradient(processExpData.;)评论家,@deepCriticLoss,<年代pan style="color:#0000FF">...[miniBatch.Observation, miniBatch.Action], targetq);<年代pan style="color:#228B22">更新关键参数。[processExpData.Critic, processExpData。CriticOptimizer] = update(<年代pan style="color:#0000FF">...processExpData.CriticOptimizer processExpData.Critic,<年代pan style="color:#0000FF">...criticGradient);<年代pan style="color:#228B22">使用deepActorGradient函数计算actor梯度。来%加速deepActorGradient函数,评论家网络是函数外部提取的%,并作为字段传递给% actorGradData输入结构。actorGradData。CriticNet = getModel(processExpData.Critic);actorGradData。米iniBatchSize = processExpData.MiniBatchSize; actorGradient = customGradient(processExpData.Actor,@deepActorGradient,<年代pan style="color:#0000FF">...miniBatch.Observation actorGradData);<年代pan style="color:#228B22">更新actor参数。[processExpData.Actor, processExpData。ActorOptimizer = update(<年代pan style="color:#0000FF">...processExpData.ActorOptimizer processExpData.Actor,<年代pan style="color:#0000FF">...actorGradient);actorParams = getLearnableParameters(processExpData.Actor);<年代pan style="color:#228B22">使用给定的TargetSmoothFactor超参数更新目标。processExpData。target批评家= syncParameters(processExpData. syncParameters)TargetCritic,<年代pan style="color:#0000FF">...processExpData.Critic processExpData.TargetSmoothFactor);processExpData。TargetActor = syncParameters(processExpData. syncParameters)TargetActor,<年代pan style="color:#0000FF">...processExpData。一个ctor ,processExpData.TargetSmoothFactor);<年代pan style="color:#0000FF">结束
临界梯度是根据deepCriticLoss
函数。
函数损失= deepCriticLoss(q,targetq) q = q{1};<年代pan style="color:#228B22">%损失为q = q的一半均方误差(观察,动作)%对qtarget损失= mse(q,重塑(targetq,大小(q)));<年代pan style="color:#0000FF">结束
在给定策略参数的情况下,计算行动者梯度以使观察-动作对的期望值最大化。这里,负号用于最大化<年代pan class="inlineequation"> 关于<年代pan class="inlineequation"> .
在这里:
是批量观测
是批处理动作
评论家网络参数化了吗<年代pan class="inlineequation">
参与者网络参数化了吗<年代pan class="inlineequation">
是迷你批量吗
函数dQdTheta = deepActorGradient(actorNet,observation,gradData)<年代pan style="color:#228B22">根据当前的观察评估行动。action = forward(actorNet,observation{:});<年代pan style="color:#228B22">%计算:q = q (s,a)q = predict(gradData.CriticNet,observation{:},action);<年代pan style="color:#228B22">%计算:qsum = -sum(q)/N使q最大化Qsum = -sum(q,<年代pan style="color:#A020F0">“所有”) / gradData.MiniBatchSize;<年代pan style="color:#228B22">% Compute: d(-sum(q)/N)/dActorParams . %dQdTheta = dlgradient(qsum,actorNet.Learnables);<年代pan style="color:#0000FF">结束
环境重置功能改变初始条件、参考轨迹和每个插曲的噪声种子。
函数in = resetFunction(in)<年代pan style="color:#228B22">干扰标称参考振幅和频率。LeadA = max(2 + 0.1*randn,0.1);LeadF = max(1 + 0.1*randn,0.1);<年代pan style="color:#228B22">打乱标称间距。L = 22 + 3*randn;<年代pan style="color:#228B22">扰动初始状态。InitialPositions = [250 200 150 100 50] + 5*randn(1,5);initialvelocity = [10 10 10 10 10 10] + 1*randn(1,5);<年代pan style="color:#228B22">更新噪音种子。ParamNoiseSeed = randi(100,1,3);AccelNoiseSeed = randi(100,1,5) + 100;VelNoiseSeed = randi(100,1,5) + 200;PosNoiseSeed = randi(100,1,5) + 300;<年代pan style="color:#228B22">更新模型变量。in = setVariable(in,<年代pan style="color:#A020F0">“L”L);in = setVariable(in,<年代pan style="color:#A020F0">“LeadA”, LeadA);in = setVariable(in,<年代pan style="color:#A020F0">“LeadF”, LeadF);in = setVariable(in,<年代pan style="color:#A020F0">“InitialPositions”, InitialPositions);in = setVariable(in,<年代pan style="color:#A020F0">“InitialVelocities”, InitialVelocities);in = setVariable(in,<年代pan style="color:#A020F0">“ParamNoiseSeed”, ParamNoiseSeed);in = setVariable(in,<年代pan style="color:#A020F0">“AccelNoiseSeed”, AccelNoiseSeed);in = setVariable(in,<年代pan style="color:#A020F0">“VelNoiseSeed”, VelNoiseSeed);in = setVariable(in,<年代pan style="color:#A020F0">“PosNoiseSeed”, PosNoiseSeed);<年代pan style="color:#0000FF">结束
建立评论家和演员网络。
函数[critical,actor] = createNetworks(obsInfo,actInfo) rng(0);hiddenLayerSize = 64;numObs = prod(obsInfo.Dimension);numAct = prod(actInfo.Dimension);<年代pan style="color:#228B22">创建评论家网络。obsInput = featureInputLayer(numObs,<年代pan style="color:#0000FF">...归一化=<年代pan style="color:#A020F0">“没有”,<年代pan style="color:#0000FF">...Name = obsInfo.Name);actInput = featureInputLayer(numAct,<年代pan style="color:#0000FF">...归一化=<年代pan style="color:#A020F0">“没有”,<年代pan style="color:#0000FF">...Name = actInfo.Name);catPath = [concatenationLayer(1,2,Name= .<年代pan style="color:#A020F0">“concat”) fullyConnectedLayer (hiddenLayerSize Name =<年代pan style="color:#A020F0">“fc1”) reluLayer (Name =<年代pan style="color:#A020F0">“relu1”) fullyConnectedLayer (hiddenLayerSize Name =<年代pan style="color:#A020F0">“取得”) reluLayer (Name =<年代pan style="color:#A020F0">“relu2”) fullyConnectedLayer (Name =<年代pan style="color:#A020F0">“q”));net = layerGraph();net = addLayers(net,obsInput);net = addLayers(net,actInput);net = addLayers(net,catPath);net = connectLayers(net,obsInfo. net)的名字,<年代pan style="color:#A020F0">“concat /三机一体”);net = connectLayers(net,actInfo. net)的名字,<年代pan style="color:#A020F0">“concat / in2”);Net = dlnetwork(Net);<年代pan style="color:#228B22">创建临界函数逼近器。rlQValueFunction(net,obsInfo,actInfo);批评家=加速(批评家,正确);<年代pan style="color:#228B22">创建角色网络。scale = (actInfo.)UpperLimit - actInfo.LowerLimit)/2;bias = actInfo。lowerLimit + scale; obsPath = [ featureInputLayer(numObs,<年代pan style="color:#0000FF">...归一化=<年代pan style="color:#A020F0">“没有”,<年代pan style="color:#0000FF">...Name = obsInfo.Name) fullyConnectedLayer (hiddenLayerSize Name =<年代pan style="color:#A020F0">“fc1”) reluLayer (Name =<年代pan style="color:#A020F0">“relu1”) fullyConnectedLayer (numAct Name =<年代pan style="color:#A020F0">“取得”) reluLayer (Name =<年代pan style="color:#A020F0">“relu2”) fullyConnectedLayer (numAct Name =<年代pan style="color:#A020F0">“一个fc3”文件) tanhLayer (Name =<年代pan style="color:#A020F0">“tanh1”) scalingLayer(规模=规模,<年代pan style="color:#0000FF">...偏差=偏差,<年代pan style="color:#0000FF">...Name = actInfo.Name)];net = layerGraph;net = addLayers(net,obsPath);Net = dlnetwork(Net);<年代pan style="color:#228B22">创建角色函数逼近器。actor = rlContinuousDeterministicActor(net,obsInfo,actInfo);Actor = accelerate(Actor,true);<年代pan style="color:#0000FF">结束
参考文献
Rajamani, Rajesh。<年代pan class="emphasis">车辆动力学与控制.2.机械工程系列。纽约,纽约海德堡:施普林格,2012。
另请参阅
功能
runEpisode
|<年代pan itemscope itemtype="//www.tatmou.com/help/schema/MathWorksDocPage/SeeAlso" itemprop="seealso">设置
|<年代pan itemscope itemtype="//www.tatmou.com/help/schema/MathWorksDocPage/SeeAlso" itemprop="seealso">清理