AI ツール · A2
CAD 部品情報の抽出、BOM 整理と図面の一括出力
STEP アセンブリを読み込み、部品情報の抽出と質量の推定、部品番号と材料のチェックを行い、BOM、指摘一覧、部品ごとの三面図を 1 回の実行で出力する。
個人プロジェクト(自主制作)2026年10月に作成した公開デモ。自作の汎用モデルを使用手法と要件の定義、結果の確認は本人が担当し、コードは AI コーディングツールで実装して本人が確認した業務で作成した CATIA 向けの同種ツールは機密のため公開しておらず、本ページでも CATIA は実行していない
1 回の実行結果
- 部品の種類
- 17
- 部品インスタンス
- 32
- 三面図
- 17
- 検出した指摘
- 5エラー 4 件 · 警告 1 件
- 総処理時間
- 3.84s
サンドボックス Linux x86_64、vCPU 2 個、Python 3.13.16、OCP 7.8.1.1
処理の流れ
STEP アセンブリ
中間ファイル形式 STEP AP242。PRODUCT の id を部品番号、name を名称とし、材料は AP242 の材料プロパティに記述する。
input/*.stepアセンブリツリーの走査
製品定義ごとにサブアセンブリを再帰的に展開し、部品ごとのインスタンス数と配置を集計する。
step_reader.pyプロパティと質量
厳密な B-rep 上で体積と表面積を積分し、バウンディングボックスを求める。体積 × 密度表で質量を推定する。
geometry.pyルールチェック
部品番号の正規表現と正規化、材料の未設定・密度表にない材料、部品番号の重複(形状を比較)、ソリッドの妥当性。
rules.pyBOM · 三面図 · ファイル名
BOM を CSV / XLSX / JSON で出力し、HLR 投影で A4 三面図を作成する。ファイル名は「部品番号_名称」に統一する。
report.py · drawing.py · naming.py
入力:デモ用アセンブリ

汎用ロボット関節の簡略構造。中空モータ、波動歯車減速機ユニット、クロスローラベアリング、カバー、締結部品からなり、モータと減速機の 2 つのサブアセンブリを含む。寸法はデモ用の切りのよい仮の値で、いずれの製品とも無関係である。同じフォルダの Python スクリプトで OpenCASCADE によりパラメトリックにモデリングし、STEP に出力した。
ルールチェックを確かめるため、デモデータには意図的に 4 か所の誤りを入れた。
- フロントカバーがリアカバーの部品番号 RJ-STR-002 を流用している
- ドライバ基板の部品番号が rj_elc_002 になっている(大文字小文字と区切り文字のみの違い)
- エンコーダディスクに材料が設定されていない
- M3×8 ボルトの部品番号が RJ-FST-02 になっている(桁数の誤り)
指摘一覧
ルールチェックの出力(issues.csv)をそのまま重大度順に並べた。部品番号の重複は該当する 2 部品それぞれに 1 件ずつ記録する。
| 区分 | 部品番号 | 名称 | 内容 |
|---|---|---|---|
| エラー | RJ-ELC-001 | エンコーダディスク | 材料が未設定のため、質量を推定していない |
| エラー | RJ-FST-02 | 六角ボルト M3×8 | 部品番号「RJ-FST-02」は命名規則に合わず、自動修正しない。桁数が誤っている場合、正しい番号を判断できないため人が確認する |
| エラー | RJ-STR-002 | フロントカバー | 部品番号 RJ-STR-002 を形状の異なる 2 部品が共用している(リアカバー、フロントカバー) |
| エラー | RJ-STR-002 | リアカバー | 部品番号 RJ-STR-002 を形状の異なる 2 部品が共用している(リアカバー、フロントカバー) |
| 警告 | RJ-ELC-002 | ドライバ基板 | 部品番号「rj_elc_002」は大文字小文字と区切り文字のみが不統一のため、RJ-ELC-002 に正規化した |
BOM
集計 BOM(17 行)。表はボタンを押したときに bom.json を読み込み、列見出しで並べ替えできる。
ダウンロード
三面図
部品ごとに A4 図面 1 枚(SVG)を第一角法で作成し、ファイル名は部品番号に基づく。線は OpenCASCADE の隠線処理(HLR)による投影で、外形寸法のみ記入した。図面を選ぶと拡大表示する。
Python ツール
ツールはリポジトリの tools/cad-bom/ にある。形状カーネルはオープンソースの OpenCASCADE(cadquery-ocp)、BOM の表計算ファイルは openpyxl で作成し、依存バージョンは requirements.txt に固定した。以下の数値はコミットした output/run.json による。処理時間を除き、pytest がツールを再実行して出力をバイト単位で照合する。
処理段階ごとの時間
| 処理段階 | 秒 |
|---|---|
| STEP とアセンブリツリーの読み込み | 0.25 |
| 体積、表面積、バウンディングボックス | 0.14 |
| ルールチェック | 0.00 |
| BOM の集計 | 0.00 |
| CSV / XLSX の書き出し | 0.02 |
| 三面図(HLR) | 2.53 |
| アセンブリ全体図 | 0.91 |
| マニフェストとチェックサム | 0.00 |
| 総処理時間 | 3.84 |
再現手順
cd tools/cad-bom
python -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python -m cadbom input/robot-joint-demo.step
.venv/bin/python -m pytestGitHub 上のツールフォルダ · tools/cad-bom
比較:同じ作業を手作業で行うと、部品を 1 つずつ開いてプロパティを確認し、体積を測り、部品番号を照合したうえで、図面を 1 枚ずつ作成して名前を付け直す必要がある。ツールは 1 回の実行で完了し、結果を何度でも照合できる。
主要コード
アセンブリツリーの走査(step_reader.py)
def walk(label: TDF_Label, location: TopLoc_Location, depth: int) -> Occurrence:
node = Occurrence(definition(label), location, depth)
if XCAFDoc_ShapeTool.IsAssembly_s(label):
components = TDF_LabelSequence()
XCAFDoc_ShapeTool.GetComponents_s(label, components, False)
for i in range(1, components.Length() + 1):
component = components.Value(i)
referred = TDF_Label()
XCAFDoc_ShapeTool.GetReferredShape_s(component, referred)
placement = location.Multiplied(XCAFDoc_ShapeTool.GetLocation_s(component))
node.children.append(walk(referred, placement, depth + 1))
return node部品番号のチェックと正規化(rules.py)
def check_part_number(raw: str | None, name: str, pattern: str) -> Checked:
if raw is None:
return Checked("", [Issue("PN_AMBIGUOUS", "", name, {})])
regex = re.compile(pattern)
if regex.fullmatch(raw):
return Checked(raw, [])
fixed = normalize_part_number(raw)
if regex.fullmatch(fixed):
return Checked(fixed, [Issue("PN_NORMALIZED", fixed, name, {"raw": raw, "fixed": fixed})])
return Checked(raw, [Issue("PN_FORMAT", raw, name, {"raw": raw, "pattern": pattern})])HLR による投影図の作成(drawing.py)
def project(shape: TopoDS_Shape, view: str, deflection: float = 0.02, hidden: bool = True) -> Projection:
direction, x_dir = VIEWS[view]
algo = HLRBRep_Algo()
algo.Add(shape)
algo.Projector(HLRAlgo_Projector(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(*direction), gp_Dir(*x_dir))))
algo.Update()
algo.Hide()
result = HLRBRep_HLRToShape(algo)
visible = _polylines(result.VCompound(), deflection) + _polylines(result.OutLineVCompound(), deflection)
hidden_lines = []
if hidden:
hidden_lines = _polylines(result.HCompound(), deflection) + _polylines(
result.OutLineHCompound(), deflection)
return Projection(visible, hidden_lines)CATIA 自動化との対応
業務では CATIA 向けの同種ツール(VBA / Python で CATIA のオートメーション API を呼び出すもの)を作成したが、機密のため公開していない。下表は本ページの各処理が CATIA V5 のオブジェクトモデルのどの概念に対応するかを文章で示すだけであり、本ページでは CATIA を実行しておらず、CATIA マクロのコードも掲載していない。
| 本ページ(STEP + OpenCASCADE) | CATIA V5 オートメーションで対応する概念 |
|---|---|
| XCAF のアセンブリツリー、コンポーネントと配置 | ProductDocument → Product.Products(インスタンス)と ReferenceProduct |
| STEP PRODUCT の id / name | Product.PartNumber / Product.Nomenclature |
| AP242 の材料と密度表 | 部品に適用した材料とその密度、またはユーザー定義の Parameters |
| BRepGProp による体積・表面積 | SPAWorkbench の Measurable(Volume、Area)または Product.Analyze |
| HLR による三面図 SVG | DrawingDocument でビューを作成し、ExportData で PDF / DXF に出力 |
| 「部品番号_名称」のファイル名 | PartNumber などのプロパティからファイル名を付けて保存 |
制約
- デモ用アセンブリは自作の汎用モデルで、形状を簡略化している(歯形や転動体はモデル化していない)。いずれの製品も表していない。
- 質量は体積 × 公称密度による推定値で、めっき・塗装、ねじ、公差、購入部品の内部構造は考慮していない。
- 三面図は HLR による投影の模式図で、外形寸法(バウンディングボックス)のみを記入し、公差、断面、詳細寸法はない。加工には使えない。
- デモは STEP と OpenCASCADE に基づき、CATIA では実行していない。上表の CATIA のインタフェースは概念上の対応を示すだけである。