0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

オシロスコープのプローブホルダーをFreeCADのPythonスクリプトで作ってみた(Part Design編)

Last updated at Posted at 2025-05-04

Part Designワークベンチ

先日のオシロスコープのプローブホルダーをFreeCADのPythonスクリプトで作ってみたではPartワークベンチの機能を使いましたが、今回はPart Designワークベンチの機能を使って同様なプローブホルダーを作ってみました。

image.png

手順は下記のようにしました。

  • 加算直方体で最初の外形を作成
  • 厚みツールでくり抜いて箱型を形成
  • スケッチの押し出しでプローブ穴の壁部分を形成
  • スケッチの押し出しでリブとプローブスリットの壁部分を形成
  • ポケットでプローブ穴をくり抜く
  • ポケットでプローブスリットをくり抜く
  • 外形の壁を楕円形にくり抜く
  • 対象のエッジを選んでフィレットを形成

今回も最後にフィレットを形成していますが、Part DesignワークベンチのフィレットはPartワークベンチのそれよりも制約が厳しく、前回の形状では形成できませんでした。そこで、フィレット形成前の形状を少し変更して、フィレットと他のエッジとの干渉を避けるようにしました。

import FreeCAD as App
import Part
import Sketcher

# パラメーター
wt = 1.35   #肉厚
filletR = 1 #フィレット半径
sizeX = 90  #外形(x)
sizeY = 25  #外形(y)
sizeZ = 25  #外形(z)
hPosX = [12, 34, 56, 78]    #プローブ穴位置(x)
hPosY = 16  #プローブ穴位置(y)
hDia = 13   #プローブ穴直径
hSlit_w = 5     #プローブスリット幅
hSlitb_w = 10   #プローブスリットの壁部分の幅
hSlit_t = 5     #プローブスリット開口部の厚み
hDepth = 7.5    #プローブ穴の深さ

def box(
    body,   #追加先ボディ
    name = 'Box',   #名前
    size = (10, 20, 30),    #サイズ
):
    feature = App.ActiveDocument.addObject('PartDesign::AdditiveBox', name)
    feature.Length = size[0]
    feature.Width = size[1]
    feature.Height = size[2]
    b = body.addObject(feature)
    doc.recompute()
    return b[0]

# 厚みツール
def thickness(
    body,
    shape,  #適用対象図形
    face,
    name = 'Thickness',
    wth = 1 #厚み
):
    th = body.newObject('PartDesign::Thickness', name)
    th.Base = shape, face
    th.Value = wth
    return th

#スケッチを作ってアタッチ
def sketch_attach(
    shape,  #適用対象図形
    face,
    name = 'Sketch'
):
    sk = bd_ph.newObject('Sketcher::SketchObject', name)
    sk.AttachmentSupport = shape, face
    sk.MapMode = 'FlatFace'
    return sk

# スケッチに長方形を追加する
def sketch_square(
    sketch,
    pos = (0, 0),
    size = (10, 10)
):
    n_sg0 = len(sketch.Geometry)    #作業前のスケッチ内の図形要素数
    vt = [  #頂点リスト
        App.Vector(pos[0], pos[1], 0)
        ,App.Vector((pos[0]+size[0]), pos[1], 0)
        ,App.Vector((pos[0]+size[0]), (pos[1]+size[1]), 0)
        ,App.Vector(pos[0], (pos[1]+size[1]), 0)
    ]
    #ラインの頂点リスト
    l_ln = [[vt[i], vt[(i+1) % len(vt)]] for i in range(len(vt))]
    #ラインを追加
    for ln in l_ln:
        sketch.addGeometry(Part.LineSegment(*ln))
    #一致拘束を追加
    for i in range(len(l_ln)):
        ln0 = n_sg0 + i     #基準のライン番号
        ln1 = n_sg0 + (i+1) % len(l_ln) #制約を適用することで移動するライン番号
        sketch.addConstraint(Sketcher.Constraint("Coincident", ln0, 2, ln1, 1))

# スケッチに円を追加する
def sketch_circle(
    sketch,
    pos = (0, 0),
    r = 10      #半径
):
    v_pos = App.Vector(pos[0], pos[1], 0)
    v_ax = App.Vector(0, 0, 1)
    sketch.addGeometry(Part.Circle(v_pos, v_ax, r))

# スケッチに楕円を追加する
def sketch_ellipse(
    sketch,
    center = (0, 0),    #中心座標 
    ma_ax = (0, 20),    #長軸
    mi_ax = (10, 0)     #短軸
):
    v_c = App.Vector(center[0], center[1], 0)
    v_a = App.Vector((center[0]+ma_ax[0]), (center[1]+ma_ax[1]), 0)
    v_i = App.Vector((center[0]+mi_ax[0]), (center[1]+mi_ax[1]), 0)
    sketch.addGeometry(Part.Ellipse(v_a, v_i, v_c))

# 押し出し
def pad(
    body,
    profile,
    name = 'Pad',
    len = 10    #押し出し長
):
    pd = body.newObject('PartDesign::Pad', name)
    pd.Profile = profile
    pd.Length = len
    if type(profile) != list:
        profile.Visibility = False
    return pd

# ポケット作成
def pocket(
    body,
    profile,
    name = 'Pocket',
    ptype = 'Dimension',
    len = 10    
):
    pk = body.newObject('PartDesign::Pocket', name)
    pk.Profile = profile
    pk.Type = ptype
    pk.Length = len
    if type(profile) != list:
        profile.Visibility = False
    return pk


doc = App.activeDocument()
if doc == None:
    #アクティブドキュメントが存在しなければ新規作成
    doc = App.newDocument()

#プローブホルダーのボディ
bd_ph = doc.addObject('PartDesign::Body','probeholder')

#外形の箱
ph = box(bd_ph, 'outline', (sizeX, sizeY, sizeZ))

#肉厚分の箱にくり抜く
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (sizeZ-0.1) < f.CenterOfMass.z < (sizeZ+0.1)]
ph = thickness(bd_ph, ph, bf, 'box_ol', wt)
doc.recompute()

#プローブ穴の壁を作る
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (wt-0.1) < f.CenterOfMass.z < (wt+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_holewall')
for hx in hPosX:
    #プローブ穴の壁
    sketch_circle(sk, [hx, hPosY], ((hDia / 2) + wt))
ph = pad(bd_ph, sk,'holewall', (hDepth-wt))
doc.recompute()

#リブを作る
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (wt-0.1) < f.CenterOfMass.z < (wt+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_rib')
#リブ(横)
sketch_square(sk, [0, (hPosY-(wt/2))], [sizeX, wt])
for hx in hPosX:
    #リブ(縦)
    sketch_square(sk, [(hx-(wt/2)), 0], [wt, hPosY])
    #スリットの壁
    sketch_square(sk, [(hx-(hSlitb_w/2)), (hPosY+(wt/2))], [hSlitb_w, (sizeY-hPosY-(wt/2))])
ph = pad(bd_ph, sk,'rib', (hDepth-wt))
doc.recompute()

#プローブ穴を作る
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (0-0.1) < f.CenterOfMass.z < (0+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_hole')
for hx in hPosX:
    #プローブ穴
    sketch_circle(sk, [hx, -(hPosY)], (hDia / 2))
ph = pocket(bd_ph, sk,'hole', 'ThroughAll')
doc.recompute()

#プローブスリットを作る
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (0-0.1) < f.CenterOfMass.z < (0+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_slit')
for hx in hPosX:
    #スリット
    sketch_square(sk, [(hx-(hSlit_w/2)), -(hPosY)], [hSlit_w, -(sizeY-hPosY)])
ph = pocket(bd_ph, sk,'slit', 'ThroughAll')
doc.recompute()

#外形の横壁を楕円形にくり抜く
faces = ph.Shape.Faces
bf = [f'Face{i+1}' for i,f in enumerate(faces) if (0-0.1) < f.CenterOfMass.x < (0+0.1)]
sk = sketch_attach(ph, bf[0], 'sk_sidewall')
#楕円
center = (-(sizeY), sizeZ)  #中心座標 
w = sizeY-wt-filletR    #幅
h = sizeZ-hSlit_t       #高さ
if w > h:
    ma_ax = (w, 0)  #長軸
    mi_ax = (0, h)  #短軸
else:
    ma_ax = (0, h)
    mi_ax = (w, 0)
sketch_ellipse(sk, center, ma_ax, mi_ax)
ph = pocket(bd_ph, sk,'sidewall', 'ThroughAll')
doc.recompute()

#フィレット
edges = ph.Shape.Edges
##外形のエッジのうちフィレット対象を抽出
fil_edges = []
for i, ed in enumerate(edges):
    sel = False
    for vx in ed.Vertexes:
        if (
         #リブの接続部
         ((wt-0.1) < vx.Z < (wt+0.1))
         #スリットの外側
         or (((sizeY-0.1) < vx.Y < (sizeY+0.1)) and ((hSlit_t-0.1) < vx.Z < (hSlit_t+0.1)))
        ):
            sel = True
            break
    if sel:
        fil_edges.append(f'Edge{i+1}')
fil = bd_ph.newObject('PartDesign::Fillet','Fillet')
fil.Radius = filletR
fil.Base = (ph, fil_edges)
doc.recompute()
ph = fil

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?