HILS と画像検査の組み合せ

 

dSPACE - MAN - ADAS: dSPACE tool chain used by MAN

youtu.be

 

ETAS Camera-based Test System
 Vision Based LABCAR Testing: Camera Based Testing of HMI
 f:id:sato-7411:20200723145227p:plain

  

自動車開発において HILS (Hardware-In-the-Loop-Simulation) が使われています。自動車には, エンジンやメーターも含め, 様々な ECU (Electronic Control Unit) が使われています。

各メーカーで ECU を開発する際, ECU コネクタに接続されている車のセンサーやアクチュエータ, 通信の電気信号を, Digital/Analog の I/O ボードですべて模擬できれば, ECU からは実車で走行しているのと同じに見えて, 様々な条件下でのテストが行えます。

ECU から見て車をシミュレーションするハードウェア・ソフトウェアが HILS です。車全体をシミュレーションすることから, エンジン ECU, 車両制御 ECU, オートマチックトランスミッション ECU, ブレーキ ECU, メーター ECU, といった複数の ECU を接続して, テストすることが可能です。

HILS のコンピュータは, 車の走行状態や周囲環境をシミュレーションするために, モデル (プラントモデル) を使って計算しています。プラントモデルは, 各ベンダーが提供する, リアルタイムPC上で動作する自動車モデルや, MATLAB/Simulink で独自に作られた計算モデルで構成されています。

例えば, 詳細なエンジンモデルでは, インマニでの吸気, インジェクターの燃料噴射, イグニッションの点火時期, シリンダー・ピストンでの混合気の圧縮をシミュレーションし, 出力トルクを計算します。車両モデルでは, 設定された, 道路の勾配, 路面の摩擦係数, 車体の前面積, 車重, 気温, 気圧, などから走行抵抗を計算し, 車速を計算します。

HILS では, アクセルやブレーキの操作を時系列に記録したデータを読み込んで, パターン走行する, 自動テストが行われています。自動パターン走行時に, メーター表示を画像検査して, 車の走行を監視することができます。

 

1μsオーダーのアナログ出力や入力が必要な場合

Simulink HDL Coder (VHDL コード生成)

Fixed-Point Designer (Simulink ブロックの固定小数点 HDL ライブラリ[HM1] にアクセスする)

Simulink-programmable FPGA I/O module, analog outputs and analog inputs, PCI Express board

jp.mathworks.com

 

「はじめての FPGA 設計」坂巻佳壽美 著、東京電機大学出版局

f:id:sato-7411:20211223193508p:plain

Intel Quartus Prime (旧 Altera Quatus II)

P55:FPGAバイス (Cyclone II EP2C20F484C7) では、100 ns 程度が高速動作の限界ではないかと推測

P188:ステートマシンとは

注目コラム:2 電流制限抵抗やプルアップ抵抗の値の決め方、9 チャタリングというやっかいな現象と対策、12 パワーオン・リセット回路

 

 

 

 

 

 

Simulink カスタム・ブロック

□ 参考ページ

自作ブロックライブラリをSimulinkライブラリに追加する
http://www.mathworks.co.jp/support/solutions/ja/data/1-9IUXR7/?solution=1-9IUXR7
ライブラリの EnableLBRepository プロパティを 'on' に設定
https://jp.mathworks.com/help/simulink/ug/adding-libraries-to-the-library-browser_ja_JP.html

簡単なマスクの作成
https://jp.mathworks.com/help/simulink/ug/how-to-mask-a-block.html

S-Function でメモリを割り当てて, アクセスする方法
https://www.mathworks.com/matlabcentral/answers/488429-how-to-allocate-memory-and-access-it-in-s-function 

 

 

□ カスタムブロックの作成

フォルダ
C:\Program Files\MATLAB\R2020b\toolbox\simulink\simdemos\simfeatures\src
にアクセスするために, MATLAB を管理者権限で起動する (Windows 10)

Simulink で空のモデルに User-Defined Functions\S-function ブロックを配置する

f:id:sato-7411:20211111150634p:plain

ダブルクリックでブロックパラメーターのウィンドウを表示

f:id:sato-7411:20211111150833p:plain

S-Function 名を, system から timestwo に変更。編集
MATLAB で timestwo.c が開くので, 名前を付けて保存で GBLInport.c に変更
S-Function 名も, GBLInport に変更。適用, OK
Simulink を一旦閉じる。保存しなくて良い
(GBLOutport は GBLInport から作成)

 

GBLInport.c をエディターで編集

#define S_FUNCTION_NAME GBLInport
#define S_FUNCTION_LEVEL 2

 

#define MDL_INITIAL_SIZES
static void mdlInitializeSizes(SimStruct *S)
入力ポート数, 出力ポート数, 状態数, パラメータ数, 等の特性を指定する

ssSetNumSFcnParams でパラメータ数を指定する(4)
ssSetNumContStates と ssSetNumDiscStates で状態数を指定する(0)

入力ポート
  ssSetNumInputPorts で入力ポート数を指定する(0)

出力ポート
  ssSetNumOutputPorts で出力ポート数を指定する(1)

ssSetNumRWork と ssSetNumIWork でワークベクターのサイズを指定する(0)
ssSetSFcnParamTunable シミュレーション中にパラメータを変更不可にする場合は0にする(1)

ssSetOutputPortWidth でサイズは固定(1)
ssSetOutputPortDataType で型は動的(DYNAMICALLY_TYPED)

(サンプルと同じ設定)
ssSetNumSampleTimes(S, 1)
ssSetNumPWork(S, 0)
ssSetNumModes(S, 0)
ssSetNumNonsampledZCs(S, 0)

ssSetOptions を指定(0)

 

#define MDL_SET_OUTPUT_PORT_DATA_TYPE
static void mdlSetOutputPortDataType(SimStruct *S, int_T port, DTypeId id)
ssSetOutputPortDataType で型を id にする

 

#define MDL_INITIALIZE_SAMPLE_TIMES
static void mdlInitializeSampleTimes(SimStruct *S)
ssSetSampleTime
ssSetOffsetTime
index = 0
sample time and offset values = [CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]

 

#define MDL_INITIALIZE_CONDITIONS
static void mdlInitializeConditions(SimStruct *S)
処理無し

 

#define MDL_START
static void mdlStart(SimStruct *S)
Simulink はシミュレーションの開始で, オプションとして, 本関数を実行する
ユーザーデータのセットアップや状態の初期化を行う

char buffer[256];
mxGetString( ssGetSFcnParam(S,0), buffer, 128 ); //mxGetString は mxChar* から char* への変換。ssGetSFcnParam(S,0) は1番目のパラメータ(P1)
ssSetUserData(S, getInputArrayPtr(buffer));

 

#define MDL_OUTPUTS
static void mdlOutputs(SimStruct *S, int_T tid)
メイン処理
#define portIndex (int32_T) mxGetPr(ssGetSFcnParam(S,1))[0]
double *inputArray;
inputArray = ssGetUserData(S);
if (ssGetOutputPortDataType(S, 0) == SS_DOUBLE)
*(real_T *) ssGetOutputPortRealSignal(S,0) = inputArray[portIndex];

 

#define MDL_UPDATE
static void mdlUpdate(SimStruct *S, int_T tid)
処理無し

 

#define MDL_DERIVATIVES
static void mdlDerivatives(SimStruct *S)
処理無し

 

static void mdlTerminate(SimStruct *S)
処理無し

 

(outport の場合)

#define MDL_INITIAL_SIZES
ssSetNumSFcnParams 2
ssSetNumInputPorts 1
ssSetNumOutputPorts 0
ssSetInputPortWidth 0,1
ssSetInputPortDataType 0,DYNAMICALLY_TYPED
ssSetInputPortRequiredContiguous 0,true
ssSetInputPortDirectFeedThrough 0,1
ssSetSFcnParamTunable 0,1
ssSetSFcnParamTunable 1,1

# define MDL_SET_INPUT_PORT_DATA_TYPE
ssSetInputPortDataType 0,id

#define MDL_INITIALIZE_SAMPLE_TIMES
index = 0
sample time and offset values = [CONTINUOUS_SAMPLE_TIME, 0.0]

#define MDL_START
ssSetUserData(S, getOutputArrayPtr(buffer));

#define MDL_OUTPUTS
outputArray = ssGetUserData(S);
if (ssGetInputPortDataType(S, 0) == SS_DOUBLE)
outputArray[portIndex] = *(real_T *) ssGetInputPortRealSignal(S,0);

 

 

MATLAB のコマンドウィンドウで, フォルダ
C:\Program Files\MATLAB\R2020b\toolbox\simulink\simdemos\simfeatures
に移動して, ビルド
mex .\src\GBLInport.c
GBLInport.mexw64 が出来る

 

 

次に, S-Function を配置して, S-Function 名に GBLInport を入力し, 適用ボタンを押下すると, S-Function パラメーターが空欄のままだと, エラーメッセージが出る

f:id:sato-7411:20211111150957p:plain

f:id:sato-7411:20211111151022p:plain

S-Function パラメーターに項目を4つ入力する。適用, OK

f:id:sato-7411:20211111151104p:plain

 

 

マウス右クリック, マスク, マスクの作成

f:id:sato-7411:20211111151519p:plain

 

アイコンと端子, はそのまま

f:id:sato-7411:20211111151649p:plain

 

パラメーターとダイアログ, で, パラメーター配下にエディットを4個追加
それぞれのプロンプトと名前を編集。適用

f:id:sato-7411:20211111151819p:plain

f:id:sato-7411:20211111151904p:plain

 
初期化とドキュメンテーションはそのまま。適用, OK

  

S-Function ブロックをダブルクリックすると, パラメーターの入力画面が開くようになる
ModelName に仮に 'ParentModel' を入力

f:id:sato-7411:20211111151952p:plain

 

Simulink モデルを, 例えば C:\Program Files\MATLAB\R2020b\toolbox\chbstyle フォルダに, GBLPortLib_Sample.slx と名前を付けて保存

f:id:sato-7411:20211111154035p:plain



 

新規で空のライブラリを作成。ブロックをコピー, 貼付け。コマンドウィンドウで
set_param(gcs,'EnableLBRepository','on');
を実行する。ライブラリを GBLPortLib.slx と名前を付けて保存

f:id:sato-7411:20211111152554p:plain

 

ファイルの配置

ファイルを同じフォルダにコピーする。ファイルが二重になってしまうが, 手順を分かりやすくするため
slblocks.m は他のものからコピーして, 内容を現在のブロックに合わせて変更する
GBLOutport.c
GBLInport.mexw64
GBLOutport.c
GBLOutport.mexw64
slblocks.m

f:id:sato-7411:20211111152702p:plain

 

パスの追加

MATLAB のホーム, 環境, パスの設定, フォルダを追加, で上記フォルダを追加する

Simulink を起動し, ライブラリブラウザーを開く。最初は My Library が表示されていないので, マウス右クリック, ライブラリ ブラウザーを更新, を選択すれば表示される

f:id:sato-7411:20211111152802p:plain

f:id:sato-7411:20211111152841p:plain

 

 

□ モデル(model_main)と S-Function の両方から参照できる共通モジュールを作る

modcomm.c を作成
model_main.c で modcomm.h をインクルード
S-Function のCコードで modcomm.h をインクルード

NewSimModel_main.c で
#define noInputs 2
#define noOutputs 1
static double localInputArray_NewSimModel[noInputs];
static double localOutputArray_NewSimModel[noOutputs];
を作る (値の格納先)

そのポインタを NewSimModel_main.c の Start で registerModule(名前, 入力数, 入力ptr, 出力数, 出力ptr) を使って共通モジュールに登録する。後で名前("NewSimModel")を使って検索できる

S-Function では mdlStart で ssSetUserData を行う。ここで, getInputArrayPtr を使って名前で検索

mdlOutputs で ssGetUserData で参照 (結果として NewSimModel_main.c の localInputArray_NewSimModel を参照する)
ポートインデックスはパラメータ mxGetPr(ssGetSFcnParam(S,1))[0] で取得
mxGetPr(ssGetSFcnParam(S,1))[0] は2番目のパラメータ(P2)のインデックス0
newsimmodel_data.c にそれがある

S-Function のパラメータ
-- GBLInport.c --
moduleName 自分があるモデル名"NewSimModel"
portIndex モデル内でのポートのインデックス
b_useConstant
constValue

-- GBLOutport.c --
moduleName 自分があるモデル名"NewSimModel"
portIndex モデル内でのポートのインデックス

rt_main で参照する場合は
#include "modcomm.h"
#define Inports_NewSimModel getInputArrayPtr("NewSimModel")
#define Outports_NewSimModel getOutputArrayPtr("NewSimModel")
Inports_NewSimModel[0] = val1;
Inports_NewSimModel[1] = val2;
/* OneStep_NewSimModel(); */
val3 = Outports_NewSimModel[0];

 

 

□ ModelName が設定されていること, InportID と OutportID に抜けと重複が無いことを確認

allBlocks = find_system('NewSimModel', 'LookUnderMasks', 'all', 'BlockType', 'S-Function');
inBlocks = find_system(allBlocks, 'LookUnderMasks', 'all', 'FunctionName', 'GBLInport');
outBlocks = find_system(allBlocks, 'LookUnderMasks', 'all', 'FunctionName', 'GBLOutport');
for noInputs = 1:length(inBlocks)
fprintf('%s, %s\n', get_param(inBlocks{noInputs},'ModelName'), get_param(inBlocks{noInputs},'InportID'));
end
for noOutputs = 1:length(outBlocks)
fprintf('%s, %s\n', get_param(outBlocks{noOutputs},'ModelName'), get_param(outBlocks{noOutputs},'OutportID'));
end

あるシステムでは, m ファイルで ModelName の自動書き込み, InportID, OutportID の自動割り振りを行っている 

 

 

RT-Linux

リアルタイムOSのために Debian に RT-Linux をインストールしました。

環境:
Debian 11, Xfce
Intel Core i5-6300U vPro (2.4GHz, 2core, 4threads)
SSD 256GB
メモリ 8GB
14インチ液晶 フルHD (1920x1080)
ノートPC

 
Synaptic パッケージマネージャ (root 権限必要)
セクション:カーネルとモジュール

現在
linux-headers-5.10.0-9-amd64    5.10.70-1    Header files for Linux 5.10.0-9-amd64
linux-headers-5.10.0-9-common    5.10.70-1    Common header files for Linux 5.10.0-9
linux-headers-amd64    5.10.70-1    Linux amd64 構成向けヘッダファイル (メタパッケージ)
linux-image-5.10.0-8-amd64    5.10.46-5    Linux 5.10 for 64-bit PCs (signed)
linux-image-5.10.0-9-amd64    5.10.70-1    Linux 5.10 for 64-bit PCs (signed)
linux-image-amd64    5.10.70-1    64 ビットマシン用 Linux (メタパッケージ)

候補
linux-headers-5.10.0-9-rt-amd64    5.10.70-1    Header files for Linux 5.10.0-9-rt-amd64
linux-headers-5.10.0-9-common-rt    5.10.70-1    Common header files for Linux 5.10.0-9-rt
linux-headers-rt-amd64    5.10.70-1    Header files for Linux rt-amd64 configuration (meta-package)
linux-image-5.10.0-8-rt-amd64    5.10.46-5    Linux 5.10 for 64-bit PCs PREEMPT RT (signed)
linux-image-5.10.0-9-rt-amd64    5.10.70-1    Linux 5.10 for 64-bit PCs, PREEMPT RT (signed)
linux-image-rt-amd64    5.10.70-1    64 ビットマシン用 Linux (メタパッケージ)

 
指定してインストール。再起動後に RT-Linux になる。uname -a でシステム情報を確認

 
ドキュメント1
https://wiki.linuxfoundation.org/realtime/documentation/howto/applications/start

gcc example.c -pthread -o example
su
パスワード: ****
./example
※ su でないと実行できない (create pthread failed エラーが出る)。以下, 同じ

example.zip

 
定周期タスクを実行する場合

gcc example_cyclic.c -pthread -o example_cyclic
./example_cyclic

example_cyclic.zip

 
ドキュメント2
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux_for_real_time/7/html/reference_guide/sect-using_library_calls_to_set_priority

こちらの rt_main.c を rt_main_sched.c に変えて, rt_main.obj を作成。リンカはそのまま
実行時, 他のすべてのアプリケーションを閉じる (例えば, DebianCUI モードに切り替える)

rt_main_sched.zip

 
/* 本プロセスの優先度を設定する */
#include <sched.h>

struct sched_param sp = { .sched_priority = 99 };
ret = sched_setscheduler(0, SCHED_FIFO, &sp);
if (ret == -1) {
    perror("sched_setscheduler");
    return 1;
}

/* 実行結果 */

f:id:sato-7411:20211103020739p:plain

f:id:sato-7411:20211103020804p:plain

 

□ Start, OnStep, Terminate 等で呼び出せるように書き換え

rt_main_sched.zip

NewSimModel_main.zip (再掲)

 

 

MATLAB Coder, Simulink Coder (旧名 Real-Time Workshop)

□ Simulink のコード生成

モデルファイル *.mdl は, 最初に, Simulink Coder で, モデル記述ファイル *.rtw に変換される。*.rtw はオリジナルのモデルを高レベル言語で記述していて, Target Language Compiler に渡される。次に, *.tlc ファイルに記述されたルールに従って, Cコードが割り当てられ, Target Language Compiler によってターゲットに向けたCコードが作られる。

 

□ サンプル

NewSimModel.zip

コード生成

①モデル

f:id:sato-7411:20211102233917p:plain

②ソルバー

f:id:sato-7411:20211102234037p:plain

③ターゲットの選択

f:id:sato-7411:20211102234129p:plain

④モデルのビルド

f:id:sato-7411:20211102234208p:plain

⑤結果

f:id:sato-7411:20211102234328p:plain

コンパイル・ビルドのために必要なフォルダ
simulink/r2018b/rtw/c/src (前もってコンパイルしてライブラリを作る)
simulink/r2018b/include

 

 

□ MATLAB ドキュメント

rtw_gs_ja_JP
rtw_ug_ja_JP
rtw_ref_ja_JP
rtw_tlc
rn
coder_gs_ja_JP
coder_ug_ja_JP
coder_ref_ja_JP
rn (1)
rtw_getting_started 

 

□ 説明

作成されるファイル

NewSimModel.rtw
  コンパイル済みモデルの ASCII ファイル
  デフォルトでは Real-Time Workshop はビルド完了後に本ファイルを削除する

NewSimModel.c
  NewSimModel.h と NewSimModel_private.h をインクルードする
  NewSimModel_data.c 以外のデータを定義
  GRT ラッパー関数 (MdlStart, MdlOutputs, MdlUpdate, MdlInitializeSizes, MdlInitializeSampleTimes)
  モデル登録コード
  アルゴリズムコード

NewSimModel.h
  モデルについて次を定義する
    データ構成
    入出力インターフェイス
    リアルタイムモデルデータ構成 (NewSimModel_rtM) のアクセスマクロを経由したインターフェイス
  モデルに次をインクルードする
    Simulink データシンボルの出力
    Stateflow マシン親データの出力
    rtM を含んだモデルデータ構成
    モデル入出力関数

NewSimModel_private.h
  モデルに必要なローカル定数とローカルデータの定義
  生成されたコードに自動的にインクルードされる
  次を含む
    Simulink データシンボルの入力
    Stateflow マシン親データの入力
    Stateflow 入出力
    Real-Time Workshop 詳細 (様々なマクロ, 列挙型, 等)

NewSimModel_types.h
  リアルタイムモデルデータ構成とパラメータデータ構成の事前定義

NewSimModel_data.c
  条件に応じて, 生成されたコードは, パラメータデータ構成と定数ブロックI/Oデータ構成を含む
  これらデータ構成が使われていなければ, NewSimModel_data.c は生成されない
  これらデータ構成は NewSimModel.h で extern 宣言される
  次を含む
    定数ブロックI/Oパラメータ
    NewSimModel.h と NewSimModel_private.h のインクルード
    定数パラメータ

subsystem.c
  サブシステムが別ファイルで書かれたときの, 各, 非インライン, 非仮想, のサブシステムのコード

subsystem.h
  非インライン, 非仮想, のサブシステムの外部シンボル

NewSimModel.exe (PC上) 又は NewSimModel (UNIX上) ※ build フォルダでなく現フォルダに作られたもの
  make ユーティリティを使って開発システムが作った実行ファイル

NewSimModel.mk
  Real-Time Workshop が生成した makefile。本ファイルで実行ファイルをビルドする

rtmodel.h
  grt_main.c や grt_malloc_main.c などの静的メインプログラムに必要な #include 文を含む
  これらモジュールはコード生成時に作られるものではないため, モデル固有のデータ構成や入出力にアクセスするために rtmodel.h をインクルードする
  自分のメインプログラムモジュールを作る場合は rtmodel.h をインクルードすること

rtwtypes.h
  GRT ターゲットでは tmwtypes.h と simstruc_types.h をインクルードする
  Real-Time Workshop Embedded Coder ERT ターゲットでは, ビルド設定に応じて rtwtypes.h は最適化され, 定義, 列挙型, などは tmwtypes.h や simstruc_types.h に直接含まれる

rt_nonfinite.c
  inf や minus inf や nan のためのグローバル非定型値の宣言と初期化

rt_nonfinite.h
  非定型の値や関数の外部参照を定義

rtw_proj.tmw
  現在の Real-Time Workshop プロジェクトの名前を変更したとき, いつオブジェクトをリビルドするかを決めるために, make ユーティリティによって使われるファイル

 
使用するフォルダ

作業フォルダ
  「実行ファイルを生成する」を選択した場合に, Real-Time Workshop は NewSimModel.exe (PC上) 又は NewSimModel (UNIX上) を作業フォルダに作成する

ビルドフォルダ NewSimModel_grt_rtw (model_target_rtw)
  生成されたコードと他のすべてのファイル (実行ファイル以外) を保存する

プロジェクトフォルダ slprj
  Model ブロック経由で参照するモデルをビルドするとき, ファイルが本フォルダに保存される
  サブフォルダには次が保存される
    シミュレーションコード
    いくつかの Real-Time Workshop コード
    モデル間で共有するユーティリティコード
    その他のファイル
  Real-Time Workshop のユーザーは特に次を使用する
    Model 参照 RTW ターゲットファイル: slprj/target/modelname/
    Model 参照 RTW ターゲットとスタンドアロンのコード生成で使われるMATファイル: slprj/target/modelname/tmwinternal
    共有 (固定小数) ユーティリティ: slprj/target/_sharedutils

 
ビルドフォルダには次のファイルが保存される
  model.c 生成コード
  model.h 生成コードヘッダ
  model.mk 生成 makefile

オプションに応じて次のファイルが保存される
  model.rtw
  オブジェクトファイル (.obj 又は .o)
  サブシステムの生成コード
  HTML 要約報告
  TLC プロファイラ報告
  ブロックI/Oやパラメータ調整情報 (model_capi.c)
  パラメータや信号の C-API コード
  Real-Time Workshop プロジェクト (model.tmw)

 
 

□ チュートリアル1 (一般 Real-Time プログラムのビルド)

作業フォルダを準備する

1. フォルダ作成 (D:\f14example)

2. そのフォルダに移動

3. Simulink モデルを開く (f14)

4. 別名で保存 (f14rtw.mdl)
  コード生成が正しく行われるように, シミュレーションパラメータを変更する
  特に, 一般 Real-Time (GRT) や他のターゲットでは, 固定ステップのソルバーを使う必要がある

 
コンフィグレーションパラメータを設定する

1. シミュレーション − コンフィグレーションパラメータ, を選択

2. ソルバーで以下を設定
  Start Time: 0.0
  Stop Time: 60
  Solver options: Type=Fixed-step, ode5
  Fixed step size: 0.05
  Mode: SingleTasking

3. Apply ボタン押下

4. モデルの保存

 
ターゲットコンフィグレーションを選択する

システムターゲットファイル, テンプレート makefile, make コマンドを選択する
ここでは 一般 Real-Time ターゲット (GRT) を使用する
GRT ターゲットはワークステーションで実行可能なスタンドアロンのプログラムをビルドする

1. コード生成 − ターゲットの選択

2. システム ターゲット ファイル

3. 参照ボタンを押下
  ターゲットコンフィグレーションを選択すると, Real-Time Workshop は自動的に, システムターゲットファイル, テンプレート makefile, make コマンドを選択する

4. Generic Real-Time Target を選択し, OK ボタンを押下する

画面にはシステムターゲットファイル (grt.tlc), テンプレート makefile (grt_default_tmf), make コマンド (make_rtw) が表示される

5. (Debug タブ)

6. (Symbols タブ)

7. (Comments タブ)

8. 「コード生成のみ」のオプションがチェックされていないことを確認する

 
Build フォルダの構成

本サンプルでは f14rtw_grt_rtw フォルダが作成される。次のファイルが格納される
f14rtw.c: スタンドアロン Cコード
f14rtw_data.c: パラメータ初期値
rt_nonfinite.c: 非有限型 (Inf, NaN, -Inf) を初期化する関数
f14rtw.h: パラメータと状態変数の定義を含むインクルードヘッダーファイル
f14rtw_types.h: コードで使われるデータ型の事前定義
f14rtw_private.h: 共通インクルード定義を含むヘッダーファイル
rt_nonfinite.h: 非有限型の入力定義
rtwtypes.h: Simulink simstruct データ型の静的インクルードファイル
rtmodel.h: 静的メインプログラムで生成コードをインクルードするためのマスターヘッダーファイル (このファイル名はいつも同じで, f14rtw.h を単にインクルードしている)
f14rtw.mk: GRT ターゲットのテンプレートから作られた makefile

 

 

□ チュートリアル2 (データロギング)

Real-Time Workshop は, 各モデルの実行時間ステップで, システム状態, 出力, シミュレーション時間, を MAT ファイルデータ (デフォルトでは model.mat) として保存する機能を持っている

コンフィグレーションパラメータの Data Import/Export で設定する。Simulink モデルの出力を MATLAB ワークスペースに保存する設定と同じである。Real-Time Workshop では次の点が異なる。例えば, シミュレーション時間 tout は rt_tout に保存される

チュートリアルでは, シミュレーション時間とシステム出力を f14rtw.mat に保存する。よって, MATLAB ワークスペースにそれをロードして, シミュレーション時間とそれに対する出力を図示することができる

Part1: シミュレーション中のデータロギング

Part2: 生成したコードでのデータロギング

 

 

□ チュートリアル4 (生成コードの確認)

本サンプルでは「コード生成のみ」を選択する

ブロックI/O最適化機能を有効にしたとき, Real-Time Workshop はブロック出力に可能な限りローカル変数を使用する

バッファ最適化なしでのコード生成
コンフィグレーションパラメータ − 最適化
Signal storage reuse のチェックを外す

バッファ最適化ありでのコード生成
コンフィグレーションパラメータ − 最適化
Signal storage reuse をチェックする
Enable local block outputs はチェック
Reuse block outputs はチェック
Eliminate superfluous temporary variables (Expression folding) は未チェック

 

 

□ makefile の変更

MATLAB_ROOT     = /home/chbstyle/simulink/r2018b
START_DIR       = /home/chbstyle/Src/Work211022_gcc
PRODUCT         = /home/chbstyle/Src/Work211022_gcc/NewSimModel
CMD_FILE        = NewSimModel.rsp

OBJS            = rt_logging.obj NewSimModel.obj rtGetInf.obj rtGetNaN.obj rt_nonfinite.obj
MAIN_OBJ        = rt_main.obj

SRCS = $(MATLAB_ROOT)/rtw/c/src/rt_logging.c $(START_DIR)/NewSimModel_grt_rtw/NewSimModel.c $(START_DIR)/NewSimModel_grt_rtw/rtGetInf.c $(START_DIR)/NewSimModel_grt_rtw/rtGetNaN.c $(START_DIR)/NewSimModel_grt_rtw/rt_nonfinite.c
xx MAIN_SRC = $(MATLAB_ROOT)/rtw/c/src/common/rt_main.c
MAIN_SRC = /home/chbstyle/Src/Work211022_gcc/rt_main.c

DEFINES = -DMODEL=NewSimModel -DNUMST=2 -DNCSTATES=1 -DHAVESTDIO -DRT -DUSE_RTMODEL -DONESTEPFCN -DTERMFCN

CC     = /usr/bin/gcc
CFLAGS               = -c -I. -I.. -I/user/include -I$(MATLAB_ROOT)/include $(DEFINES)

LD     = /usr/bin/g++
LDFLAGS              = -s -L/usr/lib

 
# SOURCE-TO-OBJECT
%.obj : %.c
        $(CC) $(CFLAGS) -Fo"$@" $(subst /,\,"$<")
※NewSimModel.rsp の中身は *.obj なので -Fo"$@" を -o "$@" にする

 
# Create a standalone executable
$(PRODUCT) : $(OBJS) $(PREBUILT_OBJS) $(MAIN_OBJ)
        $(LD) $(LDFLAGS) -o $(PRODUCT) @$(CMD_FILE) $(subst /,\,$(subst /,\,$(SYSTEM_LIBS))) $(subst /,\,$(subst /,\,$(TOOLCHAIN_LIBS)))
※ここで SYSTEM_LIBS と TOOLCHAIN_LIBS は空

 
ファイル数が少なければ, gcc と g++(ld) を使って, 上記を参考に, 手動で obj と実行ファイルを作成できる
/usr/bin/gcc -c -I. 〜 -o rt_main.obj 〜 rt_main.c
/usr/bin/g++ -s 〜 -o NewSimModel @NewSimModel.rsp
./NewSimModel 

 

 

□ rt_main.c の変更

rt_OneStep() の前に
NewSimModel_U.In1 = val1;
NewSimModel_U.In2 = val2;

// The resolution of this integer timer is 0.001, which is the step size of the task.
// Timer of this task consists of two 32 bit unsigned integers.(0 to 4,294,967,295)
// When the low bit overflows to 0, the high bits increment.
NewSimModel_M->Timing.clockTick1
NewSimModel_M->Timing.clockTickH1

rt_OneStep() の後に
val3 = NewSimModel_Y.Out1;

 
/* 時間制御 */
#include <time.h>

struct timespec tstart, tcurrent;
timespec_get(&tstart, TIME_UTC);

timespec_get(&tcurrent, TIME_UTC);

struct timespec telapsed;
telapsed.tv_sec = tcurrent.tv_sec - tstart.tv_sec - (tstart.tv_nsec > tcurrent.tv_nsec ? 1 : 0);
telapsed.tv_nsec = tcurrent.tv_nsec - tstart.tv_nsec + (tstart.tv_nsec > tcurrent.tv_nsec ? 1000000000 : 0);

struct timespec tsim;
tsim.tv_sec = ( (long)NewSimModel_M->Timing.clockTick1) / 1000;
tsim.tv_nsec = ( ( (long)NewSimModel_M->Timing.clockTick1) % 1000) * 1000000;

truct timespec treq, trem;
treq.tv_sec = tsim.tv_sec - telapsed.tv_sec - (telapsed.tv_nsec > tsim.tv_nsec ? 1 : 0);
treq.tv_nsec = tsim.tv_nsec - telapsed.tv_nsec + (telapsed.tv_nsec > tsim.tv_nsec ? 1000000000 : 0);
if (treq.tv_sec >= 0 && treq.tv_nsec > 0) nanosleep(&treq, &trem);

 
/* 再生パターン読み込み */
#include <stdlib.h>

int process_step = 0;
int row_cnt = 0;
int row_num_max = 0;
char* ptr_excel_sheet_str = 0;
struct timespec start_time_ns, t_ns;

double rcv_test_start = 1;
double snd_sequence_time = 0;

timespec_get(&t_ns, TIME_UTC);

if ( (process_step == 0) && (rcv_test_start == 1))
{
    FILE *fp;
    char str[4096];
    int row_num;

    row_num = 0;
    if ( (fp = fopen("/home/chbstyle/Src/Work211022_gcc/Test_Pattern.csv", "rt")) == NULL)
    {
        (void)printf("rt_main : fopen error Test_Pattern.csv" );
        process_step = 0;
        rcv_test_start = 0;
    }
    while ( fgets(str, 4096, fp) != NULL )
    {
        row_num++;
    }
    fclose(fp);

    row_num_max = row_num;
    ptr_excel_sheet_str = (char *)malloc(row_num_max*3*2048); // col_max=3,len_max=2048

    row_num = 0;
    if ( (fp = fopen("/home/chbstyle/Src/Work211022_gcc/Test_Pattern.csv", "rt")) == NULL)
    {
        (void)printf("rt_main : fopen error Test_Pattern.csv" );
        process_step = 0;
        rcv_test_start = 0;
    }
    while ( fgets(str, 4096, fp) != NULL )
    {
        char *tp;
        tp = strtok( str, ",\r\n" ); //",¥r¥n"
        sprintf(ptr_excel_sheet_str + row_num*3*2048 + 0*2048, "%s", tp); // Time
        tp = strtok( NULL, ",\r\n" ); //",¥r¥n"
        sprintf(ptr_excel_sheet_str + row_num*3*2048 + 1*2048, "%s", tp); // AP
        tp = strtok( NULL, ",\r\n" ); //",¥r¥n"
        sprintf(ptr_excel_sheet_str + row_num*3*2048 + 2*2048, "%s", tp); // BK
        row_num++;
    }
    fclose(fp);

    row_cnt = 2; // row_cnt=0 is 'Title', row_cnt=1 is 'STEP/SLOPE'
    start_time_ns = t_ns;
    snd_sequence_time = 0;

    char value_str[4096];
    sprintf(value_str, "%s", (ptr_excel_sheet_str + row_cnt*3*2048 + 1*2048 ));
    NewSimModel_U.In1 = atof(value_str) / 100.0; // Driver_Accelerator
    sprintf(value_str, "%s", (ptr_excel_sheet_str + row_cnt*3*2048 + 2*2048 ));
    NewSimModel_U.In2 = atof(value_str) / 100.0; // Driver_Brake

    process_step++;
}
else if ( (process_step > 0) && (rcv_test_start == 1))
{
    struct timespec t_diff;
    t_diff.tv_sec = t_ns.tv_sec - start_time_ns.tv_sec - (start_time_ns.tv_nsec > t_ns.tv_nsec ? 1 : 0);
    t_diff.tv_nsec = t_ns.tv_nsec - start_time_ns.tv_nsec + (start_time_ns.tv_nsec > t_ns.tv_nsec ? 1000000000 : 0);
    if ( ( ( (double)t_diff.tv_sec) + ( ( (double)t_diff.tv_nsec)/1000000000)) >= atof( ptr_excel_sheet_str + (row_cnt+1)*3*2048 + 0*2048 ) ) // sec
    {
        row_cnt++;
    }
    else
    {
    }

    snd_sequence_time = ( (double)t_diff.tv_sec) + ( ( (double)t_diff.tv_nsec)/1000000000); // sec

    char value_str[4096], mode_str[4096];
    double val;

    sprintf(value_str, "%s", (ptr_excel_sheet_str + row_cnt*3*2048 + 1*2048 ));
    sprintf(mode_str, "%s", (ptr_excel_sheet_str + 1*3*2048 + 1*2048 ));
    if ( (strcmp(mode_str, "SLOPE") == 0) && (row_cnt <= (row_num_max - 2)) )
    {
        char value2_str[4096];
        sprintf(value2_str, "%s", (ptr_excel_sheet_str + (row_cnt+1)*3*2048 + 1*2048 ));
        double a, b, c, d;
        a = atof(value_str);
        b = atof(value2_str);
        c = atof( ptr_excel_sheet_str + row_cnt*3*2048 + 0*2048 );
        d = atof( ptr_excel_sheet_str + (row_cnt+1)*3*2048 + 0*2048 );
        val = (b - a) / (d - c) * (snd_sequence_time - c) + a ;
    }
    else
    {
        val = atof(value_str);
    }
    NewSimModel_U.In1 = val / 100.0; // Driver_Accelerator

    sprintf(value_str, "%s", (ptr_excel_sheet_str + row_cnt*3*2048 + 2*2048 ));
    sprintf(mode_str, "%s", (ptr_excel_sheet_str + 1*3*2048 + 2*2048 ));
    if ( (strcmp(mode_str, "SLOPE") == 0) && (row_cnt <= (row_num_max - 2)) )
    {
        char value2_str[4096];
        sprintf(value2_str, "%s", (ptr_excel_sheet_str + (row_cnt+1)*3*2048 + 2*2048 ));
        double a, b, c, d;
        a = atof(value_str);
        b = atof(value2_str);
        c = atof( ptr_excel_sheet_str + row_cnt*3*2048 + 0*2048 );
        d = atof( ptr_excel_sheet_str + (row_cnt+1)*3*2048 + 0*2048 );
        val = (b - a) / (d - c) * (snd_sequence_time - c) + a ;
    }
    else
    {
        val = atof(value_str);
    }
    NewSimModel_U.In2 = val / 100.0; // Driver_Brake

    //(void)printf("%lf %lf %lf\n", snd_sequence_time, NewSimModel_U.In1, NewSimModel_U.In2);

    if ( row_cnt == (row_num_max - 1) )
    {
        process_step = 0;
        rcv_test_start = 0;
        free(ptr_excel_sheet_str);
    }
    else
    {
    }
}
else
{
}

 
/* 結果書き込み */
(void)printf("%lf %lf\n", snd_sequence_time, NewSimModel_Y.Out1);

 
/* 画面表示 */
int prev_cnt = 0;

if (prev_cnt != row_cnt)
{
    prev_cnt = row_cnt;
    (void)printf("%lf %lf %lf %lf\n", snd_sequence_time, NewSimModel_U.In1, NewSimModel_U.In2, NewSimModel_Y.Out1);
}
else
{
}


rt_main.zip

Test_Pattern.zip

NewSimModel.zip (再掲)

 
/* 実行結果 */

f:id:sato-7411:20211102234441p:plain

f:id:sato-7411:20211102234505p:plain

 

□ rt_main.c の書き換え, NewSimModel_main.c の作成
外部から Start, OnStep, Terminate 等で呼び出せるように, NewSimModel_main.c を作成
それで実行するように rt_main.c を書き換え (例えば, 複数のモデルを実行する)

NewSimModel_main.zip

rt_main.zip

Test_Pattern.zip (再掲)

NewSimModel.zip (再掲)

( rt_main_win.zipWindows テスト用 )

( NewSimModel_dll.zipWindowsFormsApplication1.zipWindowsFormsApplication2.zipWindows テスト用、DLL形式、GUI C# )

 

 

※ 「((」が, はてな記法で脚注になるので, 間にスペースを入れました

 

 

 

回転数とトルクの伝達 (Simscape の使い方)

■ Simscape を使って自動車モデルを構築する

回転数・トルク (回転ドメイン) の変数型:
  foundation.mechanical.rotational.rotational
  w, ‘rad/s’ , % Angular velocity
  t, ‘N*m’, % Torque

車速・力 (並進ドメイン) の変数型:
  foundation.mechanical.translational.translational
  v, ‘m/s’, % Velocity
  f, ‘N’, % Force


■ Simscape によるプラントモデリング入門

IntroPlantModeling_with_Simscape__v20170622

スルー変数

アクロス変数

物理ドメイン アクロス変数 スルー変数
電気 抵抗にかかる電圧(AV) 電流の流れる方向(TV)
機械 (回転) 角速度 トルク
機械 (並進) 並進速度

 f:id:sato-7411:20200705003811p:plain

 

■ 参考になるドキュメント

Modeling in MathWorks Simscape by building a model of an automatic gearbox
ZF-ECOMAT 4 (HP 504 C / HP 594 C / HP 604 C)
Simscape language
理想的なギアは次の式で表される
Win = ratio × Wout
Tin = - Tout ÷ ratio
nodes
  機械 (回転) ドメイン (rotational) にて, 2つのノード (I, O) を定義
parameters
  初期値や単位と共にパラメータ (ratio ,1, ’1′) を宣言。シミュレーション開始前のみ変更可能な値
variables
  初期値や単位と共に変数を宣言
  t_in, 0, ‘N*m’
  t_out, 0, ‘N*m’
  w, 0, ‘rad/s’
setup
  変数とノードの関係を設定する。パラメータの正当性チェックも追加可能
  t_in は I.t と のスルー変数
  t_out は O.t と
のスルー変数
  w は I.w と O.w のアクロス変数
  ratio は 0 より大
equations
  変数, ノード, パラメータ, 入力/出力, それぞれの時間微分, の数学的な関係を定義する
  本件においては, トルク (t_in, t_out), 角速度 (w), 無単位パラメータ (ratio)
  == は, 他の言語のような, 右辺と左辺の等号, 代入, 比較演算子, とは異なる
  等式は, シミュレーション実行中は, 連続して同時に評価される
  等式は, DAE (微分方程式), ODE (常微分方程式), またはその両方, で, ベクター/マトリックスも使える。IF文で条件付き等式も指定できる
  Simscape ではすべての等式が連続時間で評価される。変数, 入力, 出力, 時間, などの値は, 区分的連続として定義される。区分的連続とは, 小時間の繰り返しにおいては, 値は連続で, ある場合には, 値が変化することを意味している。パラメータと定数は時間では変化しない。equations セクション内で, 共通のシミュレーション時間は time 関数で参照できる。それゆえに, 変数, 入力, 出力などの時間微分も使用できる

 

■ タイヤモデル (並進⇔回転変換)

□ Simscape

ノードは, 回転数・トルク, 車速・力, の両方を作成

力とトルク (スルー変数) について
  駆動力 [N] = トルク [N*m] / タイヤ半径 [m]  (参考:Urban Cafeteria

角速度と速度 (アクロス変数) について
  角速度 [rad/s] = 速度 [m/s] / タイヤ半径 [m]

    ここで, 速度 [m/s] / タイヤ半径 [m] = 角速度 [rad/s], 速度が「円周÷2」 [m/s] のとき 1 [rad/s] だから

 

Ideal Force Sensor
C に対する R の力 F を表示する
C と R の速度は同じ

 

Ideal Translational Velocity Source
C に対して R の速度を S にする
C に対して R にかかる力は変化無し

 

Ideal Torque Sensor
C に対する R のトルク T を表示する
C と R の回転数は同じ

 

□ Ideal Rotational Velocity Sensor
C に対する R の回転数 W を表示する
C に対して R にかかるトルクは変化無し

 

Ideal Torque Source
S へ正のトルク値を入力すると, C から R へ S のトルクを発生させる
C と R の回転数は同じ

 

Google カレンダーの自動変更 (JavaScript)

□ 概要
カレンダーを変更するために Google Clould Platform で API キーと OAuth 2.0 クライアント ID の両方を作る (API Discovery Document は無くて良い)。クライアント ID 作成時に JavaScript origin を指定する (例、https://hoge-hoge.com) (リダイレクト URL は不要)。google-api-javascript-client を使う (async defer src で読み込み)。初期処理で gapi.load と gapi.client.init を実行。gapi.auth2.getAuthInstance().signIn() でユーザー認証を行う。resource 変数に予定を作る。gapi.client.request で予定をカレンダーに書き込み。

 

 

□ 予備知識

Google Clould Platform
    https://console.cloud.google.com/

 

 

□ 参考ページ

google-api-javascript-client:
https://github.com/google/google-api-javascript-client

Request Body Sample:
https://github.com/google/google-api-javascript-client/blob/master/samples/requestWithBody.html

Getting Started:
https://github.com/google/google-api-javascript-client/blob/master/docs/start.md

JavaScript Quickstart:
https://developers.google.com/calendar/api/quickstart/js

Events Insert:
https://developers.google.cn/calendar/api/v3/reference/events/insert

Try Lifelog 様:
http://lifelog.main.jp/wordpress/?p=2202

 

 

□ カレンダー API の有効化

https://console.cloud.google.com/
プロジェクトを作成後、
上部ボタン左、ナビゲーション メニュー

APIとサービス
ライブラリ
Google Calender API
有効にする

 

 

□ APP_KEY の作成

APIとサービス
認証情報
上部ボタン、認証情報を作成
API キー
******

f:id:sato-7411:20220111232354p:plain

 

f:id:sato-7411:20220111232454p:plain

 

 

□ OAuth 2.0 クライアント ID の作成 (ウェブ アプリケーション)

APIとサービス
認証情報
上部ボタン、認証情報を作成
OAuth クライアント ID

アプリケーションの種類:ウェブ アプリケーション
名前:ウェブ クライアント2

承認済みの JavaScript 生成元: https://hoge-hoge.com
承認済みのリダイレクト URI: (なし)

クライアント ID:******
クライアント シークレット:******
JSON をダウンロード (未使用)

f:id:sato-7411:20220111232634p:plain

 

f:id:sato-7411:20220111232736p:plain

 

f:id:sato-7411:20220111232841p:plain

 

 

□ Client with Request Body サンプルの試し方 (Lolipop 環境)
https://github.com/google/google-api-javascript-client/blob/master/samples/requestWithBody.html
本ファイルを JavaScript origin の場所に配置する

変更①:
var apiKey = '...

変更②:
var clientId = '...

変更③:
var resource = { ...
"start": {
"end": {
予定の日付は近日に変更
,"visibility": "public" を追加。予定の設定が、予定あり、公開、になる

Lolipop にアップロード、ブラウザで https://hoge-hoge.com/requestWithBody.html を開く
Sign in ボタンを押下
OAuth 同意画面が開くので
ユーザーを選択
Continue
Continue

Make Request ボタンを押下

 

 

(20220228 追加コメント)

1回目
[Sign In] [Make Request]
※ Make Request ボタンを押しても、何も起こらない

Sign In ボタン押下

アカウント (hoge.hoge@gmail.com) のログインと認証 (許可) (「招待元のデベロッパーを信頼できる場合のみ、続行してください。」)

[Sign In] [Make Request] の画面に戻る
Sign In ボタンは何度も押せて、認証画面に進む
Make Request ボタン押下

Calendar entry successfully created 


2回目
※ セッションを閉じ (ブラウザを閉じ)、ブラウザを再起動した後。またはPC再起動後
[Sign In] [Make Request]
Make Request ボタン押下

Calendar entry successfully created 
Sign In ボタンで認証しなくても、カレンダーに書き込める

 

 

(20220315 追加)

□ 他の一般ユーザーでのカレンダー書き込み

指定する calendarId は、「一般公開して誰でも利用できるようにする」が設定されていること

プロジェクトに (テスト) ユーザーを追加する: API とサービス, OAuth 同意画面, テストユーザー, ADD USERS

JavaScript (Project No. での apiKey, clientId) では、ログインした人のカレンダーに書き込める

makeRequest で calenderId を指定する、他のページから書き込めてしまうので primary にはしない

Google カレンダーの「特定のユーザーとの共有」に「ユーザーを追加」した場合、複数ユーザーから1つのカレンダーに書き込めるか?
→ 書き込めない、表示のみ