![見出し画像](https://assets.st-note.com/production/uploads/images/164872109/rectangle_large_type_2_a94225d85cd00122d1fc86194b6f6920.png?width=1200)
生成AIを使ってキャラクター画像から3Dモデル作成とCAE解析
こんにちは。
オープンCAE Advent Calendar 2024の8日目の記事です
普段から何かシミュレーションできるものはないか探しており、キャラクターを使った解析は家族への受けも良いのでやっています。
例えばミッフィーちゃん周りの流れとか・・・・
他には、ミッフィーちゃんを使ったエアバッグ衝突シミュレーションとか・・・
こういったものを無償のオープンソースCAE解析ツールで行っています。
しかし、ミッフィーちゃんは有名過ぎてキャラクターの著作権や3D CADモデルの制作者への配慮とか気になりますよね。
ミッキーの著作権は2023年12月31日に切れたとか。
そこでだ、オリジナルキャラクターの画像から3Dモデルを生成AIを使って作り出し、それを使ってCAE解析をしようというモチベーションが生まれました。
キャラクター画像の入手
というわけで、こちらの書籍を書いている方にキャラクター画像を頂けるか聞いてみました。
交渉の結果、「いいよ」って言ってもらえたので、画像をもらいました。
いただいた画像はこちらです。
![](https://assets.st-note.com/img/1733595197-5C4qKipAQrYXzBeN08vUk2oy.png?width=1200)
画像生成AI
画像の生成AIツールはこちらを利用しました。
無料でも月に600クレジットが発行され利用することができます。
1回に25くらい使用するのかな。
こんな感じでとりあえず画像をアップロードします。
![](https://assets.st-note.com/img/1733594698-X39gMYt6HVEd1qNsK8R5CJhS.png?width=1200)
数分で画像からモデルができます。
![](https://assets.st-note.com/img/1733594733-gVBOI21bk9Sj7zpmDiJ4aoYs.png?width=1200)
stepファイルとかstlファイルとかが出力できるので、今回はstlファイルを出力してシェル要素としてCAE解析をするようにしました。
LS-DYNA形式からRadioss形式へ変換
今回は、ミッフィーちゃんでも行ったエアバッグシミュレーションをしたいので、LS-PrePostでstlファイルを読み込みメッシュ作成をしました。
![](https://assets.st-note.com/img/1733594836-c3wF2z8bdNSDU5KPt0yjlHks.png?width=1200)
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にアップしているエアバッグシミュレーションを使ってキャラクターを変更してシミュレーションしました。
りびぃ君をお借りして、画像生成からエアバッグ保護シミュレーションをやってみました。
— カマキリ🐲CAE頑張る (@t_kun_kamakiri) November 18, 2024
まずまずです! pic.twitter.com/Afg8tS672g
無駄に応力とか出しちゃう
— カマキリ🐲CAE頑張る (@t_kun_kamakiri) November 18, 2024
※単位系はmm-kg-msなのでGPa pic.twitter.com/dj0AhtqoSw
いい感じですね。
ちいかわもやってみた
適当な画像を拾ってきて。。。
![](https://assets.st-note.com/img/1737172222-h2xSdED3tQoz9MKuOcIC4Yne.jpg?width=1200)
ご覧の通り…
生成AI... pic.twitter.com/jGHjmo4vOQ
— カマキリ🐲CAE頑張る (@t_kun_kamakiri) January 17, 2025
吹き出しとかなしにしてみる。
![](https://assets.st-note.com/img/1737172218-Eer1WnJTk9NxBhcHV8mzUGDp.png)
生成AI...
— カマキリ🐲CAE頑張る (@t_kun_kamakiri) January 18, 2025
吹き出しなしにしてみた https://t.co/Pqe3LErARj pic.twitter.com/J1CWiB1nMj
いいかんじ pic.twitter.com/QPhlELZDPQ
— カマキリ🐲CAE頑張る (@t_kun_kamakiri) January 18, 2025
面白いですね。
ちいかわにいたっては、そもそも3Dモデル公開されていますがね。。
ちいかわはおそらく身長65cmだろう pic.twitter.com/bpRSSa3sQl
— カマキリ🐲CAE頑張る (@t_kun_kamakiri) November 10, 2024
ブログ➡宇宙に入ったカマキリ(物理ブログ)
Youtube➡エンジニアの自宅CAE工房
X➡@t_kun_kamakiri
LINE公式➡こちら
計算力学固体力学2級アプリ➡こちら
計算力学熱流体2級アプリ➡こちら
計算力学振動2級アプリ➡こちら
円管内の流れ(技術書典16)➡こちら
2次元円柱まわりの流れ(技術書典17)➡こちら