CadQueryチュートリアル

Pythonで3Dモデリングが出来るソフトウェア「CadQuery」の使い方を紹介します。

通常のCADと違い、Pythonのコードとして管理が出来るためGit等での差分表示が見やすくなります。

「OpenSCAD」と同じようなソフトウェアです。

最終的には3Dプリンターでモデルを出力してみましょう!

 

3Dデータの基本

3Dプリンターを所有している方は、配布されているSTLファイルでの造形を試したことがあると思います。

3Dを表現する上でのデータ形式は大まかに「ソリッド」と「サーフェス」の2種類が存在します。

ソリッドは3Dの中身が詰まっているイメージで、サーフェスは3Dの表面だけのハリボテのイメージです。

3Dプリンターでよく目にする「STL」形式は「サーフェス」に分類されます。

表面だけのデータであると、あとから寸法を測定することや値を変えることが不可能になってしまいます。

あくまでハリボテで中身が存在していない表面だけのデータなのであとから再利用することが困難なのです。

3Dゲームに向いてるのはサーフェスですが、物作りにはソリッドです。

そのため、私は「ソリッド」に分類される「STEP」形式を強くオススメしておきます。

最近の3DプリンターならSTEPファイルも扱うことが出来ますし、CADで開いて編集することも可能です。

また、ソリッドからサーフェスに変換も可能です。(ハリボテのサーフェスから中身が詰まったソリッドに変換することは出来ません)

 

CadQueryでは、サーフェスのSTLでも、ソリッドのSTEPでも、データを出力することが出来ますが、以上の理由から「STEP」で出力することを強くオススメします。

 

CadQueryの起動

CadQueryは基本的にコマンドラインで使用するソフトウェアです。

そのままでは扱いづらいため、GUIを備えたエディタ「CQ-editor」が存在しています。

以下より実際に3Dモデルを出力するためのコードの紹介をしていくため、「CQ-editor」または「CadQuery」を動作させておいてください。

CQ-editorは最新版を使用する場合はひと手間必要ですが、少し古めのリリースビルドで良ければインストールの手間なしに試すことができます。

そのため、このページで使用するCQ-editorは主にリリースビルドの0.2を使用しています。

(古いバージョンなので不具合等が存在する場合があります。このバージョンは日本語コメントの保存が不可能なことは確認しています。)

本家のドキュメントはこちらを参照:CadQuery 2 Documentation

 

箱を作る

CadQuery Tutorial Box 001

基本的な箱を作る方法です。

import cadquery as cq
result = cq.Workplane("XY").box(20.0, 10.0, 5.0)
show_object(result)

解説

1行目の「import cadquery as cq」でCadQueryモジュールを読み込んでいます。

ここはPythonの機能になりますが、「as cq」部分で別名を付けてモジュールを読み込んでいるため、CadQueryの機能を「cq」で呼び出すことができます。

「import cadquery」だけであると、「cadquery」と記述しないと呼び出すことができません。

2行目の「result = 」で変数を用意しています。これから作る3Dモデルを保管しておく箱のイメージです。

Pythonでの変数は、アルファベット・数字・アンダースコアが使用可能のため、任意に名付けることが可能です。

次に変数に代入される右辺「cq.Workplane("XY").box(20.0, 10.0, 5.0)」を見ていきましょう。

「cq.」でCadQueryの機能を呼び出します。

続いてWorkplane関数で作業する平面を指定しています。

「XY」を指定していますが、XY, YZ, XZ, front, back, top, bottom, left, rightが指定できるようです。

3次元のモデルを作る際には軸方向を気にしなくてはいけません。

それぞれの次元を、X, Y, Zで表すのが一般的ですが、方向に関しては統一された決まりはありません。

CQ-editorに関しては、XY平面で高さ方向がZ軸となっているため、「Workplane("XY")」をよく使うことになるでしょう。

「box(20.0, 10.0, 5.0)」はその名の通り3Dの箱(ボックス)を作る関数で、引数でX, Y, Z軸方向のサイズを指定します。

幅が20mm・奥行きが10mm・高さが5mmの箱が生成されます。

3行目の「show_object(result)」では、変数に保存された3DモデルをCQ-editorに表示する関数です。

先ほど用意した変数「result」に保存されているものを表示します。

座標系の設定メモ

自分がよく使うCAD類についてまとめておきます。

  • CQ-editor(CadQuery)XY平面・高さ方向Z軸・変更不可
  • Altium Designer XY平面・高さ方向Z軸・変更不可
  • Autodesk Inventor XZ平面・高さ方向Y軸・ViewCube設定変更にて変更可能
  • SOLIDWORKS XZ平面・高さ方向Y軸・表示方向ツールバーからZ上方向ビューを適用を選択して変更可能
  • FreeCAD XY平面・高さ方向Z軸・変更不可

以上のことから、XY平面(高さ方向Z軸)を使用するモデル作りをしていくのが良いのではと思っています。

使用した関数

 

オフセットを付けて箱を作る

CadQuery Tutorial Box 002

先程の方法では、生成した箱の中心が原点となります。

上下にシンメトリーなモデルを作成するときには良いかもしれませんが、私の用途では原点の上、プラス方向にモデルを作ることが多いので、その方法を紹介します。

import cadquery as cq
result = cq.Workplane("XY").box(20.0, 10.0, 5.0, centered=(True, True, False))
show_object(result)

解説

2行目のbox関数ですが、オプションにて各軸の中心を使用するかが選べます。

「box(20.0, 10.0, 5.0, centered=(True, True, False))」最後に追記されたcentered=指定で、X軸、Y軸、Z軸を中心にするか選択しています。

今回は高さ方向(Z軸)のみ、プラス方向にモデルを作成してほしかったので、3つ目のZ軸のみFalseにしています。

もちろん全部Falseにすれば各座標のマイナス方向にはモデルが作成されません。

なお、「centered=」は省略も可能なので、「box(20.0, 10.0, 5.0, (True, True, False))」と表記することも可能です。

使用した関数

 

2Dスケッチから押し出して箱を作る

CadQuery Tutorial Box 003

2次元の長方形のスケッチから押し出しして3Dモデルを作る方法です。

単純な箱ならbox関数で良いと思いますが、例えば円筒形のような形状など複雑になるに従って、断面図を描き、それを押し出して3Dモデル化することが多くなると思います。

import cadquery as cq
result = cq.Workplane("XY").rect(20.0, 10.0).extrude(5.0)
show_object(result)

解説

四角形を描くrect関数と、2次元のスケッチから押し出しして3Dモデルを作成するextrude関数を使用しています。

使用した関数

 

箱を2個作る

CadQuery Tutorial Box 004

箱を2個作り、それを1つの3Dモデルとして結合する方法です。

import cadquery as cq
result = (
    cq.Workplane("XY")
    .box(20.0, 10.0, 5.0, centered=(True, True, False))
    .faces(">Z")
    .workplane()
    .box(6.0, 6.0, 3.0, centered=(True, True, False), combine=True)
    )
show_object(result)

解説

resultに代入する文が長くなるので、「()」で囲って複数行で記述しています。

箱を作るところまでは同じなのでその後の解説をします。

「faces(">Z")」では、最初に作成した箱の上面を指定しています。

faces関数は”面”を指定する関数であり、フィルター内容に「>Z」を指定しているため、一番大きなZ軸の値の面、つまり最初に作成した箱の上部の面を指し示すことになります。

その面に対して、workplane関数で平面を作成し、その平面上にまたbox関数で箱を作成しています。

今までのbox関数との違いは「combine=True」の指定です。

この指定によって、その下に存在するモデルとの結合を示し、2個の別々の3Dモデルではなく、結合された1個の3Dモデルを生成しています。

使用した関数

 

箱を3個作る

CadQuery Tutorial Box 005

箱を3個作り、それを1つの3Dモデルとして結合する方法です。

import cadquery as cq
result = (
    cq.Workplane("XY")
    .box(20.0, 10.0, 5.0, centered=(True, True, False))
    .faces(">Z")
    .workplane()
    .moveTo(4.0, 0.0)
    .box(6.0, 6.0, 3.0, centered=(True, True, False), combine=True)
    .moveTo(-4.0, 0.0)
    .box(6.0, 6.0, 3.0, centered=(True, True, False), combine=True)
    )
show_object(result)
import cadquery as cq
result = (
    cq.Workplane("XY")
    .box(20.0, 10.0, 5.0, centered=(True, True, False))
    .faces(">Z")
    .workplane()
    .center(4.0, 0.0)
    .box(6.0, 6.0, 3.0, centered=(True, True, False), combine=True)
    .center(-8.0, 0.0)
    .box(6.0, 6.0, 3.0, centered=(True, True, False), combine=True)
    )
show_object(result)
import cadquery as cq
result = (
    cq.Workplane("XY")
    .box(20.0, 10.0, 5.0, centered=(True, True, False))
    .faces(">Z")
    .workplane()
    .moveTo(4.0, 0.0)
    .rect(6.0, 6.0)
    .moveTo(-4.0, 0.0)
    .rect(6.0, 6.0)
    .extrude(3.0, combine=True)
    )
show_object(result)

どのコードも同じ動きをします。

解説

原点に対する位置を指定できるmoveTo関数にてboxを作る位置を変えています。

1個目のコードがmoveTo関数にて座標を絶対値指定する方法。

原点は0, 0のまま動きません。

2個目のコードがcenter関数にて原点を移動してしまう方法。

これは相対的にしか指定できず、原点も移動するためあまりオススメはできません。

3個目のコードはmoveTo関数を使用しているが、boxではなく押し出しで箱を作る方法。

前回の例と違って、rect関数を2回記述しているのに対して、extrude関数は1回しか記述していません。

これは押し出し出来るものは同時に全て押し出されるためです。

使用した関数

 

箱を4個以上作る

CadQuery Tutorial Box 006

箱を4個以上作り、それを1つの3Dモデルとして結合する方法です。

import cadquery as cq
result = (
    cq.Workplane("XY")
    .box(20.0, 10.0, 5.0, centered=(True, True, False))
    .faces(">Z")
    .workplane()
    .rect(6.0, 6.0, forConstruction=True)
    .vertices()
    .box(2.0, 2.0, 1.5, centered=(True, True, False), combine=True)
    )
show_object(result)
import cadquery as cq
result = (
    cq.Workplane("XY")
    .box(20.0, 10.0, 5.0, centered=(True, True, False))
    .faces(">Z")
    .workplane()
    .pushPoints([(3.0, 3.0), (3.0, -3.0), (-3.0, 3.0), (-3.0, -3.0)])
    .box(2.0, 2.0, 1.5, centered=(True, True, False), combine=True)
    )
show_object(result)

どのコードも同じ動きをします。

解説

workplane関数までは共通なのでそれ以降の解説をします。

1個目のコードではrect関数とvertices関数を使用しています。

しかもrect関数には謎の「forConstruction=True」オプションが渡されています。

これは、rect関数で描ける長方形を描かずに、下書きとして使用しているイメージです。

その長方形に対して、vertices関数で頂点を取得します。

長方形ですので、4個の頂点が存在します。

その頂点群に対してbox関数で箱を作成しています。

2個目のコードでは、pushPoints関数を使用して、複数の点を自分で指定しています。

この関数の場合は点の数は任意に設定できます。

使用した関数

 

製作例:SFPケージケース

SFPケージを単体で扱う際に、足を折らないようにするための保護具です。

小分け梱包する際に、SFPケージのトレイを切るのは大変なので設計しました。

import cadquery as cq
result = (
    cq.Workplane("XY")
    # SFP CAGE CASE
    .box(52.0, 18.0, 14.0, centered=(True, True, False))
    .faces(">Z or <Z")
    .shell(-1.0)
    # SFP CAGE SUPPORT LEFT
    .moveTo(-19.7, 7.0)
    .rect(10.6, 2.0)
    .moveTo(-7.9, 7.0)
    .rect(7.0, 2.0)
    .moveTo(2.1, 7.0)
    .rect(7.0, 2.0)
    .moveTo(12.25, 7.0)
    .rect(1.7, 2.0)
    .moveTo(21.9, 7.0)
    .rect(1.6, 2.0)
    # SFP CAGE SUPPORT RIGHT
    .moveTo(-17.2, -7.0)
    .rect(15.6, 2.0)
    .moveTo(-2.9, -7.0)
    .rect(7.0, 2.0)
    .moveTo(6.0, -7.0)
    .rect(4.8, 2.0)
    .moveTo(17.1, -7.0)
    .rect(2.0, 2.0)
    # SFP CAGE SUPPORT BACK
    .moveTo(23.85, 7.15)
    .rect(2.3, 1.7)
    .moveTo(24.0, 2.4)
    .rect(2.0, 1.8)
    .moveTo(24.0, -2.4)
    .rect(2.0, 1.8)
    .moveTo(24.3, -7.15)
    .rect(1.4, 1.7)
    # SFP CAGE SUPPORT EXTRUDE
    .extrude(4.0, combine=True)
    )
show_object(result)

 

製作例:連結ソケット28Pケース

連結ソケット28Pを単体で扱う際に、足を折らないようにするための保護具です。

小分け梱包する際に、スポンジを切るのは大変なので設計しました。

import cadquery as cq
result = (
    cq.Workplane("XY")
    # DIP SOCKET CASE
    .box(38.0, 20.0, 9.0, centered=(True, True, False))
    .faces(">Z or <Z")
    .shell(-1.0)
    .faces("<Z")
    .box(36.0, 18.0, 4.0, centered=(True, True, False), combine=True)
    .rect(34.0, 16.0, forConstruction=True)
    .vertices()
    .circle(0.5)
    .cutThruAll()
    .rect(34.0, 16.0)
    .cutThruAll()
    .faces(">Z")
    .rect(36.0, 18.0, forConstruction=True)
    .vertices()
    .hole(1.0, 5.0)
    .faces("not (>X or <X or >Y or <Y)")
    .edges("|Z")
    .fillet(2.0)
    .faces(">Z")
    .rect(35.0, 20.0)
    .cutBlind(-1.0)
    .faces("<<Z[2]")
    .edges("|Y")
    .fillet(0.98)
    )
show_object(result)

 

製作例:スタンド

2.4mmのシャフトを立てるための治具です。

import cadquery as cq
result = (
    cq.Workplane("XY")
    .circle(10.0)
    .extrude(2.0)
    .faces(">Z")
    .chamfer(1.0)
    .faces(">Z")
    .circle(3.0)
    .extrude(4.0)
    .faces(">Z")
    .chamfer(0.5)
    .faces(">Z")
    .cboreHole(1.4, 2.4, 4, depth=5)
    )
show_object(result)

 

製作例:Bambu Lab P1S / X1-Carbon 用のゴミシューター

3Dプリンタから排出されるフィラメントのゴミを、設置した棚の後ろから落とすためのシューターを急遽設計しました。

変数でパラメータをいじれるようになっています。

現状だと背面から180mmの位置にシューター出口が来るようになっています。

急ぎで適当に書いたので、コード自体は雑なところがあるかもしれません。

import cadquery as cq
depth=180.0
h_offset=10.0
chute_t=1.0
chute_r=4.0
tab_t=1.0
tab_r=10.0
result = (
    cq.Workplane("XZ")
    .workplane(offset=-25)
    .lineTo(0, tab_t)
    .lineTo(130 - h_offset, depth)
    .lineTo(130, depth)
    .lineTo(130, depth + 50)
    .lineTo(130 - h_offset, depth + 50)
    .lineTo(80 - h_offset, depth + 50)
    .lineTo(-100, tab_t)
    .lineTo(-100, 0)
    .close()
    .extrude(50)
    .faces("<Z or >X")
    .shell(-1.0 * chute_t, kind="intersection")
    .faces("<Z")
    .workplane()
    .moveTo(-108, 25)
    .rect(18, 50)
    .moveTo(8, 25)
    .rect(18, 50)
    .extrude(-1.0 * tab_t, combine=True)
    .moveTo(-109, 25)
    .hole(3.2)
    .edges("not (<Z or <<Z[9] or <<Z[10] or >X or <X or <<X[-2] or <<X[-3] or <<X[-4] or <<X[-5] or <<X[-6] or >>X[-12])")
    .fillet(chute_r)
    .edges("(<<X[-1] or >>X[-28]) and |Z")
    .fillet(tab_r)
    )
show_object(result)

 

製作例:単純ゴミシューター

上記ゴミシューターから別の場所に導くための単純なシューターです。

import cadquery as cq
chute_l=250.0
chute_w=60.0
chute_d=60.0
chute_t=2.0
chute_r=4.0
result = (
    cq.Workplane("XY")
    .box(chute_l, chute_w, chute_d, centered=(True, True, False))
    .faces(">Z or >X or <X")
    .shell(-1.0 * chute_t)
    .edges(">>Z[0] and |X")
    .fillet(chute_r)
    .edges(">>Z[2] and |X")
    .fillet(chute_r - chute_t)
    )
show_object(result)

 

参考リンク

参考にした情報等へのリンクをメモしておきます。

公式サイト

 

その他情報