fullseye

断面形状の計測(翼型・羽根・押し出し材) — 使い方ガイド

この族は何をする道具箱か

閉じた輪郭を「弦」で正規化して、その断面の性質を数字にする層です。入力は (N, 2)(x, y) 点列(閉輪郭)、出力は厚み分布・キャンバー線・前縁半径・後縁隙間、そして設計形状との偏差です。

翼型が典型ですが、原理は「細長い閉断面を弦に沿って測る」だけなので、送風ファンの羽根、羽根車、タービン翼、押し出し材、ガスケット、板金の断面にそのまま使えます。

12 op / 4 カテゴリ(numpy のみ。台帳は opsprofile.py、実体は profileops.py):

なぜ足したか —— 在庫を数えた結果

2-D レジストリには輪郭を扱う op が 97 個ありました(*_xld の生成・選択・平滑化・モーメント・当てはめ、convex_hulldistance_transform …)。しかし:

やりたいこと 既存で近いもの なぜ代わりにならないか
弦に沿った厚み分布 get_region_thickness 領域の太さで、弦方向の分布ではない
キャンバー線 —— 無い
前縁半径 hx_fit_circle_contour 円は当てられるが「どこが前縁か」を決める層が無い
輪郭同士の符号つき偏差 hx_dist_ellipse_contour / distance_transform 前者は楕円限定、後者はラスタ

つまり「形はいくらでも作れて選べるのに、断面として測る層が空いていた」。

最小の例(そのまま動きます)

import numpy as np

import profileops

# 1) 設計形状は閉形式から(真値が計算で出る)
ref = profileops.profile_synth_naca4("2412", n=801)

# 2) 既知の欠陥を注入して「測定品」を作る —— 検出力を測るために要る
measured = profileops.profile_perturb(ref, "thicken", amount=0.002)

# 3) 断面としての量
t = profileops.profile_thickness(ref, 401)          # (x, 厚み)
cam = profileops.profile_camber(ref, 401)           # (x, キャンバー)
r_le = profileops.profile_leading_edge_radius(ref)  # 前縁半径
gap = profileops.profile_trailing_edge_gap(ref)     # 後縁の開き

assert abs(np.max(t[:, 1]) - 0.12) < 5e-4                  # 下 2 桁 = 最大厚み
assert abs(r_le - 1.1019 * 0.12 ** 2) < 0.05 * r_le        # NACA の閉形式
assert gap > 1e-3                                          # 既定係数は後縁が開く

# 4) 設計形状との偏差 —— 注入した 0.002 がそのまま戻るか
dev = profileops.profile_deviation(measured, ref, n=400)
assert abs(dev["mean"] - 0.002) < 0.0002

# 5) 3 度回して 0.7 ずらしても、欠陥は消えない
a = np.radians(3.0)
rot = np.array([[np.cos(a), -np.sin(a)], [np.sin(a), np.cos(a)]])
moved = measured @ rot.T + np.array([0.7, -0.3])
assert abs(profileops.profile_deviation(moved, ref, n=400)["mean"] - 0.002) < 0.0002

# 6) 同じ形どうしなら偏差はほぼ 0(検査の床)
assert profileops.profile_deviation(ref, ref, n=400)["rms"] < 1e-5

使う順序

flowchart TD
    N["profile_synth_naca4<br/>閉形式の設計形状"]
    M["測定した輪郭<br/>(スキャン・撮影・CAD)"]
    N --> PB["profile_perturb<br/>既知の欠陥を注入"]
    PB --> M
    M --> FR["profile_chord_frame<br/>弦・前縁・後縁"]
    FR --> NO["profile_normalise<br/>弦長 1・前縁が原点"]
    NO --> SI["profile_sides<br/>上面 / 下面"]
    SI --> TH["profile_thickness"]
    SI --> CA["profile_camber"]
    NO --> LE["profile_leading_edge_radius"]
    SI --> TE["profile_trailing_edge_gap"]
    M --> AL["profile_align<br/>chord / rigid / none"]
    N --> AL
    AL --> DV["profile_deviation<br/>符号つき法線偏差"]
    N --> DV
    DV --> OUT["合否・工程管理<br/>ロット別ヒートマップ"]
    TH --> OUT
    CA --> OUT
    LE --> OUT

閉形式との一致(NACA 4 桁)

閉形式 実測(NACA 2412、点数 801)
最大厚み 下 2 桁 / 100 = 0.12 0.12006(x = 0.300)
最大キャンバー 1 桁目 / 100 = 0.02 0.01882(x = 0.415)★定義差
前縁半径 1.1019 t² = 0.01587 0.01594(frac=0.001)
後縁隙間 既定係数なら開く 0.00390

公開表とも突き合わせてあります —— UIUC の naca2412.dat の上面 15 点と閉形式の差は最大 1.0e-4 翼弦

★ 対称翼で 0 になるか —— この族で一番効いた検査

対称翼(NACA 0012 など)のキャンバーは定義上 0 です。ここが 0 にならなければ、どこかで規約が破れています。

実際、最初の実装は 0.001257 を返しました。これは後縁の半隙間そのもの。原因は「弦 = 最も離れた 2 点」という素朴な定義で、後縁が開いていると角の片方を拾って弦が 0.07 度傾くことでした。有翼(2412)だけを見ていたら「キャンバーが 7 % 低い」で片づけていたはずです。

いまは後縁を隙間の中点に取り直してから前縁を決め直すので、対称翼で 0.00001 になります。

もう 1 つ、前縁と後縁の区別も直観が逆でした。「前縁は丸い = 曲率が大きい」と考えて 3 点円の曲率で判定したところ、上下面がほぼ接する後縁のほうが曲率が大きく出て、前縁と後縁が入れ替わりました。正しい弁別は「少し入ったところが太いほう」です。

前縁半径は当てはめ窓で変わる

鼻先は円ですが、少し離れると円ではありません。窓を広げると系統的に過大になります(NACA の閉形式 1.1019 t² と突き合わせた実測、点数 4001):

frac NACA 0012 NACA 2412 NACA 0021 NACA 0008
閉形式 0.01587 0.01587 0.04859 0.00705
0.001(既定) 0.01590 0.01594 0.04780 0.00730
0.005 0.01708 0.01716 0.04801 0.00870
0.01 0.01872 0.01880 0.04897 0.01042
0.03 0.02517 0.02526 0.05412 0.01688

既定を 0.03 にしていたころは閉形式の 1.6 倍を返していました(docstring には「1 % 以内」と書いてありましたが、それは確かめる前の推測でした)。厚い翼ほど鈍いので窓の影響が小さく、薄い翼ほど敏感です。

★ 位置合わせが欠陥を吸収する(この族でいちばん危ない罠)

一様な厚み増(0.002)は、どの合わせ方でも平均 0.00199 として戻ります。3 度回して並進を掛けても同じ。ここは安心してよい。

問題は、欠陥が合わせの基準そのものに乗っているときです。前縁を 0.003 削った形を比べた実測:

合わせ方 最悪の偏差 その位置 前縁側の最悪
"chord"(既定) -0.00283 x = 1.000(後縁!) -0.00096
"rigid" -0.00166 x = 0.009 -0.00166
"none" -0.00309 x = 0.001 -0.00309

前縁を削ると前縁の位置そのものが動くので、前縁と後縁を合わせる "chord"損失を反対の端へ移し、前縁での落ち込みを 3 分の 1 に見せます。"rigid"(全点の最小二乗)は場所は正しく出しますが、ずれを全体へ散らして半分に見せます。同じ座標系にある("none")ときだけ、注入した 0.003 がそのまま出ます

欠陥が基準に乗る場所(前縁・後縁・取り付け面)を検査するときは、合わせの自由度をその場所から外して取るか、測定側の座標系を先に決めて "none" で比べること。

相似(スケール推定)は提供していません —— 大きさの誤差そのものを吸収するからです。profile_align は常に scale = 1.0 を返します(推定していないことの明示)。

検査の床 —— 検出したい量と同じ桁なら意味が無い

同じ形を自分自身と比べたときの偏差が、検出したい欠陥と同じ桁なら、その検査は何も見つけません。

最初の実装は rms 6.05e-4 でした(狙う欠陥 1e-3 と同じ桁)。原因は「合わせる前に取り方が揃っていなかった」こと —— 生の輪郭と取り直した輪郭では弦の枠(とくに後縁の中点)がわずかに違い、同一の形でも 0.02 度の回転と 7.8e-4 の並進が入っていました。比べる前に同じ取り方で取り直すようにして、床は 1.13e-6 になりました(535 倍)。

その結果、注入した欠陥はここまで測り返せます:

注入 測定
一様な厚み増 0.002 平均 +0.001993
同上 0.0005 平均 +0.000497
同上 0.0001 平均 +0.000099
前縁侵食 0.003 最小 -0.00309(none)
うねり 0.001 ±0.00104(平均は打ち消し)
ねじれ 0.5 度 ±0.0018(平均は打ち消し)

0.0001 翼弦は、弦 1 m の羽根なら 0.1 mm です。

取り直しは前縁の分解能を落とす

profile_resample は等弧長です。NACA の生成は余弦分布(鼻先が密)なので、等弧長に直すと前縁の分解能が落ちます:

取り直しの点数 元形状からの rms
150 5.4e-4
300 4.1e-4
600 1.7e-4
1200 1.1e-4
2400 2.0e-5

「取り直しても形は同じ」ではありません。比べる 2 本は同じ点数で取り直すこと(profile_deviation はそうしています)。

型を 1 つも作らなかった理由

輪郭は既存の pairs((N, 2)(x, y))です。この repo で pairs は「1 価の関数データ」(spectrum / MTF / ray_fan)と「多角形」(filled_polygon が食う)の両方に使われており、後者と同じ立場を採りました。

混ぜたら嘘になるかを確かめた結果:

型を分けると、97 個ある既存の輪郭 op(gen_region_contour_xld / smooth_contours / convex_hull 系 / filled_polygon)との接続が切れます。そこを切る損のほうが大きい、という判断です。

次にどこへ繋がるか