プロファイル内を自由に動き回れる円の最大直径の求め方

プロファイル内を自由に動き回れる円の最大直径の求め方

kandennti
Mentor Mentor
575件の閲覧回数
7件の返信
メッセージ1/8

プロファイル内を自由に動き回れる円の最大直径の求め方

kandennti
Mentor
Mentor

回答が得られなくとも、ヒントやアイデアでも構わないので、よろしくお願いいたします。

 

適当に描いたスケッチなのですが、このようなプロファイルがあるとします。

1.png

プロファイル内を自由に動き回れる円の最大直径をどうやったら求められるか?

で悩んでおります。

(要はポケット形状をエンドミルで加工する際、直径幾つの工具を選べば良いかを

 プログラミング的に求められないか?と思っています)

 

スケッチの拘束(接線・一致・直角)を利用すると形状が限定されて、

添付ファイルの場合はφ10.442だとわかります。

1.png

 

"イヤイヤ、そんなの何処が一番狭いかわかっているのだから、最短距離を測定すれば

良いだけじゃないか" と言うのも正論です。

1.png

これらの場合、"これとこれの最短距離が最大直径になる”と分かるので測定する

だけで済みますが、プログラミング的に見つけるのが少し厄介なんです。

(この程度だと直ぐに見つけられる人間の目と脳は素晴らしいです)

 

総当たりで行うとしても、隣り合った線同士は最短距離0となるため、

それ以上離れたものの場合でも、正しい答えかどうかを判断出来ず・・・。

例えば、この辺りだと"2.583mm"となりますが、実際のφ10.442から遠い数値です。

1.png

 

添付したファイルは直線と円弧なので良いのですが、スプラインの要素が出てくると

さらに難問となり、”そもそも、それを求めることが出来るのか?”とも感じています。

 

スケッチの様に平面的な方法でも無くても構いませんし、精度の高い答えで無くても

構いません。何か思いつくことがあればよろしくお願いします。

0 件のいいね
解決済み
576件の閲覧回数
7件の返信
返信 (7)
メッセージ2/8

adachitech7
Consultant
Consultant

@kandennti さん、

 

とりあえずヒントになりそうな情報としてお聞きください。

 

閉じたプロファイルを押し出してから外側にシェル化。空のファイルに挿入してアセンブリを組みます。

01.PNG

 

 

そのアセンブリに円柱のピンを挿入、平面ジョイントを付与します。

02.PNG

 

 

内側の適当なところにドラッグ。

03.PNG

 

 

ピンと内面との間に接線関係を付与。

04.PNG

 

 

これでピンをドラッグすると内面に沿って動くようになります。

 

 

円柱の直径が最短部よりも大きければどこかで干渉が発生するはずです。これを何とかして検知して円柱の直径を徐々に小さくして再トライするようなアルゴリズムができればいいのですが。。。

05.PNG

メッセージ3/8

adachitech7
Consultant
Consultant
解決済み

@kandennti さん、

 

もう1つヒントを。

 

パスに沿った平面作成。

01.PNG

 

 

交点抽出してから直線を描く。

02.PNG03.PNG

 

 

パスに沿った平面を少しずつずらしながら直線の距離を測っていきます。

04.png

 

 

この表からグラフを描いて近似式を求めて直線の長さが最短になる分割ポイントを割り出します。

05.png

 

 

この近似式から求めた位置はこんな感じですね。

99.PNG

メッセージ4/8

Bunga777
Mentor
Mentor
解決済み

多分自動プログラミングに関しての取り組みだろうと推測します。

 

まず、自動で工具径を決めるのか?それともユーザーにある程度提示してもらうのか?みたいなところもポイントになりそうですね。

 

例えば、ライブラリに入っている工具径から一番加工時間の短くなるものをとりあえず選ぶという状況だとすると、話が少しシンプルになってくる気がします。

 

提案して頂いた形状を加工する場合に、いくつかの径が一筆書きでエンドミルが回るかどうか?だけを検査するとして、例えばφ8、φ10、φ12がライブラリにあり、そのどれが良いか?みたいな場合と仮定します。

 

φ8の時、とりあえず径よりも大きく押し出します。話を簡単にするために径の倍とします。

 

Monosnap Autodesk Fusion 360 2023-09-01 08.01.29.png

そして出来たボディの全ての面を選択して、オフセットでエンドミル径の半分をオフセットします。

 

Monosnap Autodesk Fusion 360 2023-09-01 08.02.18.png

出来上がったボディは1つのままなので、φ8ではいっぱつ加工が可能です。

 

ではφ12だとどうでしょうか?

Monosnap Autodesk Fusion 360 2023-09-01 08.03.23.png

オフセットさせるとボディが2つになりました。φ12で通れる軌跡は2つに分かれることが分かります。

Monosnap Autodesk Fusion 360 2023-09-01 08.03.54.png

 

ならばendmill というパラメータを作って、これをループで変えてあげれば、ボディの数が増えるしきい値が簡単に分かるはずです。

 

Monosnap Autodesk Fusion 360 2023-09-01 08.06.55.png

Monosnap Autodesk Fusion 360 2023-09-01 08.09.36.png

Monosnap Autodesk Fusion 360 2023-09-01 08.10.02.png

これで数値を変えていくとφ10とφ11の間がしきい値だと分かります。

 

こんな感じのチェックがプログラミングで可能ならば、とりあえずいっぱつで加工が可能かどうかの判断ができそうです。

 

ただ、一発での加工がベストではないので悩ましいところで、ユーザーに荒取りはどのエンドミルにするのか?仕上げはどうするのか?あたりを選択させる方式も良くない?と思ったりはします。

欲を言えば荒取りはいくつも選択できて、最初はφ12を使って、次にφ6で追い込んで、φ4で仕上げるみたいな事が可能だとかなりできる幅が広がりそうです。

メッセージ5/8

kandennti
Mentor
Mentor

@adachitech7 さん ありがとうございます。

 

これをAPIで作る自信がちょっと無かったです・・・。

でもこれが一番自然な考え方ですね。

メッセージ6/8

kandennti
Mentor
Mentor

@adachitech7 さん ありがとうございます。

 

自分も類似したことを考えていました。

プロファイルに対してパッチを作成し、エッジ上に平面を作成。

トリムした際のカットされた部分の長さかな? と思っていました。

1.png

 

その後、もっと簡単なアイデアが浮かんだので、このアイデアは保留にしています。

メッセージ7/8

kandennti
Mentor
Mentor

@Bunga777 さん ありがとうございます。

 

昨日の帰宅途中に類似したアイデアを思いつきました。

不思議な事に、このような事はPCの前から離れると思いつきます。

 

1.png

スケッチのままオフセットします。この状態で終了した場合、このスケッチの

プロファイルの数は2個です。(APIではプロファイル数を数えられます)

 

1.png

オフセットの距離をより大きくすることで、オフセット後のラインが分割されます。

この場合はプロファイルが3個になります。

今のところこの方向性で、いかに効率良くプロファイル数2個と3個の境界を

見つけるか と言う所です。

メッセージ8/8

kandennti
Mentor
Mentor

やっとの思いで進めました。

 

スケッチプロファイルを選択する事で、自由に動き回れる円の最大直径を表示するスクリプトです。

# Fusion360API Python script

import traceback
import adsk
import adsk.core as core
import adsk.fusion as fusion
import time

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface
        des: fusion.Design = app.activeProduct
        
        msg: str = 'プロファイルを選択/ESCキーで中止'
        selFilter: str = 'Profiles'
        sel: core.Selection = select_ent(msg, selFilter)
        if not sel:
            return

        t = time.time()

        unitsMgr: core.UnitsManager = des.unitsManager

        diameter = unitsMgr.convert(
            get_max_diameter(sel.entity, 0.001),
            unitsMgr.internalUnits,
            unitsMgr.defaultLengthUnits,
        )

        print(f"{time.time() - t} s")
        ui.messageBox(f"φ{diameter}です!", "最大直径")

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


def get_max_diameter(
        prof: fusion.Profile,
        tolerance: float = 0.01,
) -> float:
    '''
    プロファイル内を加工するための最大直径の取得
    '''
    skt: fusion.Sketch = prof.parentSketch

    loop: fusion.BRepLoop = None
    outer = [loop for loop in prof.profileLoops if loop.isOuter][0]
    edges = [e.sketchEntity for e in outer.profileCurves]

    directionPnt: core.Point3D = get_direction_point(edges)
    if not directionPnt:
        print("direction point not found")
        return -1

    outerEdges = core.ObjectCollection.createWithArray(edges)

    keepProfCount = skt.profiles.count + 1
    app: core.Application = core.Application.get()

    endValue = get_first_value(prof)
    splitCount = 10
    startValue = 0
    step = 1
    stateValue = 0
    while step > tolerance:
        step = (endValue - startValue) / splitCount
        for idx in range(splitCount):
            stateValue = startValue + (step * idx)

            try:
                app.executeTextCommand(u"Transaction.Start kantoku_offset")

                skt.offset(outerEdges, directionPnt, stateValue)
                resProfCount = skt.profiles.count

                if resProfCount > keepProfCount:
                    endValue = stateValue
                    startValue = startValue if idx == 0 else stateValue - step
                    break
            except:
                    endValue = stateValue
                    startValue = startValue if idx == 0 else stateValue - step
                    break
            finally:
                app.executeTextCommand(u"Transaction.Abort")

    return stateValue * 2


def get_first_value(
        prof: fusion.Profile,
) -> float:
    '''
    バウンダリングボックスから最初のオフセット目標値を取得
    '''
    bBox: core.BoundingBox3D = prof.boundingBox
    maxPnt: core.Point3D = bBox.maxPoint
    minPnt: core.Point3D = bBox.minPoint

    return max(
        [
            abs(maxPnt.x - minPnt.x),
            abs(maxPnt.y - minPnt.y),
            abs(maxPnt.z - minPnt.z),
        ]
    )


def get_direction_point(
        edges: list[fusion.BRepEdge],
        isInner: bool = True,
) -> core.Point3D:
    '''
    オフセット用参照ポイント
    edgesは閉じている状態の必要あり
    '''
    crvs = [e.geometry for e in edges]

    tempMgr: fusion.TemporaryBRepManager = fusion.TemporaryBRepManager.get()
    wireBody, _ = tempMgr.createWireFromCurves(crvs)
    faceBody: fusion.BRepBody = tempMgr.createFaceFromPlanarWires([wireBody])

    crv: core.Curve3D = crvs[0]
    eva: core.CurveEvaluator3D = crv.evaluator

    stPnt: core.Point3D = None
    _, stPnt, _ = eva.getEndPoints()

    _, prm = eva.getParameterAtPoint(stPnt)

    vec: core.Vector3D = None
    _, vec, _ = eva.getCurvature(prm)
    vecs = [vec.copy() for _ in range(2)]
    [vec.scaleBy(v) for vec, v in zip(vecs, [0.001, -0.001])]

    pnts = [stPnt.copy() for _ in range(2)]
    [p.translateBy(vec) for vec, p in zip(vecs, pnts)]

    if isInner:
        expectedValue = fusion.PointContainment.PointOnPointContainment
    else:
        expectedValue = fusion.PointContainment.PointOutsidePointContainment

    for pnt in pnts:
        res: fusion.PointContainment = faceBody.pointContainment(pnt)

        if res == expectedValue:
            return pnt

    return None


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

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

※もし試される方は、無限ループに陥る可能性が有る為、他のドキュメントは保存し

 閉じてから実行して下さい。

 

最初に添付したf3dファイルで試すとこんな結果です。

1.png

最短距離が10.442mmなので、まあまあの精度です。

そもそも、そんなにシビアな直径の工具は特注するしかないので、

"じゃ φ10で加工するか"

となるはずです。

 

オフセットだけで判断している為、現実的には凹角があった場合は適切か?

凹R以下の直径である必要ではないか?等、これだけでは判断しきれない

気もしてます。難しい。