Bambu Lab A1 miniの出力時間が気になる

# 3Dプリンターの出力時間を解析したい

最近、ぼちぼちと[3Dプリンターで出力している](https://qiita.com/kotauchisunsun/items/55d48a4649bb13cc04fc)のですが、造形にかかる時間が気になります。
　私が持っているBambu Lab A1 miniはそこそこ早い部類の3Dプリンターだと思うのですが、1時間を超え始めるとつらいなぁと思ったりすることもあります。
　そういったときに、**元のモデルのサイズから縮小すると出力時間が短くなる**という傾向に気づきました。これはある意味で、あたりまえで、あるモデルから0.8倍で出すとすると、立体なので0.8の3乗、約51%の体積量になるはずです。では、それって本当にあってるんだっけ？ということが確認したくなったのでやりました。

# 問題設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139643/fdb5a09b-291d-def8-6016-d6cc9e4b1c91.png)

　対象となるのは単なる立方体で10～60mmと10mmずつ刻みました。あとは、ノズル径を0.2mm Standardと0.28mm Extra Draftの2種類。充填パターンをグリッド・ジャイロイドとしています。充填密度は15%固定。

# 充填パターンについて

　充填パターンは、3Dプリンターの見えない部分をどのような形状で埋めるか？ということを指定します。例えば、以下の画像はジャイロイドというパターン。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139643/74feea4e-7713-5708-2bc2-e53a9008c4f9.png)

BambuStudioにはこの項目がたくさんあり、どれを選べばいいか迷いました。
その時に、出会ったのが、この記事で、

https://note.com/takafumisoma/n/n9a9c022e6c6e

超雑にまとめると、**ジャイロイドがフィラメントの使用量が少なくて強度が出て一番いいよ**。と書いてあります。そのため、ざっくりとジャイロイドを選ぶ。ということをしていたため、基本となるグリッドとジャイロイドで比較してみよう。と思いました。

# データ

実際に出力すると面倒なので、BambuStudioのシミュレーションの時間やフィラメント量をコピペしてます。

<details><summary>データ</summary>

| ノズル径 (mm) | ノズル径^2 | 出力サイズ (mm) | 出力サイズ^3 | 充填パターン | フィラメント量 (g) | 造形時間 (sec) |
|:------------:|:---------:|:-------------:|:-----------:|:------------:|:-----------------:|:--------------:|
| 0.2          | 0.04      | 10            | 1000        | グリッド      | 0.72              | 370            |
| 0.2          | 0.04      | 20            | 8000        | グリッド      | 3.73              | 798            |
| 0.2          | 0.04      | 30            | 27000       | グリッド      | 10.05             | 1302           |
| 0.2          | 0.04      | 40            | 64000       | グリッド      | 20.32             | 1914           |
| 0.2          | 0.04      | 50            | 125000      | グリッド      | 37.17             | 2850           |
| 0.2          | 0.04      | 60            | 216000      | グリッド      | 60.73             | 4260           |
| 0.28         | 0.0784    | 10            | 1000        | グリッド      | 0.73              | 272            |
| 0.28         | 0.0784    | 20            | 8000        | グリッド      | 3.77              | 586            |
| 0.28         | 0.0784    | 30            | 27000       | グリッド      | 9.88              | 968            |
| 0.28         | 0.0784    | 40            | 64000       | グリッド      | 20.62             | 1499           |
| 0.28         | 0.0784    | 50            | 125000      | グリッド      | 37.51             | 2442           |
| 0.28         | 0.0784    | 60            | 216000      | グリッド      | 59.74             | 3445           |
| 0.2          | 0.04      | 10            | 1000        | ジャイロイド  | 0.72              | 1226           |
| 0.2          | 0.04      | 20            | 8000        | ジャイロイド  | 3.55              | 1654           |
| 0.2          | 0.04      | 30            | 27000       | ジャイロイド  | 9.68              | 1436           |
| 0.2          | 0.04      | 40            | 64000       | ジャイロイド  | 20.11             | 2703           |
| 0.2          | 0.04      | 50            | 125000      | ジャイロイド  | 36.17             | 4680           |
| 0.2          | 0.04      | 60            | 216000      | ジャイロイド  | 58.71             | 7500           |
| 0.28         | 0.0784    | 10            | 1000        | ジャイロイド  | 0.72              | 270            |
| 0.28         | 0.0784    | 20            | 8000        | ジャイロイド  | 3.6               | 589            |
| 0.28         | 0.0784    | 30            | 27000       | ジャイロイド  | 9.69              | 1086           |
| 0.28         | 0.0784    | 40            | 64000       | ジャイロイド  | 20.14             | 2075           |
| 0.28         | 0.0784    | 50            | 125000      | ジャイロイド  | 35.97             | 3576           |
| 0.28         | 0.0784    | 60            | 216000      | ジャイロイド  | 58.2              | 5760           |

</details>


# 解析と考察

ChatGPTにプログラムの大筋書いてもらって、手直ししています。
回帰はLassoで行って、グラフを書かせています。

<details><summary>解析コード</summary>

```python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score
from matplotlib.colors import to_rgba

# Data Loading
data = {
    "Nozzle Diameter": [0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.28, 0.28, 0.28, 0.28, 0.28, 0.28, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.28, 0.28, 0.28, 0.28, 0.28, 0.28],
    "Nozzle Diameter^2": [0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.0784, 0.0784, 0.0784, 0.0784, 0.0784, 0.0784, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.0784, 0.0784, 0.0784, 0.0784, 0.0784, 0.0784],
    "Output Size": [10, 20, 30, 40, 50, 60, 10, 20, 30, 40, 50, 60, 10, 20, 30, 40, 50, 60, 10, 20, 30, 40, 50, 60],
    "Output Size^3": [1000, 8000, 27000, 64000, 125000, 216000, 1000, 8000, 27000, 64000, 125000, 216000, 1000, 8000, 27000, 64000, 125000, 216000, 1000, 8000, 27000, 64000, 125000, 216000],
    "Infill Pattern": ["Grid", "Grid", "Grid", "Grid", "Grid", "Grid", "Grid", "Grid", "Grid", "Grid", "Grid", "Grid", "Gyroid", "Gyroid", "Gyroid", "Gyroid", "Gyroid", "Gyroid", "Gyroid", "Gyroid", "Gyroid", "Gyroid", "Gyroid", "Gyroid"],
    "Filament Amount": [0.72, 3.73, 10.05, 20.32, 37.17, 60.73, 0.73, 3.77, 9.88, 20.62, 37.51, 59.74, 0.72, 3.55, 9.68, 20.11, 36.17, 58.71, 0.72, 3.6, 9.69, 20.14, 35.97, 58.2],
    "Printing Time": [370, 798, 1302, 1914, 2850, 4260, 272, 586, 968, 1499, 2442, 3445, 1226, 1654, 1436, 2703, 4680, 7500, 270, 589, 1086, 2075, 3576, 5760]
}

df = pd.DataFrame(data)

# One-Hot Encoding
df_encoded = pd.get_dummies(df, columns=["Infill Pattern"])
#print(df_encoded)

# Normalization
scaler = StandardScaler()
features_to_normalize = ["Nozzle Diameter", "Nozzle Diameter^2", "Output Size", "Output Size^3","Infill Pattern_Grid", "Infill Pattern_Gyroid"]
df_normalized = df_encoded.copy()
df_normalized[features_to_normalize] = scaler.fit_transform(df_encoded[features_to_normalize])

# Regression Model
regression_features = ["Nozzle Diameter", "Nozzle Diameter^2", "Output Size", "Output Size^3","Infill Pattern_Grid", "Infill Pattern_Gyroid"]

# Filament Amount Regression
model_filament = Lasso(alpha=0.01, max_iter=10000)
model_filament.fit(df_normalized[regression_features], df_encoded["Filament Amount"])
filament_pred = model_filament.predict(df_normalized[regression_features])
filament_r2 = r2_score(df_encoded["Filament Amount"], filament_pred)

# Printing Time Regression
model_time = Lasso(alpha=0.01, max_iter=10000)
model_time.fit(df_normalized[regression_features], df_encoded["Printing Time"])
time_pred = model_time.predict(df_normalized[regression_features])
time_r2 = r2_score(df_encoded["Printing Time"], time_pred)

# Print equations and R^2 values
filament_eq = f"Filament Amount = {model_filament.intercept_:.2f} + " + " + ".join([f"{coef:.2f}*{feature}" for coef, feature in zip(model_filament.coef_, regression_features)])
time_eq = f"Printing Time = {model_time.intercept_:.2f} + " + " + ".join([f"{coef:.2f}*{feature}" for coef, feature in zip(model_time.coef_, regression_features)])

print("Filament Amount Model:")
print(f"Equation: {filament_eq}")
print(f"R^2: {filament_r2:.2f}")

print("\nPrinting Time Model:")
print(f"Equation: {time_eq}")
print(f"R^2: {time_r2:.2f}")

# Color Settings
pattern_colors = {True: "blue", False: "green"}
tone_factors = {0.2: 0.6, 0.28: 1.0}

# Plotting
plt.figure(figsize=(14, 7))

names = {True:"Grid", False:"Gyroid"}

# Filament Amount
plt.subplot(1, 2, 1)
for (pattern, nozzle), group in df_encoded.groupby(["Infill Pattern_Grid", "Nozzle Diameter"]):
    color = to_rgba(pattern_colors.get(pattern, "gray"), tone_factors[nozzle])
    plt.scatter(group["Output Size"], group["Filament Amount"], label=f"{names[pattern]}-{nozzle:.2f}mm", color=color)
    # Prediction Line
    normalized_group = df_normalized.loc[group.index]
    y_pred = model_filament.predict(normalized_group[regression_features])
    plt.plot(group["Output Size"], y_pred, color=color, linestyle="--")

plt.xlabel("Output Size (mm)")
plt.ylabel("Filament Amount (g)")
plt.title("Prediction of Filament Amount")
plt.legend()

# Printing Time
plt.subplot(1, 2, 2)
for (pattern, nozzle), group in df_encoded.groupby(["Infill Pattern_Grid", "Nozzle Diameter"]):
    color = to_rgba(pattern_colors.get(pattern, "gray"), tone_factors[nozzle])
    plt.scatter(group["Output Size"], group["Printing Time"], label=f"{names[pattern]}-{nozzle:.2f}mm", color=color)
    # Prediction Line
    normalized_group = df_normalized.loc[group.index]
    y_pred = model_time.predict(normalized_group[regression_features])
    plt.plot(group["Output Size"], y_pred, color=color, linestyle="--")

plt.xlabel("Output Size (mm)")
plt.ylabel("Printing Time (sec)")
plt.title("Prediction of Printing Time")
plt.legend()

plt.tight_layout()
plt.show()
```
</details>


```
Filament Amount Model:
Equation: Filament Amount = 21.76 + -0.04*Nozzle Diameter + -0.00*Nozzle Diameter^2 + 2.78*Output Size + 17.97*Output Size^3 + 0.31*Infill Pattern_Grid + -0.00*Infill Pattern_Gyroid
R^2: 1.00

Printing Time Model:
Equation: Printing Time = 2219.21 + -338.53*Nozzle Diameter + -0.00*Nozzle Diameter^2 + 137.88*Output Size + 1508.63*Output Size^3 + -493.70*Infill Pattern_Grid + 0.00*Infill Pattern_Gyroid
R^2: 0.92
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139643/81b45009-fbd6-a95b-df28-a487c54da6c1.png)

　ざっくりと決定係数を見る限りよく近似できてそうです。グラフを見ても、フィラメント量はほぼ完全に近似できてそうです。
 　今回の数式は、先んじて正規化を行っているため、どの変数をどれくらい変化させるとどうか。みたいなものはちょっと議論しにくいので、係数の比較だけ行います。
　フィラメント量に関して議論すると、切片が21.76があります。係数で一番大きいのは出力サイズの三乗なので、やはり体積に比例する部分が大きい。というのは直感と正しいです。グリッドにするとちょっとフィラメント量が増える？という数値感ではありますが、切片の21.76とかに比べると誤差なので、あまり気にしなくていいかな。という感触です。
　出力時間に関しては、切片が2219.21あります。ノズル径に関して-338.53あるので、ノズルの直径を大きくするのは出力時間削減に役に立つのは直感と合います。またフィラメント量と同じように出力サイズの三乗に比例する係数が1508.63と非常に大きいので、ここが支配的な値であることはやはり明確です。最後に、充填パターンですが、これはグリッドの方が-493.70低くなるという結果で、**逆に言えば、ジャイロイドは出力に時間がかかる。ということです。**
  おおむね数式の感じとグラフの感じからすると、

- **フィラメント量は体積の要素が支配的**
- **出力時間は体積の時間が支配的**
- **ジャイロイドとグリッドだと、グリッドの方が出力時間が短い**
- **ノズルの直径を大きくすると出力時間削減になる**

# 感想

　まぁあたりまえ体操みたいな。フィラメント量や出力時間は体積に依存しそうですね。依存しました。はいそうですね。という。ただ、やっぱりわからない部分もあって、出力時間に対しては、ノズルの直径の二乗が効いてくると思ったんですよね。でも解析結果ではそうならなかったので、なんでかな。という感触はあります。
 　思ったより衝撃的なのはジャイロイドが時間がかかる。というところでした。これは似た話をすると、Bambu Studioには充填密度というパラメーターがあります。これは先に話したように、どれだけモデルの中を埋めるか。という変数になります。このパラメーターを10%から20%にしよう。と考えたとき、単純に使用するフィラメント量が2倍になるので、時間が大きく変わるだろう。これはある意味論理的な考え方だと思います。しかし、今回の実験ではグリッドとジャイロイドで充填密度は同じです。ということはそれほどフィラメント量は変わりません。実際のデータからも数式からもほぼほぼ誤差です。よくよく考えれば同じフィラメント量を使っていて、グリッドで出力したときとジャイロイドで出力したときと3Dプリンターの動作速度が同じなら差が出ないはずです。なのに、出力時間は差が出る。というのは、なかなか面白いです。この傾向はデータからも、グラフからも如実にわかりました。
　こう考えると、雑に速く出したい。という要望であれば、グリッドで0.28mmで出すのが賢明なんだろうなぁ。と思いました。