Python で Cura をバッチ実行するためには
ソフトウェアエンジニアの花岡です。今回は [Cura][] という OSS のスライサを Python でバッチ実行することについて書いてみたいと思います。
OS は macOS で、Cura のバージョンは 3.2.1 です。
Cura とは
[Cura][] は 3D プリント用のスライサです。3D モデルを入力としてレイヤーごとにスライスし造形のための [G コード][]を出力します。さらに 3D プリンタと接続して G コードを送信し実際にプリントさせることも可能です。
Cura はプラグインアーキテクチャを採用していてスライサの機能は [CuraEngine][] で実現されています。Cura と CuraEngine はソケットで接続されます。
CUI の CuraEngine は Python から subprocess
モジュールを使って実行することが可能です。
目的
Cura はスライス後にプリント推定時間を表示します。複数の 3D モデルに対して Cura のプリント推定時間を取得し、弊社のアルゴリズムの結果と比較したいというのが目的です。複数のプリント設定でも比較したいため Cura の GUI で手で実行するのは厳しく、CuraEngine をバッチ実行する必要がありました。
CuraEngine の実行
CuraEngine のヘルプは以下の通りです。
CuraEngine slice [-v] [-p] [-j <settings.json>] [-s <settingkey>=<value>] [-g] [-e<extruder_nr>] [-o <output.gcode>] [-l <model.stl>] [--next]
-v
Increase the verbose level (show log messages).
-p
Log progress information.
-j
Load settings.def.json file to register all settings and their defaults.
-s <setting>=<value>
Set a setting to a value for the last supplied object,
extruder train, or general settings.
-l <model_file>
Load an STL model.
-g
Switch setting focus to the current mesh group only.
Used for one-at-a-time printing.
-e<extruder_nr>
Switch setting focus to the extruder train with the given number.
--next
Generate gcode for the previously supplied mesh group and append that to
the gcode of further models for one-at-a-time printing.
-o <output_file>
Specify a file to which to write the generated gcode.
The settings are appended to the last supplied object:
CuraEngine slice [general settings]
-g [current group settings]
-e0 [extruder train 0 settings]
-l obj_inheriting_from_last_extruder_train.stl [object settings]
--next [next group settings]
... etc.
-v
オプションを渡すことで標準エラーに Print time:
から始まるプリント推定時間が出力されます。
Cura からは GUI で様々な設定をすることができるのですが、対応する CuraEngine の設定がドキュメント化されていません。
GitHub の issue を漁ると、Cura のスライス時の設定は cura.log に出力されていることがわかりました。そこで
- Cura の設定ごとに cura.log から該当のログを抽出
- ログから CuraEngine のコマンドラインオプションを再構築
- CuraEngine を実行
することにしました。また cura.log に
DEBUG - ... [90]: [Backend] Calling engine with: ['/Applications/Ultimaker Cura.app/Contents/MacOS/CuraEngine', 'connect', '127.0.0.1:49674', '-j', '/Applications/Ultimaker Cura.app/Contents/MacOS/resources/definitions/fdmprinter.def.json', '']
というログがあったため、同じように -j
オプションを渡しました。
ログからコマンドラインオプションの抽出
cura.log から抽出した 1 つのログは以下のようなかんじです。1 行が長すぎるので途中省略しています。
DEBUG - ... [90]: [Backend] [WARNING] -s support_interface_density="100" ... -s machine_end_gcode="G91 ;Relative movement
DEBUG - ... [90]: [Backend] G0 F15000 X8.0 Z0.5 E-4.5 ;Wiping+material retraction
...
machine_end_gcode
という設定に対しては
G91 ;Relative movement
G0 F15000 X8.0 Z0.5 E-4.5 ;Wiping+material retraction
...
という改行を含んだものが設定値なのでログでは複数行に渡っています。
これをパースして CuraEngine のコマンドラインオプションのためのリストを再構築するためのコードが以下です。
def get_args_from_log(log_path):
log = _get_purified_log(log_path)
for m in re.finditer(r'-s ([^=]+)="([^"]+)"|-e[01]|-g', log, re.MULTILINE):
arg = m.group(0)
if arg.startswith('-s'):
yield '-s'
yield '='.join(m.group(1, 2))
else:
yield arg
[line.split('[Backend]', 1)[1] for line in f]
lines[0] = lines[0].split('[WARNING]', 1)[1]
return '\n'.join(line.strip() for line in lines)
ログでは常に設定値が二重引用符で囲まれていますが CuraEngine のコマンドラインオプションとしては二重引用符を取り除く必要があります。
CuraEngine を実行してプリント推定時間を取得
def estimate(stl, log_path):
args = get_args_from_log(log_path)
command = [
CURA_ENGINE, 'slice',
'-v',
'-j', FDMPRINTER_DEFINITION,
*args,
'-l', str(stl.path),
]
try:
process = subprocess.run(
command,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
timeout=10 * 60,
)
except subprocess.TimeoutExpired:
return 0
stderr = process.stderr.decode()
for line in stderr.split('\n'):
if line.startswith('Print time:'):
time_in_seconds = int(line.split(':', 1)[1])
return time_in_seconds
return 0
今回標準出力は不要なので /dev/null
に捨てています。タイムアウト時や Print time:
が標準エラーに見つからなかった場合はエラーとして 0 を返しています。
検証
3D モデルは https://www.thingiverse.com/thing:11622/#files の 4 MB の stl ファイルを使ってみます。Cura に D&D したところ 08h 05min というプリント推定時間が表示されました。このときの cura.log を抽出し estimate()
を実行すると 29114 という結果が得られました。8 時間 5 分は 29100 秒ですので 29114 という結果は正しいものと考えられます。
次に https://www.thingiverse.com/thing:2064269/#files の Vise_Sliding_Jaw.stl で試してみます。こちらは GUI では 06h 43min と表示されるのですが、estimate()
では 1046 という結果になりました。このようにいくつかの 3D モデルでは Cura とバッチ実行とで異なる結果になり、現在のところ解決できていません。弊社ではこの問題を解決してくれるエンジニアを募集しています。
最後に
これで、様々な設定の cura.log を保存し、様々な設定と様々な 3D モデルで検証することができるようになりました。
弊社では Python and/or GCP の得意なエンジニアを募集しています。興味のある方は是非こちらからご応募ください。
その他の記事
Other Articles
関連職種
Recruit