Excel+脚本生成Simulink模型IO/观测量/标定量:MBD开发的效率密码
1.必要性在基于模型设计MBD, Model-Based Design的工程实践中Simulink作为核心建模工具其模型的规范性、可维护性和开发效率直接决定了项目的进度与质量。尤其是在汽车电子、工业控制等复杂控制系统开发中Simulink模型往往包含上百个输入输出IO信号、中间观测量和标定量若采用手动创建、配置的方式不仅耗时费力更易引发命名混乱、参数错配等问题成为MBD开发流程中的效率瓶颈。而Excel脚本如MATLAB脚本的组合恰好解决了这一痛点——通过Excel实现参数的集中管理、可视化配置再通过脚本将配置信息自动映射到Simulink模型中实现IO信号、中间观测量、标定量的批量生成与统一配置。这种方式并非简单的“技巧优化”而是MBD开发流程标准化、工程化落地的必要手段其必要性贯穿于建模、调试、迭代、协同的全生命周期。2.示例2.1 创建excel管理变量类型我们可以在Excel中按照项目规范搭建标准化模板分别创建“IO信号表”“中间观测量表”“标定量表”填写名称、数据类型、单位、上下限、采样周期等所有属性示例如下核心内容包括名称取值范围宽度描述存储类型数据类型单位初始值等。2.2 编写matlab脚本随后编写MATLAB脚本通过读取Excel表格中的数据批量创建IO端口、中间观测量信号、标定量参数并自动配置所有属性。由于脚本写的时间着实有些久远就不分布解析了直接贴下代码。逻辑并不复杂根据excel内容读取一些必要的参数比如名称取值范围存储类型初始值这些。function SigIntoInt(~) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5 %step 1,读表内容 ExcellFileuigetfile({*.xlsx;*.xls;},选择Interface文件);%选择文件夹返回文件夹带扩展名字符 if isequal(ExcellFile,0) error(User Canceled); else ExcleFileIndexstrfind(ExcellFile,.); InterfaceNameExcellFile(1:ExcleFileIndex-1); InPortAllreadtable(ExcellFile,sheet,In,VariableNamingRule,preserve); OutPortAllreadtable(ExcellFile,sheet,Out,VariableNamingRule,preserve); MPPortAllreadtable(ExcellFile,sheet,MP,VariableNamingRule,preserve); CALPortAllreadtable(ExcellFile,sheet,Cal,VariableNamingRule,preserve,Format,auto); datatype{boolean,uint8,uint16,uint32,single,int8,int16,int32}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Sheet IN内容创建 for i1:size(InPortAll,1) %Name 有空值直接报错否则继续执行 if strcmp(InPortAll{i,1},) error(请检查接口表Sheet In中Name不允许为空) end if ~isnumeric(InPortAll{i,2:3}) error(请检查Sheet In中MinMax非数值) end %Width 存在数值NaN、非数值字符空置或为0时直接给-1 if isnan(InPortAll{i,4}) | ~isnumeric(InPortAll{i,4}) | isempty(InPortAll{i,4}) | InPortAll{i,4}0 InPortAll{i,4}-1; end %discrition 不允许内容单纯为数值类型 if isnumeric(InPortAll{i,5}) error(Sheet In中Discrition内容请修改为字符类型); end %Storageclass 直接写Auto %DataType 检查datatype是否为规定值否则报错 for j1:length(datatype) if strcmp(InPortAll{i,7},datatype{1,j}) datatypedif0; break; end end if datatypedif1 error(请检查Sheet In中datatype是否正确) end %Unit 不允许为数值 if isnumeric(InPortAll{i,8}) error(请确认Sheet In中Uint是否为字符); end %InitialValue 初始值为NAn或非数值或为空时直接赋0 if isnan(InPortAll{i,9}) | ~isnumeric(InPortAll{i,9}) | isempty(InPortAll{i,9}) InPortAll{i,9}0; end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %sheet Out内容创建 for i1:size(OutPortAll,1) %Name 有空值直接报错否则继续执行 if strcmp(OutPortAll{i,1},) error(请检查接口表Sheet Out中Name不允许为空) end if ~isnumeric(OutPortAll{i,2:3}) error(请检查Sheet Out中MinMax非数值) end %Width 存在数值NaN、非数值字符空置或为0时直接给-1 if isnan(OutPortAll{i,4}) | ~isnumeric(OutPortAll{i,4}) | isempty(OutPortAll{i,4}) | OutPortAll{i,4}0 OutPortAll{i,4}-1; end %discrition 不允许内容单纯为数值类型 if isnumeric(OutPortAll{i,5}) error(Sheet Out中Discrition内容请修改为字符类型); end %Storageclass 直接写Auto %DataType 检查datatype是否为规定值否则报错 for j1:length(datatype) if strcmp(OutPortAll{i,7},datatype{1,j}) datatypedif0; break; end end if datatypedif1 error(请检查Sheet Out中datatype是否正确) end %Unit 不允许为数值 if isnumeric(OutPortAll{i,8}) error(请确认Sheet Out中Uint是否为字符); end %InitialValue 初始值为NAn或非数值或为空时直接赋0 if isnan(OutPortAll{i,9}) | ~isnumeric(OutPortAll{i,9}) | isempty(OutPortAll{i,9}) OutPortAll{i,9}0; end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Sheet MP内容创建 for i1:size(MPPortAll,1) %Name 有空值直接报错否则继续执行 if strcmp(MPPortAll{i,1},) error(请检查接口表Sheet MP中Name不允许为空) end if ~isnumeric(MPPortAll{i,2:3}) error(请检查Sheet MP中MinMax非数值) end %Width 存在数值NaN、非数值字符空置或为0时直接给-1 if isnan(MPPortAll{i,4}) | ~isnumeric(MPPortAll{i,4}) | isempty(MPPortAll{i,4}) | MPPortAll{i,4}0 MPPortAll{i,4}-1; end %discrition 不描述 %Storageclass 直接写Auto %DataType 检查datatype是否为规定值否则报错 for j1:length(datatype) if strcmp(MPPortAll{i,7},datatype{1,j}) datatypedif0; break; end end if datatypedif1 error(请检查Sheet MP中datatype是否正确) end %Unit 直接输出 %InitialValue 初始值为NAn或非数值或为空时直接赋0 if isnan(MPPortAll{i,9}) | ~isnumeric(MPPortAll{i,9}) | isempty(MPPortAll{i,9}) MPPortAll{i,9}0; end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Sheet Cal内容创建 for i1:size(CALPortAll,1) %Name 有空值直接报错否则继续执行 if strcmp(CALPortAll{i,1},) error(请检查接口表Sheet Cal中Name不允许为空) end if ~isnumeric(CALPortAll{i,2:3}) error(请检查Sheet Cal中MinMax非数值) end %Width 维度可不写 %discrition 默认输出 %Storageclass 不写 %DataType 检查datatype是否为规定值否则报错 for j1:length(datatype) if strcmp(CALPortAll{i,7},datatype{1,j}) datatypedif0; break; end end if datatypedif1 error(请检查Sheet Cal中datatype是否正确) end %Unit 默认输出 %InitialValue 初始值为NAn或非数值或为空时直接赋0 if strcmp(CALPortAll{i,9},) error(请检查Sheet Cal中初始值不能为空); end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% end %输出.m文件 output_m_file strcat(InterfaceName,.m); fid fopen(output_m_file,wt); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for i1:size(InPortAll,1) InName_str strcat(InPortAll{i,1}, ,Simulink.Signal,;\n); fprintf(fid,InName_str{1}); InStorageClass_strstrcat(InPortAll{i,1},.CoderInfo.StorageClass, ,,Auto,,;\n); fprintf(fid,InStorageClass_str{1}); InDiscrition_strstrcat(InPortAll{i,1},.Description, ,,InPortAll{i,5},,;\n); fprintf(fid,InDiscrition_str{1}); InDataType_strstrcat(InPortAll{i,1},.DataType, ,,InPortAll{i,7},,;\n); fprintf(fid,InDataType_str{1}); if isnan(InPortAll{i,2}) | ~isnumeric(InPortAll{i,2}) InMin_strstrcat(InPortAll{i,1},.Min, ,[],;\n); else InMin_strstrcat(InPortAll{i,1},.Min, ,num2str(InPortAll{i,2}),;\n); end fprintf(fid,InMin_str{1}); if isnan(InPortAll{i,3}) | ~isnumeric(InPortAll{i,3}) InMax_strstrcat(InPortAll{i,1},.Max, ,[],;\n); else InMax_strstrcat(InPortAll{i,1},.Max, ,num2str(InPortAll{i,3}),;\n); end fprintf(fid,InMax_str{1}); if isempty(InPortAll{i,4}) InUint_strstrcat(InPortAll{i,1},.DocUnits, ,,,;\n); else InUint_strstrcat(InPortAll{i,1},.DocUnits, ,,InPortAll{i,8},,;\n); end fprintf(fid,InUint_str{1}); InWidth_strstrcat(InPortAll{i,1},.Dimensions, ,num2str(InPortAll{i,4}),;\n); fprintf(fid,InWidth_str{1}); InDimensionsMode_strstrcat(InPortAll{i,1},.DimensionsMode, ,,auto,,;\n); fprintf(fid,InDimensionsMode_str{1}); InComplexity_strstrcat(InPortAll{i,1},.Complexity, ,,auto,,;\n); fprintf(fid,InComplexity_str{1}); InSampleTime_strstrcat(InPortAll{i,1},.SampleTime, ,-1,;\n); fprintf(fid,InSampleTime_str{1}); InInitialValue_strstrcat(InPortAll{i,1},.InitialValue, ,,num2str(InPortAll{i,9}),,;\n); fprintf(fid,InInitialValue_str{1}); fprintf(fid,\n); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5 for i1:size(OutPortAll,1) OutName_str strcat(OutPortAll{i,1}, ,Simulink.Signal,;\n); fprintf(fid,OutName_str{1}); OutStorageClass_strstrcat(OutPortAll{i,1},.CoderInfo.StorageClass, ,,Auto,,;\n); fprintf(fid,OutStorageClass_str{1}); OutDiscrition_strstrcat(OutPortAll{i,1},.Description, ,,OutPortAll{i,5},,;\n); fprintf(fid,OutDiscrition_str{1}); OutDataType_strstrcat(OutPortAll{i,1},.DataType, ,,OutPortAll{i,7},,;\n); fprintf(fid,OutDataType_str{1}); if isnan(OutPortAll{i,2}) | ~isnumeric(OutPortAll{i,2}) OutMin_strstrcat(OutPortAll{i,1},.Min, ,[],;\n); else OutMin_strstrcat(OutPortAll{i,1},.Min, ,num2str(OutPortAll{i,2}),;\n); end fprintf(fid,OutMin_str{1}); if isnan(OutPortAll{i,3}) | ~isnumeric(OutPortAll{i,3}) OutMax_strstrcat(OutPortAll{i,1},.Max, ,[],;\n); else OutMax_strstrcat(OutPortAll{i,1},.Max, ,num2str(OutPortAll{i,3}),;\n); end fprintf(fid,OutMax_str{1}); if isempty(OutPortAll{i,4}) OutUint_strstrcat(OutPortAll{i,1},.DocUnits, ,,,;\n); else OutUint_strstrcat(OutPortAll{i,1},.DocUnits, ,,OutPortAll{i,8},,;\n); end fprintf(fid,OutUint_str{1}); OutWidth_strstrcat(OutPortAll{i,1},.Dimensions, ,num2str(OutPortAll{i,4}),;\n); fprintf(fid,OutWidth_str{1}); OutDimensionsMode_strstrcat(OutPortAll{i,1},.DimensionsMode, ,,auto,,;\n); fprintf(fid,OutDimensionsMode_str{1}); OutComplexity_strstrcat(OutPortAll{i,1},.Complexity, ,,auto,,;\n); fprintf(fid,OutComplexity_str{1}); OutSampleTime_strstrcat(OutPortAll{i,1},.SampleTime, ,-1,;\n); fprintf(fid,OutSampleTime_str{1}); OutInitialValue_strstrcat(OutPortAll{i,1},.InitialValue, ,,num2str(OutPortAll{i,9}),,;\n); fprintf(fid,OutInitialValue_str{1}); fprintf(fid,\n); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5 for i1:size(MPPortAll,1) MPName_str strcat(MPPortAll{i,1}, ,Simulink.Signal,;\n); fprintf(fid,MPName_str{1}); MPStorageClass_strstrcat(MPPortAll{i,1},.CoderInfo.StorageClass, ,,Auto,,;\n); fprintf(fid,MPStorageClass_str{1}); MPDiscrition_strstrcat(MPPortAll{i,1},.Description, ,,,;\n); fprintf(fid,MPDiscrition_str{1}); MPDataType_strstrcat(MPPortAll{i,1},.DataType, ,,MPPortAll{i,7},,;\n); fprintf(fid,MPDataType_str{1}); if isnan(MPPortAll{i,2}) | ~isnumeric(MPPortAll{i,2}) MPMin_strstrcat(MPPortAll{i,1},.Min, ,[],;\n); else MPMin_strstrcat(MPPortAll{i,1},.Min, ,num2str(MPPortAll{i,2}),;\n); end fprintf(fid,MPMin_str{1}); if isnan(MPPortAll{i,3}) | ~isnumeric(MPPortAll{i,3}) MPMax_strstrcat(MPPortAll{i,1},.Max, ,[],;\n); else MPMax_strstrcat(MPPortAll{i,1},.Max, ,num2str(MPPortAll{i,3}),;\n); end fprintf(fid,MPMax_str{1}); MPUint_strstrcat(MPPortAll{i,1},.DocUnits, ,,,;\n); fprintf(fid,MPUint_str{1}); MPWidth_strstrcat(MPPortAll{i,1},.Dimensions, ,num2str(MPPortAll{i,4}),;\n); fprintf(fid,MPWidth_str{1}); MPDimensionsMode_strstrcat(MPPortAll{i,1},.DimensionsMode, ,,auto,,;\n); fprintf(fid,MPDimensionsMode_str{1}); MPComplexity_strstrcat(MPPortAll{i,1},.Complexity, ,,auto,,;\n); fprintf(fid,MPComplexity_str{1}); MPSampleTime_strstrcat(MPPortAll{i,1},.SampleTime, ,-1,;\n); fprintf(fid,MPSampleTime_str{1}); MPInitialValue_strstrcat(MPPortAll{i,1},.InitialValue, ,,num2str(MPPortAll{i,9}),,;\n); fprintf(fid,MPInitialValue_str{1}); fprintf(fid,\n); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5 for i1:size(CALPortAll,1) CalName_str strcat(CALPortAll{i,1}, ,Simulink.Parameter,;\n); fprintf(fid,CalName_str{1}); CalInitialValue_strstrcat(CALPortAll{i,1},.Value, ,num2str(CALPortAll{i,9}),;\n); fprintf(fid,CalInitialValue_str{1}); CalDataType_strstrcat(CALPortAll{i,1},.DataType, ,,CALPortAll{i,7},,;\n); fprintf(fid,CalDataType_str{1}); if isnan(CALPortAll{i,2}) | ~isnumeric(CALPortAll{i,2}) CalMin_strstrcat(CALPortAll{i,1},.Min, ,[],;\n); else CalMin_strstrcat(CALPortAll{i,1},.Min, ,num2str(CALPortAll{i,2}),;\n); end fprintf(fid,CalMin_str{1}); if isnan(CALPortAll{i,3}) | ~isnumeric(CALPortAll{i,3}) CalMax_strstrcat(CALPortAll{i,1},.Max, ,[],;\n); else CalMax_strstrcat(CALPortAll{i,1},.Max, ,num2str(CALPortAll{i,3}),;\n); end fprintf(fid,CalMax_str{1}); fprintf(fid,\n); end pcode(output_m_file); end2.3 生成.m或.p运行脚本选取所需生成的excel后会自动创建一个.m和p文件文件内容主要是参数的必须属性如下所示sUwbLocation_L_DistanceAC THBPackage.Signal; sUwbLocation_L_DistanceAC.CoderInfo.StorageClass ExportedGlobal; sUwbLocation_L_DistanceAC.Description Distance between tag C and base station A; sUwbLocation_L_DistanceAC.DataType single; sUwbLocation_L_DistanceAC.Min []; sUwbLocation_L_DistanceAC.Max []; sUwbLocation_L_DistanceAC.DocUnits m; sUwbLocation_L_DistanceAC.Dimensions -1; sUwbLocation_L_DistanceAC.DimensionsMode auto; sUwbLocation_L_DistanceAC.Complexity auto; sUwbLocation_L_DistanceAC.SampleTime -1; sUwbLocation_L_DistanceAC.InitialValue 0; sUwbLocation_L_DistanceBC THBPackage.Signal; sUwbLocation_L_DistanceBC.CoderInfo.StorageClass ExportedGlobal; sUwbLocation_L_DistanceBC.Description Distance between tag C and base station B; sUwbLocation_L_DistanceBC.DataType single; sUwbLocation_L_DistanceBC.Min []; sUwbLocation_L_DistanceBC.Max []; sUwbLocation_L_DistanceBC.DocUnits m; sUwbLocation_L_DistanceBC.Dimensions -1; sUwbLocation_L_DistanceBC.DimensionsMode auto; sUwbLocation_L_DistanceBC.Complexity auto; sUwbLocation_L_DistanceBC.SampleTime -1; sUwbLocation_L_DistanceBC.InitialValue 0; yUwbLocation_x_IntersectionX1 THBPackage.Signal; yUwbLocation_x_IntersectionX1.CoderInfo.StorageClass ExportedGlobal; yUwbLocation_x_IntersectionX1.Description The x-coordinate of intersection point 1; yUwbLocation_x_IntersectionX1.DataType single; yUwbLocation_x_IntersectionX1.Min []; yUwbLocation_x_IntersectionX1.Max []; yUwbLocation_x_IntersectionX1.DocUnits m; yUwbLocation_x_IntersectionX1.Dimensions -1; yUwbLocation_x_IntersectionX1.DimensionsMode auto; yUwbLocation_x_IntersectionX1.Complexity auto; yUwbLocation_x_IntersectionX1.SampleTime -1; yUwbLocation_x_IntersectionX1.InitialValue 0; yUwbLocation_x_IntersectionX2 THBPackage.Signal; yUwbLocation_x_IntersectionX2.CoderInfo.StorageClass ExportedGlobal; yUwbLocation_x_IntersectionX2.Description The y-coordinate of intersection point 1; yUwbLocation_x_IntersectionX2.DataType single; yUwbLocation_x_IntersectionX2.Min []; yUwbLocation_x_IntersectionX2.Max []; yUwbLocation_x_IntersectionX2.DocUnits m; yUwbLocation_x_IntersectionX2.Dimensions -1; yUwbLocation_x_IntersectionX2.DimensionsMode auto; yUwbLocation_x_IntersectionX2.Complexity auto; yUwbLocation_x_IntersectionX2.SampleTime -1; yUwbLocation_x_IntersectionX2.InitialValue 0; yUwbLocation_y_IntersectionY1 THBPackage.Signal; yUwbLocation_y_IntersectionY1.CoderInfo.StorageClass ExportedGlobal; yUwbLocation_y_IntersectionY1.Description ; yUwbLocation_y_IntersectionY1.DataType single; yUwbLocation_y_IntersectionY1.Min []; yUwbLocation_y_IntersectionY1.Max []; yUwbLocation_y_IntersectionY1.DocUnits m; yUwbLocation_y_IntersectionY1.Dimensions -1; yUwbLocation_y_IntersectionY1.DimensionsMode auto; yUwbLocation_y_IntersectionY1.Complexity auto; yUwbLocation_y_IntersectionY1.SampleTime -1; yUwbLocation_y_IntersectionY1.InitialValue 0; yUwbLocation_y_IntersectionY2 THBPackage.Signal; yUwbLocation_y_IntersectionY2.CoderInfo.StorageClass ExportedGlobal; yUwbLocation_y_IntersectionY2.Description ; yUwbLocation_y_IntersectionY2.DataType single; yUwbLocation_y_IntersectionY2.Min []; yUwbLocation_y_IntersectionY2.Max []; yUwbLocation_y_IntersectionY2.DocUnits m; yUwbLocation_y_IntersectionY2.Dimensions -1; yUwbLocation_y_IntersectionY2.DimensionsMode auto; yUwbLocation_y_IntersectionY2.Complexity auto; yUwbLocation_y_IntersectionY2.SampleTime -1; yUwbLocation_y_IntersectionY2.InitialValue 0; cUwbLocation_x_PointA Simulink.Parameter; cUwbLocation_x_PointA.Value 0; cUwbLocation_x_PointA.DataType single; cUwbLocation_x_PointA.Min []; cUwbLocation_x_PointA.Max []; cUwbLocation_y_PointA Simulink.Parameter; cUwbLocation_y_PointA.Value 0; cUwbLocation_y_PointA.DataType single; cUwbLocation_y_PointA.Min []; cUwbLocation_y_PointA.Max []; cUwbLocation_x_PointB Simulink.Parameter; cUwbLocation_x_PointB.Value 1; cUwbLocation_x_PointB.DataType single; cUwbLocation_x_PointB.Min []; cUwbLocation_x_PointB.Max []; cUwbLocation_y_PointB Simulink.Parameter; cUwbLocation_y_PointB.Value 0; cUwbLocation_y_PointB.DataType single; cUwbLocation_y_PointB.Min []; cUwbLocation_y_PointB.Max [];2.4 运行.p或.文件生成变量生成后直接运行.p / .m文件后会在工作区中生成excel中管理的变量内容。运行仿真后打开.slx模型后运行仿真即可。