LoginSignup
13
3

More than 5 years have passed since last update.

M-FunctionでのMATLAB関数の使い方とmxArrayエラーの回避方法

Posted at

はじめに

この記事では、SimulinkのM-Functionを使用して自作関数を作るときに最初に躓いたところを書きました。
なぜこうなるのかまではまだ理解できていないので、こうすれば動いたという内容です。

主に以下の2点です
・標準のMATLAB関数が「サポートされていません」と出る
 解決策:coder.extrinsic('関数名')で宣言する
・「mxArrayにできません」のエラーがでる
 解決策:変数の初期化処理を入れる
     構造体ならgetfield, setfield関数を使う

例として、Simulink上で現在時刻を出力する処理を実装する流れで説明していきます。

1.MATLAB動作とSimulinkへの実装失敗

まず、MATLABでは現在時刻を出力する標準関数があります。
now関数で現在時刻の数値データを取得でき、
その数値をdatestr関数にいれると見慣れた時刻表示の文字列にできます。
now.png

そこでSimulinkにてシンプルな実装をしてみます
・NowTimeという名前のM-Functionを作成。入力無し、出力1つ(y)。
・M-Functionの内容は、y = now; のみ。
・出力にDisplayを接続
now2.png

感覚的には、現在時刻の数値データがDisplayに表示されるはずです。

2.標準のMATLAB関数が「サポートされていません」

ところが実際に実行すると、「'now'はコード生成でサポートされていません」というエラーが出てきます。
コード生成.png
MATLAB標準関数なのにサポートされてないの?と言いたくなりますが、この関数を使うという宣言文のようなものを入れることで使用できます。具体的には
coder.extrinsic(string);
を入れます。
この宣言で、stringで使いたい関数名を入れることで、そのM-Function内でMATLAB関数が使用できます。
例:coder.extrinsic('now');
coder追加.png

3.mxArrayのエラー

ただし、これで実行すると、今度は「mxArrayにはできません」というエラーが出てきます。
mxArrayエラー.png

簡単に言えば、実行時の時点で、出力yを何の型(short型かfroat型か)で作ればいいのか、どのくらいの大きさを確保すればいいのか分からないという状態みたいです。
now関数は戻り値が数値型ですが、外部関数のため実行時には分からないのです。

これを解決するため、now関数の前に、yは小数点ありの数値型であると宣言します。
具体的には、「y=0.1;」を先に入れることで、実行時に数値データであると確定させます。
時刻出力成功.png
以上の修正をして実行すると、無事Displayに現在時刻の数値データが表示されました。

ちなみにこの表示された数値をdatestr関数に入れると日時が表示されるので、数値が正しく出力されていることが分かります。
時刻確認.png

補足 配列が戻り値の場合

mxArrayのエラーは配列も同様であり、例えば戻り値が[3 4 1]みたいな1×3の配列が来る関数を使用したときに、戻り値を変数aに入れてa(3)を取り出そうとするとエラーになります。
これも実行時にはaが1×3の配列か不明なので、その添え字が正しいか判断付かないためです。
回避するためには、a=zeros(1,3);や a=[0 0 0];というように、配列の大きさを事前に書いておく必要があります。逆に言うと、配列数が変わる関数の場合、次の構造体と同様の対応が必要になります。

補足 構造体が戻り値の場合

now関数の戻り値は数値でしたので変数の型を事前に定義できましたが、MATLAB関数には戻り値が構造体のものも多くあります。当然ながら構造体を一つ一つ記述するのは大変な手間ですし、多数なデータが入っているので型自体分からないというのも多くあります。
figure関数の戻り値とかまさにそうですね。
例えばax=figure;と書いて図を出し、x軸の目盛りを変更しようとすると、
ax.XTick = [2 4 6 8 10];
と書きたくなりますが、axの下にXTickがあるか実行時は分からないのでエラーになります。
そこで使うのが、「getfield」「setfield」です。
これは構造体の値を操作する関数です。
詳細はMATLABのヘルプに任せますが、先ほどの例で行くと、
setfield(ax, 'XTick', [2 4 6 8 10]);
と書くと、やりたかったことが実現できます。
また、データを取得したい場合は、例えば
str = getfield(ax, 'Title', 'String');
と書くことで、
str = ax.Title.String;
と同じく処理になり、図のタイトルの文字列がstrに入ります。

おわりに

以上で現在時刻のデータが無事データが出力できました。
ただ、このままでは人の目では読めませんので、見やすくする方法を別の記事で記載しようと思います。

また、M-Functionではなく、S-Function(MATLAB System)だとこの問題も起きないようですが、使い方が変わってきます。そちらも出来次第、記事にしようと思います。

13
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
3