MATLAB/Simulink 从零打通:HEV 能量管理 GA 联合仿真保姆级建模指南

1. 遗传算法 (Genetic Algorithm, GA) 核心机制

遗传算法是一种启发式全局优化算法,灵感来源于自然界的生物进化过程。它通过模拟"优胜劣汰"的机制,在复杂的解空间中寻找最优解。图片中提到的三个核心操作是 GA 迭代进化的基石:

  • 选择 (Selection):
    • 目的: 从当前种群中挑选出适应度(Fitness)较高的个体,让它们有更大的机会将基因传递给下一代。在 EMS 优化中,适应度高的个体通常意味着更低的油耗和更稳定的电池电量。
    • 常见方法: 轮盘赌选择(适应度越高,被选中的概率越大)、锦标赛选择(随机挑几个个体打擂台,赢的留下)。
  • 交叉 (Crossover / Recombination):
    • 目的: 模拟生物交配,将两个父代个体的部分基因进行交换,从而产生具有新特征的子代。这是 GA 探索新解空间的主要手段。
    • 常见方法: 单点交叉(在基因序列的某一点切断并交换后半段)、多项式交叉(SBX,常用于连续实数变量的优化)。
  • 变异 (Mutation):
    • 目的: 以一定的低概率随机改变个体的某个基因值。这能维持种群的多样性,防止算法过早收敛到局部最优解(即早熟)。
    • 常见方法: 高斯变异、均匀变异。

2. Matlab:使用 ga 优化 EMS 参数

在混合动力汽车(HEV)中,能量管理系统 (Energy Management System, EMS) 是车辆的大脑,负责在发动机和电机之间分配扭矩,以达到最省油或排放最低的目的。基于规则的 EMS(如逻辑门限值策略)通常包含大量需要人为标定的参数(如:启动发动机的车速阈值、电机助力的扭矩界限等)。

在这个阶段,Matlab 的作用是将 GA 与 EMS 结合起来进行自动化标定:

  • 优化变量 (Variables): 即 EMS 控制策略中的各项阈值参数。
  • 适应度函数 (Fitness Function): 通常是一个联合代价函数。对于 HEV,目标是最小化燃油消耗,同时保证电池的荷电状态(SOC)在循环工况结束时能够维持平衡。
    J=Fueltotal+α⋅(ΔSOC)2 J = \text{Fuel}_{total} + \alpha \cdot (\Delta SOC)^2 J=Fueltotal+α⋅(ΔSOC)2
    (其中 α\alphaα 为惩罚系数)
  • 工作流: Matlab 的 ga 函数会生成一组参数,将其传递给 Simulink 模型,运行完整个循环工况(如 WLTC)后,Simulink 返回总油耗和终端 SOC,Matlab 计算出代价 JJJ 作为适应度,并以此指导下一代参数的生成。

3. Simulink:并联混动整车框架(论文仿真平台)建模方案

根据您的要求,以下是详细完整的并联混动(Parallel HEV)Simulink 整车架构建模说明,包含核心模块、参数设置及连接方式。并联混动的特点是发动机和电机都可以直接驱动车轮(通常在变速箱前或后耦合)。

A. 核心模块与参数设置

1. 驾驶员模块 (Driver Model)

  • 使用的模块: Discrete PID Controller (离散 PID 控制器)。
  • 功能: 模拟驾驶员踩油门和刹车,使车辆跟随目标工况车速。
  • 参数: 比例系数 KpK_pKp、积分系数 KiK_iKi、输出限幅(正值为加速踏板开度 0~100%,负值为制动踏板开度 -100%~0)。

2. 能量管理控制器 (EMS Controller)

  • 使用的模块: Stateflow ChartMATLAB Function
  • 功能: 接收驾驶员需求和车辆状态,分配发动机和电机的扭矩命令。
  • 参数: 由上述 GA 算法优化的参数(如:纯电行驶最高车速阈值、发动机启停 SOC 阈值、最优工作线 OOL 数据表)。

3. 发动机系统 (Engine System)

  • 使用的模块: Generic Engine (Powertrain Blockset) 或 组合 1-D Lookup Table
  • 参数: 发动机最大转矩包络线(随转速变化)、燃油消耗率 MAP 图 (g/kWhg/kWhg/kWh)、发动机转动惯量、怠速转速。
  • 输出变量: 实际输出扭矩、瞬时燃油消耗量(积分后输出全局变量 out_Fuel)。

4. 电机系统 (Motor System)

  • 使用的模块: Mapped Motor2-D Lookup Table 计算效率。
  • 参数: 峰值扭矩/连续扭矩曲线、电机效率 MAP 图、电机及控制器转动惯量。

5. 电池系统 (Battery System)

  • 使用的模块: Equivalent Circuit Battery (等效电路模型)。
  • 参数: 电池容量 (Ah)、初始 SOC、开路电压 OCV 曲线(随 SOC 变化)、充放电内阻 R0R_0R0。
  • 输出变量: 电池电压、充放电电流 (out_I_bat)、实时 SOC (out_SOC)。

6. 传动与整车动力学 (Drivetrain & Vehicle Dynamics)

  • 使用的模块: Simple Gear (减速器/变速箱)、Vehicle Body (纵向动力学本体)、Tire (Magic Formula) (轮胎模型)。
  • 参数:
    • 整车质量 mmm (kg)、迎风面积 AAA (m2m^2m2)、空气阻力系数 CdC_dCd。
    • 主减速比、各挡位传动比、传动效率。
    • 车轮有效半径。
B. 信号连接方式 (拓扑结构)

为了形成闭环仿真,各模块的连接拓扑如下:

  1. 需求输入端:
    • Drive Cycle Source (工况源) 的 [目标车速] 与整车反馈的 [实际车速] 相减得到误差信号,输入给 [驾驶员模块]
  2. 控制信号下发:
    • [驾驶员模块] 输出的加速/制动踏板信号,连同当前的 [实际车速] 和提取自电池模块的 [SOC] ,共同接入 [EMS 控制器]
    • [EMS 控制器] 根据控制逻辑,输出 [发动机目标扭矩 Teng_cmdT_{eng\_cmd}Teng_cmd][电机目标扭矩 Tmot_cmdT_{mot\_cmd}Tmot_cmd][机械制动力需求]
  3. 机械动力传递 (并联耦合):
    • Teng_cmdT_{eng\cmd}Teng_cmd 输入 [发动机模块] ,产生实际扭矩 Teng_actT{eng\_act}Teng_act。
    • Tmot_cmdT_{mot\cmd}Tmot_cmd 输入 [电机模块] ,产生实际扭矩 Tmot_actT{mot\_act}Tmot_act。
    • 将 Teng_actT_{eng\act}Teng_act 与 Tmot_actT{mot\_act}Tmot_act 通过 Sum 模块相加(代表并联扭矩耦合),总扭矩送入 [传动与整车动力学] 模块的输入轴。
  4. 电气回路交互:
    • [电机模块] 根据其实际输出扭矩、转速和效率 MAP 计算出电功率需求(正为消耗,负为发电)。
    • 此电功率需求转换为电流信号输入给 [电池系统]
    • [电池系统] 计算并输出实时的 out_SOCout_I_bat 返回给整车系统监控。
  5. 动力学反馈:
    • [传动与整车动力学] 模块根据输入的总驱动力矩和阻力(风阻、坡度阻力、滚阻),计算出 [实际车速] ,并通过反馈回路引回至最前端的 [驾驶员模块],完成闭环。
    • 动力学模块还将轮端转速通过传动比反推为发动机和电机的实际转速,作为对应模块的输入条件。

建立好上述 Simulink 框架后,即可利用 Matlab 脚本配置 ga 函数,让其自动调用该 .slx 模型进行成百上千次的迭代,从而找到最优的能量管理参数组合。

请在你的电脑 D 盘新建一个空文件夹,命名为 hev_test01,接下来的所有操作都在这个文件夹内进行。


第一步:创建底层物理参数(MATLAB 脚本)

在建模型之前,必须先把汽车的物理属性灌入内存。

  1. 打开 MATLAB R2022b。
  2. 在顶部工具栏的 Home(主页) 选项卡下,点击左上角的 New Script(新建脚本)
  3. 将以下完整代码复制并粘贴到这个白色的编辑器窗口中:
matlab 复制代码
% 文件名:init_veh.m
% 作用:初始化所有将被 Simulink 调用的物理参数和标定地图

% --- 1. 整车基础参数 ---
m_veh = 1500;          % 整车质量 (kg)
A_f = 2.2;             % 迎风面积 (m^2)
Cd = 0.3;              % 空气阻力系数
rho = 1.225;           % 空气密度 (kg/m^3)
r_wh = 0.35;           % 轮胎滚动半径 (m)
f_roll = 0.015;        % 滚动阻力系数
g = 9.81;              % 重力加速度 (m/s^2)

% --- 2. 传动系统 ---
i_final = 4.1;         % 主减速器传动比
eta_trans = 0.95;      % 传动效率

% --- 3. 电池参数 ---
Q_bat_Ah = 40;         % 电池容量 (Ah)
Q_bat_As = Q_bat_Ah * 3600; % 电池容量 (安秒)
U_bat = 350;           % 电池母线电压 (V)
soc_init = 0.6;        % 电池初始荷电状态 (60%)

% --- 4. 发动机万有特性 MAP (3x3 简化矩阵) ---
% 行代表转速断点 (rpm)
eng_spd_bkpt = [1000, 2000, 3000]; 
% 列代表扭矩断点 (Nm)
eng_trq_bkpt = [50, 100, 150];     
% 对应的燃油消耗率 (单位: g/s)
eng_fuel_map = [
    1.2, 2.5, 4.0;
    1.5, 2.8, 4.2;
    2.0, 3.5, 5.0
];

% --- 5. GA 待优化的 EMS 参数 (预先赋一个假的值占位) ---
V_ev_max = 40;         % 纯电行驶最高限速 (km/h)
SOC_low = 0.3;         % 强制充电 SOC 下限
  1. Ctrl + S 保存,文件名输入 init_veh.m,保存在 hev_test01 文件夹下。
  2. 点击编辑器顶部绿色的 Run(运行) 按钮。此时,你看向 MATLAB 主界面右侧的 Workspace(工作区),里面必须出现上述所有的变量名及其对应的值。

  1. 在 MATLAB 命令行窗口输入 simulink 并回车。
  2. 在弹出的起始页中,点击 Blank Model(空白模型)
  3. Ctrl + S,将模型命名为 HEV_Model.slx,保存在 hev_test01 文件夹下。
  4. 配置求解器(极其关键): 按键盘组合键 Ctrl + E,弹出 Configuration Parameters 窗口。
    • 在左侧菜单点击 Solver
    • Start time0
    • Stop time1180(NEDC 循环的总秒数)。
    • Solver selection -> Type 的下拉菜单中,选择 Fixed-step
    • Solver selection -> Solver 的下拉菜单中,选择 discrete (no continuous states)
    • Solver details -> Fixed-step size 的框中,严格填入 0.01
    • 点击右下角的 OK

第三步:按图索骥提取所有模块并设置参数

点击 Simulink 顶部工具栏的 Library Browser(库浏览器) 图标(四个红蓝方块)。请严格按照以下路径寻找模块,拖拽到空白画板上,并双击配置。

1. 信号源与需求端

  • 路径: Powertrain Blockset -> Vehicle Scenarios -> 找到 Drive Cycle Source ,拖拽出来。
    • 双击它: Drive cycle sourceNEDCOutput velocity unitsm/sOutput sample period0.01。点击 OK。
  • 路径: Simulink -> Continuous -> 找到 Discrete PID Controller ,拖拽出来。
    • 双击它: Proportional (P)0.8Integral (I)0.05。点击顶部 PID Advanced 选项卡,勾选 Limit outputUpper limit1Lower limit-1。点击 OK。

2. 能量管理大脑 (EMS)

  • 路径: Simulink -> User-Defined Functions -> 找到 MATLAB Function ,拖拽出来。
    • 双击模块正中心 ,会打开一个代码编辑器。清空里面所有的字,把下面这段代码一字不落地粘贴进去:
matlab 复制代码
function [T_eng_cmd, T_mot_cmd] = EMS_Rule(pedal, v_mps, soc, V_ev_max, SOC_low)
    % 输入:pedal(油门踏板-1到1), v_mps(实际车速m/s), soc(当前电量), V_ev_max(纯电车速阈值), SOC_low(电量下限阈值)
    % 输出:T_eng_cmd(发动机目标扭矩), T_mot_cmd(电机目标扭矩)
    
    % 将踏板开度转换为总需求扭矩 (假设最大整车扭矩需求为 250 Nm)
    T_req = pedal * 250; 
    v_kmh = v_mps * 3.6; % m/s 转 km/h
    
    if T_req <= 0
        % 纯刹车状态:发动机停机,电机负扭矩回收 (最多回收 50Nm)
        T_eng_cmd = 0;
        T_mot_cmd = max(T_req, -50);
    else
        % 驱动状态
        if (v_kmh < V_ev_max) && (soc > SOC_low)
            % 满足低速且有电:纯电模式
            T_eng_cmd = 0;
            T_mot_cmd = T_req;
        elseif soc <= SOC_low
            % 电量低于阈值:行车充电模式 (发动机提供驱动力,还要额外发 40Nm 的电)
            T_eng_cmd = T_req + 40;
            T_mot_cmd = -40;
        else
            % 高速或大负荷:混合驱动 (发动机主导出 70%,电机辅助出 30%)
            T_eng_cmd = T_req * 0.7;
            T_mot_cmd = T_req * 0.3;
        end
    end
end
复制代码
*   粘贴完后,直接关掉这个代码编辑器。你会看到原本的模块变成了 5 个输入端口和 2 个输出端口。

3. 数学运算与常量模块 (需要拖出多个)

  • 路径: Simulink -> Math Operations
    • 找到 Add 拖出来。双击,List of signs 填入 +-
    • 再找到 Add 拖出来。双击,List of signs 填入 ++
    • 再找到 Add 拖出来。双击,List of signs 填入 +-
    • 找到 Gain 拖出来,你需要拖 4 个。
      • 第 1 个 Gain 双击,Gain 框填入:i_final * eta_trans / r_wh。(作用:扭矩转驱动力)
      • 第 2 个 Gain 双击,Gain 框填入:0.5 * rho * Cd * A_f。(作用:算风阻系数)
      • 第 3 个 Gain 双击,Gain 框填入:1 / m_veh。(作用:算加速度)
      • 第 4 个 Gain 双击,Gain 框填入:1 / U_bat。(作用:功率算电流)
    • 找到 Math Function 拖出来。双击,Function 下拉选 square。(作用:算车速平方)
  • 路径: Simulink -> Discrete
    • 找到 Discrete-Time Integrator ,拖出 3 个。(分别用来积分得到实际车速、总油耗、消耗的电量)
  • 路径: Simulink -> Sources
    • 找到 Constant ,拖出 3 个。
      • 第 1 个 Constant 双击,Constant valuem_veh * g * f_roll。(作用:滚动阻力)
      • 第 2 个 Constant 双击,Constant valueV_ev_max。(作用:给 EMS 传参数)
      • 第 3 个 Constant 双击,Constant valueSOC_low。(作用:给 EMS 传参数)

4. 查表与输出 (核心)

  • 路径: Simulink -> Lookup Tables -> 找到 2-D Lookup Table 拖出来。
    • 双击它。Table dataeng_fuel_mapBreakpoints 1eng_spd_bkptBreakpoints 2eng_trq_bkpt
  • 路径: Simulink -> Sinks -> 找到 To Workspace ,拖出 3 个。
    • 第 1 个双击:Variable name 改为 out_FuelSave format 务必选 Array
    • 第 2 个双击:Variable name 改为 out_SOCSave format 务必选 Array
    • 第 3 个双击:Variable name 改为 out_I_batSave format 务必选 Array

第四步:纯手工连线工程 (严丝合缝的拓扑图)

现在,把模块在画板上稍微摆开一点。我们将进行点对点的连线操作(鼠标左键按住起点端口的箭头,拖到终点端口松开)。

回环 1:踏板需求生成

  1. Drive Cycle Source 的右侧端口,连到第一个 Add (+-) 模块的上端口 (+)。
  2. 把该 Add (+-) 模块的右侧输出端口,连到 Discrete PID Controller 的左侧输入端口。
  3. Discrete PID Controller 的输出端口,连到 MATLAB Function 的第 1 个输入端口(叫 pedal)。

回环 2:给 EMS 装上参数

  1. 把填了 V_ev_max 的那个 Constant 模块,连到 MATLAB Function 的第 4 个输入端口(叫 V_ev_max)。
  2. 把填了 SOC_low 的那个 Constant 模块,连到 MATLAB Function 的第 5 个输入端口(叫 SOC_low)。

回环 3:动力合成与车速计算(最复杂的物理网络)

  1. MATLAB Function 的第 1 个输出端口(T_eng_cmd),连到 Add (++) 模块的上端口 (+)。
  2. MATLAB Function 的第 2 个输出端口(T_mot_cmd),连到 Add (++) 模块的下端口 (+)。
  3. 把该 Add (++) 模块输出(此时是总扭矩),连到第 1 个 Gain 模块(填了 i_final * eta_trans... 的那个)。
  4. 把这第 1 个 Gain 模块的输出(此时是总驱动力),连到第三个 Add (+-) 模块的上端口 (+)。
  5. 把填了 m_veh * g * f_roll 的那个 Constant 模块的输出(滚动阻力),连到第三个 Add (+-) 模块的下端口 (-)。
  6. 把第三个 Add (+-) 模块的输出(此时是合力),连到第 3 个 Gain 模块(填了 1 / m_veh 的那个)。
  7. 把第 3 个 Gain 模块的输出(此时是加速度),连到第 1 个 Discrete-Time Integrator 的输入。
  8. 关键反馈: 这个 Discrete-Time Integrator 的输出端口,代表的是实际车速
    • 从这根线上拉出一条分支,往回连 到第一个 Add (+-) 模块的下端口 (-)。(作用:和目标车速做差算误差)
    • 再从这根线上拉出第二条分支,连到 MATLAB Function 的第 2 个输入端口(叫 v_mps)。
    • 再从这根线上拉出第三条分支,连到 Math Function (square) 的输入端口。

回环 4:完善阻力计算

  1. 把刚才 Math Function (square) 的输出,连到第 2 个 Gain 模块(填了风阻系数的那个)。
  2. 把第 2 个 Gain 模块的输出,强行拉到 第三个 Add (+-) 模块的下端口 (-)(也就是和滚动阻力一起作为减数)。如果你拉不进去,就在滚动阻力那根线上右键,添加一个交叉点,把风阻也并入那根阻力线。

回环 5:算油耗 (连接 2-D Table)

  1. MATLAB Function 第 1 个输出 T_eng_cmd 拉一条分支,连到 2-D Lookup Table 的第二个输入端口(扭矩输入)。
  2. (简化处理)将发动机转速恒定设为一个假值:拖出一个 Constant 模块,值写 2000,连到 2-D Lookup Table 的第一个输入端口。
  3. 2-D Lookup Table 的输出,连到第 2 个 Discrete-Time Integrator
  4. 将该积分器的输出,连到名为 out_FuelTo Workspace 模块。

回环 6:算 SOC (电池模型)

  1. MATLAB Function 第 2 个输出 T_mot_cmd 拉一条分支。
  2. 为了算功率,再拖一个 Gain 模块,值填 2000 * 0.1047(简化版恒定转速乘转化系数)。将电机扭矩连入该 Gain。
  3. 将算出的电功率输出连入第 4 个 Gain 模块(值为 1 / U_bat)。
  4. 这时的输出就是电池电流。将其分支,一根连给名为 out_I_batTo Workspace 模块。
  5. 将电流主线连入第 3 个 Discrete-Time Integrator(积分算消耗容量)。
  6. 再拖出一个 Add (+-) 模块和一个 Constant(值为 soc_init)。
  7. Constant 连入 +,将积分器输出除以 Q_bat_As (用一个新的 Gain 模块) 连入 -
  8. 这个 Add (+-) 的输出就是实时 SOC。拉一根分支接给名为 out_SOCTo Workspace 模块。
  9. 最后一次反馈: 将实时 SOC 信号线拉一条长线,接回 MATLAB Function 的第 3 个输入端口(叫 soc)。

按下 Ctrl + S,你的 Simulink 模型大功告成!不要关闭它,将其最小化。


第五步:在 MATLAB 编写 GA 终极优化代码

回到 MATLAB 主界面。我们将编写适应度函数和主运行脚本。

  1. 新建脚本,复制粘贴以下代码,保存为 fitness_eval.m
matlab 复制代码
function cost = fitness_eval(x)
    % 1. 把 GA 新生成的参数送到基础工作区
    assignin('base', 'V_ev_max', x(1));
    assignin('base', 'SOC_low', x(2));
    
    % 2. 在基础工作区重新加载基础物理参数
    evalin('base', 'init_veh');
    
    try
        % 3. 关键修复:指定 SrcWorkspace 为 'base'!
        % 这样 Simulink 就能直接找到变量,不再弹警告。
        simOut = sim('HEV_Model.slx', ...
            'SrcWorkspace', 'base', ...   % <--- 就是改了这里!
            'ReturnWorkspaceOutputs', 'on');
        
        % 4. 提取数据
        fuel_arr = simOut.get('out_Fuel');
        soc_arr = simOut.get('out_SOC');
        
        fuel_final = fuel_arr(end);
        soc_final = soc_arr(end);
        
        % 5. 核心惩罚逻辑
        if soc_final < 0.6
            penalty = 500000 * (0.6 - soc_final)^2; 
        else
            penalty = 0; 
        end
        
        cost = fuel_final + penalty;
        
        % 6. 心跳打印:只打印干净清爽的结果
        fprintf('成功 | V_ev: %4.1f km/h | SOC_low: %.3f | 最终代价: %.2f\n', x(1), x(2), cost);
        
    catch ME
        % 如果 Simulink 内部报错(例如代数环死锁),打印错误并淘汰该参数
        fprintf('失败 | 模型崩溃跳过: %s\n', ME.message);
        cost = 1e6; 
    end
end
  1. 新建脚本,复制粘贴以下代码,保存为 run_ga_opt.m
matlab 复制代码
% 这是整个项目的启动总闸

clear; clc;
% 1. 先跑一遍初始化脚本,铺垫基础数据
init_veh; 

% 2. 告诉 GA 我们有 2 个变量要寻找
nvars = 2; 

% 3. 画出寻找的圈子 (Lower Bound 和 Upper Bound)
% x(1)是纯电车速,最低 20,最高 80
% x(2)是电量下限,最低 0.2,最高 0.4
lb = [20, 0.2]; 
ub = [80, 0.4]; 

% 4. 配置遗传算法的参数
% 种群大小 20,也就是每代挑 20 组参数去跑 Simulink
% 跑 15 代结束战斗
opts = optimoptions('ga', 'PopulationSize', 20, 'MaxGenerations', 15, ...
    'Display', 'iter', 'PlotFcn', @gaplotbestf);

disp('系统准备就绪,正在疯狂调用 Simulink 模型进行进化,请去倒杯水...');

% 5. 启动!
[best_params, best_cost] = ga(@fitness_eval, nvars, [], [], [], [], lb, ub, [], opts);

% 6. 宣告胜利
fprintf('\n===== 优化结束 =====\n');
fprintf('遗传算法找到的最完美纯电最高限速是: %.2f km/h\n', best_params(1));
fprintf('遗传算法找到的最完美 SOC 下限是: %.4f\n', best_params(2));
fprintf('此时的最低综合代价 (油耗) 是: %.2f\n', best_cost);

终局操作: 确保 init_veh.mHEV_Model.slxfitness_eval.mrun_ga_opt.m 这四个文件都在同一个文件夹里。打开 run_ga_opt.m,点击绿色的 运行 按钮。如果你前四步做到了一丝不苟,你现在会看到 MATLAB 开始自动狂跑进度条并绘制进化曲线了。

bash 复制代码
===== 优化结束 =====
遗传算法找到的最完美纯电最高限速是: 20.00 km/h
遗传算法找到的最完美 SOC 下限是: 0.4000
此时的最低综合代价 (油耗) 是: 15766.03
相关推荐
Ulyanov4 小时前
基于 Python 的三维动态导弹攻防演示系统设计与实现:从架构到实战的深度剖析
开发语言·python·qt·架构·雷达电子对抗
小雅痞4 小时前
[Java][Leetcode middle] 15. 三数之和
java·算法·leetcode
图码4 小时前
矩阵数据结构入门指南:声明、初始化与基本操作
运维·数据结构·线性代数·算法·矩阵
苍煜4 小时前
Java自定义注解-SpringBoot实战
java·开发语言·spring boot
XS0301064 小时前
Java ArrayList
java·开发语言
Navigator_Z4 小时前
LeetCode //C - 1030. Matrix Cells in Distance Order
c语言·算法·leetcode
暴力求解4 小时前
Linux---保存信号
linux·运维·服务器·开发语言·操作系统
钝挫力PROGRAMER4 小时前
贫血模型的改进
java·开发语言·设计模式·架构
lsx2024064 小时前
AngularJS 事件处理机制详解
开发语言