はじめに
振動・疲労解析の実務で、加速度や応力の時系列データからサイクルカウント(レインフロー法など)を行う機会は多いと思います。普段はPythonのrainflowパッケージや商用ソフトを使っている方も多いのではないでしょうか。
CSVを一つ確認したいだけなのにPythonを書く、商用ソフトを立ち上げる、などの手間を減らしたくて、ブラウザだけで完結するレインフロー解析ツールを個人開発でリリースしました。
それが今回紹介する、頻度解析ツール「WaveCount」 になります。
CSVをドラッグ&ドロップするだけで、フィルター前処理から4種類のサイクルカウント(レインフロー・レンジペア・レベルクロッシング・ピーク・バレー)、ヒストグラム・行列表示、CSVエクスポートまでを一気通貫でこなせます。インストール不要、サーバーにデータを送らずブラウザ内で完結する設計にしています。
この記事では、「なぜ作ったか」よりも「実装で地味にハマったポイント」を中心に書きます。同じような解析ツールを自作したい方の参考になれば嬉しいです。
なぜ作ったか(手短に)
業務でレインフロー解析をPythonでやることが多かったのですが、「ちょっと波形を見てサイクルを数えたいだけ」なのに毎回スクリプトを書くのが面倒でした。ブラウザで完結するツールがあれば、CSVを放り込むだけで済むはずだと思い、React + TypeScriptで作り始めました。
すでに同じシリーズとして波形確認・FFT解析ツール「WaveAxis」、振動3D可視化ツール「WaveScope」も公開しており、WaveCountはその3本目にあたります。
技術スタック
- React + Vite + TypeScript
- Recharts(時系列グラフ・ヒストグラム描画)
- Web標準のみでバターワースフィルタ・レインフロー計算を自前実装(外部の解析ライブラリは意図的に使っていません)
- Vitestで、ASTM E1049の規格例題波形を使ったアルゴリズム検証テストを実装
ハマったポイント①:レインフロー4点法のスタック操作
ASTM E1049-85の4点法は、文章で読むと分かった気になるのですが、実装するとエッジケースで詰まりがちです。今回は以下のようなスタックベースの実装にしました。
export function extractCycles(turningPoints: number[]): RawCycle[] {
const cycles: RawCycle[] = [];
const points: number[] = [];
for (const x of turningPoints) {
points.push(x);
while (points.length >= 3) {
const x1 = points[points.length - 3];
const x2 = points[points.length - 2];
const x3 = points[points.length - 1];
const rangeX = Math.abs(x3 - x2);
const rangeY = Math.abs(x2 - x1);
if (rangeX < rangeY) {
break;
} else if (points.length === 3) {
cycles.push({ x1, x2, count: 0.5 }); // 半サイクル
points.shift();
} else {
cycles.push({ x1, x2, count: 1.0 }); // 全サイクル
const last = points.pop() as number;
points.pop();
points.pop();
points.push(last);
}
}
}
// 残った転回点は全部半サイクルとして払い出す
while (points.length > 1) {
cycles.push({ x1: points.shift() as number, x2: points[0], count: 0.5 });
}
return cycles;
}
ポイントは、「スタックの先頭3点が最初の3点かどうか(=波形の開始点を含むかどうか)」で半サイクル・全サイクルの扱いが変わることです。ここを間違えると、規格の例題波形(-2, 1, -3, 5, -1, 3, -4, 4, -2)で合計サイクル数が合わなくなります。
検証は、Pythonのrainflowパッケージの出力と全サイクル分(range・mean・count)を突き合わせて一致することを確認しました。この規格例題は「開いた列」(始点と終点が一致しない)なので、合計サイクル数が必ず4.0になるという性質があり、これを固定のテストケースとして常時実行しています。
ハマったポイント②:バターワースフィルタのゼロ位相化
疲労解析の前処理としてフィルタをかける場合、位相遅れがあると転回点の位置がズレて、サイクルカウントの精度に影響します。そのためfiltfilt(前向き・後ろ向きの2パス適用によるゼロ位相フィルタリング)が必須です。
これも外部ライブラリに頼らず自前実装したのですが、パディング処理(信号の端で偶数反転パディングを行わないと、フィルタ処理直後の端点で大きな振動的アーチファクトが出る)が地味に重要でした。バンドパスは、ローパス・ハイパスそれぞれにゼロ位相を適用してからカスケードする方式にしています。
ハマったポイント③:Rechartsでの対数スケールヒストグラム
サイクルカウントのヒストグラムは、少数の大振幅サイクルと多数の小振幅サイクルが混在するため、Y軸を対数スケールにしたくなります。しかしRechartsのscale="log"は、カウント0のビンがあると描画が壊れるという制約があります(log(0)が-Infinityになるため)。
これを避けるため、log10(count + 1)で事前に変換した値を、通常の線形スケール軸に描画する方式にしました。
function toLog(count: number): number {
return Math.log10(count + 1);
}
function fromLog(value: number): number {
return Math.round(10 ** value - 1);
}
Y軸の目盛りラベル表示時にfromLogで逆変換すれば、見た目は対数スケールのまま、実際のカウント数をそのまま表示できます。
同様に、時系列グラフの範囲選択機能(ReferenceAreaで選択範囲を描画)でも、ifOverflow="discard"(デフォルト)だと、浮動小数点の丸め誤差で軸の端をわずかに超えただけで要素ごと非表示になる、という罠がありました。ifOverflow="extendDomain"を明示することで解決しています。
精度検証:ASTM規格の例題波形で試してみる
「自前実装のアルゴリズムが本当に正しいのか」は、この手のツールで一番気になるところだと思います。ASTM E1049に載っている標準的な例題波形を使えば、誰でも同じ手順で検証できるので、実際にWaveCountで試した結果を載せます。
使った波形
以下の9点のシーケンスです(ASTM E1049の解説でよく使われる例題)。
-2, 1, -3, 5, -1, 3, -4, 4, -2
CSVにするとこうなります(1列目:時間[s]、2列目:値。サンプリング周波数1Hzとして読み込みます)。
0,-2
1,1
2,-3
3,5
4,-1
5,3
6,-4
7,4
8,-2
期待される正解値
Pythonのrainflowパッケージ(ASTM E1049-85準拠の実装)で計算した結果と突き合わせています。
| range | mean | count |
|---|---|---|
| 3 | -0.5 | 0.5 |
| 4 | -1.0 | 0.5 |
| 4 | 1.0 | 1.0 |
| 8 | 1.0 | 0.5 |
| 9 | 0.5 | 0.5 |
| 8 | 0.0 | 0.5 |
| 6 | 1.0 | 0.5 |
合計サイクル数:4.0
この波形は「開いた列」(始点と終点が一致しない)なので、合計サイクル数が必ず4.0になるという性質があり、この一致確認をアルゴリズムの妥当性チェックの基準にしています。
WaveCountでの実行結果
上記のCSVをWaveCountに読み込んで、サンプリング周波数1Hz・解析範囲を全区間に設定し、クラス数9、ヒストグラム横軸をレンジ(全振幅)でレインフロー解析を実行した結果が以下です。
| range | count |
|---|---|
| 3~4 | 0.5 |
| 4~5 | 1.5 |
| 6~7 | 0.5 |
| 8~9 | 1.5 |
合計サイクル数が4.0、各サイクルのrange/mean/countが上表と一致していることが確認できます。
参考:サポートしている4つのカウント手法
| 手法 | 概要 |
|---|---|
| レインフロー(ASTM E1049 4点法) | 疲労評価で最も信頼性が高いとされる標準手法 |
| レンジペアカウント | 単純な波形ではレインフローと同等の結果になることが多い簡易版 |
| レベルクロッシングカウント | ISO 12107にも掲載される、各レベルの交差回数を数える手法 |
| ピーク・バレーカウント | 山・谷の値そのものを分布として見る、入門的な手法 |
あえて疲労寿命・被害度計算(S-N曲線を使った計算)は実装していません。材料定数がユーザー環境依存で、下手に組み込むと「このツールの計算を信じていいのか」という余計な検証コストをユーザーに強いることになるためです。サイクルデータをCSVで全件出力できるようにしているので、各自の環境・材料定数で計算してもらう設計にしています。
使ってみてほしい方
- レインフロー解析の結果を、ちょっとした確認・検算のために使いたい方
- Pythonスクリプトを毎回書くほどでもない、小規模な波形解析をしたい方
- 複数のカウント手法を横並びで比較したい方
無料デモ版でも合成波形(サイン波・ランダム波)で全機能を試せます。CSVアップロードのみ買い切り課金(WaveAxisシリーズ共通ライセンス)となっています。
WaveAxisシリーズポータルサイト
https://www.waveaxis.jp/

