🕳️レッツ穴あけ🕳️

🕳️レッツ穴あけ🕳️

kandennti
Mentor Mentor
2,572件の閲覧回数
18件の返信
メッセージ1/19

🕳️レッツ穴あけ🕳️

kandennti
Mentor
Mentor

昭和っぽいタイトルにも関わらずご覧になった皆様こんにちは。
APIなお話です。

 

2023年の4月のアップデートでCAMに関するAPIが大幅に強化されました。
当時はプレビューとしての導入で、”作製しても公開しないでください”と言う扱いでした。
しかし8月のアップデートで”最初に導入した分は変更しないので公開してもOKです”
(その後も強化され続けており、一部はまだプレビューです)と言うことで、
正式な機能となりました。
ご興味ある方のためにコンテンツとして書き残しておきたいと思います。


本コンテンツは、Fusion 360 Ver2.0.16976の環境で作成しています。
内容的には、今後大幅には変わらないと思っています。
又、当方がC++がわからない為、pythonスクリプトでの説明となります。
もう一つ付け加えておくと、当方win環境で行っていますが、恐らくmacでも動作すると
思います、多分。(←確認出来ないので)


スクリプトの作成方法は・・・ちょっと検索して頂けると見つかると思います。
念のため、公式のHelpはこちらです。
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-9701BBA7-EC0E-4016-A9C8-964AA4838954 
日本語訳してくださった方がいらっしゃいます。
https://cad.vdlz.xyz/3dCad/Fusion360/Documents/API/CreatingAScriptOrAdd-In.html

 

とりあえずこの様な単純なデータを用意しました。添付しておきます。
pic1.png
φ10とφ20の貫通穴があいているだけです。

 

続いて、製造に切り替えてみて下さい。既にセットアップを作成しφ10の穴あけについては作製しています。
(切削条件等は今回のテーマでは無い為、デフォルトにて作成しています)
pic2.PNG

この状態で利用出来る、非常に基本的なスクリプトをご用意しました。

 

 

 

# Fusion360API Python script

import traceback
import adsk.core as core
import adsk.cam as cam

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # CAM環境チェック
        if not can_execute():
            ui.messageBox("製造に切り替えてください")
            return

        # CAM環境取得
        camObj: cam.CAM = app.activeProduct

        # セットアップの取得
        setup: cam.Setup = get_setup(camObj)
        if not setup:
            return

        # 情報を表示
        name: str = setup.name
        count: int = setup.operations.count
        msg: str = f"セットアップ[{name}]のオペレーション数は{count}個です。"

        ui.messageBox(msg)

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


def get_setup(camObj: cam.CAM) -> cam.Setup:
    '''
    最初のセットアップを取得
    '''
    setups: cam.Setups = camObj.setups
    if setups.count < 1:
        return None
    
    return setups[0]


def can_execute() -> bool:
    '''
    CAMワークスペースか? IDでチェック
    '''
    app: core.Application = core.Application.get()
    ui: core.UserInterface = app.userInterface

    ws: core.Workspace = ui.activeWorkspace

    return ws.id == "CAMEnvironment"

 

 

 


正直に申し上げると、これらの処理は4月以前でも動かせた内容です・・・。

 

run関数内のコメント部分を追いかけると、何を行っているか?大体感じて頂けるかと思います。

 

"CAM環境チェック"部分ではアクティブなドキュメント(作業中のドキュメント)が
製造になっているかどうかをチェックして、製造になっていない場合はスクリプトを終了しています。

 

 

 

def can_execute() -> bool:
    '''
    CAMワークスペースか? IDでチェック
    '''
    app: core.Application = core.Application.get()
    ui: core.UserInterface = app.userInterface

    ws: core.Workspace = ui.activeWorkspace

    return ws.id == "CAMEnvironment"

 

 

 

”ワークスペース”とは"デザイン"とか"レンダリング"等それぞれの作業領域です。
それぞれにはIDが割り当てられており確認することが出来ます。


"CAM環境取得"部分は、製造を取り仕切る親分です。
画面上ではブラウザツリーの一番上のこちらです。(多分)
pic3.PNG
"デザイン"時の一番上は、"ルートコンポーネント"と呼ばれる部分ですが、製造では異なります。


"セットアップの取得"では、先頭のセットアップを取得しています。
もうセットアップの説明は不要ですよね。赤矢印部分です。
pic4.PNG
get_setup関数内で登場する"setups"は緑矢印部分です。


"情報を表示"部分では、先程取得したセットアップから、セットアップ自身の名前と
セットアップ内にあるオペレーションの数を表示させます。


実行させてみると、こんな感じです。
pic5.PNG

あぁ以前は赤矢印部分は"Fusion360”だったのですが、8月のUpdateで

変更されたんですよね・・・アナウンス無かったのですが。

 

無事にダイアログが表示されれば、スクリプトの作成も成功です!

キナ臭い?試しに穴あけのオペレーション削除し実行してみて下さい。
オペレーション数が0になるはずです。
興味を持ってここまで読んだ方であれば、φ20の穴あけを作れるはずです。
作成して実行してみて下さい。2個になるはずです。

 

調子に乗って、セットアップを削除して実行しても・・・何も起こりません。
その為の処理をしなかった為です。
逆に、エラーが発生しない事にお気づきでしょうか?

エラーが発生し、スクリプトが途中で中断しない様にするための処理は行った為、
エラーは発生しない様な処理はしています。
結構大事なのですが、結構厄介な事も事実です。

実はスクリプト(やアドイン)で、行いたい処理を作ること自体は案外出来ます。
え?何故かって? それは行いたいことがゴールになっている為、モチベーションが
高いからです。(何とか作ってやろうと言う気持ちがあるはずです)
厄介なのは、開発者にとって"やって欲しくない操作を防ぐ"事です。
その操作に気が付けば対処出来ると思うので、気付けるかどうかが勝負どころです。
(開発者の"おかしいところがあったら教えて~"は、そういう意味です)


と、今回はほぼ実用的な内容が無い状態となりましたが、CAMの自動化を行う為には
避けられない部分ですので、ご了承ください。

解決済み
2,573件の閲覧回数
18件の返信
返信 (18)
メッセージ2/19

kandennti
Mentor
Mentor

本来、初回に記載すべきとは思いつつも、後回しにした部分です。
(前置きばかりで、一切実行出来るものを記載しないのも変だろう と言う判断です)

 

今後、オンラインヘルプのURLを付けている部分がありますが、過去の経験からして
アップデートのタイミングでURLが変更されている為、リンク切れを起こす可能性が高いです。
その為、こちらにアクセス
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-36B1FFB5-5291-4532-8F11-90E912769B34 


続いて
"Programming Interface"-"Fusion 360 API Reference Manual"-"Objects"
まで開いた状態で、ブラウザの検索で目的のオブジェクト名を検索してください。

help.PNG

 

 

但し、この方法でも見つける事が出来なくなる可能性が有ります。
https://forums.autodesk.com/t5/fusion-360-api-and-scripts/api-help-table-of-contents-improvements/m-...

勝手な解釈ながら
"オブジェクトが増えてヘルプが見にくくない?もう少しジャンル分けしようかと思っているけど"
的なお話が5月に出ているので、近い将来に表示が変わる可能性が高いです。


本来であれば自分のブログにでも記載すれば良さそうな内容を、勝手ながらこちらに記載した
理由ですが
・ファイルの添付が可能。
・アクセス数の多さから、興味のある方が見る可能性の高さ。
実は昨年3件、今年も1件、メールやDM等でAPIでの相談や協力依頼のお話が来たのですが、
全て海外の方でCAMの自動化に関するものでした。
又、CAMのAPIが強化された4月以降のフォーラムを見ていても、CAMの自動化に取り組んでいる方が
複数人いる事を感じており、日本の方々は興味があるものかどうかに僕が興味を感じているのが本音です。

実際の所、どうでしょうか?

メッセージ3/19

Bunga777
Mentor
Mentor

凄く興味があるけど、具体的に何ができるようになるのかを知りたかったりする。

0 件のいいね
メッセージ4/19

kandennti
Mentor
Mentor

@Bunga777 さん

 

以前とは異なり、4月以降のCAMのAPIでは

・セットアップの作成

・オペレーションの作成

・ツールの作成、ツールライブラリからの工具選択

が出来るようになりました。逆に言うとそれ以前は出来ませんでした。

 

まだこの先の事を全く書いていないのですが、最終的には

デザインでモデルを作成後、処理スタートして上記の処理を行えれば

"CAM自動化"と呼べるかな?とは思っています。

 

ポスト処理は出来るのですが、個人的には

"ツールパス確認後にポスト処理はすべき"

と思っているので、そこまではやらないつもりです。

(公式のサンプルにその辺りの処理はあるので)

 

”穴あけ”をテーマにしたのは、比較的簡単なのが理由ですが、

Fusion360の穴あけは手動でも操作が簡単なのであまり意味が無いかも

しれませんね。(今まで操作したCAMの中では一番操作が簡単かも)

メッセージ5/19

Bunga777
Mentor
Mentor

@kandennti さん

>(今まで操作したCAMの中では一番操作が簡単かも)

 

これは、改めて今実感させられている事であったりします。

5軸用にと薦められて高めなCADCAMを導入したところ、Fusion360に慣れてしまった体ではあまりの操作性の違いと身動きの取れなさに四苦八苦しています。

 

CADからCAMへの流れのスムーズさ、設計変更へCAMが柔軟に追従するシームレス感に慣れてしまっているので、これはやはり5軸でもある程度はFusion360で行った方が業務が圧倒的に早いなぁと方向転換を思案中だったりします。

 

なので、APIでさらに自動化とか、おいおいおいとなっています。

メッセージ6/19

kandennti
Mentor
Mentor

情報を取得するだけでは面白くない為、実際にオペレーション(ツールパス)を

作っていきましょう。
引き続き最初に添付したドキュメントを利用して行きます。

 

オペレーション(ツールパス)をAPIで1から作る事も出来るのですが、個人的には
テンプレートを読み込む方法をお勧めします。
・・・手動操作でのテンプレート機能を御存知でしょうか?
一度使ったオペレーションを再利用する機能だと思ってもらって良いと思います。

 

"ドリル1"のオペレーションのコンテキストメニュー(右クリック)を表示させると

"テンプレートとして保存"があるので、そちらをクリックして下さい。
pic1.png
複数のオペレーションを選択しても1つのテンプレートとして作る事が可能ですが、

今回は一つのオペレーションで行います。

 

ダイアログが表示されますが、取りあえず今回はこのまま保存します。"位置"は

取りあえずローカルです。
pic3.png

 

続いて"ユーティリティ"タブ-"管理"-"テンプレートライブラリ"をクリックします。
pic2.png

ダイアログが表示されるので、先程テンプレートを作成した際に指定した"位置"の

"ローカル"にするとテンプレートがあります。


先程のテンプレートを選択状態にし、コンテキストメニューの"エクスポート"又は

メニューのエクスポートで、分かりやすい位置にエクスポートして下さい。
(拡張子は.f3dhsm-templateになります)
pic4.png

 

試しにエクスポートしたテンプレートをエディタで開いてみて下さい。
(自分の場合は、drill.f3dhsm-templateと言うファイル名にしました)

pic5.png
細かな中身は別として、フォーマットはxmlです。
しかし圧倒的なボリューム感。手を抜いたのでツールにホルダーを付けてないのですが、
ホルダーがある場合はホルダー情報も記載される為、さらにボリュームが大きくなります。

 

次にこのテンプレートファイルを現在のスクリプトのフォルダにコピー又は移動してください。
前置きが長くなりましたが、これで準備が整いました。
作成したテンプレートを読み込む様にスクリプトを修正しました。

 

# Fusion360API Python script

import traceback
import adsk.core as core
import adsk.cam as cam
import pathlib

THIS_DIR = pathlib.Path(__file__).resolve().parent

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # CAM環境チェック
        if not can_execute():
            ui.messageBox("製造に切り替えてください")
            return

        # CAM環境取得
        camObj: cam.CAM = app.activeProduct

        # セットアップの取得
        setup: cam.Setup = get_setup(camObj)
        if not setup:
            return

        # テンプレートを読み込み(セットアップには入れていない)
        templatePath = str(THIS_DIR / "drill.f3dhsm-template")
        template: cam.CAMTemplate = cam.CAMTemplate.createFromFile(templatePath)

        # テンプレートをセットアップに入れる
        setup.createFromCAMTemplate(template)

        # 再計算不要なオペレーションを除く全てのオペレーションを計算する
        camObj.generateAllToolpaths(True)

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

 

他の関数は前回と同じままで変更していません。又、"テンプレートを読み込み(セットアップには入れていない)"部分の

ファイルパスは、先程エクスポートしたテンプレートファイル名にして下さい。


実はテンプレートを読み込む方法は複数あります。
こちらの公式のサンプルでもテンプレートを読み込んでいますが、古い方法の為コードがちょっと異なります。

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-AEF0708D-B657-4E9F-9032-4535E0D1C417 

 

取りあえず実行してみましょう。

d5215446-efd5-4a4c-b802-74bf3dde40a1.png

無事テンプレートが読み込めました! え?オペレーションがエラーじゃないか!って?
手動でテンプレートを読み込んでも同じです!!と言いたいところですが、
本来の目的の"自動化"には程遠い結果ですね。

 

御察しの通り、オペレーションエラーの理由は穴の面を指定していないからです。
pic7.png

 

テンプレートのエクスポート方法の説明となってしまいましたが、

次回は、この面の設定方法をご紹介します。

メッセージ7/19

kandennti
Mentor
Mentor

引き続きお話は、面の設定方法です。ある意味CAMのAPIでもメインディッシュな部分です。

 

CAMに限らず、スクリプトのユーザーに対話的な選択作業を促す、
"UserInterface.selectEntity"メソッドがあります。
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-c05f97b8-9c8f-404b-8b21-f170d194ef8e 

 

これをテストするため次のスクリプトを作りました。
選択可能な要素を指定するフィルタは、円筒面としています。

 

# Fusion360API Python script

import traceback
import adsk.core as core

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # 円筒面の選択
        msg: str = "円筒面を選択してください"
        selFilter: str = "CylindricalFaces"
        sel: core.Selection = ui.selectEntity(msg, selFilter)
        if not sel:
            return
        
        ui.messageBox("面を選択しました")

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

 


選択フィルタと指定出来るものは、公式ヘルプのこちらに一覧があります。
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-03033DE6-AD8E-46B3-B4E6-DADA8D389E4E 

 

実際に実行してもらうと、円筒面以外は選択出来ない事が確認出来ると思います。
円筒面を選択するとこの様なダイアログが表示され終了します。
pic1.png
これは問題無いですね。実はSelectEntityメソッドは以前から指摘されているにも関わらず、
修正されていないバグ(?)があります。

もしユーザーがスクリプトの使用を中止したい場合は、どうすれば良いでしょうか?
他のコマンド同様、ESCキーを押すことでコマンドを中止することが出来ます。
先程のスクリプトを再度実行し、ESCキーを押してください。
pic2.png
残念ながらエラーとなります。これ直してくれないんですよね~長年。

 

仕方が無いので例外処理を組み込んで、エラーとなったら
"ユーザーが作業をキャンセルした"
と判断するようにしてみましょう。

 

# Fusion360API Python script

import traceback
import adsk.core as core

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # 円筒面の選択
        msg: str = "円筒面を選択してください"
        selFilter: str = "CylindricalFaces"
        sel: core.Selection = select_ent(msg, selFilter)
        if not sel:
            ui.messageBox("キャンセルしました")
            return
        
        ui.messageBox("面を選択しました")

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


def select_ent(
    msg: str,
    filter: str
) -> core.Selection:

    try:
        app: core.Application = core.Application.get()
        ui: core.UserInterface = app.userInterface
        sel: core.Selection = ui.selectEntity(msg, filter)
        return sel
    except:
        return None

 


実際に実行しESCキーを押してみてください。今度はエラーにもなりませんし
円筒面も選択出来ます。ユーザーに優しいですね!

 


と言うことで、ここまでは前置きです。
前回のスクリプトに、今の円筒面を選択する処理を付け加え、テンプレートで
読み込んだオペレーションに穴面として設定する処理を付け加えてみましょう。

 

# Fusion360API Python script

import traceback
import adsk.core as core
import adsk.cam as cam
import pathlib

THIS_DIR = pathlib.Path(__file__).resolve().parent

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # CAM環境チェック
        if not can_execute():
            ui.messageBox("製造に切り替えてください")
            return

        # CAM環境取得
        camObj: cam.CAM = app.activeProduct

        # セットアップの取得
        setup: cam.Setup = get_setup(camObj)
        if not setup:
            return

        # 穴面の選択
        msg: str = "穴の面を選択してください"
        selFilter: str = "CylindricalFaces"
        sel: core.Selection = select_ent(msg, selFilter)
        if not sel:
            return

        # テンプレートを読み込み(セットアップには入れていない)
        templatePath = str(THIS_DIR / "drill.f3dhsm-template")
        template: cam.CAMTemplate = cam.CAMTemplate.createFromFile(templatePath)

        # テンプレートをセットアップに入れる
        opes = list(setup.createFromCAMTemplate(template))
        opeDrill: cam.Operation = opes[0]

        # オペレーションの'穴仕上'に選択した面をセットする
        prm: cam.CAMParameter = opeDrill.parameters.itemByName('holeFaces')
        holeFaces: cam.CadObjectParameterValue = prm.value
        holeFaces.value = [sel.entity]

        # 再計算不要なオペレーションを除く全てのオペレーションを計算する
        camObj.generateAllToolpaths(True)

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


def select_ent(
    msg: str,
    filter: str
) -> core.Selection:

    try:
        app: core.Application = core.Application.get()
        ui: core.UserInterface = app.userInterface
        sel: core.Selection = ui.selectEntity(msg, filter)
        return sel
    except:
        return None


def get_setup(camObj: cam.CAM) -> cam.Setup:
    """
    最初のセットアップを取得
    """
    setups: cam.Setups = camObj.setups
    if setups.count < 1:
        return None
    
    return setups[0]


def can_execute() -> bool:
    """
    CAMワークスペースか? IDでチェック
    """
    app: core.Application = core.Application.get()
    ui: core.UserInterface = app.userInterface

    ws: core.Workspace = ui.activeWorkspace

    return ws.id == "CAMEnvironment"

 

"オペレーションの'穴仕上げ'に選択した面をセットする"までは前回のものに、
円筒面を選択する処理を付け加えただけです。

 

CAMのOperationはCADのオブジェクトと異なり、あまり多くのプロパティを持っていません。
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-C890D1C0-0E77-427A-AF99-1B96E418CD00 
イヤ、結構ありますね・・・。但し、今回必要な"形状"タブの"穴仕上げ"のプロパティが
ありません。
pic3.png

 

"穴仕上げ"のようなオペレーションの細かな各設定は、Operation.parametersプロパティの
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-4666BEBA-7F4B-48D6-A71D-5E98B239D0E9 
itemByNameメソッドでCAMParameterオブジェクトとして取得する必要があります。

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-67DE555D-AE32-4864-98FA-14BD05613E77 


今回のサンプルの場合は、この様な処理部分ですね。

 

        prm: cam.CAMParameter = opeDrill.parameters.itemByName("holeFaces")

 

では、itemByNameメソッドの引数(今回の"holeFaces")は、どうすれば分かるの?
と言う疑問です。

 

GUIの画面に戻り、オペレーションのコンテキストメニューの"編集"をクリックし
ダイアログを表示させます。
調べたい項目にマウスカーソルを持っていくと、通常は簡易的なヘルプ等が表示されますが、
Shiftキーを押しながらマウスカーソルを移動させると、違うものがポップアップで
表示されます。
pic4.png
その中に"パラメータ名"と言う項目があり、この名前がitemByNameメソッドの引数で
利用出来る名前となります。(GUIの"式"に使えるのも、こちらのパラメータ名です)

'穴仕上げ'ではダメでしたが、一つ上の'選択モード'ではマウスカーソルを乗せた際に
表示されるケバブメニュー(点が3個縦に並んだ部分)をクリックすると、
"パラメータ名をコピー"が有るので、そちらの方が簡単ですね。

・・・ケバブメニューって呼び名知りませんでした。
https://manabink.com/2021/05/03/%E3%82%A6%E3%82%A7%E3%83%96%E3%82%B5%E3%82%A4%E3%83%88%E3%82%84%E3%8... 
webの世界のネーミングは発想がすごいですね。確かにケバブかも。

 

続いてのこちらの2行で、選択された面を'穴仕上げ'に割り当てています。

 

        holeFaces: cam.CadObjectParameterValue = prm.value
        holeFaces.value = [sel.entity]

 


最後に代入する値はリストです。1個であってもリストを代入します。


では、スクリプトを実行して元々作っていた"ドリル1"と同じ面を選択してみましょう。
pic6.png
今回はオペレーションにもエラーが出ていません。

 

折角なので、元々作っていた"ドリル1"とテンプレートで作られたオペレーションを
選択し、コンテキストメニューの"比較して編集"をクリックします。
pic7.png

 

左下の表示させる項目を"不一致"にしてみて下さい。
pic8.png
何も表示されなくなりました。= 全ての設定が一致していると言う事ですね。
テンプレートを作成する元のオペレーションとの比較なので当然ですね。
("比較して編集"は本当に便利な機能ですね。)


今回は、itemByNameメソッドで得たCAMParameterのvalueプロパティはCadObjectParameterValue
オブジェクトでしたが、

 

        holeFaces: cam.CadObjectParameterValue = prm.value

 


何時もCadObjectParameterValueオブジェクトとは限りません。
オペレーションの細かな設定には、数値だったり文字だったりスケッチの線等様々です。


この辺りの説明は、公式ヘルプでもかなりのスペースを確保して説明されています。
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-A08218F6-3885-4677-9CAD-7234BCEE85CC 
正直な所、結構複雑ので一読されることをお勧めします。

 


と言う事で、テンプレートで作成したオペレーションの項目の設定方法でした。
ん? "ユーザーに面を選択させていたら、自動化じゃないじゃないか" って?
そうです。これは自動化じゃないですね。あくまで項目の設定方法です。

"それではどうすべきなのか?" と言うお話は次回以降で。

メッセージ8/19

kandennti
Mentor
Mentor

前回はオペレーションの'穴仕上げ'の項目の設定を行いました。
各項目は大体あのような感じです。・・・領域はちょっと違うのですが。

 

実は、こちらで

https://forums.autodesk.com/t5/fusion-360-ri-ben-yu/rettsu-xueake-/m-p/12183607#M46764 

"オペレーション(ツールパス)をAPIで1から作る事も出来るのですが、個人的には
テンプレートを読み込む方法をお勧めします。"
と記載しましたが、その理由をご説明出来るようになりました。

 

厳密には確認は行っていませんが、前回と同様なオペレーションのドリルを作ると
こんな感じになります。
(まだツールライブラリからのツールの取得方法をお伝えしていませんが・・・)

def create_drilling(
    setup: cam.Setup,
    tool: cam.Tool,
    face: fusion.BRepFace,
) -> cam.Operation:
    opeipt: cam.OperationInput = setup.operations.createInput("drill")
    opeipt.tool = tool

    prm: cam.CAMParameter = opeipt.parameters.itemByName('holeFaces')
    holeFaces: cam.CadObjectParameterValue = prm.value
    holeFaces.value = [face]

    return setup.operations.add(opeipt)

実際に出来上がったオペレーションとサンプルに入れていた元のオペレーション(ドリル1)を、

"比較して編集"-"不一致"で表示させると、貫通穴としての項目がヒットします。
もちろん元のオペレーションはその様に設定していたためです。

pic4.png

 

つまり、APIでオペレーションを新作すると各項目の値はデフォルトで作成されている
はずです。

 

個人的な意見ですが、APIでオペレーションを新作するデメリットは次の通りです。
・デフォルトと異なる値を、毎回parameters.itemByNameで見つけ出し
 修正する必要がある。
(例えばクーラントがエアー仕様の機械の場合、新作する度に修正する必要がある)
・仮に内部でデフォルトの値がUpdateで変更された場合、変更に気が付かず
 意図しない設定値となる可能性がある。

 

逆にテンプレートの呼び出しを利用するメリットは、上記の逆ですが、さらに
・作るオペレーションの設定値をGUI上で確認・設定が出来る。
・恐らくオペレーションの全ての項目がテンプレートに書き出されている為、
 Updateによりデフォルトが変更されていても、影響を受けない。

が考えられます。

 

テンプレートの呼び出しのデメリットはあまり感じませんが、強いて挙げるのであれば
テンプレートファイル内に工具が入っている事です。
例えばテンプレートではφ10ドリルで作っていますが、スクリプトを使用する際には
φ10を使わない形状で呼び出した場合は、利用していないφ10ドリルがドキュメントの
ツールライブラリに残ってしまいます。

 

ちょっとわかりにくいので、実行出来るサンプルを。
まずテンプレートファイルを開きます。比較的最初の方に"diameter="が見つかると思います。

pic1.png

これがツールの直径の設定値です。

 

これを異なる数値、例えばこの様に変更してみます。

diameter="20"

 

次に、前回のスクリプトをちょっと修正します。
(run関数以外はそのままです)

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # CAM環境チェック
        if not can_execute():
            ui.messageBox("製造に切り替えてください")
            return

        # CAM環境取得
        camObj: cam.CAM = app.activeProduct

        # セットアップの取得
        setup: cam.Setup = get_setup(camObj)
        if not setup:
            return

        # 穴面の選択
        msg: str = "穴の面を選択してください"
        selFilter: str = "CylindricalFaces"
        sel: core.Selection = select_ent(msg, selFilter)
        if not sel:
            return

        # テンプレートを読み込み(セットアップには入れていない)
        templatePath = str(THIS_DIR / "drill.f3dhsm-template")
        template: cam.CAMTemplate = cam.CAMTemplate.createFromFile(templatePath)

        # テンプレートをセットアップに入れる
        opes = list(setup.createFromCAMTemplate(template))
        opeDrill: cam.Operation = opes[0]

        # オペレーションの'穴仕上'に選択した面をセットする
        prm: cam.CAMParameter = opeDrill.parameters.itemByName('holeFaces')
        holeFaces: cam.CadObjectParameterValue = prm.value
        holeFaces.value = [sel.entity]

        # ドキュメントのツールライブラリの最初のツールを取得
        tool: cam.Tool = camObj.documentToolLibrary[0]

        # ツールを設定する
        opeDrill.tool = tool

        # 再計算不要なオペレーションを除く全てのオペレーションを計算する
        camObj.generateAllToolpaths(True)

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

テンプレートをセットアップに入れた後、オペレーションのツールの設定をドキュメントの
ツールライブラリの最初のツール(φ10ドリル)に設定しています。

 

実行すると新たなオペレーションが作成され、両方とも同じ工具を使用していますが、
ツールライブラリで確認して見ると先程のテンプレートで作成した[diameter="20"]
の工具が追加されています。

pic2.png

緑印部のお掃除ボタンをクリックしても未使用の工具だと確認出来るはずです。

 

実はこの無駄なツールのインポートを防ぐことが出来ます。
先程のテンプレートファイルの<tool>タグ から</tool>タグ間をゴッソリ削除します。

pic3.png

 

この状態で先程のスクリプトを再度実行してみると、エラー無く終了します。
テンプレートファイルはツール情報無しでも問題ない事がわかります。
※このツールの無いテンプレートファイルはGUIで使用しても問題ありません。

 

と言う訳で、個人的にはオペレーションはAPIで作り出すのではなく、テンプレートを
作製し読み込んだ方がお勧めですよ。細かな設定がGUIで設定した方が楽です。

メッセージ9/19

kandennti
Mentor
Mentor

前々回はUserInterface.selectEntityメソッドを利用して、スクリプトの
ユーザーに面を選択させましたが、この部分も自動化したいですね。

 

実はCAMの自動化で一番難しいのは、CAD要素から必要なものを探し出す事
や判断する事だと思っています。

 

まずはセットアップの'モデル'で設定されているボディにアクセスする方法です。

pic1.png

# Fusion360API Python script

import traceback
import adsk.core as core
import adsk.cam as cam
import pathlib

THIS_DIR = pathlib.Path(__file__).resolve().parent

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # CAM環境チェック
        if not can_execute():
            ui.messageBox("製造に切り替えてください")
            return

        # CAM環境取得
        camObj: cam.CAM = app.activeProduct

        # セットアップの取得
        setup: cam.Setup = get_setup(camObj)
        if not setup:
            return
        
        # セットアップの'モデル'で設定されているボディを取得
        prm = setup.parameters.itemByName("job_model")
        bodies = list(prm.value.value)
        if len(bodies) < 1:
            return

        # ボディの名前を取得
        names = [b.name for b in bodies]

        # 表示
        ui.messageBox("\n".join(names))

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


いよいよですが、このボディから必要な面を探し出さなければなりません。
その為には "穴の面" として条件を確認しましょう。

 

まずは円筒面である必要があります。

    # 円筒面のみ
    faces = [f for f in body.faces
        if f.geometry.objectType == core.Cylinder.classType()]

内包表記にif文使っているので、わかりにくいかもしれません。

普通に書くとこんな感じです。(内包表記大好き)

    faces = []
    f: fusion.BRepFace = None
    for f in body.faces:
        if f.geometry.objectType == core.Cylinder.classType():
            faces.append(f)

 

続いて3軸のみとしたいため、円筒面の軸がZ軸と並行なもののみに限定します。

    # 円筒面の軸が、Z軸と並行なもののみ
    faces = [f for f in faces
        if axisZ.isParallelTo(f.geometry.axis)]

 

後は、円筒面が表側か裏側かを判断する必要があります。
ん~ちょっと表現が悪いですね。要は穴かボスかを判断する必要があります。

pic2.png

 

    # ボスの円筒面は除去
    faces = [f for f in faces if is_hole(f)]

 

is_hole関数は、ややこしい処理をしています。

def is_hole(
    face: fusion.BRepFace,
) -> bool:
    '''
    穴かボスか
    '''
    point: core.Point3D = face.pointOnFace
    eva: core.SurfaceEvaluator = face.evaluator

    normal: core.Vector3D = None
    _, normal = eva.getNormalAtPoint(point)
    normal.normalize()
    normal.scaleBy(0.001)

    checkPnt: core.Point3D = point.copy()
    checkPnt.translateBy(normal)

    cog: core.Point3D = face.centroid

    return cog.distanceTo(checkPnt) < cog.distanceTo(point)

説明はちょっと省きますが(知りたい方がいればコメント下さい)穴かボスかを
判断出来ています。
・・・ひょっとしたらisParamReversedプロパティだけでも判断出来るのかなぁ。

 

本来であればもっと条件が必要となると思うのですが、とりあえず今回は
これぐらいにしておきます。この辺りが突き詰めきれてないですね。

 

ボディとZ軸を引数として、穴面を探し出す関数をこの様にしておきます。
必要となる条件が増えた際は、この関数を修正すればOKな状態の方が
後々も楽なはずです。

def find_hole_faces(
    body: fusion.BRepBody,
    axisZ: core.Vector3D,
) -> list[fusion.BRepFace]:
    '''
    穴面を探し出す
    '''
    # 円筒面のみ
    faces = [f for f in body.faces
        if f.geometry.objectType == core.Cylinder.classType()]

    # 円筒面の軸が、Z軸と並行なもののみ
    faces = [f for f in faces
        if axisZ.isParallelTo(f.geometry.axis)]

    # ボスの円筒面は除去
    faces = [f for f in faces if is_hole(f)]

    return faces

 

これで不完全ながら穴の面を指定する必要がなくなりましたので、前々回のものを
修正しこの様になりました。

# Fusion360API Python script

import traceback
import adsk.core as core
import adsk.fusion as fusion
import adsk.cam as cam
import pathlib

THIS_DIR = pathlib.Path(__file__).resolve().parent

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # CAM環境チェック
        if not can_execute():
            ui.messageBox("製造に切り替えてください")
            return

        # CAM環境取得
        camObj: cam.CAM = app.activeProduct

        # セットアップの取得
        setup: cam.Setup = get_setup(camObj)
        if not setup:
            return
        
        # セットアップの'モデル'で設定されているボディを取得
        prm = setup.parameters.itemByName("job_model")
        bodies = list(prm.value.value)
        if len(bodies) < 1:
            return

        # 穴面の取得
        holeFaces: list[fusion.BRepFace] = find_hole_faces(
            bodies[0],
            core.Vector3D.create(0,0,1)
        )

        # ドキュメントのツールライブラリの最初のツールを取得
        tool: cam.Tool = camObj.documentToolLibrary[0]

        # 全ての穴面に対してオペレーションを作る
        face: fusion.BRepFace = None
        for face in holeFaces:

            # テンプレートを読み込み(セットアップには入れていない)
            templatePath = str(THIS_DIR / "drill.f3dhsm-template")
            template: cam.CAMTemplate = cam.CAMTemplate.createFromFile(templatePath)

            # テンプレートをセットアップに入れる
            opes = list(setup.createFromCAMTemplate(template))
            opeDrill: cam.Operation = opes[0]

            # オペレーションの'穴仕上'に選択した面をセットする
            prm: cam.CAMParameter = opeDrill.parameters.itemByName('holeFaces')
            holeFaces: cam.CadObjectParameterValue = prm.value
            holeFaces.value = [face]

            # ツールを設定する
            opeDrill.tool = tool

        # 再計算不要なオペレーションを除く全てのオペレーションを計算する
        camObj.generateAllToolpaths(True)

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


def find_hole_faces(
    body: fusion.BRepBody,
    axisZ: core.Vector3D,
) -> list[fusion.BRepFace]:
    '''
    穴面を探し出す
    '''
    # 円筒面のみ
    faces = [f for f in body.faces
        if f.geometry.objectType == core.Cylinder.classType()]

    # 円筒面の軸が、Z軸と並行なもののみ
    faces = [f for f in faces
        if axisZ.isParallelTo(f.geometry.axis)]

    # ボスの円筒面は除去
    faces = [f for f in faces if is_hole(f)]

    return faces


def is_hole(
    face: fusion.BRepFace,
) -> bool:
    '''
    穴かボスか
    '''
    point: core.Point3D = face.pointOnFace
    eva: core.SurfaceEvaluator = face.evaluator

    normal: core.Vector3D = None
    _, normal = eva.getNormalAtPoint(point)
    normal.normalize()
    normal.scaleBy(0.001)

    checkPnt: core.Point3D = point.copy()
    checkPnt.translateBy(normal)

    cog: core.Point3D = face.centroid

    return cog.distanceTo(checkPnt) < cog.distanceTo(point)


def get_setup(camObj: cam.CAM) -> cam.Setup:
    """
    最初のセットアップを取得
    """
    setups: cam.Setups = camObj.setups
    if setups.count < 1:
        return None
    
    return setups[0]


def can_execute() -> bool:
    """
    CAMワークスペースか? IDでチェック
    """
    app: core.Application = core.Application.get()
    ui: core.UserInterface = app.userInterface

    ws: core.Workspace = ui.activeWorkspace

    return ws.id == "CAMEnvironment"

 

最初の添付データを使用した実行結果は、こちらです。

pic3.png

 

今回は穴面を指定せずに、穴数分のオペレーションが作成されています。ん?

pic4.png

φ20の穴にφ10のドリル使って終わっているじゃないか!

 

と中途半端な状態ですが、CADの面データから必要な面を探し出す一例として
ご紹介しました。
本当にこれは難しいと思います。自動化での価値を大きく左右するのは
この必要なものを探し出す・判断する部分だと思います。
CAMの自動化には、絶対にCADの形状を扱うためのAPIの知識が必要です。

 

今までツールについてはほぼ扱ってきてませんでしたので、
次回以降はこの辺りをやりましょう。

メッセージ10/19

kandennti
Mentor
Mentor

今回はツールを取り扱ってみます。
オペレーションを作成する際は、個人的にテンプレートの使用をお勧めしましたが、
ツールの新作に関しては、Tool.createFromJsonメソッド一択の様です。

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-072FCA71-EE7F-4BA3-88AD-AE38C38E05F0 

 

Jsonフォーマットとは、JavaScriptの・・・興味がある方は検索して下さい。
十分な程に市民権を得たファイルのフォーマットです。
これを一から作成するのは困難なので、別の方法で考えます。

 

個別のツールをエクスポートする方法は、恐らく無いと思うのですが、
ツールライブラリをエクスポートすることは出来ます。

 

お手元のツールの含まれているドキュメントをまず開いてください。
"製造"-"ユーティリティ"タブ-"管理"の"工具ライブラリ"をクリック。

pic1.png

 

表示されたダイアログの任意のドキュメント上で、コンテキストメニューの
"ライブラリをエクスポート"をクリック。

pic2.png

 

"ファイルを保存"のダイアログで分かりやすい場所で任意の名前で保存して
下さい。その際"ファイルの種類"は"(*.json)"にして下さい。

pic3.png

 

エディタで開いてもらえればわかりますが、テンプレートファイル程では
無いものの結構なボリュームです。Tool.createFromJsonメソッドのヘルプの
説明書きを見ると"すべてのツール パラメーターを含む必要があります。"と
記載されていますが、素の状態で間違えずに書くのはちょっと困難ですね・・・。

 

このエクスポートしたツールライブラリのJsonファイルを、今のスクリプトと
同じフォルダにコピペ・又は移動しておいてください。

 

もし、エクスポートするのが手間だったり上手くいかない場合は、後に使う予定の
Jsonフォーマットを添付しておきますので、そちらを利用してみてください。

(json添付出来ない・・・市民権を得ていないのか?)

 

 

続いて、このJsonファイルをAPIで読み込みドキュメントのツールライブラリに
ツールを作製するスクリプトを作りました。

# Fusion360API Python script

import traceback
import adsk.core as core
import adsk.cam as cam
import pathlib
import json

THIS_DIR = pathlib.Path(__file__).resolve().parent

# Jsonファイル名は、実際に読み込むファイル名に修正してください↓
TOOL_LIB_PATH = str(THIS_DIR / "Tools_Drill_Lib.json")

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # CAM環境の切り替え・取得
        camObj: cam.CAM = get_cam_product()

        # ドキュメントのツールライブラリ取得
        docToolLib: cam.DocumentToolLibrary = camObj.documentToolLibrary

        # Jsonファイルをツールライブラリに追加
        import_tool_lib_json(
            docToolLib,
            TOOL_LIB_PATH,
        )

        ui.messageBox("Done")

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


def import_tool_lib_json(
    toolLib: cam.ToolLibrary,
    path: str
) -> None:
    '''
    ドキュメントのツールライブラリに
    エクスポートされたJsonファイルを読み込み
    ツールを追加する
    '''
    # encodingの指定はこの様にするっぽい
    with open(path, encoding = "utf-8_sig") as f:
        toolsData = json.load(f)["data"]

    for data in toolsData:
        tool: cam.Tool = cam.Tool.createFromJson(
            json.dumps(data)
        )
        toolLib.add(tool)


def get_cam_product() -> cam.CAM:
    '''
    CAMの取得
    '''
    app: core.Application = core.Application.get()
    activete_cam_env()

    return app.activeProduct


def activete_cam_env() -> None:
    '''
    CAMアクティブ
    '''
    app: core.Application = core.Application.get()
    ui: core.UserInterface = app.userInterface

    camWS: core.Workspace = ui.workspaces.itemById('CAMEnvironment') 
    camWS.activate()

get_cam_product関数ですが、"製造"以外のワークスペースの状態でスクリプトを
実行しても、強制的に"製造"に切り替えます。
何度も試しているうちに、毎回警告されてイライラするので変更しました。

 

実行する前に、新たなドキュメントを作成して下さい。
予めツールライブラリを開いてドキュメントのツールライブラリが空っぽな事を
確認してみて下さい。(しなくても構いませんが)

pic4.png

 

先程のスクリプトを実行すると無事ツールが読み込まれました。

pic5.png

 

ドキュメントはツールライブラリのエクスポートは出来るのですが、
インポートは出来ないんですよね。

pic6.png

"異なるドキュメント間で全てのツールをコピーしたい!"と言う場面があれば
APIでも出来ますね。そんな場面はあまり無いかな・・・。
(APIでツールライブラリをエクスポートすることも可能です)

 

 

今回は、エクスポートしたJsonファイルからドキュメントのツールライブラリに
インポート(正しくはツールを新作)しただけで、まだ利用していませんね。
次回以降はその辺を試してみましょう。

メッセージ11/19

kandennti
Mentor
Mentor

え~長すぎて、飽きてません?

 

前回はエクスポートしたツールライブラリを読み込みました。

今回は穴径に対してツールを選択しつつオペレーションを作るようにします。

その為に今回は、穴数を増やしてもう少し手ごたえあるf3dファイルにします。

pic1.png

最初に添付したファイル名が"let's_hole.f3d"でしたが、よく考えたら"違うな・・・"
と思い、今回は"let's_drilling.f3d"としています。

 

又、コードが長すぎる上、テンプレートファイル、ツールライブラリ用ファイルも含め
一式を添付しました。ご興味ある方はどうぞ。

 

 

さて、修正したコードのrun関数はこちらです。全体的な処理の流れは分かるのでは?
と思います。

 

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # 時間測定開始
        sTime = time.time()

        # CAM環境の切り替え・取得
        camObj: cam.CAM = get_cam_product()

        # セットアップの取得
        setup: cam.Setup = get_setup(camObj)
        if not setup:
            return

        # セットアップの'モデル'で設定されているボディを取得
        prm = setup.parameters.itemByName("job_model")
        bodies = list(prm.value.value)
        if len(bodies) < 1:
            return

        # 穴面の取得
        holeFaces: list[fusion.BRepFace] = find_hole_faces(
            bodies[0],
            core.Vector3D.create(0,0,1)
        )

        # シリンダの半径毎にグループ化
        facesGroups: dict[float: list[fusion.BRepFace]] = group_by_radius(holeFaces)

        # ドキュメントのツールライブラリ取得
        docToolLib: cam.DocumentToolLibrary = camObj.documentToolLibrary

        # Jsonファイルをツールライブラリに追加
        import_tool_lib_json(
            docToolLib,
            TOOL_LIB_PATH,
        )

        # 全ての穴面のグループに対してオペレーションを作る
        for radius in facesGroups.keys():
            # 直径の一致するツールの取得
            tool: cam.Tool = get_tool(radius * 2)

            if not tool:
                continue # 見つからないので無視

            # テンプレートを読み込み(セットアップには入れていない)
            templatePath = str(THIS_DIR / "drill.f3dhsm-template")
            template: cam.CAMTemplate = cam.CAMTemplate.createFromFile(templatePath)

            # テンプレートをセットアップに入れる
            opes = list(setup.createFromCAMTemplate(template))
            opeDrill: cam.Operation = opes[0]

            # オペレーションの'穴仕上'に選択した面をセットする
            prm: cam.CAMParameter = opeDrill.parameters.itemByName("holeFaces")
            holeFaces: cam.CadObjectParameterValue = prm.value
            holeFaces.value = [facesGroups[radius][0]]

            # 同じ直径を選択をONにする
            prm: cam.CAMParameter = opeDrill.parameters.itemByName("selectSameDiameter")
            prm.value.value = True

            # ツールを設定する
            opeDrill.tool = tool

        # 再計算不要なオペレーションを除く全てのオペレーションを計算する
        camObj.generateAllToolpaths(True)

        ui.messageBox(f"Done : {time.time() - sTime}s")

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

 

 

"シリンダの半径毎にグループ化"部分は、見つけ出した穴面毎にオペレーションを作るのは
無駄が多すぎです。その為"group_by_radius"関数で穴の半径毎にグループ化させています。

 

半径毎にループさせていますが"直径の一致するツールの取得"部分でツールライブラリ
からツールを取得しています。今回のメインとなる部分はここです。
get_tool関数はこの様にしています。

 

def get_tool(
    diameter: float,
) -> cam.Tool:
    
    # cam取得
    camObj: cam.CAM = get_cam_product()

    # ツールライブラリ取得
    toolLib: cam.DocumentToolLibrary = camObj.documentToolLibrary

    # ツールライブラリの検索用のクエリ作成
    query: cam.ToolQuery = toolLib.createQuery()

    # 検索条件を設定
    query.criteria.add(
        "tool_type", core.ValueInput.createByString(
            "drill"
        )
    )
    query.criteria.add(
        "tool_diameter.max", core.ValueInput.createByReal(
            diameter + 0.001
        )
    )
    query.criteria.add(
        "tool_diameter.min", core.ValueInput.createByReal(
            diameter - 0.001
        )
    )

    # 条件を満たしたツールの取得
    res: cam.ToolQueryResult = query.execute()

    # 見つかった最初のツールのみ返す
    if len(res) > 0:
        return res[0].tool
    else:
        return None

 

通常であれば、ツールライブラリ内のツールをループさせ必要なツールを見つけ出す必要が
有りそうなのですが、ToolQueryオブジェクトを用意してくれました。

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-FA839F5C-09FB-4E3F-A277-9ED88E5D15EB 

データベース等で必要となるデータを取得する方法と同じ考え方ですね。

 

主な流れは、インスタンスを作成し、criteriaプロパティに必要となる条件を設定し、
executeメソッドで結果を得ます。便利です。
データパネルからデータを見つけ出す為にも、Query採用してくれると処理時間が短く
なりそうなのですが・・・。

 

今回の検索条件は、
"tool_type"(工具タイプ)がドリルで"tool_diameter"(工具径)が指定された直径なのです。
".max"や".min"を付け加えると、上限下限を設定出来るのですが、"+0.001"や"-0.001"を
入れています。実数の場合はピッタリの数値は信用しないでください。
取って付けたり例で申し訳ないのですが、こんな条件の場合は

 

    query.criteria.add(
        "tool_feedPerRevolution.min", core.ValueInput.createByReal(
            0.02
        )
    )

 

微妙な端数がある為、このツールはヒットしません。

pic2.png

APIフォーラムでも問題として指摘されていましたが、実数はその様なものとして受け入れ
".max"や".min"で上手く処理するしか方法が無いですね。

 

"同じ直径を選択をONにする"は、テンプレートファイルを作成する際に入れていなかった
こちらにチェックを入れています。

pic3.png

 

実際にスクリプトを実行するとこんな感じです。

穴あけだと処理が早すぎですね・・・。自宅PCでは3秒弱で会社PCでは
1秒かかりませんでした。

シミュレーション後、一か所だけ穴あけされていませんが、ツールライブラリに

該当するドリルが無かったため加工されていません。

忘れたのではなく、意図的です!!

 

取りあえずここまでで終わりにしようかと考えていたのですが、セットアップの作成処理を
やっていませんでしたので、次回はセットアップの作成をしましょう。

(諸事情により直ぐは書けないかも)

メッセージ12/19

Bunga777
Mentor
Mentor

全然飽きてはいないですよぉ。

こういうのをどうやったら業務に活かせるだろうか?と考えながら読んでいます。

メッセージ13/19

kandennti
Mentor
Mentor
解決済み

今回は予告通りセットアップの作成です。作業手順としては全く逆ですね。

 

セットアップの作成はオペレーション(テンプレート)やツール(json)のような
方法は恐らく無く、SetupsオブジェクトのcreateInputメソッドとaddメソッドを
利用しての制作方法になります。

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-2E6403A0-736C-4C3C-9BB4-13FF6E1D6525 

デザインでの押し出し等のフィーチャと同じ手順ですね。

 

createInputメソッドで作成されたSetupInputのインスタンスのparameters
プロパティで、細かな設定を行って行きます。
こちらもオペレーションやツールの設定と同様です。

 

ちょっと忘れていたのですが、パラメータ名の調べ方はShiftキーを押しながら
マウスカーソルを乗せる等の方法をご紹介しましたが

https://forums.autodesk.com/t5/fusion-360-ri-ben-yu/rettsu-xueake-/m-p/12185697#M46771 

別の方法がありました。

 

pic1.png

サンプルアドインの中に"CAM_API_Utilities"が入っていますが、
これを利用すると、parametersプロパティの一覧をファイルに書き出してくれます。
"起動時に実行"にチェックが入れられないのは残念なんですが。

 


さて、セットアップ作成込みのサンプルですが、単に1個だけ作成するのは
説得力が無い為、このようなデータを用意しました。

pic2.png

6方向の面に穴があけられている状態です。


3軸で行うことを想定している為、穴の軸方向を元にセットアップを作成し
それぞれにオペレーションを作成します。
例によって肥大化したスクリプト(テンプレート等も含む)とサンプル
f3dファイルは添付しております。

 

とりあえずrun関数のみですが、全体的な流れです。

 

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        # 時間測定開始
        sTime = time.time()

        # CAM環境の切り替え・取得
        camObj: cam.CAM = get_cam_product()

        # デザイン側のルートコンポーネント取得
        # 製造に切り替えてからRootComponenの取得方法
        root: fusion.Component = camObj.designRootOccurrence.component

        # ターゲットのボディの取得
        if root.bRepBodies.count < 1: return
        targetBody: fusion.BRepBody = root.bRepBodies[0]

        # 穴面の取得
        holeFaces: list[fusion.BRepFace] = find_hole_faces(
            targetBody,
        )

        # 軸方向で穴面をグループ分け
        axisGroups = group_by_axis(holeFaces)

        # ドキュメントのツールライブラリ取得
        docToolLib: cam.DocumentToolLibrary = camObj.documentToolLibrary

        # Jsonファイルをツールライブラリに追加
        import_tool_lib_json(
            docToolLib,
            TOOL_LIB_PATH,
        )

        # 軸方向毎にセットアップを作成
        for axisArray in axisGroups.keys():

            # 同一軸方向の穴面
            faces = axisGroups[axisArray]

            # セットアップ作成
            setup: cam.Setup = create_setup(
                camObj,
                targetBody,
                faces[0],
            )

            # シリンダの半径毎にグループ化
            facesGroups: dict[float: list[fusion.BRepFace]] = group_by_radius(faces)

            # 全ての穴面のグループに対してオペレーションを作る
            for radius in facesGroups.keys():
                # 直径の一致するツールの取得
                tool: cam.Tool = get_tool(radius * 2)

                if not tool:
                    continue # 見つからないので無視

                # ドリルオペレーション作成
                create_drill_operation(
                    setup,
                    tool,
                    facesGroups[radius],
                )

        # 再計算不要なオペレーションを除く全てのオペレーションを計算する
        camObj.generateAllToolpaths(True)

        ui.messageBox(f"Done : {time.time() - sTime}s")

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

 

穴軸の方向でループを回しつつ、穴径毎にループを回しているので、インデントが

深くて悲惨ですね・・・。

 

今回はセットアップを作製するため、セットアップ内の"モデル"を設定します。

pic4.png

その為には、"デザイン"時のルートコンポーネントを取得します。
ここのブラウザツリーの先頭部分の事です。
pic3.png
ん~正確には、製造時のここを取得しているのかな?
pic5.png

そのルートコンポーネントの取得方法はこの様な感じです。

 

        # デザイン側のルートコンポーネント取得
        # 製造に切り替えてからRootComponenの取得方法
        root: fusion.Component = camObj.designRootOccurrence.component

 

4月のUpdate以前はこれが出来ませんでした。

実はこれが取得出来れば"製造"のまま、APIのモデリングが出来ちゃいます。

 

実際に実行した感じはこの様になります。

 

6方向の穴あけが2秒強で作製されました。正に秒殺ですね!
これであれば"CAMの自動化"と呼んでも良いレベルだと思いますが、
実用的にするには、この3~5倍ぐらいのコード量が必要になるのでは無いかな?
と感じています。

 

長々と書き綴りましたが、これで一旦終了とします。
良い穴あけライフを!!

 

メッセージ14/19

Bunga777
Mentor
Mentor

凄いなぁ……。

どうにか実用的な形にしたいな。

 

これ、例えばCAMのデモにあるこの手のモデル。

これは割り出し5軸を実現するCAMのモデルですが、この辺のモデルを半自動的に作れるワークフローがあると、凄まじく便利になると思われます。

Monosnap Autodesk Fusion 360 2023-08-30 15.32.40.png

今回は穴あけのみでしたが、例えば穴あけはライブラリ内にある工具のみで行い、穴径の足りない部分はボアで仕上げるとか。

 

加工順でいうと3D負荷制御で荒加工する。

足らない部分を負荷制御の取り残し加工で追い込む。

平面仕上げ、輪郭仕上げ、(外観→ポケットも含む)

 

みたいな処理を半自動的に行ってくれる様になると凄まじく便利になると思います。

世の中自動プログラミングみたいのがいくつか出ていますが、まだまだ実用的ではないので、さてこれらの処理を自動で行わせる事がはたしてできるの?って思ったりします。

メッセージ15/19

kandennti
Mentor
Mentor

@Bunga777 さん。

 

試してはいないのですが、オペレーションの"工具方向"もAPIで設定出来るはずなので、

割り出し5軸も当然出来るはずだと思っていますし、他のオペレーションに関しても

恐らく可能だと思っています。

 

難しいのはCADの面データから必要な面を見つけ出したり、正しいかどうかの判断です。

 

昨日作ったこちらのサンプルですが、2Dポケットの指定した領域が内側か外側かを

どうやって判断するか?の問題です。

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/is-a-curve-reverted-or-not/td-p/12200707 

 

何をどう考えたか?を英語で説明する自信が無かったため、

こちらに記載しました。

https://kantoku.hatenablog.com/entry/2023/08/30/161729 

 

CAMの自動化を行うためには、どうしてもCADのAPIの知識と

数学的な知識(僕が欠落している部分・・・)が結構必要だと思っています。

 

海外の方は、このようなFusion360のCAMの自動化に取り組まれている方が

数名いるように感じています。国内の方では見ないですね・・・。

(他のCAMの自動化で、全ての金型の製作部品の加工プログラムを2日ぐらいで

作製してしまう会社は知ってます)

 

これで飯が食えたらありがたいのですがw

メッセージ16/19

Bunga777
Mentor
Mentor

うまくやれば飯が食える気がしますよ。

Fusion360は他のCAMに比べて桁違いに安いですし、ポストもわかりやすいですし。

結局最近入れたDMU50は配布されているポストほぼそのままで割り出し5軸までは普通に使えるっぽいので、新しいCADCAMに大金を注ぎ込まなきゃ良かったかなぁ……って頭を抱えながら眠る今日このごろですよ。

 

私もプログラムやろうかなぁ……。

今からやって追いつくかなぁ。

メッセージ17/19

k-yamaguchiJ3GYK
Advocate
Advocate

@kandennti 様

 

お世話になっております。

 

「レッツ穴あけ」


このスレは大変勉強になりました(私的に永久保存スレです)

 

テンプレートの勧めもおっしゃる通りだと思います

 

私はよくテンプレートを使用して作業をしているのですが
「行程条件設定は同じだが刃物だけはそのつど選択し直したい」パターンの場合


現状ではダミー用の刃物を設定した物をテンプレートとして保存して
それを読み出しそれの刃物選択をしなおして、、としていました。


しかし選択しなおしを忘れるとトラブルにつながるのでどうにかならないかと悩んでおりました。

 

今回のこの授業「レッツ穴あけ」で初期刃物設定を空にしたテンプレートが
作れるようになった事が私のみならず他作業者も大喜びしております!

 

ほかの数値項目の変更も出来るので
いままで届かなかったかゆい部分がかなりすっきりできました。

 

たとえばその項目は
数値欄を空にしたいと思っていたのですが(エラーが出る様に)


テンプレートをエクスポートしてその数値を空に編集したものを実行すると
その項目には初期設定式が入りました


これだと意図しない数値で実行が出来てしまうので
空ではなく「マイナス数値」を入れて強制的にエラーが出る様にしてみました。
(通常でマイナス数値いれてもエラーが出て入力終了ができないのでテンプレート作成が出来ない)

 

 

@kandennti さまが

今回進められている自動化への取り組みはとても期待しています、

 

ちなみに他力な質問なのですが
Fusion360拡張機能の「穴認識」みたいに
穴加工のみの自動化なら現状でアドインまで形に出来たりするのでしょうか?

 

拡張機能の「穴認識」はなかなか使い勝手が良いとは言えず
使用はしていません、

 

問題と感じる事は
認識した穴形状に対する加工設定を「ドリル」「ボア」「円形」など
選択出来るわけではなく、

 

またその設定に使われる刃物の選択がライブラリ指定は出来るものの
そこに無い場合などはFusion360初期登録工具を登録してしまうなど
(該当工具が無い場合はエラーになってほしい)

 

ここまで話してなんですが
実はかなり前を最後に「穴認識」をさわってはいないのでバージョンアップで
使い勝手が良いものになっていたりするのでしょか?後で確認してみます。

 

@kandennti さまが取り組まれている内容は

ポケット加工などを含む全加工と思っていますが

 

穴加工だけに特化した物もかなり欲しかったりします。

 

有償アドインを作っていただけたら絶対購入したいです。

 

 

 

メッセージ18/19

kandennti
Mentor
Mentor

@k-yamaguchiJ3GYK さん ありがとうございます。

 

テンプレートの編集、お役に立てて何よりです。

4月以前はツールを作成する方法が無かったため、テンプレートを編集出来ることに気が付き

チョロチョロいじっていた事が今回のコンテンツで役立ちました。

 

今は特に何も進めていません。(あっちも質問しっぱなしだな・・・)

有償アドインについても今のところ考えていません。と言うか、とてもじゃないのですが

万人が満足できるようなものは出来ないです。

 

取り組まれている海外の方も、所属する企業さん専用のものや、Fusion360を販売しつつ

加工について付加サービス(例えばcpsファイルの編集等)を行っている方ではないかと

思います。

 

そのように"万人向け"では無く"特定の企業向け"であれば、何度も打ち合わせ、トライ&エラー

を繰り返し、それなりのものは出来ると思います。

 

「穴認識」は試したことが無かったため試してみましたが、セットアップは事前に作る必要が

有るのですね。確かに気持ちはわかります。

恐らく"万人向け"で機能を作ろうとすると、この様にするしかないのだと思います。

今の自分には、ここまでも作れそうに無いですね。

 

え~後は、思うことをツラツラと・・・。

 

"CAMの自動化が欲しい"と思うのは、恐らく小ロット多品種で類似品が多いものを対応している方

だろうとは思っています。(極端な話、加工時間よりNCプログラム作成の方が時間のかかる方)

人件費の高い国の場合、もう目の前のPCに仕事させるしかコストを下げる方法が無いのでは?

と思ってます。

 

CAMのAPIの強化は、長年APIフォーラムを見ていて要望がかなりありました。

4月のUpdateで大幅に強化され、かなりの事が出来るようになった実感があります。

Autodeskさんもこの辺を推しても良いような気もします。"Fusion360なら自動化出来ます!"と。

 

こちらのサンプルで、モデリングからツールパスの作成・ポスト処理まで行ってますが、

恐らく多くの方が望んでいるのは、これじゃ無いような気がします。

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-995d634e-a8ef-4f2b-a826-1d32171f4998 

"じゃぁ お前がサンプル作れよ!"と言われると、今のところ本コンテンツが限界かな。

メッセージ19/19

Bunga777
Mentor
Mentor

そうそう、まさに小ロット多品種なんですよね。

 

で、日々のNCプログラミングなんてのは部品が変わってもほぼやることは変わらないわけで、ざっくり荒取りして平面仕上げて輪郭仕上げて、穴ならセンターもみ付けしてドリルで穴開けて……などなどね。

 

形状に合わせて自動的に今までの経験から手順はほぼ決まっているわけですよ。

多くのプログラムを作っているうちに、こんなもん自動でできるんじゃないの?とか思ったりするんですよね。

 

ところが、ならどういう手順でやるの?決定事項はどれ?選択肢はどうする?などを考えていくと、新人にプログラムの仕方を教えるよりもずっと面倒だったりするんですよね。実は思ったよりも沢山の選択肢の中から、無意識のうちに機械的に選択をして、工程を組んでいたりすることに気付かされるんですよね。

 

私はテンプレートすら作らずに、ユーザーのデフォルト設定に全て入れこんで使ってしまっているので、皆さんの書き込みを読んで、今更ながらテンプレートを作ってみるかなぁと思ってみたりしています。

 

あ、、そういえば最近のアップデートでユーザーのデフォルト設定がまるっと消えてませんでしたか?

私のところではあちこちのデフォルト設定が消えていて、なんだこれは!と思いながら最近は直しつつ使っていたりしました。まぁこれもテンプレートを作れば解決する話なので、今後のためにもテンプレートくらいは作ろうと思った次第です。

 

と無駄に色々書きましたが、 @kandennti さんの取り組みには私も凄く期待しているので宜しくお願い致します。