Main Content

MATLAB Coder を使用した外部 C コードのユニット テスト

この例では、MATLAB® Coder™ で MATLAB® ユニット テストを使用して外部 C コードをテストする方法を示します。

C コードをテストする場合、MATLAB Coder を使用して MATLAB にコードを取り込みます。次に、MATLAB テスト フレームワークを使用してユニット テストを記述します。MATLAB の高度な数値計算や可視化機能を利用して、柔軟で豊富なテストを作成できます。

以下の例では、次のような操作方法を説明します。

  1. C コードを MATLAB Coder を使用して生成する MEX 関数として MATLAB に取り込みます。

  2. MATLAB テスト フレームワークを使用してユニット テストを記述します。

  3. MEX 関数上でテストを実行します。

Embedded Coder® をお持ちの方は、ソフトウェアインザループ (SIL) またはプロセッサインザループ (PIL) 実行によるユニット テストを使用して、生成されたスタンドアロン コード (スタティック ライブラリまたは共有ライブラリ) 上でテストを実行できます。

ファイルの確認

この例で使用するファイルにアクセスするには、[Open Script] をクリックします。

kalmanfilter.c

kalmanfilter.c は、例でテストされる C 関数です。移動するオブジェクトの位置を、その過去の位置に基づいて推定します。

kalmanfilter.h

kalmanfilter.h は、kalmanfilter.c のヘッダー ファイルです。

position.mat

position.mat には、オブジェクトの位置が含まれます。

callKalmanFilter.m

callKalmanFilter は、coder.ceval を使用して kalmanfilter を呼び出します。

function [a,b] = callKalmanFilter(position)
% Copyright 2014 - 2016 The MathWorks, Inc.

numPts = size(position,2);

a = zeros(2,numPts,'double');
b = zeros(2,numPts,'double');
y = zeros(2,1,'double');

% Main loop
for idx = 1: numPts
    z = position(:,idx);     % Get the input data
    
    % Call the initialize function
    coder.ceval('kalmanfilter_initialize');
    
    % Call the C function
    coder.ceval('kalmanfilter',z,coder.ref(y));
    
    % Call the terminate function
    coder.ceval('kalmanfilter_terminate');
    
    a(:,idx) = [z(1); z(2)];
    b(:,idx) = [y(1); y(2)];
end
end

TestKalmanFilter.m

TestKalmanFilter は、予測位置と実際の位置の間の誤差が指定された許容誤差を超えているかどうかテストします。ユニット テストはクラスベースのユニット テストです。詳細については、MATLAB でのクラスベースのユニット テストの作成を参照してください。

MEX 関数をテストする場合でも、TestKalmanFilter でのユニット テストは、生成された MEX 関数から元の MATLAB 関数を呼び出します。MATLAB Coder がテストを実行する場合、MATLAB 関数の呼び出しは MEX 関数の呼び出しに置き換えられます。MATLAB は callKalmanFilter 内の coder.ceval 呼び出しを認識しないため、これらのテストを MATLAB で直接実行することはできません。

classdef TestKalmanFilter < matlab.unittest.TestCase
    % Copyright 2014 - 2016 The MathWorks, Inc.
    
    methods ( Test )
        
        function SSE_LessThanTolerance( testCase )
            load position.mat;
            [z,y] = callKalmanFilter( position );
            
            tolerance = 0.001; % tolerance of 0.0001 will break
            A = z-1000*y;
            error = sum(sum(A.^2));
            
            testCase.verifyLessThanOrEqual( error, tolerance);
            
            % For debugging
            plot_kalman_filter_trajectory(z,1000*y);
        end
        
        function SampleErrorLessThanTolerance( testCase )
            load position.mat;
            [z,y] = callKalmanFilter( position );
            
            tolerance = 0.01;   % tolerance of 0.001 will break
            A = z-1000*y;

            testCase.verifyEqual(1000*y, z, 'AbsTol', tolerance);
            % For debugging
            plot_kalman_filter_trajectory(z,1000*y);
            
            [value, location] = max(A(:));
            [R,C] = ind2sub(size(A),location);
            disp(['Max value ' num2str(value) ' is located at [' num2str(R) ',' num2str(C) ']']);
        end
    end
end

run_unit_tests_kalman.m

run_unit_tests_kalmanruntests を呼び出して、TestKalmanFilter.m でテストを実行します。

% Run unit tests
% Copyright 2014 - 2016 The MathWorks, Inc.

runtests('TestKalmanFilter')

plot_kalman_filter_trajectory.m

plot_kalman_filter_trajectory は、オブジェクトの推定位置と実際の位置の軌跡をプロットします。各ユニット テストはこの関数を呼び出します。

MATLAB Coder アプリでの MEX の生成およびユニット テストの実行

MATLAB Coder アプリを開くには、MATLAB のツールストリップの [アプリ] タブの [コード生成] の下で、MATLAB Coder アプリのアイコンをクリックします。

コード生成の準備をするには、アプリの手順を進めます。

  • [ソース ファイルの選択] ページで、エントリポイント関数に callKalmanFilter を指定します。

  • [入力の型を定義] ページで、入力引数 x に 2 行 310 列の double の配列を指定します。

ユニット テストは変数 positionposition.mat から読み込み、positioncallKalmanFilter に渡します。したがって、callKalmanFilter への入力には position がもっているプロパティが指定されていなければなりません。MATLAB ワークスペースで、position.mat を読み込むと、position が 2 行 310 列の double の配列であることがわかります。

  • この例では、[実行時の問題の確認] ステップを省略します。

MEX コード生成のアプリを設定します。callKalmanFilter が外部 C コードを統合しているため、C ソースおよびヘッダー ファイルの名前を指定します。

  1. [ビルド タイプ] には、MEX を指定します。

  2. [詳細設定] をクリックします。

  3. [カスタム コード] タブで次のように設定します。

  • [生成されたファイルのカスタム C コード] で、[ヘッダー ファイル] を選択します。カスタム コード フィールドで、#include "kalmanfilter.h" と入力します。

  • [追加ソース ファイル] フィールドで、kalmanfilter.c と入力します。

MEX 関数を生成するには、[生成] をクリックします。

生成された MEX 上でユニット テストを実行します。

  1. [コードの検証] をクリックします。

  2. テスト ファイルのフィールドで、run_unit_tests_kalman を指定します。

  3. [次を使用して実行][生成コード] に設定していることを確認します。

  4. [生成されたコードの実行] をクリックします。

アプリがテスト ファイルを実行すると、ユニット テストの callKalmanFilter の呼び出しが callKalmanFilter_mex の呼び出しに置き換えられます。ユニット テストは元の MATLAB 関数ではなく MEX 関数上で実行されます。

アプリはテスト出力を [テスト出力] タブに表示します。ユニット テストをパスしました。

プロットから、推定位置の軌跡が実際の位置の軌跡に収束することがわかります。

C コード変更後のユニット テストの実行

C コードを変更する場合に、ユニット テストを実行するには、次の手順に従います。

  1. C コードを呼び出す MATLAB 関数の MEX 関数を再生成します。

  2. 検証のステップを繰り返します。

たとえば、y[r2] に割り当てられている値に 1.1 を乗算するように kalmanfilter.c を変更します。

y[r2] += (double)d_a[r2 + (i0 << 1)] * x_est[i0] * 1.1;

アプリを使用して編集できるのは、アプリの [ソース コード] ペインにリストされている MATLAB ファイルのみであるため、アプリの外部で kalmanfilter.c を編集します。

変更された関数の MEX 関数を生成するには、[生成] をクリックします。

ユニット テストを実行するには、次の手順に従います。

  1. [コードの検証] をクリックします。

  2. テスト ファイルを run_unit_tests に、[次を使用して実行][生成コード] に設定していることを確認します。

  3. [生成されたコードの実行] をクリックします。

誤差が指定された許容誤差を超えるため、テストに失敗します。

プロットは、推定位置の軌跡と実際の位置の軌跡の間の誤差を示します。

コマンド ライン ワークフローを使用した MEX の生成およびユニット テストの実行

coder.runTest を使用して外部 C コードでユニット テストを実行するために、コマンド ライン ワークフローを使用できます。C コードを呼び出す MATLAB 関数でユニット テストを実行するテスト ファイルを指定します。

C コードを呼び出す MATLAB 関数の MEX 関数を生成します。この例では、callKalmanFilter の MEX を生成します。

MEX コード生成用の構成オブジェクトを作成します。

cfg = coder.config('mex');

外部ソース コードおよびヘッダー ファイルを指定します。

cfg.CustomSource = 'kalmanfilter.c';
cfg.CustomHeaderCode = '#include "kalmanfilter.h"';

callKalmanFilter への入力の型を決定するには、位置ファイルを読み込みます。

load position.mat

MEX 関数を生成するには、codegen を実行します。callKalmanFilter への入力に position と同じ型を指定します。

codegen -config cfg callKalmanFilter -args position
Code generation successful.

ユニット テストを MEX 関数上で実行します。テスト ファイルに run_unit_tests_kalman を、関数に callKalmanfilter を指定します。coder.runTest がテスト ファイルを実行する場合、ユニット テストの callKalmanFilter の呼び出しが callKalmanFilter_mex の呼び出しに置き換えられます。ユニット テストは元の MATLAB 関数ではなく MEX 関数上で実行されます。

coder.runTest('run_unit_tests_kalman', 'callKalmanFilter')
Running TestKalmanFilter
Current plot held
.Current plot held
Max value 0.0010113 is located at [2,273]
.
Done TestKalmanFilter
__________


ans = 

  1x2 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   2 Passed, 0 Failed, 0 Incomplete.
   21.8139 seconds testing time.

参考

関連するトピック