見出し画像

生成AIを使ってキャラクター画像から3Dモデル作成とCAE解析

こんにちは。
オープンCAE Advent Calendar 2024の8日目の記事です

普段から何かシミュレーションできるものはないか探しており、キャラクターを使った解析は家族への受けも良いのでやっています。

例えばミッフィーちゃん周りの流れとか・・・・

他には、ミッフィーちゃんを使ったエアバッグ衝突シミュレーションとか・・・

こういったものを無償のオープンソースCAE解析ツールで行っています。

しかし、ミッフィーちゃんは有名過ぎてキャラクターの著作権や3D CADモデルの制作者への配慮とか気になりますよね。

ミッキーの著作権は2023年12月31日に切れたとか。

そこでだ、オリジナルキャラクターの画像から3Dモデルを生成AIを使って作り出し、それを使ってCAE解析をしようというモチベーションが生まれました。

キャラクター画像の入手

というわけで、こちらの書籍を書いている方にキャラクター画像を頂けるか聞いてみました。

交渉の結果、「いいよ」って言ってもらえたので、画像をもらいました。

いただいた画像はこちらです。

画像生成AI

画像の生成AIツールはこちらを利用しました。

こんな感じでとりあえず画像をアップロードします。

数分で画像からモデルができます。

stepファイルとかstlファイルとかが出力できるので、今回はstlファイルを出力してシェル要素としてCAE解析をするようにしました。

LS-DYNA形式からRadioss形式へ変換

今回は、ミッフィーちゃんでも行ったエアバッグシミュレーションをしたいので、LS-PrePostでstlファイルを読み込みメッシュ作成をしました。

LS-PrePostはLS-DYNA形式のフォームで節点座標や要素番号が出力されるので、これをRadioss形式に変換する必要があります。
変換はテキストベースで可能ですので、ChatGPTにお願いして、変換プログラムを作りました。

import re

def read_data_from_file(file_path):
    with open(file_path, 'r') as file:
        lines = file.readlines()
    
    node_data = []
    element_data = []
    is_node_section = False
    is_element_section = False

    for line in lines:
        # コメント行をスキップ
        if line.strip().startswith("$#"):
            continue

        if line.strip() == "*NODE":
            is_node_section = True
            is_element_section = False
            continue
        elif line.strip() == "*ELEMENT_SHELL":
            is_element_section = True
            is_node_section = False
            continue
        elif line.strip().startswith("*"):
            is_node_section = False
            is_element_section = False
        
        if is_node_section:
            node_data.append(line.strip())
        elif is_element_section:
            element_data.append(line.strip())

    return node_data, element_data


def convert_node_to_openradioss(node_data):
    header = (
        "#---1----|----2----|----3----|----4----|----5----|----6----|----7----|----8----|----9----|---10----|\n"
        "/NODE\n"
        "#  node_ID                  Xc                  Yc                  Zc\n"
    )
    radioss_nodes = []
    for line in node_data:
        if line.strip() == "":  # 空行をスキップ
            continue
        parts = re.split(r'\s+', line.strip())
        try:
            node_id = int(parts[0])
            x, y, z = map(float, parts[1:])
            # 各フィールドを整列
            radioss_nodes.append(f"{node_id:10d}{x:20.7f}{y:20.7f}{z:20.7f}")
        except ValueError:
            print(f"Skipping invalid line in node data: {line}")
    return header + "\n".join(radioss_nodes)


def convert_element_shell_to_openradioss(element_data):
    header = (
        "#---1----|----2----|----3----|----4----|----5----|----6----|----7----|----8----|----9----|---10----|\n"
        "/SHELL/1\n"
        "# shell_ID  node_ID1  node_ID2  node_ID3  node_ID4                         phi_s               Thick\n"
    )
    radioss_elements = []
    for line in element_data:
        if line.strip() == "":  # 空行をスキップ
            continue
        parts = re.split(r'\s+', line.strip())
        try:
            shell_id = int(parts[0])
            node_id1, node_id2, node_id3, node_id4 = map(int, parts[2:6])
            radioss_elements.append(f"{shell_id:10d}{node_id1:10d}{node_id2:10d}{node_id3:10d}{node_id4:10d}")
        except ValueError:
            print(f"Skipping invalid line in element data: {line}")
    return header + "\n".join(radioss_elements)


# ヘッダーおよびフッター
header_text = (
    "#RADIOSS STARTER\n"
    "#---1----|----2----|----3----|----4----|----5----|----6----|----7----|----8----|----9----|---10----|\n"
    "# Created by Gmsh, Radioss Mesh Interface by PaulAltair sharp@altair.com\n"
    "#---1----|----2----|----3----|----4----|----5----|----6----|----7----|----8----|----9----|---10----|\n"
    "/BEGIN\n"
    "#Runname\n"
    "ballbase_0000\n"
    "#   Invers      Irun\n"
    "      2022         0\n"
    "#    Input_mass_unit   Input_length_unit     Input_time_unit\n"
    "                  kg                  mm                  ms\n"
    "#     Work_mass_unit    Work_length_unit      Work_time_unit\n"
    "                  kg                  mm                  ms\n"
    "##\n"
    "#---1----|----2----|----3----|----4----|----5----|----6----|----7----|----8----|----9----|---10----|\n"
)

footer_text = (
    "##--------------------------------------------------------------------------------------------------\n"
    "## End Of Radioss Block Deck\n"
    "##--------------------------------------------------------------------------------------------------\n"
    "/END\n"
)

# ファイルパス
file_name = "rivi"
input_file_path = f'{file_name}.k'
output_file_path = f'{file_name}.rad'

# ファイルからデータを読み込む
node_data, element_data = read_data_from_file(input_file_path)

# データを変換
converted_nodes = convert_node_to_openradioss(node_data)
converted_elements = convert_element_shell_to_openradioss(element_data)

# 結果を出力ファイルに書き込み
with open(output_file_path, 'w') as output_file:
    output_file.write(header_text + converted_nodes + "\n\n" + converted_elements + "\n" + footer_text)

print(f"変換されたデータが '{output_file_path}' に出力されました。")

こちらで無事Radioss形式に変換ができました。

OpenRadiossでエアバッグシミュレーション

無事Radioss形式に変換できたので、githubにアップしているエアバッグシミュレーションを使ってキャラクターを変更してシミュレーションしました。

いい感じですね。

ブログ➡宇宙に入ったカマキリ(物理ブログ)
Youtube➡エンジニアの自宅CAE工房
X➡@t_kun_kamakiri
LINE公式➡こちら
計算力学固体力学2級アプリ➡こちら
計算力学熱流体2級アプリ➡こちら
計算力学振動2級アプリ➡こちら
円管内の流れ(技術書典16)➡こちら
2次元円柱まわりの流れ(技術書典17)➡こちら

いいなと思ったら応援しよう!