メインコンテンツ

このページは機械翻訳を使用して翻訳されました。最新版の英語を参照するには、ここをクリックします。

CANを使用してSimulinkモデルのアプリを開発する

この例では、App Designer を使用してテスト アプリケーションのユーザー インターフェイス (UI) を構築し、仮想 CAN チャネルを使用してそれを Simulink ® モデルに接続する方法を示します。

テスト アプリケーション UI は、自動車クルーズ コントロール アプリケーションの Simulink モデルに仮想 CAN バス インターフェイスを提供するために、MATLAB ® App Designer といくつかの Vehicle Network Toolbox ™ 関数を使用して構築されます。テスト アプリケーション UI を使用すると、ユーザーはクルーズ コントロール アルゴリズム モデルに入力刺激を提供し、モデルからフィードバックされた結果を観察し、CAN メッセージをログに記録してテスト刺激をキャプチャし、ログに記録された CAN メッセージを再生してアルゴリズム モデルの問題をデバッグおよび修正できます。この例では、次の領域で CAN 通信を実装するために使用される主要な Vehicle Network Toolbox 関数とブロックを示します。

  • CAN経由でテストするためのSimulinkアルゴリズムモデルとの通信をサポートするテストアプリケーションUI

  • CANデータのログ記録と再生をサポートするテストアプリケーションUI

  • Simulinkアルゴリズムモデル

UIに仮想CANチャネル通信を追加する

このセクションでは、Simulink クルーズ コントロール アルゴリズム テスト アプリケーション モデルに CAN チャネル インターフェイスを追加するために使用される主要な Vehicle Network Toolbox 関数について説明します。これには次のトピックが含まれます。

  • 利用可能なCANチャネルのリストを取得する

  • チャネル作成のためのチャネル情報のフォーマット

  • UIでチャネルを作成する

  • CANメッセージを送受信するためのUIを構成する

  • チャネルの開始と停止

  • 選択したメッセージの抽出

App Designer を開く

App Designer でテスト アプリケーション UI を開きます。App Designer でテスト アプリケーション UI を開いた状態で、「デザイン」ビューと「コード」ビューを切り替えて、コントロールと対応する MATLAB コードを調べ、仮想 CAN チャネルを介して Simulink クルーズ コントロール アルゴリズム モデルと通信することができます。

準備スクリプトを実行して必要なデータを事前にロードし、App Designer を開いてテスト アプリを開発します。

helperPrepareTestBenchParameterData;
appdesigner('CruiseControlTestUI.mlapp')

利用可能なCANチャネルの一覧

まず、ユーザーが選択できる利用可能な CAN チャネルのリストを検索して表示するメカニズムを実装します。このため、テスト アプリケーション UI の左上隅に「チャネル構成」メニュー項目を追加しました。「CAN チャネルの選択」サブメニューがあります。

ユーザーが CAN チャネルの選択 サブメニューをクリックすると、サブメニュー コールバックを介してヘルパー関数 getAvailableCANChannelInfo(app) が呼び出されます。次のコードに示すように、getAvailableCANChannelInfo() は Vehicle Network Toolbox 関数 canChannelList を使用して利用可能な CAN チャネルを検出します。

function getAvailableCANChannelInfo(app)
    % Get a table containing all available CAN channels and devices.
    app.canChannelInfo = canChannelList;
    
    % Format CAN channel information for display on the UI.
    app.availableCANChannelsForDisplay = formatCANChannelEntryForDisplay(app);
    
    % Save the number of available constructors.
    app.numConstructors = numel(app.canChannelInfo.Vendor);
end

canChannelList を実行して、利用可能な CAN チャネル情報がどのように保存されるかを確認します。

canChannels = canChannelList
canChannels=4×6 table
      Vendor         Device       Channel    DeviceModel    ProtocolMode     SerialNumber
    ___________    ___________    _______    ___________    _____________    ____________

    "MathWorks"    "Virtual 1"       1        "Virtual"     "CAN, CAN FD"       "0"      
    "MathWorks"    "Virtual 1"       2        "Virtual"     "CAN, CAN FD"       "0"      
    "Vector"       "Virtual 1"       1        "Virtual"     "CAN, CAN FD"       "100"    
    "Vector"       "Virtual 1"       2        "Virtual"     "CAN, CAN FD"       "100"    

canChannelList から返されたチャネルのリストは UI プロパティ app.canChannelInfo に保存され、上記のように表示されます。

チャネル設定用のチャネルリストのフォーマット

ユーザーは、「CAN チャネル選択」listdlg から CAN チャネルを選択します。listdlg は、ユーザーの選択に対応するインデックスを返します。このインデックスはヘルパー関数 formatCANChannelConstructor に渡されます。

function canChannelConstructor = formatCANChannelConstructor(app, index)
    canChannelConstructor = "canChannel(" + "'" + app.canChannelInfo.Vendor(index) + "'" + ", " + "'" + app.canChannelInfo.Device(index) + "'" + ", " + app.canChannelInfo.Channel(index) + ")";
end

上記のコード フラグメントに示されているように、formatCANChannelConstructor は、CAN チャネルのテーブル app.canChannelInfo に格納されている文字列を使用して、ユーザーがチャネル セレクタ リスト ダイアログ ボックスから選択したチャネルに対応するチャネル オブジェクト コンストラクター文字列を組み立てます。CAN チャネル コンストラクター文字列の例を確認するには、以下のコードを実行します。

index = 1;
canChannelConstructor = "canChannel(" + "'" + canChannels.Vendor(index) + "'" + ", " + "'" + canChannels.Device(index) + "'" + ", " + canChannels.Channel(index) + ")"
canChannelConstructor = 
"canChannel('MathWorks', 'Virtual 1', 1)"

CAN チャネル コンストラクター文字列は、アプリ UI プロパティ app.canChannelConstructorSelected に保存され、後でアプリケーション UI で選択された CAN チャネル オブジェクトを作成するために、また Simulink クルーズ コントロール アルゴリズム モデルで CAN チャネル インターフェイスを実装する Vehicle Network Toolbox Simulink ブロックを更新するために使用されます。

UIでCANチャネルを作成する

UI が最初に開かれて使用されると、app.canChannelConstructorSelected に格納されているフォーマット済みの CAN チャネル コンストラクター文字列がヘルパー関数 setupCANChannel によって使用され、CAN チャネル オブジェクトのインスタンスが作成され、ネットワーク構成データベース (.dbc) ファイルに接続され、次のコード フラグメントに示すようにバス速度が設定されます。結果のチャネル オブジェクトは、UI プロパティ app.canChannelObj に保存されます。

function setupCANChannel(app)
    % Open CAN database file.
    db = canDatabase('CruiseControl.dbc');
    
    % Create a CAN channel for sending and receiving messages.
    app.canChannelObj = eval(app.canChannelConstructorSelected);
    
    % Attach CAN database to channel for received message decoding.
    app.canChannelObj.Database = db;
    
    % Set the baud rate (can only do this if the UI has channel initialization access).
    if app.canChannelObj.InitializationAccess
        configBusSpeed(app.canChannelObj, 500000);
    end
end 

CAN データベース オブジェクトの例を表示するには、次のコマンドを実行します。

db = canDatabase('CruiseControl.dbc')
db = 
  Database with properties:

             Name: 'CruiseControl'
             Path: 'C:\Users\jvijayak\OneDrive - MathWorks\Documents\MATLAB\ExampleManager\jvijayak.Bdoc23b.j2277816\vnt-ex00964061\CruiseControl.dbc'
        UTF8_File: 'C:\Users\jvijayak\OneDrive - MathWorks\Documents\MATLAB\ExampleManager\jvijayak.Bdoc23b.j2277816\vnt-ex00964061\CruiseControl.dbc'
            Nodes: {2×1 cell}
         NodeInfo: [2×1 struct]
         Messages: {2×1 cell}
      MessageInfo: [2×1 struct]
       Attributes: {'BusType'}
    AttributeInfo: [1×1 struct]
         UserData: []

CAN チャネル オブジェクトの例を表示するには、次のコマンドを実行します。

% Instantiate the CAN channel object using the channel constructor string.
canChannelObj = eval(canChannelConstructor);

% Attach the CAN database to the channel object.
canChannelObj.Database = db
canChannelObj = 
  Channel with properties:

   Device Information
            DeviceVendor: 'MathWorks'
                  Device: 'Virtual 1'
      DeviceChannelIndex: 1
      DeviceSerialNumber: 0
            ProtocolMode: 'CAN'

   Status Information
                 Running: 0
       MessagesAvailable: 0
        MessagesReceived: 0
     MessagesTransmitted: 0
    InitializationAccess: 1
        InitialTimestamp: [0×0 datetime]
           FilterHistory: 'Standard ID Filter: Allow All | Extended ID Filter: Allow All'

   Channel Information
               BusStatus: 'N/A'
              SilentMode: 0
         TransceiverName: 'N/A'
        TransceiverState: 'N/A'
       ReceiveErrorCount: 0
      TransmitErrorCount: 0
                BusSpeed: 500000
                     SJW: []
                   TSEG1: []
                   TSEG2: []
            NumOfSamples: []

   Other Information
                Database: [1×1 can.Database]
                UserData: []

setupCANChannel は次の Vehicle Network Toolbox 関数を使用します。

  • canChannel は、eval コマンドと、アプリ UI プロパティ app.canChannelConstructorSelected に保存されている CAN チャネル コンストラクター文字列を使用して、チャネル オブジェクトをインスタンス化します。結果のチャネル オブジェクトは、アプリ UI プロパティ app.canChannelObj. に保存されます。

  • canDatabase を使用して、DBC ファイルを表す CAN データベース (.dbc) オブジェクトを作成します。このオブジェクトは、チャネル オブジェクトの Database プロパティに格納されます。

CANメッセージを送信するためのセットアップ

選択した CAN チャネル オブジェクトを設定し、それを UI プロパティ app.canChannelObj に保存したら、次のステップでは、以下のコード フラグメントに示すように、ヘルパー関数 setupCANTransmitMessages を呼び出します。setupCANTransmitMessages は、UI から送信する CAN メッセージを定義し、メッセージ ペイロードに信号を入力し、各信号に値を割り当て、CAN チャネルが開始されたら定期的に送信するメッセージをキューに入れます。

function setupCANTransmitMessages(app)
    % Create a CAN message container.
    app.cruiseControlCmdMessage = canMessage(app.canChannelObj.Database, 'CruiseCtrlCmd');
    
    % Fill the message container with signals and assign values to each signal.
    app.cruiseControlCmdMessage.Signals.S01_CruiseOnOff = logical2Numeric(app, app.cruisePowerCheckBox.Value);
    app.cruiseControlCmdMessage.Signals.S02_Brake =  logical2Numeric(app, app.brakeOnOffCheckBox.Value);
    app.cruiseControlCmdMessage.Signals.S03_VehicleSpeed =  app.vehicleSpeedSlider.Value;
    app.cruiseControlCmdMessage.Signals.S04_CoastSetSw =  logical2Numeric(app, app.cruiseCoastSetCheckBox.Value);
    app.cruiseControlCmdMessage.Signals.S05_AccelResSw =  logical2Numeric(app, app.cruiseAccelResumeCheckBox.Value);
    
    % Set up periodic transmission of this CAN message.  Actual transmission starts/stops with CAN channel start/stop.
    transmitPeriodic(app.canChannelObj, app.cruiseControlCmdMessage, 'On', 0.1);
end

CAN メッセージ オブジェクトがどのようになっているかを確認するには、次のコマンドを実行します。

cruiseControlCmdMessage = canMessage(canChannelObj.Database, 'CruiseCtrlCmd')
cruiseControlCmdMessage = 
  Message with properties:

   Message Identification
    ProtocolMode: 'CAN'
              ID: 256
        Extended: 0
            Name: 'CruiseCtrlCmd'

   Data Details
       Timestamp: 0
            Data: [0 0]
         Signals: [1×1 struct]
          Length: 2

   Protocol Flags
           Error: 0
          Remote: 0

   Other Information
        Database: [1×1 can.Database]
        UserData: []

cruiseControlCmdMessage.Signals
ans = struct with fields:
    S03_VehicleSpeed: 0
      S05_AccelResSw: 0
      S04_CoastSetSw: 0
           S02_Brake: 0
     S01_CruiseOnOff: 0

setupCANTransmitMessages は次の Vehicle Network Toolbox 関数を使用します。

  • canMessage は、CAN データベース オブジェクトで定義された CAN メッセージを構築します。

  • transmitPeriodic は、UI プロパティ app.cruiseControlCmdMessage に格納されているメッセージを、UI プロパティ app.canChannelObj に格納されているチャネル オブジェクトによって定義されたチャネルで、最後の引数で指定されたレート (この場合は 0.1 秒ごと) で定期的に送信するためにキューに入れます。

CANメッセージを受信するための設定

UI は、Simulink モデル内のクルーズ コントロール アルゴリズムからのフィードバックを使用してプロットを更新するために、定期的に CAN メッセージを受信する必要があります。これを実現するには、まずこのコード フラグメントに示すように、MATLAB タイマー オブジェクトを作成します。

% create a timer to receive CAN msgs
app.receiveCANmsgsTimer = timer('Period', 0.5,...
    'ExecutionMode', 'fixedSpacing', ...
    'TimerFcn', @(~,~)receiveCANmsgsTimerCallback(app));

タイマー オブジェクトは、0.5 秒ごとにタイマー コールバック関数 receiveCANmsgsTimerCallback を呼び出します。以下のコード フラグメントに示されている receiveCANmsgsTimerCallback は、バスからすべての CAN メッセージを取得し、ヘルパー関数 getCruiseCtrlFBCANmessage を使用して、クルーズ コントロール アルゴリズム モデルからフィードバックされた CAN メッセージを抽出し、抽出された CAN メッセージ データで UI プロットを更新します。

% receiveCANmsgsTimerCallback Timer callback function for GUI updating
function receiveCANmsgsTimerCallback(app)
    try
        % Receive available CAN messages.
        msg = receive(app.canChannelObj, Inf, 'OutputFormat', 'timetable');
        
        % Update Cruise Control Feedback CAN message data.
        newFbData = getCruiseCtrlFBCANmessage(app, msg);
        
        if ~newFbData
            return;
        end
        
        % Update target speed and engaged plots with latest data from CAN bus.
        updatePlots(app);
    catch err
        disp(err.message)
    end
end

receive コマンドから返されるメッセージを確認するには、次のコードを実行します。

% Queue periodic transmission of a CAN message to generate some message data once the channel
% starts.
transmitPeriodic(canChannelObj, cruiseControlCmdMessage, 'On', 0.1);

% Start the channel.
start(canChannelObj);

% Wait 1 second to allow time for some messages to be generated on the bus.
pause(1);

% Retrieve all messages from the bus and output the results as a timetable.
msg = receive(canChannelObj, Inf, 'OutputFormat','timetable')
msg=10×8 timetable
        Time        ID     Extended          Name            Data      Length      Signals       Error    Remote
    ____________    ___    ________    _________________    _______    ______    ____________    _____    ______

    0.016169 sec    256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.12518 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.23315 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.34315 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.45114 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.56016 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.6692 sec      256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.77918 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.88211 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.98119 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 

% Stop the channel.
stop(canChannelObj)

receiveCANmsgsTimerCallback は次の Vehicle Network Toolbox 関数を使用します。

  • receive は CAN バスから CAN メッセージを取得します。この場合、関数は前回の呼び出し以降のすべてのメッセージを取得し、結果を MATLAB timetable として出力するように構成されています。

選択した CAN メッセージを抽出

ヘルパー関数 getCruiseCtrlFBCANmessage は、取得したすべての CAN メッセージから「CruiseCtrlFB」メッセージを除外し、これらのメッセージから tspeedFb 信号と engagedFb 信号を抽出し、これらを tspeedFb 信号と engagedFb 信号の MATLAB 時系列オブジェクトに連結します。これらの時系列オブジェクトは、それぞれ UI プロパティ app.tspeedFbapp.engagedFb に保存されます。保存された時系列信号は、UI 上の各信号のプロットを更新するために使用されます。seconds メソッドを使用して、timetable に格納されている時間データを期間配列から各信号の timeseries オブジェクト内の秒単位の同等の数値配列に変換していることに注意してください。

function newFbData = getCruiseCtrlFBCANmessage(app, msg)
    % Exit if no messages were received as there is nothing to update.
    if isempty(msg)
        newFbData = false;
        return;
    end
    
    % Extract signals from all CruiseCtrlFB messages.
    cruiseCtrlFBSignals = canSignalTimetable(msg, "CruiseCtrlFB");
    
    % if no messages then just return as there is nothing to do
    if isempty(cruiseCtrlFBSignals)
        newFbData = false;
        return;
    end
    
    if ~isempty(cruiseCtrlFBSignals)
        % Received new Cruise Control Feedback messages, so create time series from CAN signal data
        % save the Target Speed feedback signal.
        if isempty(app.tspeedFb) % cCeck if target speed feedback property has been initialized.
            app.tspeedFb = cell(2,1);
            
            % It appears Simulink.SimulationData.Dataset class is not
            % compatible with MATLAB Compiler, so change the way we store data
            % from a Dataset format to cell array.
            
            % Save target speed actual data.
            app.tspeedFb = timeseries(cruiseCtrlFBSignals.F02_TargetSpeed, seconds(cruiseCtrlFBSignals.Time),...
                'Name','CruiseControlTargetSpeed');
        else  % Add to existing data.
            % Save target speed actual data.
            app.tspeedFb = timeseries([app.tspeedFb.Data; cruiseCtrlFBSignals.F02_TargetSpeed], ...
                [app.tspeedFb.Time; seconds(cruiseCtrlFBSignals.Time)],'Name','CruiseControlTargetSpeed');
        end
        
        % Save the Cruise Control Engaged actual signal.
        % Check if Cruise engaged property has been initialized.
        if isempty(app.engagedFb)    
            app.engagedFb = cell(2,1);

            % It appears Simulink.SimulationData.Dataset class is not
            % compatible with MATLAB Compiler, so change the way we store data
            % from a Dataset format to cell array.
            
            % Save cruise engaged command data.
            app.engagedFb = timeseries(cruiseCtrlFBSignals.F01_Engaged,seconds(cruiseCtrlFBSignals.Time),...
                'Name','CruiseControlEngaged');
        else  % Add to existing logsout.
            % Save cruise engaged command data.
            app.engagedFb = timeseries([app.engagedFb.Data; cruiseCtrlFBSignals.F01_Engaged], ...
                [app.engagedFb.Time; seconds(cruiseCtrlFBSignals.Time)],'Name','CruiseControlEngaged');
        end
        
        newFbData = true;
    end
end

ヘルパー関数 getCruiseCtrlFBCANmessage は次の Vehicle Network Toolbox 関数を使用します。

  • canSignalTimetable は、CAN メッセージ CruiseCtrlFB. からの信号を含む MATLAB timetable を返します。

CANチャネルを開始する

CAN チャネル オブジェクト app.canChannelObj がインスタンス化され、メッセージの送受信が設定されたら、チャネルを開始できます。ユーザーが UI で Start Sim をクリックしたときに、Simulink モデルの実行を開始する直前にチャネルが開始されるようにします。これを実現するために、ヘルパー関数 startSimApplication が呼び出されます。以下のコード フラグメントに示されている startSimApplication は、仮想 CAN チャネルを使用しているかどうかを確認します。これは、デスクトップ シミュレーションのみを使用している場合に意味をなす唯一のタイプであるためです。次に、bdIsLoaded コマンドを使用して、接続する Simulink モデルがメモリにロードされているかどうかを確認します。モデルがロードされ、まだ実行されていない場合は、新しい信号データを受け入れるために UI プロットがクリアされ、ヘルパー関数 startCANChannel を使用して CAN チャネルが開始され、モデルが開始されます。

function startSimApplication(app, index)
    % Start the model running on the desktop.
    
    % Check to see if hardware or virtual CAN channel is selected, otherwise do nothing.
    if app.canChannelInfo.DeviceModel(index) == "Virtual"
        % Check to see if the model is loaded before trying to run.
        if bdIsLoaded(app.mdl)
            % Model is loaded, now check to see if it is already running.
            if ~strcmp('running',get_param(app.mdl,'SimulationStatus'))
                % Model is not already running, so start it
                % flush the CAN Receive message buffers.
                app.tspeedFb = [];
                app.engagedFb = [];
                
                % Clear figure window.
                cla(app.tspeedPlot)
                cla(app.engagedPlot)
                
                % Start the CAN channels and update timer if it isn't already running.
                startCANChannel(app);
                
                % Start the model.
                set_param(app.mdl, 'SimulationCommand', 'start');
                
                % Set the sim start/stop button icon to the stop icon indicating the model has
                % been successfully started and is ready to be stopped at the next button press.
                app.SimStartStopButton.Icon = "IconEnd.png";
                app.StartSimLabel.Text = "Stop Sim";
            else
                % Model is already running, inform the user.
                warnStr = sprintf('Warning: Model %s is already running', app.mdl);
                warndlg(warnStr, 'Warning');
            end
        else
            % Model is not yet loaded, so warn the user.
            warnStr = sprintf('Warning: Model %s is not loaded\nPlease load the model and try again', app.mdl);
            warndlg(warnStr, 'Warning');
        end
    end
end

ヘルパー関数 startCANChannel は以下のコード フラグメントに示されています。この関数は、チャネルを開始する前に、チャネルがまだ実行されていないことを確認します。次に、MATLAB タイマー オブジェクトを開始し、前のセクションで説明したタイマー コールバック関数 receiveCANmsgsTimerCallback が 0.5 秒ごとに呼び出され、バスから CAN メッセージ データを取得します。

function startCANChannel(app)
    % Start the CAN channel if it isn't already running.
    try
        if ~app.canChannelObj.Running
            start(app.canChannelObj);
        end
    catch
        % do nothing.
    end
    
    % Start the CAN receive processing timer - check to see if it is already running. This allows us to change CAN channels
    % with or without starting and stopping the model running on the real time target.
    if strcmpi(app.receiveCANmsgsTimer.Running, 'off')
        start(app.receiveCANmsgsTimer);
    end
end

startCANchannel は次の Vehicle Network Toolbox 関数を使用します。

  • start は CAN チャネルの実行を開始します。stop コマンドが発行されるまで、チャネルはオンラインのままになります。

CANチャネルを停止する

ユーザーが UI で Stop Sim をクリックしたときに、CAN チャネルを停止する直前に Simulink モデルを停止します。これを実現するために、ヘルパー関数 stopSimApplication が呼び出されます。以下のコード フラグメントに示されている stopSimApplication は、仮想 CAN チャネルを使用しているかどうかを確認します。これは、デスクトップ シミュレーションのみを使用している場合に意味をなす唯一のタイプであるためです。次に、Simulink モデルを停止し、ヘルパー関数 stopCANChannel を呼び出して CAN チャネルを停止します。

function stopSimApplication(app, index)
    % Stop the model running on the desktop.
    try
        % Check to see if hardware or virtual CAN channel is selected.
        if app.canChannelInfo.DeviceModel(index) == "Virtual"
            % Virtual channel selected, so issue a stop command to the
            % the simulation, even if it is already stopped.
            set_param(app.mdl, 'SimulationCommand', 'stop')
            
            % Stop the CAN channels and update timer.
            stopCANChannel(app);
        end
        
        % Set the sim start/stop button text to Start indicating the model has
        % been successfully stopped and is ready to start again at the next
        % button press.
        app.SimStartStopButton.Icon = "IconPlay.png";
        app.StartSimLabel.Text = "Start Sim";
    catch
        % Do nothing at the moment.
    end
end

ヘルパー関数 stopCANChannel は以下のコード フラグメントに示されています。この関数は、CAN チャネルを停止し、次に MATLAB タイマー オブジェクトを停止して、前述のタイマー コールバック関数 receiveCANmsgsTimerCallback がバスからメッセージを取得するために呼び出されないようにします。

function stopCANChannel(app)
    % Stop the CAN channel.
    stop(app.canChannelObj);
    
    % Stop the CAN message processing timer.
    stop(app.receiveCANmsgsTimer);
end

stopCANchannel は次の Vehicle Network Toolbox 関数を使用します。

  • stop は CAN チャネルを停止します。別の start コマンドが発行されるまで、チャネルはオフラインのままになります。

CANログと再生機能の追加

このステップでは、CAN メッセージをログに記録、保存、再生する関数を追加するために使用される主要な Vehicle Network Toolbox 関数について説明します。この機能を実装するには、最初に作成したものと同じ 2 番目の CAN チャネルをインスタンス化します。2 番目の CAN チャネル オブジェクトは最初のオブジェクトと同一であるため、最初の CAN チャネル オブジェクトと同じメッセージを表示および収集します。ユーザーが UI で ログ記録の開始 をクリックすると、2 番目の CAN チャネルが開始されます。ユーザーが ログの停止 をクリックするまで、チャネルは実行され、メッセージの収集を継続します。ユーザーが CAN メッセージのログ記録を停止すると、2 番目の CAN チャネル オブジェクトのメッセージ バッファーに蓄積されたすべてのメッセージを取得し、再生するメッセージを抽出して、MAT ファイルに保存します。メッセージが保存されると、最初の CAN チャネルを使用してメッセージを再生し、デバッグとアルゴリズムの検証のために Simulink クルーズ コントロール アルゴリズム モデルに入力刺激を提供できます。

この説明では、次のトピックについて説明します。

  • CANメッセージをログに記録するためのチャネルを設定する

  • チャネルの開始と停止

  • 記録されたCANメッセージの取得と抽出

  • 抽出したメッセージをファイルに保存する

  • ファイルから保存したメッセージを読み込む

  • 記録されたCANメッセージの再生を開始する

  • 記録されたCANメッセージの再生を停止する

UI で CAN チャネル オブジェクトを設定して CAN メッセージをログに記録する

最初の CAN チャネルがインスタンス化された方法とまったく同じように、app.canChannelConstructorSelected に格納されているフォーマット済みの CAN チャネル コンストラクター文字列が新しいヘルパー関数 setupCANLogChannel によって使用され、CAN チャネル オブジェクトの 2 番目のインスタンスが作成され、最初のチャネルに使用されたのと同じネットワーク構成データベース (.dbc) ファイルに接続され、以下のコード フラグメントに示すようにバス速度が設定されます。結果のチャネル オブジェクトは UI プロパティ app.canLogChannelObj に保存されます。

function setupCANLogChannel(app)
    % Open CAN database file.
    db = canDatabase('CruiseControl.dbc');
    
    % Create a CAN channel for sending and receiving messages.
    app.canLogChannelObj = eval(app.canChannelConstructorSelected);
    
    % Attach CAN database to channel for received message decoding.
    app.canLogChannelObj.Database = db;
    
    % Set the baud rate (can only do this if the UI has channel initialization access).
    if app.canLogChannelObj.InitializationAccess
        configBusSpeed(app.canLogChannelObj, 500000);
    end
end

CAN データベース オブジェクトの例を確認するには、次のコードを実行します。

db = canDatabase('CruiseControl.dbc')
db = 
  Database with properties:

             Name: 'CruiseControl'
             Path: 'C:\Users\jvijayak\OneDrive - MathWorks\Documents\MATLAB\ExampleManager\jvijayak.Bdoc23b.j2277816\vnt-ex00964061\CruiseControl.dbc'
        UTF8_File: 'C:\Users\jvijayak\OneDrive - MathWorks\Documents\MATLAB\ExampleManager\jvijayak.Bdoc23b.j2277816\vnt-ex00964061\CruiseControl.dbc'
            Nodes: {2×1 cell}
         NodeInfo: [2×1 struct]
         Messages: {2×1 cell}
      MessageInfo: [2×1 struct]
       Attributes: {'BusType'}
    AttributeInfo: [1×1 struct]
         UserData: []

CAN チャネル オブジェクトの例を表示するには、次のコードを実行します。

% Instantiate the CAN channel object using the channel constructor string.
canLogChannelObj = eval(canChannelConstructor);

% Attach the CAN database to the channel object.
canLogChannelObj.Database = db
canLogChannelObj = 
  Channel with properties:

   Device Information
            DeviceVendor: 'MathWorks'
                  Device: 'Virtual 1'
      DeviceChannelIndex: 1
      DeviceSerialNumber: 0
            ProtocolMode: 'CAN'

   Status Information
                 Running: 0
       MessagesAvailable: 0
        MessagesReceived: 0
     MessagesTransmitted: 0
    InitializationAccess: 0
        InitialTimestamp: [0×0 datetime]
           FilterHistory: 'Standard ID Filter: Allow All | Extended ID Filter: Allow All'

   Channel Information
               BusStatus: 'N/A'
              SilentMode: 0
         TransceiverName: 'N/A'
        TransceiverState: 'N/A'
       ReceiveErrorCount: 0
      TransmitErrorCount: 0
                BusSpeed: 500000
                     SJW: []
                   TSEG1: []
                   TSEG2: []
            NumOfSamples: []

   Other Information
                Database: [1×1 can.Database]
                UserData: []

setupCANLogChannel は次の Vehicle Network Toolbox 関数を使用します。

  • canChannel は、eval コマンドと、アプリ UI プロパティ app.canChannelConstructorSelected に保存されている CAN チャネル コンストラクター文字列を使用して、チャネル オブジェクトをインスタンス化します。結果のチャネル オブジェクトは、アプリ UI プロパティ app.canLogChannelObj. に保存されます。

  • canDatabase は、DBC ファイルを表す CAN データベース (.dbc) オブジェクトを作成します。このオブジェクトは、チャネル オブジェクトの Database プロパティに格納されます。

CANログチャネルを開始する

最初の CAN チャネルと同様に、テスト アプリケーション UI が開かれると、CAN チャネル オブジェクト app.canLogChannelObj がインスタンス化されます。ユーザーが ログ記録を開始 をクリックすると、以下のコードに示すように、ヘルパー関数 startCANLogChannel が呼び出されます。この関数は、2 番目の CAN チャネルがすでに実行されているかどうかを確認し、実行されていない場合は開始します。

function startCANLogChannel(app)
    % Start the CAN Log channel if it isn't already running.
    try
        if ~app.canLogChannelObj.Running
            start(app.canLogChannelObj);
        end
    catch
        % Do nothing.
    end
end

startCANLogChannel は次の Vehicle Network Toolbox 関数を使用します。

  • start を実行して CAN チャネルの実行を開始します。チャネルは、stop コマンドが発行されるまでオンラインのままになります。

CANログチャネルを停止する

ユーザーが UI で ログ記録の停止 をクリックすると、ボタンのコールバックによって、以下のコード フラグメントに示すヘルパー関数 stopCANLogging が呼び出されます。stopCANLogging は CAN チャネルを停止し、ユーザーが ログ記録の開始 をクリックして 2 番目の CAN チャネルを開始してから 2 番目のチャネル バッファーに蓄積されたすべてのメッセージを取得します。

function stopCANLogging(app)
    % Stop the CAN Log channel.
    stop(app.canLogChannelObj);
    
    % Get the messages from the CAN log message queue.
    retrieveLoggedCANMessages(app);
    
    % Update the button icon and label.
    app.canLoggingStartStopButton.Icon = 'IconPlay.png';
    app.StartLoggingLabel.Text = "Start Logging";
end

stopCANLogging は次の Vehicle Network Toolbox 関数を使用します。

  • stop は CAN チャネルを停止します。別の start コマンドが発行されるまで、チャネルはオフラインのままになります。

ログに記録されたメッセージの取得と抽出

ログ記録 CAN チャネルが停止されると、以下のコード フラグメントに示すヘルパー関数 retrieveLoggedCANMessages が呼び出され、2 番目のチャネル バスからすべての CAN メッセージを取得します。CAN メッセージは、receive コマンドを使用して 2 番目のチャネル バスから取得され、論理インデックスを使用して、receive コマンドによって返されたすべての message timetable から "CruiseCtrlCmd" メッセージが抽出されます。

function retrieveLoggedCANMessages(app)
    try
        % Receive available CAN message
        % initialize buffer to make sure it is empty.
        app.canLogMsgBuffer = [];
        
        % Receive available CAN messages.
        msg = receive(app.canLogChannelObj, Inf, 'OutputFormat', 'timetable');
        
        % Fill the buffer with the logged Cruise Control Command CAN message data.
        app.canLogMsgBuffer = msg(msg.Name == "CruiseCtrlCmd", :);
    catch err
        disp(err.message)
    end
end

receive コマンドから返されるメッセージが timetable 形式でどのように表示されるかを確認するには、次のコードを実行します。

% Queue periodic transmission of CAN messages to generate sample message data once the channel starts.
cruiseControlFbMessage = canMessage(db, 'CruiseCtrlFB');
transmitPeriodic(canChannelObj, cruiseControlFbMessage, 'On', 0.1);
transmitPeriodic(canChannelObj, cruiseControlCmdMessage, 'On', 0.1);

% Start the first channel.
start(canChannelObj);

% Start the second (logging) channel.
start(canLogChannelObj);

% Wait 1 second to allow time for some messages to be generated on the bus.
pause(1);

% Stop the channels.
stop(canChannelObj)
stop(canLogChannelObj)

% Retrieve all messages from the logged message bus and output the results as a timetable.
msg = receive(canLogChannelObj, Inf, 'OutputFormat','timetable')
msg=52×8 timetable
        Time        ID     Extended          Name            Data      Length      Signals       Error    Remote
    ____________    ___    ________    _________________    _______    ______    ____________    _____    ______

    0.073091 sec    256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.073094 sec    512     false      {'CruiseCtrlFB' }    {[0 0]}      2       {1×1 struct}    false    false 
    0.18515 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.18515 sec     512     false      {'CruiseCtrlFB' }    {[0 0]}      2       {1×1 struct}    false    false 
    0.29512 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.29513 sec     512     false      {'CruiseCtrlFB' }    {[0 0]}      2       {1×1 struct}    false    false 
    0.40412 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.40413 sec     512     false      {'CruiseCtrlFB' }    {[0 0]}      2       {1×1 struct}    false    false 
    0.51208 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.51208 sec     512     false      {'CruiseCtrlFB' }    {[0 0]}      2       {1×1 struct}    false    false 
    0.62109 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.62109 sec     512     false      {'CruiseCtrlFB' }    {[0 0]}      2       {1×1 struct}    false    false 
    0.73113 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.73113 sec     512     false      {'CruiseCtrlFB' }    {[0 0]}      2       {1×1 struct}    false    false 
    0.83307 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.83307 sec     512     false      {'CruiseCtrlFB' }    {[0 0]}      2       {1×1 struct}    false    false 
      ⋮

% Extract only the Cruise Control Command CAN messages.
msgCmd = msg(msg.Name == "CruiseCtrlCmd", :)
msgCmd=26×8 timetable
        Time        ID     Extended          Name            Data      Length      Signals       Error    Remote
    ____________    ___    ________    _________________    _______    ______    ____________    _____    ______

    0.073091 sec    256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.18515 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.29512 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.40412 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.51208 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.62109 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.73113 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.83307 sec     256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    0.9331 sec      256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    1.0431 sec      256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    1.1531 sec      256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    1.2631 sec      256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    1.3703 sec      256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    1.4776 sec      256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    1.5872 sec      256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
    1.6972 sec      256     false      {'CruiseCtrlCmd'}    {[0 0]}      2       {1×1 struct}    false    false 
      ⋮

retrieveLoggedCANMessages は次の Vehicle Network Toolbox 関数を使用します。

  • receive は CAN バスから CAN メッセージを取得します。この場合、関数は前回の呼び出し以降のすべてのメッセージを取得し、結果を MATLAB timetable として出力するように構成されています。

メッセージをファイルに保存する

ユーザーが UI で ログデータを保存 をクリックすると、ヘルパー関数 saveLoggedCANDataToFile が呼び出されます。この関数は、uinputfile 関数を使用してファイル ブラウザー ウィンドウを開きます。uinputfile は、ログに記録された CAN メッセージ データを保存するためにユーザーが選択したファイル名とパスを返します。Vehicle Network Toolbox 関数 canMessageReplayBlockStruct は、CAN メッセージを MATLAB timetable から CAN Replay ブロックが使用できる形式に変換するために使用されます。記録された CAN メッセージ データが変換されファイルに保存されると、後で Vehicle Network Toolbox「再生」Simulink ブロックを使用して呼び出して再生できます。replay コマンドを使用して MATLAB 内のメッセージを Vehicle Network Toolbox で再生するには、timetable 自体が関数への入力として渡されます。

function savedLoggedCANDataToFile(app)
    % Raise dialog box to prompt user for a CAN log file to store the logged data.
    [FileName,PathName] = uiputfile('*.mat','Select a .MAT file to store logged CAN data');

    if FileName ~= 0
        % User did not cancel the file selection operation, so OK to save
        % convert the CAN log data from Timetable to struct of arrays so the data is compatible
        % with the VNT Simulink Replay block.
        canLogStructOfArrays = canMessageReplayBlockStruct(app.canLogMsgBuffer);
        save(fullfile(PathName, FileName), 'canLogStructOfArrays');

        % Clear the buffer after saving it.
        app.canLogMsgBuffer = [];
    end
end

saveLoggedCANDataToFile は次の Vehicle Network Toolbox 関数を使用します。

  • canMessageReplayBlockStruct は、MATLAB timetable に保存されている CAN メッセージを、CAN メッセージ再生ブロックで使用できる形式に変換します。

ファイルからメッセージを読み込む

ユーザーが UI で ログ データの読み込み をクリックすると、ヘルパー関数 loadLoggedCANDataFromFile が呼び出されます。この関数は、uigetfile 関数を使用してファイル ブラウザー ウィンドウを開き、ユーザーが選択したログ メッセージのファイル名を返します。記録された CAN メッセージ データはファイルから読み込まれ、Vehicle Network Toolbox replay コマンドで使用するために timetable 表現に変換されます。ユーザーが Simulink モデルからデータを再生したい場合は、同じデータ ファイルを Vehicle Network Toolbox Replay Simulink ブロックで直接使用できることに注意してください。Replay ブロックは Simulink デバッガーと連携して動作し、シミュレーションがブレークポイントで停止すると再生を一時停止するため、UI から replay コマンドを使用する代わりに、Replay ブロックを使用してデータを再生することを選択できます。対照的に、replay コマンドは、Simulink モデルがブレークポイントで停止したことを認識せず、ファイルに保存されたメッセージ データを再生し続けます。この例では、replay コマンドを使用してデータを再生します。

function loadLoggedCANDataFromFile(app)
    % Raise dialog box to prompt user for a CAN log file to load.
    [FileName,PathName] = uigetfile('*.mat','Select a CAN log file to load');
    
    % Return focus to main UI after dlg closes.
    figure(app.UIFigure)
    
    if FileName ~= 0
        % User did not cancel the file selection operation, so OK to load
        % make sure the message buffer is empty before loading in the logged CAN data.
        app.canLogMsgBuffer = [];
        
        % Upload the saved message data from the selected file.
        canLogMsgStructOfArrays = load(fullfile(PathName, FileName), 'canLogStructOfArrays');
        
        % Convert the saved message data into timetables for the replay command.
        app.canLogMsgBuffer = canMessageTimetable(canLogMsgStructOfArrays.canLogStructOfArrays);
    end
end

loadLoggedCANDataFromFile は次の Vehicle Network Toolbox 関数を使用します。

  • canMessageTimetable は、CAN メッセージ Replay Simulink ブロックと互換性のある形式で保存された CAN メッセージを、replay 関数で使用するために MATLAB timetable に変換します。

記録されたメッセージの再生を開始する

ログに記録された CAN メッセージ データが UI にロードされ、再フォーマットされたら、Vehicle Network Toolbox replay コマンドを使用して再生できるようになります。ユーザーが UI で 再生を開始 をクリックすると、ヘルパー関数 startPlaybackOfLoggedCANData が呼び出されます。最初の CAN チャネルを介して記録された CAN メッセージ データを再生するには、このチャネルに関連付けられたすべてのアクティビティを停止し、バッファリングされたメッセージ データをすべてクリアする必要があります。以下のコード フラグメントに示すように、startPlaybackOfLoggedCANData は CAN メッセージの定期的な送信をオフにし、CAN チャネルを停止し、UI にバッファリングされているすべての CAN メッセージ データをクリアし、クルーズ コントロール アルゴリズム モデルからフィードバックされた信号データを表示するプロットをクリアします。その後、CAN チャネルが再起動され、記録された CAN メッセージ データが再生されます。

function startPlaybackOfLoggedCANData(app)
    % Turn off periodic transmission of CruiseCtrlCmd CAN message from UI controls.
    transmitPeriodic(app.canChannelObj, app.cruiseControlCmdMessage, 'Off');
    
    % Stop the UI CAN channel so we can instead use if for playback.
    stopCANChannel(app)
    
    % Flush the existing CAN messages stored for plotting.
    flushCANFbMsgQueue(app)
    
    % Clear the existing plots.
    cla(app.tspeedPlot)
    cla(app.engagedPlot)
    
    % Start the CAN Channel and replay the logged CAN message data.
    startCANChannel(app)
    
    % Replay the logged CAN data on the UI CAN Channel.
    replay(app.canChannelObj, app.canLogMsgBuffer);
end

startPlaybackOfLoggedCANData は次の Vehicle Network Toolbox 関数を使用します。

  • transmitPeriodic は、「CruiseCtrlCmd」メッセージでクルーズ コントロール アルゴリズム モデルに送信されるコマンド信号の定期的な送信を無効にします。

  • replay は、最初の CAN チャネルで記録された CAN メッセージ データを再生します。

記録されたメッセージの再生を停止する

ユーザーが UI で 再生を停止 をクリックすると、ヘルパー関数 stopPlaybackOfLoggedCANData が呼び出されます。記録された CAN メッセージ データの再生を停止するには、データが再生されている CAN チャネルを停止する必要があります。これが完了すると、「CruiseCtrlCmd」メッセージの定期的な送信が再度有効になり、チャネルが再起動されるため、ユーザーは再び UI から対話的に Cruise Control アルゴリズム モデルにテスト刺激信号を注入できるようになります。以下のコード フラグメントに示すように、stopPlaybackOfLoggedCANData は最初にチャネルを停止し、ログに記録されたメッセージ データの再生を停止します。ログに記録されたメッセージ データは、UI 上のローカル バッファーからクリアされるだけでなく、クルーズ コントロール アルゴリズム モデルからフィードバックされた信号データを表示するプロットからもクリアされます。「CruiseCtrlCmd」メッセージの定期的な送信が再度有効になり、CAN チャネルが再起動されます。

function stopPlaybackOfLoggedCANData(app)
    % Stop the playback CAN channel.
    stopCANChannel(app)
    
    % Flush the existing CAN messages stored for plotting.
    flushCANFbMsgQueue(app)
    
    % Clear the existing plots.
    cla(app.tspeedPlot)
    cla(app.engagedPlot)
    
    % Re-enable periodic transmission of CruiseCtrlCmd CAN message from UI controls.
    transmitPeriodic(app.canChannelObj, app.cruiseControlCmdMessage, 'On', 0.1);
    
    % Restart the CAN Channel from/To UI.
    startCANChannel(app)
end

stopPlaybackOfLoggedCANData は次の Vehicle Network Toolbox 関数を使用します。

  • stop は CAN チャネルを停止し、記録された CAN メッセージ データの再生を停止します。

  • transmitPeriodic は、「CruiseCtrlCmd」メッセージでクルーズ コントロール アルゴリズム モデルに送信されるコマンド信号の定期的な送信を再度有効にします。

  • start は CAN チャネルを再起動して、ユーザーが UI から対話形式で再度テスト刺激信号をクルーズ コントロール アルゴリズム モデルに注入できるようにします。

Simulink クルーズコントロールアルゴリズムモデルに仮想 CAN チャネル通信を追加する

このステップでは、Vehicle Network Toolbox Simulink ブロックを使用して、Simulink クルーズ コントロール アルゴリズム モデルに仮想 CAN 通信機能を追加する方法について説明します。

この説明では、次のトピックについて説明します。

  • CANメッセージ受信機能の追加

  • CANメッセージ送信機能の追加

  • UIからSimulinkモデルにCANチャネル構成情報をプッシュする

クルーズコントロールアルゴリズムSimulinkモデルを開く

ヘルパー関数を実行して、必要なデータ パラメータを使用してワークスペースを構成し、クルーズ コントロール アルゴリズム テスト ハーネス モデルを開きます。Simulink モデルを開くと、以下のセクションで説明されているモデルの部分を調べることができます。helperPrepareTestBenchParameterData に続いて helperConfigureAndOpenTestBench を実行します。

CANメッセージ受信機能の追加

クルーズ コントロール アルゴリズムの Simulink モデルがテスト UI から CAN データを受信するには、Simulink モデルを特定の CAN デバイスに接続するブロック、選択したデバイスから CAN メッセージを受信するブロック、受信したメッセージのデータ ペイロードを個々の信号にアンパックするブロックが必要です。これを実現するために、クルーズ コントロール アルゴリズム Simulink モデルに「入力」サブシステムが追加されます。「入力」サブシステムは、下のスクリーンショットに示すように、相互接続された Vehicle Network Toolbox CAN Configuratio、CAN Receive、および CAN Unpack ブロックを使用します。

CAN Configuration ブロックを使用すると、ユーザーは利用可能な CAN デバイスとチャネルのどれを Simulink モデルに接続するかを決定できます。CAN Receive ブロックは、CAN Configuration ブロックで選択された CAN デバイスとチャネルから CAN メッセージを受信します。また、ユーザーはバス上のすべてのメッセージを受信したり、フィルターを適用して選択したメッセージのみを受信したりすることもできます。CAN Unpack ブロックは、ユーザー定義のネットワーク データベース (.dbc) ファイルを読み取るように構成されています。これにより、ユーザーは、このブロックを使用して信号をアンパックするためのメッセージ名、メッセージ ID、およびデータ ペイロードを決定できます。Simulink 入力ポートは、ネットワーク データベース ファイル内のメッセージで定義された各信号のブロックに自動的に追加されます。

クルーズ コントロール アルゴリズム Simulink モデルの「入力」サブシステムは、次の Vehicle Network Toolbox Simulink ブロックを使用して CAN メッセージを受信します。

  • CAN Configuration ブロックは、Simulink モデルに接続する CAN チャネル デバイスを選択します。

  • CAN Receive ブロックは、CAN Configuration ブロックで選択された CAN デバイスから CAN メッセージを受信します。

  • CAN Unpack ブロックは、受信した CAN メッセージのペイロードを、メッセージで定義されているデータ項目ごとに 1 つずつ個別の信号にアンパックします。

CANメッセージ送信機能の追加

クルーズ コントロール アルゴリズムの Simulink モデルが CAN データをテスト UI に送信するには、Simulink モデルを特定の CAN デバイスに接続するブロック、Simulink 信号を 1 つ以上の CAN メッセージのデータ ペイロードにパックするブロック、および選択したデバイスから CAN メッセージを送信するブロックが必要です。これを実現するために、クルーズ コントロール アルゴリズム Simulink モデルに「出力」サブシステムが追加されます。「出力」サブシステムは、下のスクリーンショットに示すように、相互接続された Vehicle Network Toolbox CAN pack ブロックと CAN Transmit ブロックを使用します。

CAN Pack ブロックは、ユーザー定義のネットワーク データベース (.dbc) ファイルを読み取るように構成されています。これにより、ユーザーは、このブロックを使用して信号をパックするためのメッセージ名、メッセージ ID、およびデータ ペイロードを決定できます。ネットワーク データベース ファイル内のメッセージで定義された各信号のブロックに、Simulink 出力ポートが自動的に追加されます。CAN Transmit ブロックは、Pack ブロックによって組み立てられたメッセージを、ユーザーが Configuration ブロックで選択した CAN チャネルおよび CAN デバイスに送信します。"Outputs" サブシステムは、CAN メッセージの受信に使用されるのと同じ CAN チャネルおよびデバイスで CAN メッセージを送信するため、2 番目の CAN Configuration ブロックは必要ないことに注意してください。

クルーズ コントロール アルゴリズム Simulink モデルの "Outputs" サブシステムは、次の Vehicle Network Toolbox Simulink ブロックを使用して CAN メッセージを送信します。

  • CAN Pack ブロックは、受信した CAN メッセージのペイロードを、メッセージで定義されているデータ項目ごとに 1 つずつ個別の信号にアンパックします。

  • CAN Transmit ブロックは、CAN Configuration ブロックで選択された CAN デバイスから CAN メッセージを受信します。

UI から Simulink モデルに CAN チャネル設定をプッシュする

ユーザーは UI から使用可能な CAN デバイスとチャネルのどれを使用するかを選択するため、UI と Simulink モデル間の CAN デバイスとチャネルの構成を同期させるには、この情報を Cruise Control アルゴリズム Simulink モデルに送信する必要があります。これを実現するには、CAN Configuration、CAN Transmit、および CAN Receive ブロックで使用される CAN デバイスとチャネル情報を、UI からプログラムで構成する必要があります。ユーザーが UI の「チャネル構成/CAN チャネルの選択」メニューから CAN デバイスと CAN チャネルを選択するたびに、ヘルパー関数 updateModelWithSelectedCANChannel が呼び出されます。以下のコード フラグメントに示すように、updateModelWithSelectedCANChannel は、クルーズ コントロール アルゴリズム Simulink モデル内の CAN Configuration、CAN Transmit、および CAN Receive ブロックのブロック パスを見つけます。set_param コマンドを使用すると、これら 3 つのブロックのそれぞれに対する DeviceDeviceMenu、および ObjConstructor ブロック プロパティが、ユーザーが選択した CAN デバイスおよび CAN チャネルの対応するプロパティに設定されます。

function updateModelWithSelectedCANChannel(app, index)
    % Check to see if we are using a virtual CAN channel and whether the model is loaded.
    if app.canChannelInfo.DeviceModel(index) == "Virtual" && bdIsLoaded(app.mdl)
        % Using a virtual channel.
        
        % Find path to CAN configuration block.
        canConfigPath = find_system(app.mdl,'Variants', 'AllVariants', 'LookUnderMasks', 'all',...
            'FollowLInks', 'on', 'Name', 'CAN Configuration');
        
        % Find path to CAN transmit block.
        canTransmitPath = find_system(app.mdl,'Variants', 'AllVariants', 'LookUnderMasks', 'all',...
            'FollowLInks', 'on', 'Name', 'CAN Transmit');
        
        % Find path to CAN receive block.
        canReceivePath = find_system(app.mdl,'Variants', 'AllVariants', 'LookUnderMasks', 'all',...
            'FollowLInks', 'on', 'Name', 'CAN Receive');
        
        % Push the selected CAN channel into the simulation model CAN Configuration block.
        set_param(canConfigPath{1}, 'Device', app.canChannelDeviceSelected);
        set_param(canConfigPath{1}, 'DeviceMenu', app.canChannelDeviceSelected);
        set_param(canConfigPath{1}, 'ObjConstructor', app.canChannelConstructorSelected);
        
        % Push the selected CAN channel into the simulation model CAN Receive block.
        set_param(canReceivePath{1}, 'Device', app.canChannelDeviceSelected);
        set_param(canReceivePath{1}, 'DeviceMenu', app.canChannelDeviceSelected);
        set_param(canReceivePath{1}, 'ObjConstructor', app.canChannelConstructorSelected);
        
        % Push the selected CAN channel into the simulation model CAN Transmit block.
        set_param(canTransmitPath{1}, 'Device', app.canChannelDeviceSelected);
        set_param(canTransmitPath{1}, 'DeviceMenu', app.canChannelDeviceSelected);
        set_param(canTransmitPath{1}, 'ObjConstructor', app.canChannelConstructorSelected);
    end
end

UIとモデルを一緒に使う

モデルと UI の両方を開いた状態で、UI でモデルを操作する方法を試すことができます。シミュレーションを開始 をクリックして、モデルと UI をオンラインにします。UI の「ドライバー入力」セクションと「キャリブレーション」セクションを試して、モデルを制御し、クルーズ コントロール アルゴリズムを作動させます。クルーズ コントロールの作動と速度の値が UI に表示されます。UI コントロールを介して、前述のログ記録機能と再生機能を使用することもできます。

この方法で UI を作成すると、アプリケーションに合わせてカスタマイズできる強力で柔軟なテスト インターフェイスが提供されます。シミュレーションでアルゴリズムをデバッグおよび最適化するときに役立ちます。選択した CAN デバイスを仮想チャネルから物理チャネルに変更することで、引き続き UI を使用して、ラピッドプロトタイピング プラットフォームまたはターゲット コントローラで実行されているアルゴリズムと対話できます。