Schemdraw - eiichiromomma/CVMLAB GitHub Wiki

Schemdraw でCambria(Wordの数式フォント)と日本語 (Windows)

Pythonを使って回路図などを描けて,png, pdf, svgへの出力もできる.自由度が高いので重宝している.

ドキュメントに必要なことは書いてある.回路の記述はここらへんが参考.

with schemdraw.Drawin() as d:のスコープでd += しなくても書いてある順にパーツが置かれていく.態々as dとしているのはconfigとかpush, pop, holdとか,あとでsaveするため.

Pythonの仕様としてwith ~ asで変数を指定してもスコープでの死活概念がないので,スコープ外でsaveできる.(スコープ内の方が綺麗だがsaveは分けておいた方が見付けやすい)

Pythonの環境にはとりあえず, Jupyter Lab, matplotlib, matplotlib-fontja, schemdrawがあれば十分.

このドキュメントではWindowsで,Wordの数式フォントのCambria(Math)と日本語を無理矢理使う設定を示しておく.Cambriaなttfはそこらじゅうに落ちているのでmatplotlibでttfを読ませることもできるが,ライセンス的にダメと思われるので注意(macだとWordをインストールしてもシステムフォントに組込まれない)

パーツの向き

up, down, left, right, thetaとかで指定すれば,次に新たに指定するまでその向きが継続されるが,書かなければ書かないで可読性が落ちる.

寸法やアンカー

ドキュメントに書かれている通りで,スコープの頭でd.config(unit=2)とすると通常より短かめの記述になる(デフォルトは3)が,fontsize, 絶対量記述には反映しないので,あとからサイズ変更すると基本的に書き直しに近い作業になる.(ので,長さの指定はd.unit*0.5)のように相対値にしておくと良い)パーツそのもののサイズを変えたい場合はscale, scalex, scaleyで倍率を指定する.特にLについては貧相なのでやや盛った方が見易い.

R, L, Cとかのパーツはstart, end, centerがアンカーとして存在し,他の特殊なパーツについてはドキュメント参照

  • at: パーツ名またはパーツ名.アンカーのような感じでスタート地点を指定できる
  • anchor: 置くパーツのどのアンカーに繋げるかの指定
  • to, tox, toy: どこまでかの指定.tox, toyはそれぞれx, y座標のみ合わせる.

ラベル

中身はMatplotlibなので,その数式記述と同じで数式だけならr'$ $'で囲まれた中にLaTeX記述をする. 詳細はドキュメント参照

  • loc: どこに置くか.'top'がデフォで'bottom','left','right'がある.あとはアンカー名.
  • ofst: オフセット量.tuppleで書けばdx,dyだがパーツを置く向き依存なので,取り敢えず書いてから考える.
  • rotate: Trueにするとパーツに沿ってラベルが置かれる.
  • fontsize: そのラベルだけサイズを変えたいとき.全体はd.configで指定する.
  • color: 文字の色指定

並列回路

push, popの中での記述か,with d.hold():のスコープでの記述.後者の方がネストするので見易いっちゃ見易い.

import schemdraw
import schemdraw.elements as elm
import matplotlib.pyplot as plt
plt.rcParams["text.usetex"]=False # Wordと同じ数式フォントにするためFalse
import matplotlib.pyplot as plt
import matplotlib
import matplotlib_fontja # IPAexGothicが仕込まれるらしい
plt.rcParams['font.family']='serif'
plt.rcParams['mathtext.fontset'] = 'custom'
plt.rcParams['mathtext.rm'] = 'Cambria' # Cambria だけでMathの方と一致
plt.rcParams['mathtext.it'] = 'Cambria:italic'
plt.rcParams['mathtext.bfit'] = 'Cambria:bold:italic'
plt.rcParams['mathtext.bf'] = 'Cambria:bold'
schemdraw.config(font='IPAexGothic') # 無理矢理日本語にする

with schemdraw.Drawing() as d:
    d.config(fontsize=18.0)
    R1 = elm.ResistorIEC().linestyle('--').color('red').right().label('抵抗').dot()
    d.push() # 1 命令の脇道
    elm.Line().up(d.unit*0.5)
    elm.Resistor().right().label('push,popで並列回路')
    elm.Line().down(d.unit*0.5)
    elm.Dot()
    d.pop() # 1 の直前から再開
    R2 = elm.ResistorIEC().right().label(r'$R_2=1\Omega$')
    C1 = elm.Capacitor().right().label(r'$\dot{Z}=-j8\Omega$').dot()
    d.push() # 2
    topLn = elm.Line().right()
    L0 = elm.Inductor().down().label('標準だと$L$は貧相',loc='bottom',rotate=True,ofst=(-0.8,0.3))
    botLn = elm.Line().left().dot()
    d.pop() # 2
    I0 = elm.SourceI().down().reverse().label(r'$2\angle 45^\circ \rm A$',loc='top',rotate=True)
    E0 = elm.SourceSin().left()
    elm.Dot(open=True).label(r'$\rm a$')
    Gap = elm.Gap().left(d.unit*0.5).label('ギャップも作れる',ofst=(0,0.8))
    elm.Dot(open=True).label(r'$\rm b$',loc='right')
    with d.hold(): # push,popの代わり
        elm.Line().up(d.unit*0.4)
        elm.ResistorIEC().scale(0.4).left(d.unit*0.2)
        elm.Inductor().flip().scalex(0.4).left(d.unit*0.2)
        elm.ResistorIEC().scalex(0.4).scaley(0.3).left(d.unit*0.2).label('scale,scalex,scaleyで調整',fontsize=10,ofst=(0,-0.3))
        elm.ResistorIEC().scale(0.3).left(d.unit*0.2)
        elm.Capacitor().scale(0.4).left(d.unit*0.1)
        elm.Line().down(d.unit*0.4).dot()
    L1 = elm.Inductor().scaley(2).scalex(1.3).flip().label(r'$j21\Omega$')
    elm.Dot()
    elm.Ground()
    dLn = elm.Line().theta(135).label('任意角の線も可',rotate=True,loc='bottom',ofst=(0,0.5),color='blue')
    elm.Line().toy(R1.start).label('toy')
    elm.Line().tox(R1.start).label('tox')
    elm.CurrentLabel(top=False,length=d.unit*0.5).at(R2).label('名前があればatで指定できる')
    elm.CurrentLabel(length=d.unit*0.25, top=False).at(E0).label(r'$\bfit E_{\bf 0}$',ofst=0)
    elm.LoopCurrent(elm_list=[topLn,L0,botLn,I0],direction='ccw',theta1=-125,theta2=125,pad=0.4).label('閉路1')
    elm.CurrentLabelInline(ofst=0).at(dLn).label(r'Inline $\dot{I}_1$',rotate=True)
    elm.Dot(radius=0.05).at(L0.NW) # 極性のドットはLのアンカー NW, NEを使う
d.save('schemdraw01.png')
d.save('schemdraw01_300.png',dpi=300)
d.save('schemdraw01.pdf')
d.save('schemdraw01.svg')

sample

相互インダクタンス (Transformer)

Transformerとしてパーツはあるのだが半円4つしかなく,線を引き込む必要があったり全部アンカー基準で書く必要があり可読性が悪くなるうえ,d.unitガン無視の寸法(高さ1.6),1次2次の間隔が狭いのでMや極性や弧が書きにくいといった扱いにくさ.

なので普通のInductor置いてGapで間開けて2次側記述した方が読みやすかったりする.

import schemdraw
import schemdraw.elements as elm
import matplotlib.pyplot as plt
plt.rcParams["text.usetex"]=False # Wordと同じ数式フォントにするためFalse
import matplotlib.pyplot as plt
import matplotlib
import matplotlib_fontja # IPAexGothicが仕込まれるらしい
plt.rcParams['font.family']='serif'
plt.rcParams['mathtext.fontset'] = 'custom'
plt.rcParams['mathtext.rm'] = 'Cambria' # Cambria だけでMathの方と一致
plt.rcParams['mathtext.it'] = 'Cambria:italic'
plt.rcParams['mathtext.bfit'] = 'Cambria:bold:italic'
plt.rcParams['mathtext.bf'] = 'Cambria:bold'
schemdraw.config(font='IPAexGothic') # 無理矢理日本語にする

with schemdraw.Drawing() as d:
    d.config(fontsize=18.0)
    elm.SourceSin().reverse().up()
    elm.ResistorIEC().right()
    elm.Line().down(0.7)
    T0 = elm.Transformer(core=False).anchor('p1').right() # p1に接続
    bbT0 = T0.get_bbox()
    print(bbT0)
    # 位置決めにはBounding Boxを取得して相対値にする手段もある
    elm.Label(r'$M$',fontsize=14).at(T0.p1,dx=(bbT0.xmax-bbT0.xmin)/3,dy=0.6)
    elm.Arc2().at(T0.p1,dx=0.1,dy=0.2).to(T0.s1,dx=-0.1,dy=0.2)
    elm.Dot(radius=0.04).at(T0.p1,dx=0.2,dy=0.1)
    elm.Dot(radius=0.04).at(T0.s2,dx=-0.2,dy=-0.1)
    elm.Line().at(T0.p2).down(0.7) # p2から書き始める
    elm.Line().left()
    elm.Line().down(0.7).at(T0.s2) # s2から書き始める
    elm.Line().right()
    elm.SourceSin().up().reverse()
    elm.ResistorIEC().left()
    elm.Line().down(0.7)
BBox(xmin=-3.2162452993532734e-17, ymin=0.0, xmax=0.75, ymax=1.6)

Trans

相互インダクタンス (Inductor2つ)

可読性はこっちの方が遥かに良いが,Inductorが小さいのでscale調整が必要

import schemdraw
import schemdraw.elements as elm
import matplotlib.pyplot as plt
plt.rcParams["text.usetex"]=False # Wordと同じ数式フォントにするためFalse
import matplotlib.pyplot as plt
import matplotlib
import matplotlib_fontja # IPAexGothicが仕込まれるらしい
plt.rcParams['font.family']='serif'
plt.rcParams['mathtext.fontset'] = 'custom'
plt.rcParams['mathtext.rm'] = 'Cambria' # Cambria だけでMathの方と一致
plt.rcParams['mathtext.it'] = 'Cambria:italic'
plt.rcParams['mathtext.bfit'] = 'Cambria:bold:italic'
plt.rcParams['mathtext.bf'] = 'Cambria:bold'
schemdraw.config(font='IPAexGothic') # 無理矢理日本語にする

with schemdraw.Drawing() as d:
    d.config(fontsize=18.0)
    elm.SourceSin().reverse().up()
    elm.ResistorIEC().right()
    L1 = elm.Inductor().scalex(1.6).scaley(2).down()
    elm.Line().left()
    elm.Dot(radius=0.04).at(L1.NE,dx=-0.3)
    with d.hold(): # 2次側
        elm.Gap().at(L1.start).right(d.unit*0.5) # L1.startの横から書き始める
        L2 = elm.Inductor().scalex(1.6).scaley(2).flip().down()
        elm.Line().right()
        elm.SourceSin().up().reverse()
        elm.ResistorIEC().left()
        elm.Dot(radius=0.04).at(L2.NW,dx=0.3)
    elm.Label(r'$M$',fontsize=14).at(L1.NW,dy=0.7,dx=0.1)
    elm.Arc2().at(L1.NW,dx=-0.5,dy=0.1).to(L2.NW,dx=0.5,dy=0.1)

M

計器

用意されている以外の何かを作ろうとすると面倒で,検流計を作ろうとしても文字をド真ん中に置くという方法が無い.locでcenterアンカー使っても,align指定しても無理

ギャラリーにブリッジ回路はあるが,MeterArrowでお茶濁し.

with schemdraw.Drawing() as d:
    d.config(fontsize=24.0)
    elm.Source().label('D',loc='center',halign='center')
    elm.MeterArrow()

Meter

ブリッジ回路

ホイートストンだけ用意されている. Wheatstone classを加工しても書けるがやってる事は根性で書くだけなので複雑になれば労力は自前で用意するのと変わらなそう.

以下根性で書いた場合.長さの帳尻をsqrt(2)でやるか,tox, toyを使うかというところ. 入れ子になるとwith d.hold():も見易いかというと微妙.

import schemdraw
import schemdraw.elements as elm
import matplotlib.pyplot as plt
plt.rcParams["text.usetex"]=False # Wordと同じ数式フォントにするためFalse
import matplotlib.pyplot as plt
import matplotlib
import matplotlib_fontja # IPAexGothicが仕込まれるらしい
plt.rcParams['font.family']='serif'
plt.rcParams['mathtext.fontset'] = 'custom'
plt.rcParams['mathtext.rm'] = 'Cambria' # Cambria だけでMathの方と一致
plt.rcParams['mathtext.it'] = 'Cambria:italic'
plt.rcParams['mathtext.bfit'] = 'Cambria:bold:italic'
plt.rcParams['mathtext.bf'] = 'Cambria:bold'
schemdraw.config(font='IPAexGothic') # 無理矢理日本語にする
with schemdraw.Drawing() as d:
    len_a = 0.2*d.unit
    len_obj= 0.6*d.unit
    len_b = 0.15*d.unit
    d.config(fontsize=18.0)
    lr = elm.Line().down()
    v1 = elm.SourceSin().left(d.unit*1.41).flip()
    elm.Line().up()
    elm.Dot()
    with d.hold():
        Z1 = elm.ResistorIEC().theta(45).label(r'$R_1$',ofst=0).dot()
        with d.hold():
            elm.Source().down(d.unit*1.414).label('D',ofst=(0.05,0.7))
        elm.Dot()
        elm.Line().length(len_a).theta(-45).dot()
        with d.hold(): 
            elm.Line().length(len_b).theta(45)
            Z31 = elm.ResistorIEC().length(d.unit*0.5).theta(-45).label(r'$R_3$',ofst=0)
            elm.Line().length(len_b).theta(-135).dot()
        elm.Line().length(len_b).theta(-135)
        Z32 = elm.Capacitor().length(d.unit*0.5).theta(-45).label(r'$C_3$','bottom',ofst=(0.7,0.35))
        elm.Line().length(len_b).theta(45)
        elm.Line().tox(lr.start).theta(-45)
        elm.Dot()
    Z2 = elm.ResistorIEC().theta(-45).label(r'$R_2$','bottom',ofst=0)
    elm.Dot()
    Z4 = elm.Capacitor().theta(45).length(d.unit*0.4).label(r'$C_4$','bottom',ofst=0)
    elm.ResistorIEC().tox(lr.start).theta(45).label(r'$R_4$',loc='bottom',ofst=(-0.4,0))
    elm.CurrentLabel(length=0.75).at(v1).label(r'$\dot{V}(\omega)$',ofst=0)

bridge