以编程方式构建 CAN 通信的 Simulink 模型
此示例说明如何以编程方式构建 Simulink® 模型,以使用 CAN DBC 文件来引入 CAN 或 CAN FD 通信。使用 Simulink 的 add_block (Simulink) 和 set_param (Simulink) 函数,您可以添加并完整配置 Vehicle Network Toolbox™ 模块,以将网络通信添加到基本算法中。DBC 文件包含 CAN 报文和信号详细信息。首先需要以编程方式配置 CAN 和 CAN FD 的 Pack 和 Unpack 模块参数。这可以显著提高模型构造效率。
算法模型
示例模型 AlgorithmModel.slx
包含一个名为 Algorithm 的子系统模块。该模块表示在 Simulink 中开发的任何给定应用程序算法。出于演示目的,此子系统内部的 Gain (Simulink) 模块的值为 2。此子系统将 CAN 信号输入命名为“In1”。该输入值按增益值缩放。缩放值作为子系统的输出,命名为“Out1”。为了进行试验,可以更改增益值,并且可以用不同算法替换 Gain 模块。
CAN 数据库文件访问
您可以使用 canDatabase
函数访问 CAN DBC 文件的内容。通过此函数,可以获得关于网络节点、报文和信号的详细信息。
db = canDatabase("CANBus.dbc")
db = Database with properties: Name: 'CANBus' Path: 'C:\Users\jpyle\Documents\MATLAB\Examples\vnt-ex60686316\CANBus.dbc' Nodes: {'ECU'} NodeInfo: [1×1 struct] Messages: {2×1 cell} MessageInfo: [2×1 struct] Attributes: {} AttributeInfo: [0×0 struct] UserData: []
示例 CAN DBC 文件中定义了节点“ECU”,如下所示。
node = nodeInfo(db,"ECU")
node = struct with fields:
Name: 'ECU'
Comment: ''
Attributes: {}
AttributeInfo: [0×0 struct]
该节点接收包含信号“InitialValue”的 CAN 报文“AlgInput”。信号“InitialValue”是算法的输入。
messageInfo(db,"AlgInput")
ans = struct with fields:
Name: 'AlgInput'
ProtocolMode: 'CAN'
Comment: ''
ID: 100
Extended: 0
J1939: []
Length: 1
DLC: 1
BRS: 0
Signals: {'InitialValue'}
SignalInfo: [1×1 struct]
TxNodes: {0×1 cell}
Attributes: {}
AttributeInfo: [0×0 struct]
该节点传输包含信号“ScaledValue”的 CAN 报文“AlgOutput”。信号“ScaledValue”是算法的输出。
messageInfo(db,"AlgOutput")
ans = struct with fields:
Name: 'AlgOutput'
ProtocolMode: 'CAN'
Comment: ''
ID: 200
Extended: 0
J1939: []
Length: 2
DLC: 2
BRS: 0
Signals: {'ScaledValue'}
SignalInfo: [1×1 struct]
TxNodes: {'ECU'}
Attributes: {}
AttributeInfo: [0×0 struct]
以编程方式构建模型
打开示例模型
打开要配置的示例模型。
open AlgorithmModel
添加和配置 CAN Configuration 模块
在模型中添加并放置一个 CAN Configuration 模块。
add_block("canlib/CAN Configuration","AlgorithmModel/CAN Configuration") set_param("AlgorithmModel/CAN Configuration","position",[50,330,250,410])
设置“Device”参数,以使模型使用 MathWorks® 虚拟 CAN 设备。
set_param("AlgorithmModel/CAN Configuration","Device","MathWorks Virtual 1 (Channel 1)")
添加和配置 CAN Receive 模块
在模型中添加并放置一个 CAN Receive 模块。
add_block("canlib/CAN Receive","AlgorithmModel/CAN Receive") set_param("AlgorithmModel/CAN Receive","position",[50,200,250,280])
添加并放置一个 Terminator (Simulink) 模块。这用于连接 CAN Receive 模块的函数端口。在此示例中,我们将执行简单的报文接收。一般情况下,将 CAN Receive 放在Function-Call Subsystem (Simulink)内是使用 CAN 模块建模的首选方法。
add_block("simulink/Sinks/Terminator","AlgorithmModel/Terminator") set_param("AlgorithmModel/Terminator","position",[310,210,330,230])
设置“Device”参数,以使模型使用 MathWorks 虚拟 CAN 设备。
set_param("AlgorithmModel/CAN Receive","Device","MathWorks Virtual 1 (Channel 1)")
添加和配置 CAN Unpack 模块
在模型中添加并放置一个 CAN Unpack 模块。默认情况下,该模块处于“原始数据”模式。
add_block("canlib/CAN Unpack","AlgorithmModel/CAN Unpack") set_param("AlgorithmModel/CAN Unpack","position",[350,220,600,300])
在单次函数调用中,在 CAN Unpack 模块中设置以下参数:
DataFormat
CANdbFile
MsgList
set_param("AlgorithmModel/CAN Unpack","DataFormat","CANdb specified signals","CANdbFile",db.Path,"MsgList","AlgInput")
如果已对模块设置“DataFormat”和“CANdbFile”参数,则可以通过只包括“MsgList”参数来更改所选报文。
添加和配置 CAN Pack 模块
在模型中添加并放置一个 CAN Pack 模块。
add_block("canlib/CAN Pack","AlgorithmModel/CAN Pack") set_param("AlgorithmModel/CAN Pack","position",[1000,220,1250,300])
在单次函数调用中,在 CAN Pack 模块中设置以下参数:
DataFormat
CANdbFile
MsgList
set_param("AlgorithmModel/CAN Pack","DataFormat","CANdb specified signals","CANdbFile",db.Path,"MsgList","AlgOutput")
添加和配置 CAN Transmit 模块
在模型中添加并放置一个 CAN Transmit 模块。
add_block("canlib/CAN Transmit","AlgorithmModel/CAN Transmit") set_param("AlgorithmModel/CAN Transmit","position",[1350,220,1550,300])
设置“Device”参数,以使模型使用 MathWorks 虚拟 CAN 设备。此外,使用默认时序启用周期性传输。
set_param("AlgorithmModel/CAN Transmit","Device","MathWorks Virtual 1 (Channel 1)") set_param("AlgorithmModel/CAN Transmit", "EnablePeriodicTransmit", "on")
在模块之间建立连接
现在必须连接在模型中添加的 CAN 模块和算法模块。需要所有 CAN 模块的端口坐标。
canRxPort = get_param("AlgorithmModel/CAN Receive","PortConnectivity"); canUnpackPort = get_param("AlgorithmModel/CAN Unpack","PortConnectivity"); subSystemPort = get_param("AlgorithmModel/Subsystem","PortConnectivity"); canPackPort = get_param("AlgorithmModel/CAN Pack","PortConnectivity"); canTxPort = get_param("AlgorithmModel/CAN Transmit","PortConnectivity"); terminatorPort = get_param("AlgorithmModel/Terminator","PortConnectivity"); [canRxPortFunc,canRxPortMsg] = canRxPort.Position; [canUnpackPortIn,canUnpackPortOut] = canUnpackPort.Position; [subSystemPortIn,subSystemPortOut] = subSystemPort.Position; [canPackPortIn,canPackPortOut] = canPackPort.Position; canTxPortMsg = canTxPort.Position; terminatorPortIn = terminatorPort.Position;
添加信号线,以适当的顺序连接所有模块。
add_line("AlgorithmModel",[canRxPortMsg ; canUnpackPortIn]) add_line("AlgorithmModel",[canUnpackPortOut ; subSystemPortIn]) add_line("AlgorithmModel",[subSystemPortOut ; canPackPortIn]) add_line("AlgorithmModel",[canPackPortOut ; canTxPortMsg]) add_line("AlgorithmModel",[canRxPortFunc ; terminatorPortIn])
完成的模型
这就是构建和配置后的模型外观。
测试构建的模型
在 MATLAB 中配置 CAN 通道以与算法模型通信
使用 MathWorks 虚拟 CAN 设备的通道 2 在 MATLAB® 中创建 CAN 通道。它将与模型中的 CAN 通道通信。此外,将 CAN 数据库连接到 MATLAB 通道,使其自动解码传入的 CAN 数据。
canCh = canChannel("MathWorks","Virtual 1",2); canCh.Database = db;
对于从 MATLAB 到模型的传输,使用 CAN 数据库准备 CAN 报文作为算法的输入。
algInputMsg = canMessage(canCh.Database,"AlgInput");
运行算法模型
指定仿真时间并开始仿真
set_param("AlgorithmModel","StopTime","inf") set_param("AlgorithmModel","SimulationCommand","start")
暂停,直到仿真完全启动。
while strcmp(get_param("AlgorithmModel","SimulationStatus"),"stopped") end
运行 MATLAB 代码
启动 MATLAB CAN 通道。
start(canCh);
将具有不同信号数据的多个 CAN 报文作为模型的输入进行传输。
for value = 1:5 algInputMsg.Signals.InitialValue = value*value; transmit(canCh,algInputMsg) pause(1) end
从总线接收所有报文。请注意“AlgInput”和“AlgOutput”报文的实例、其时序和信号值。
msg = receive(canCh,Inf,"OutputFormat","timetable")
msg=10×8 timetable
Time ID Extended Name Data Length Signals Error Remote
____________ ___ ________ _____________ ___________ ______ ____________ _____ ______
0.009728 sec 100 false {'AlgInput' } {[ 1]} 1 {1×1 struct} false false
0.15737 sec 200 false {'AlgOutput'} {1×2 uint8} 2 {1×1 struct} false false
1.0121 sec 100 false {'AlgInput' } {[ 4]} 1 {1×1 struct} false false
1.1574 sec 200 false {'AlgOutput'} {1×2 uint8} 2 {1×1 struct} false false
2.0146 sec 100 false {'AlgInput' } {[ 9]} 1 {1×1 struct} false false
2.1574 sec 200 false {'AlgOutput'} {1×2 uint8} 2 {1×1 struct} false false
3.0177 sec 100 false {'AlgInput' } {[ 16]} 1 {1×1 struct} false false
3.1574 sec 200 false {'AlgOutput'} {1×2 uint8} 2 {1×1 struct} false false
4.0219 sec 100 false {'AlgInput' } {[ 25]} 1 {1×1 struct} false false
4.1574 sec 200 false {'AlgOutput'} {1×2 uint8} 2 {1×1 struct} false false
canSignalTimetable
函数提供了一种有效的方法,可以将 CAN 报文的信号值分离并针对每个报文组织成单个时间表。
signalTimeTable = canSignalTimetable(msg)
signalTimeTable = struct with fields:
AlgInput: [5×1 timetable]
AlgOutput: [5×1 timetable]
signalTimeTable.AlgInput
ans=5×1 timetable
Time InitialValue
____________ ____________
0.009728 sec 1
1.0121 sec 4
2.0146 sec 9
3.0177 sec 16
4.0219 sec 25
signalTimeTable.AlgOutput
ans=5×1 timetable
Time ScaledValue
___________ ___________
0.15737 sec 2
1.1574 sec 8
2.1574 sec 18
3.1574 sec 32
4.1574 sec 50
停止 CAN 通道。
stop(canCh)
停止算法模型
set_param("AlgorithmModel","SimulationCommand","stop")
绘制信号数据
绘制虚拟总线上出现的 CAN 报文的初始信号值和缩放信号值对时间戳的图。请注意由 MATLAB 传输的值的变化和由模型执行的数据缩放。
plot(signalTimeTable.AlgInput.Time,signalTimeTable.AlgInput.InitialValue,"Marker","square","MarkerIndices",1:5) hold on plot(signalTimeTable.AlgOutput.Time,signalTimeTable.AlgOutput.ScaledValue,"Marker","square","MarkerIndices",1:5) hold off xlabel("TimeStamp"); ylabel("CAN Signal Value"); legend("Initial Value","Scaled Value","Location","northeastoutside"); legend("boxoff");