†意識の記録† 理解のブログ

私の私の視点による私の経験の記録。私の視点で見る限り誤りのない認識で記事を書いている。一切の苦情は受け付けない。

INDEX

7777-07-07 07:07:07 | Important
当ブログに存在する全ての情報は、あくまでネット上の情報に過ぎない事を認めること。
又、あくまで私の考えを記しているに過ぎない事を認める事。

二年振りぐらいにトップページを更新。(2015年3月)
「山賊の娘ローニャ」を見ています。初回はテンポの悪いゴミ作品だと思いましたが、改善してきました。台詞回しが良いですね。
趣味:写真(カメラ)、撮影の為の小旅行、電子工作、オーディオ、PC自作
当ブログコンテンツ:徒然なるままに書かれる政治的発言など
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

組み込み系に於ける浮動小数の除算

2024-09-23 12:09:35 | Diary
こんにちは。

コンピュータは除算(割り算)が苦手です。勿論、我々人間よりは速いかも知れませんが。

Windows のアプリなどを作る限りに於いては、それほど気にしなくてよい話ですけれども、
組み込み系で、可能な限りレイテンシを減らしたい時は、気になる場面もあるでしょう。

最近は、 ARM コアの組み込み系が殆どですので、 32bit float でも除算がそこまで遅いということはありません。
例えば、 M33 コアでは、 32bit float で、加減乗算 1 クロック、除算 14 クロックです。
よほどリソースに余裕のない場合でなければ、間に合わない、ということはないでしょう。

なので、あまり今回の記事自体が有用な場面というのは無いと思うわけですけれども、
何となく興味があった、ということで、浮動小数の除算を話題にしたいと思います。


除算を、除算なしで行う方法は、勿論、逆数を求めて掛け算することです。
これは、数学的には全く等価です。

となると、高速に逆数を計算し、それが除算より速ければよい、ということになります。
本当に除算より速いかは前述のように怪しいのですが、方法として知っておこう、ということです。

このテーマで Google 検索など掛けますと、ニュートン・ラフソン法がヒットします。
収束条件はちょっと無視しますと、 X の逆数を Y とした時、初期値を y_0 として、
y_t+1 = y_t * ( 2 - X * y_t)
という漸化式を回せば、 y_t は Y に収束する、ということです。
反復回数が増えれば精度も上昇します。

問題は、初期値の設定方法です。
初期値がおかしいと収束しませんし、収束するとしても反復回数が増えてしまいます。
理想的な初期値は逆数ですので、与えられた X から、逆数の近似値を一発で求めなければなりません。

今回は、 X を 32bit float に限定して考えます。 64bit などは考えてみてください。
次のような共用体で、 float をビット分解します。

typedef union{
  FLOAT fFloat;
  struct{
    UINT bMan : 23;
    UINT bExp : 8;
    UINT bSign : 1;
  }bit;
}U_FLOAT_TYPE;

bSign が符号、 bExp が指数部、 bMan が仮数部です。
指数部は 0x7F = 0d127 でオフセットされています。
仮数部は 24bit 目を 1 に固定したケチ表現になっています。

初期値を最適化すると反復を減らせますが、その為に計算をするのは意味ないです。
そこで、初期値は次のように与えます。
bSign = 0;
bExp = ~(bExp) - 2;
bMan = ~bMan;

※ X が負値の場合は、反復した後で bSign = 1; とします。

この場合、十分な精度(10 進 7 桁)を出すには 3 回反復する必要があります。
(2 回でも用途に依っては十分です。 10 進 3 桁の精度はあります。)

精度が出ているか判定するコードは、計算資源を使ってしまいますし、反復に for 文を使うのも勿体ないです。
(ループは1回回すと、比較とインクリメントを 1 回ずつやっていまいます。)
なので、ループを展開して、

fY = fY * ( 2.0f - fX * fY);
fY = fY * ( 2.0f - fX * fY);
fY = fY * ( 2.0f - fX * fY);

こんな冗長なコードとしておくのがよい、ということになります。
この場合、代入 3 回、乗算 6 回、加算 3 回です。

これで、 X の逆数 Y が求まりますので、

A /= X;

というコードは、

Y = calc_inv(X);
A *= Y;

などと置き換えることができます。

前述したように、例えば M33 コアは 14 クロックで浮動小数の除算ができますので、
ここのコードは、最低でも 12 クロックであり、意味がないです。

とは言え、除算を用いずに、除算を実行できる、という具体的な方法として、載せておきます。



以上。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Pythonで伝達関数の解析をする話

2024-08-27 21:43:08 | Diary
こんばんは。

Python は、ちょっとしたことから凝ったことまで簡単にできるのが素晴らしいところです。
ただ、実行速度の遅さは難点ですね。

さて、今回は、ラプラス領域で記述された伝達関数の解析をする話です。
紹介するのは、 Python の Control ライブラリです。

次のように import しておきます。
import matplotlib.pyplot as plt
from control.matlab import tf, bode
import control as ctrl


ラプラス変換が何なのかは、ここでは省略します。

ラプラス領域での伝達関数は、多項式の分数で表すことができます。
伝達関数は、次のように定義します。
sys = tf(num, den)

num, den は、それぞれ、多項式の係数のリストです。

例えば、 G(s) = (s^2 + 2s + 3) / (4s^2 - 5s - 6) であれば、
sys = tf([1, 2, 3], [4, -5, -6])
とします。

直列(畳み込み)と並列(加算)は、次のようです。
sys = sys1 * sys2
sys = sys1 + sys2

フィードバックループは、次のようです。
sys = feedback(sys_g, sys_bf, sign=-1)
NFB なら sign=-1 、 PFB なら sign=1 ですし、他の倍数とすることもできます。
sys_bf は、 FB ループ内に入る伝達関数です。例えば、ノイズがループに乗る場合にバンドパスフィルタを入れたりできます。

Bode 線図は、次のようです。
mag, phase, omega = bode(sys, dB=True, Hz=True, deg=True, plot=True)
plt.show()

極と零点は、次のようです。(複素数のリストが返ってきます。)
poles = ctrl.poles(sys)
zeros = ctrl.zeros(sys)



実際に伝達関数を定義してみましょう。

大学で必ず習う、コイルに電圧を掛ける場合の、コイルを流れる電流を考えましょう。
入力を電圧 e 、出力を電流 i 、伝達関数を G(s) とします。
回路方程式は、抵抗を R [ohm] 、インダクタンスを L [H] としたら、
e = Ri + L di/dt
ですから、伝達関数は、出力 / 入力という定義より、
G(s) = 1 / (R + Ls)
と書けます。
従って、 Python では、

resistance:float = 10.0
inductance:float = 1 / 10**-3
sys_G = tf([1], [inductance, resistance])
※ 抵抗とインダクタンスはテキトーです。

となります。


次に、バネマスダンパ系を考えましょう。
入力を力 f 、出力を位置 x 、伝達関数を G(s) とします。
運動方程式は、質量 m [kg] 、粘性係数 η[kg/s] 、バネ定数 k [kg/s/s] としたら、
f = m d^2x/dt^2 + η dx/dt + kx
ですから、伝達関数は、
G(s) = 1 / (ms^2 + ηs + k)
と書けます。
従って、 Python では、

mass:float = 1.0
eta:float = 0.4
coe_k:float = 0.01
sys_G = tf([1], [mass, eta, coe_k])
※ 各定数はテキトーです。

となります。


とっても簡単ですね。

コンピュータとは、計算する機械ですから、ゲームばかりではなく、計算に使ってあげてもよいのですよ。


以上。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

スマートフォン

2024-07-22 04:07:09 | Diary
こんばんは。
最近、自分のスマートフォンの電池が劣化してきて、一日保たないようになってきたので、賞与の入ったタイミングで買い替えることにしました。

以前、 Google Pixel 8 Pro のカメラ(のスペック)を紹介する記事を挙げたと思います。
確かにカメラ性能は素晴らしいのですけれども、価格が高過ぎると感じていました。

なので、 CPU 性能、カメラ性能、価格のバランスを考えて、以下の条件としました。
・ Android 14
・ 旧世代でも良いからハイエンド CPU
・ カメラは超広角、広角、望遠の3眼以上
・ 価格 10 万円未満
・ FeliCa 搭載

こんなもんじゃろ、という条件なのですが、意外と合致するものは少なくて、初の中華スマートフォンデビュになりました。
選んだものは、 Xiaomi 13T Pro です。



前使っていた Zenphone7 と、手元にあるタブレット Pixel Table と比較してみました。

Pixel Tablet の SoC が思ったよりスコア低いのは意外ですが、まあそこは置いて於くとして、
Zenphone7 と比較すると、全体的に 50-100% ほどスコアが上がってますね。

シングルコア性能は、 Prime コアの性能と見てよいと思います。
これは実質、1つのアプリがどれだけサクサク動くか、ということです。
今でも特に困っていないのですけどね。

AnTuTu というのは、ゲームのグラフィック性能です。
自分はあまりスマートフォンでゲームしませんけれども、気になる方も居ると思ったので載せました。
簡易 VR とか体験してみたいですね。



カメラについては、タブレットと比較しても仕方ないので Pixel 8 Pro に換えました。
正直、カメラのスペックで言えば、 Pixel 8 Pro の方が上ですけれども、価格差ほどかと言われるとどうだろうと思います。

とりあえず、超広角カメラの画角は、現状より拡がったので問題ないでしょう。
望遠側は短くなりましたが、画素数も増えましたので、必要ならクロップすればよいですね。
一番使用頻度の高い標準カメラについては、普段持ち歩いている SIGMA 24mm F3.5 DG DN と近い画角になりました。これまで以上に違和感なく使用できると思います。

こうして比較して思うのは、カメラ性能を特に推してるスマートフォンを除けば、 3 眼タイプはそれほど進化していないのだな、ということです。
恐らく、消費者が一般に必要とする画角は 3 眼で十分に満たされている、ということではないでしょうか。
というか、一眼カメラを持っている人達でも、通常使用するのは 105mm ぐらいまでだと思いますね。

画角はともかく、画質は SoC の進化のお陰か、処理自体が進化しているのか、大変に改善が見られますね。
勿論、 AI 的な処理で、現実からは乖離してしまっている面もあるかも知れませんけれども。



今日の記事、特に比較の為に用いたデータは、 Microsoft Copilot に作成して頂きました。
若干、公称値と違うような気もするので、本当はチェックするべきですが、面倒で止めました。
チャット AI の進化は、ネット検索の利便性を大幅に向上させるもので、それが無料で利用できるのは大変喜ばしいことです。


以上。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PythonでGUI付のアプリを作る話

2024-06-10 23:00:55 | Diary
こんばんは。忙しかったりで少し間が開いてしまいましたね。

最近、 Python で Windows 用の GUI アプリケーションを作ったりしてます。
CUI のアプリケーションは沢山作ってきましたけれども、やはり GUI があるとテンションが変わってきます。

特に、 Flet が便利というか、少ない記述でそれっぽくモダンなデザインにできて気に入っています。


そこで今回は Flet の Tips です。
案外こういうのってサクッと見付からないので、自分用の備忘も兼ねて書いておこうと思います。

趣味でカメラをやっているということもあり、画像をいじるアプリケーションとか作ってみたくなることがあります。
そうなると、 GUI 上に画像を表示して、画像の上をすべっていくマウスカーソルの位置が欲しくなりますよね。

Flet では、 GestureDetector を使用することで、簡単にマウスカーソルの位置を取得できます。
  image = ft.Image(src="test_img.png")
  gesture_detector = ft.GestureDetector(
    content=image,
    hover_interval=10,
    on_hover=on_hover,
  )
こんな感じに、 control を定義して、 def on_hover(e:ft.HoverEvent) を呼べば、 e.local_x とかで画像上の座標が取得できるわけです。

ところが、これだとマウスを画像の上に持って行っただけでずーっと取得し続けてしまいます。
押し込んでいる間だけ取得したい、とかっていうニーズは絶対にあると思うわけです。私がやりたいです。

そこで、以下のように定義しなおします。
  gesture_detector = ft.GestureDetector(
    content=image,
    hover_interval=10,
    on_hover=on_hover,
    on_tap_down=img_click_on,
    on_tap_up=img_click_off
  )
これで、ローカル変数に is_click_on:bool とかフラグを定義して、 def img_click_on(e) などで操作してやればよい、というのが素直な思考です。
しかしやってみると、上手くいきません。

で、どうしたら良いのか、という話なんですけれども、 hover ではなく drag を利用します。
  gesture_detector = ft.GestureDetector(
    content=image,
    drag_interval=10,
    on_vertical_drag_update=on_drag,
    on_horizontal_drag_update=on_drag
  )
水平方向と垂直方向のドラッグの更新処理それぞれに、 def on_drag(e:ft.DragUpdateEvent) とか登録してやります。
画像上のカーソル位置は、先ほどと同じく、 e.local_x と e.local_y で分かります。

尚、 hover_interval と drag_interval は、ミリ秒でイベントの間隔を設定できます。 10ms でも 100Hz ですから十分でしょう。(デフォルトはゼロなので設定必須です。)

これを利用すると、虫眼鏡ツールや、指先ツールなどを実装できるようになる(かも知れません)。


以上。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする