生成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)➡こちら