LoginSignup
13
4

More than 3 years have passed since last update.

MATLABで「たたみこみ」を語る

Last updated at Posted at 2020-12-14

はじめに

 「たたみこみ」とは「合成積」のことです(と言われるとよけい分からない)。
・和室とは関係ありません
・柔道の寝技でもありません。
東北工業大学の中川先生は、「人生、たたみこみ」と書いておられますが、私は正直、そこまで達観した「たたみこみ観」は持っていないので、実践的な「たたみこみ」の説明を書いてみます。
なお、CNN(たたみこみニューラルネットワーク)については、本稿では触れません。

1.なぜ「たたみこみ」が必要なのか

 「たたみこみ」の例としては、音響のエコー(反射)が良く知られています。
下図のように人がいて、1カ所に反射物があったとします。
図1.jpg
人と反射物の距離がLで音速がVcだとすると、人が発生した音声は、反射物で反射し、2L/Vc 秒後に人に戻ってきます(2は往復なので)。
また、図2のように2カ所に反射物があり、人からの距離が2つの反射物までの距離が、それぞれLa,Lbだとしますと、それぞれの反射物からの音は、2La/Vc、2Lb/Vc 秒後に返ってきます。
図2.jpg
それぞれから返ってくる音の大きさが同じで、波形に崩れがない(線形)とします。
このとき、2つの反射物からの音は、時間をずらして加算されます。
これをMATLAB的に書くと、

sum2point.m
wave=load('wave.dat'); %波形データを読み込む
wavelen=length(wave); %波形データの点数

fs=10e3; % サンプリング周波数[Hz]
Vc=340; % 音速[m/sec]

La=4; % A点までの距離[m]
Lb=6; % B点までの距離[m]

Pa=round(2*La/Vc*fs); % A点までの往復の時間をサンプリング点数に換算
Pb=round(2*Lb/Vc*fs); % B点までの往復の時間をサンプリング点数に換算

WaveAll=zeros(1,600); % 合成波形を収納するためのゼロ配列

figure(1);

subplot(3,1,1);
plot(WaveAll);
text(20,0.5,'ゼロ配列');

WaveAll(Pa:(Pa+wavelen-1))=WaveAll(Pa:(Pa+wavelen-1))+wave;
subplot(3,1,2);
plot(WaveAll);
text(20,0.5,'A点の反射を加える');

WaveAll(Pb:(Pb+wavelen-1))=WaveAll(Pb:(Pb+wavelen-1))+wave;
subplot(3,1,3);
plot(WaveAll);
text(20,0.5,'さらにB点の反射を加える');

実行すると、下記のようなグラフが表示されます。
figure1.jpg
また、A点からの距離と、B点からの距離が近い場合は、波形が重なりますが、重なったところは両者の波形が加算された形になります。
figure2.jpg
ところで、上で説明した演算は、けっこう面倒くさいものがあります。例えて言うなら、
   y = ( 2 * a ) + ( 4.5 * a ) + ( 11 * a )
でaの値が与えられたときに、カッコの中から計算しちゃうような面倒くささがあります。
   y = ( 2 + 4.5 + 11 ) * a
で計算すべきでしょう。
たたみこみ演算は、これに近い感じです(あくまでも「感じ」ですが)。

 2.MATLABの「たたみこみ関数」convを使う

 前章の2点からの反射の例では、波形そのものは同じで、返ってくる時間だけが異なっていました(反射信号の振幅も同じ)。
そこで、伝搬時間Pa, Pb(正確には伝搬時間をサンプリング周波数で割ったもの)と振幅の1だけを用いて、これら(遅延と振幅)を表現するインパルス列を作成します。
図3.jpg

このインパルス列と信号波形の畳み込みを行ないます。
  WaveOut = conv ( WaveIn , Inp ) ;
というような使い方で、スクリプトは、例えば、

ex_conv.m
wave=load('wave.dat'); %波形データを読み込む

fs=10e3; %サンプリング周波数[Hz]
Vc=340; %音速[m/sec]

La=4; %A点までの距離[m]
Lb=6; %B点までの距離[m]

Pa=round(2*La/Vc*fs); %A点までの往復の時間をサンプリング点数に換算
Pb=round(2*Lb/Vc*fs); %A点までの往復の時間をサンプリング点数に換算

WaveAll=zeros(1,600); %合成波形を収納するためのゼロ配列

figure(2);

WaveAll(Pa)=1;
subplot(4,1,1);
plot(WaveAll);
ylim([-0.2 1.2]);
text(20,0.5,'A点反射のインパルス列');

WaveAllA=conv(wave,WaveAll); %たたみこみ
subplot(4,1,2);
plot(WaveAllA);
xlim([0 600]);
text(20,0.5,'A点の反射のみ');

WaveAll(Pb)=1;
subplot(4,1,3);
plot(WaveAll);
ylim([-0.2 1.2]);
text(20,0.5,'A点B点反射のインパルス列');

WaveAll=conv(wave,WaveAll); %たたみこみ
subplot(4,1,4);
plot(WaveAll);
xlim([0 600]);
text(20,0.5,'A点、B点の反射');

実行すると下のようなグラフが出ます。
figure3.jpg
また、A点からの反射がB点からの2倍という場合は、インパルスの振幅を変えることで対応できます。
figure3a.jpg
ここで、インパルス列とのたたみこみということで、説明しましたが、離散時間系においては、どのような波形(関数)もインパルスの組み合わせです。
よって、よく出てくる離散値のたたみこみの式、
式1.jpg
と同じことになります。
ということで、「たたみこみ」が何か、どうやるかのざっくりとした説明を終わります。

 3.多数点からの反射をいかに計算するか

前章までで、シンプルな解説は終わったので、ここからは書きたいように書いてみましょう。
反射物として板みたいなものを考えます。板はXY平面上にあるとします。
図4.jpg
板の大きさ、観測点の位置関係は、次のとおりとします。
図5.jpg
こういった場合には、セオリーとして、まず反射板をメッシュに切ります(シミュレーションの基本?)。
信号の波長とか演算精度と相談ですが、ここは0.1m格子でメッシュを切ってみましょう。
meshgridという関数が便利です。

x=-2.5:0.1:2.5;% X方向メッシュ設定
y=-1.5:0.1:1.5;% Y方向メッシュ設定

[X,Y]=meshgrid(x,y);% メッシュ生成

これで、メッシュ各点のX座標、Y座標の2次元配列ができました。
メッシュ各点から観測点Pまでの距離を求めます。
abs関数を用います。
図6.jpg

P=6; %観測点のZ座標[m]
Lxyz=abs(abs(X+1i*Y)+1i*P);

%%% 距離をサンプリング点に換算する
fs=10e3; %サンプリング周波数[Hz]
Vc=340; %音速[m/sec]
Pxyz=round(2*Lxyz/Vc*fs); % 2は往復

abs関数は絶対値計算と、複素数の大きさ計算に使えます。pythonなどでも使えるようですが。
figure8.jpg
これで、たたみこみの元になる時間遅延データが完成しました。
問題はこの後、インパルス列をどうやって生成するかです。
forループを回して「集計」するのが一般的かと思いますが、ここはヒストグラム(正しくはhistcounts)を使います。入力の振幅が揃っていないと使えないとか制約はありますが、個人的には「推し」です。

Pedges=0:600; %ヒストグラムの境界の位置
[N,edges]=histcounts(Pxyz,Pedges);

subplot(2,1,1);
histogram(Pxyz,Pedges);
xlim([0 600]);
text(20,60,'インパルス列');

WaveAll=conv(wave,N); %たたみこみ
subplot(2,1,2);
plot(WaveAll);
xlim([0 600]);
text(20,200,'たたみこみ波形');

下図のようなグラフが表示されます。

figure4.jpg

終わりに

「たまにはマジメなことを書くか」と言っておられる人もいたので、私もマジメに書いてみましたが、いかがでしたでしょうか。
ということで、終わります。
お疲れさまでした。

13
4
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
4