1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

オシロスコープのプローブホルダーを着脱式にしてみた

Posted at

着脱式プローブホルダー

先日のプローブホルダーをについて、必要に応じてオシロスコープから取り外しできるように着脱式にしてみました。楔型のベース部のみをオシロスコープやラックなどに両面テープで固定しておいて、本体部分を上からはめ込みます。

image.png

楔型のベース部は、厚みツールで肉抜きしたものと、していないものの2種類を作ってみました。
image.png

楔型といっても、底面が台形になっているので加算ウェッジでは生成できません。また、側面の角度が同一でないので押し出しでも生成できません。そこで、加算ロフトを使うことにしました。

ベース部を受ける本体側のソケット部は、加算ロフトで作った雛形をブーリアン演算でカットして形成しています。

import FreeCAD as App
import Part
import Draft
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    #プローブ穴の深さ
bsPosX = [15, 45, (sizeX-15)]   #着脱ソケット位置(x)
bsSizeX = 10    #着脱ベース底部幅/2
bsSizeY = 3     #着脱ベース厚み
bsSizeZ = 20    #着脱ベース高さ
bsGrdXZ = 1/10  #着脱ベース斜面の傾き(x/z)
bsGrdXY = 1/1   #着脱ベース斜面の傾き(x/y)
bsfilletR = 0.5 #着脱ベース部フィレット半径
scSizeX = bsSizeX + wt  #着脱ソケット幅/2
scSizeY = 3 + wt    #着脱ベース厚み
scSizeZ = sizeZ - 2 #着脱ソケット高さ
scfilletR = 0.2 #着脱ソケット部フィレット半径

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 aloft(
    body,
    sections,
    name = 'AdditiveLoft'
):
    lf = body.newObject('PartDesign::AdditiveLoft',name)
    lf.Profile = sections[0]
    lf.Sections += sections[1:]
    for s in sections:
        s.Visibility = False 
    return lf

#加算ロフトによる多面体
def aloft_hedron(
    body,
    l_vertices, #頂点リスト[[]]
    name = 'alhedron'
):
    l_w = [Draft.make_wire([App.Vector(p) for p in lp], closed=True) for lp in l_vertices]
    [body.addObject(w) for w in l_w]
    lf = aloft(body, l_w, name)
    return lf

#ブーリアン演算
def pd_boolean(
    body,   #演算を受けるボディ
    l_obj,  #演算するボディ
    type = 'Fuse',
    name = 'Boolean',
):
    bl = body.newObject('PartDesign::Boolean', name)
    bl.addObjects(l_obj)
    bl.Type = type
    for o in l_obj:
        o.Visibility = False 
    return bl

# 厚みツール
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()


#着脱ベース
for i in range(2):  #0:肉抜きなし 1:あり
    bd_bs = doc.addObject('PartDesign::Body',f'base{i}')
    x = sizeX + (30 * (i+1))
    bx0 = x - bsSizeX
    bx1 = x + bsSizeX
    xdy = bsSizeY * bsGrdXY  #背面と内側のxサイズの差
    xdz = bsSizeZ * bsGrdXZ  #上下のxサイズの差
    bz0 = sizeZ - bsSizeZ
    bz1 = sizeZ
    l2_p = [
        [(bx0+xdz+xdy, 0, bz0), (bx1-xdz-xdy, 0, bz0), (bx1-xdy, 0, bz1), (bx0+xdy, 0, bz1)]
        ,[(bx0+xdz, bsSizeY, bz0), (bx1-xdz, bsSizeY, bz0), (bx1, bsSizeY, bz1), (bx0, bsSizeY, bz1)]
    ]
    bs = aloft_hedron(bd_bs, l2_p, f'pg{i}')
    doc.recompute()

    #肉厚分の箱にくり抜く
    if i > 0:
        faces = bs.Shape.Faces
        bf = [f'Face{i+1}' for i,f in enumerate(faces) if (bsSizeY-0.1) < f.CenterOfMass.y < (bsSizeY+0.1)]
        bs = thickness(bd_bs, bs, bf, f'bs_ol{i}', wt)
        doc.recompute()

    #フィレット
    edges = bs.Shape.Edges
    fil_edges = []
    for i, ed in enumerate(edges):
        sel = False
        for vx in ed.Vertexes:
            if (
            ((0+0.1) < vx.Y)
            ):
                sel = True
                break
        if sel:
            fil_edges.append(f'Edge{i+1}')
    fil = bd_bs.newObject('PartDesign::Fillet',f'bsfil{i}')
    fil.Radius = bsfilletR
    fil.Base = (bs, fil_edges)
    doc.recompute()


#プローブホルダーのボディ
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.y < (wt+0.1)]
ph = pad(bd_ph, [ph, bf],'socketwall', (bsSizeY))

#着脱ソケット
for i, x in enumerate(bsPosX):
    bd_sc = doc.addObject('PartDesign::Body',f'socket{i}')
    bx0 = x - bsSizeX
    bx1 = x + bsSizeX
    xdy = bsSizeY * bsGrdXY  #背面と内側のxサイズの差
    xdz = scSizeZ * bsGrdXZ  #上下のxサイズの差
    bz0 = sizeZ - scSizeZ
    bz1 = sizeZ
    l2_p = [
        [(bx0+xdz+xdy, 0, bz0), (bx1-xdz-xdy, 0, bz0), (bx1-xdy, 0, bz1), (bx0+xdy, 0, bz1)]
        ,[(bx0+xdz, bsSizeY, bz0), (bx1-xdz, bsSizeY, bz0), (bx1, bsSizeY, bz1), (bx0, bsSizeY, bz1)]
    ]
    sc = aloft_hedron(bd_sc, l2_p, f'sc{i}')
    ph = pd_boolean(bd_ph, [bd_sc], 'Cut', f'bsc{i}')
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)), scSizeY], [wt, hPosY-scSizeY-(wt/2)])
    #スリットの壁
    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-filletR-bsSizeY-wt    #幅
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

##着脱ソケット部のフィレット対象を抽出
edges = ph.Shape.Edges
fil_edges = []
for i, ed in enumerate(edges):
    sel = False
    for vx in ed.Vertexes:
        if (
         #着脱ソケット部
         (((0-0.1) < vx.Y < (bsSizeY+0.1)) and ((0+0.1) < vx.Z))
        ):
            sel = True
            break
    if sel:
        fil_edges.append(f'Edge{i+1}')
fil = bd_ph.newObject('PartDesign::Fillet','Fillet')
fil.Radius = scfilletR
fil.Base = (ph, fil_edges)
doc.recompute()
ph = fil
1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?