PR

【FreeCAD】3次元翼を作成するPythonスクリプト

FreeCADで翼型を読み込んで3次元翼を作成するPythonスクリプト(マクロ)を作成した

FreeCADのバージョンは0.19

スポンサーリンク

概要

最終的にはこんな感じのものを作成する

↓FreeCADの公式ドキュメントはこちら

FreeCAD Documentation

インストールとチュートリアル

↓参考サイトと公式サイト

FreeCAD Windows でのインストール方法 - XSim
インストーラーを以下のサイトからダウンロードします……
FreeCAD: Your own 3D parametric modeler
FreeCAD, the open source 3D parametric modeler

使い方は下のサイトの「形状作成チュートリアル」を一通りやれば完全に理解できる

FreeCAD 使い方メモ - XSim
オープンソースの汎用 3D-CAD である FreeCAD の使い方のメモです……

使用するデータの形式

FreeCADにはデフォルトで2次元翼型読み込みの機能が入っている

Common Airfoil Data Import - FreeCAD Documentation

FreeCAD can import airfoil data such as that found on the UIUC Airfoil Coordinates Database or files produced by airfoil creation and annalizing software like XFLR5.

https://wiki.freecadweb.org/Common_Airfoil_Data_Import

公式が2次元翼型の読み込みにXFLR5の形式を採用しているので,この記事でも3次元翼型の読み込みにXFLR5の形式を使うことにする

↓XFLR5についてはこちら

SamplePlane

今回の記事で使うのはXFLR5で適当に作ったこの機体

これをXFLR5で出力すると次のような.xwimpファイルができる

Main Wing
0 1.5 -0.473 0.128 7.499 10 1 1 0 NACA4412 NACA4412
0.2 1.5 -0.473 0.384 7.498 10 1 1 0 NACA4412 NACA4412
0.4 1.5 -0.473 0.644 7.496 10 1 1 0 NACA4412 NACA4412
0.6 1.5 -0.473 0.908 7.495 10 1 1 0 NACA4412 NACA4412
0.8 1.5 -0.473 1.173 7.494 10 1 1 0 NACA4412 NACA4412
1 1.5 -0.473 1.433 7.492 10 1 1 0 NACA4412 NACA4412
1.2 1.5 -0.473 1.684 7.491 10 1 1 0 NACA4412 NACA4412
1.4 1.5 -0.473 1.923 7.489 10 1 1 0 NACA4412 NACA4412
1.6 1.5 -0.473 2.15 7.488 10 1 1 0 NACA4412 NACA4412
1.8 1.5 -0.473 2.366 7.487 10 1 1 0 NACA4412 NACA4412
1.999 1.5 -0.473 2.572 7.485 10 1 1 0 NACA4412 NACA4412
2.199 1.5 -0.473 2.731 7.484 10 1 1 0 NACA4412 NACA4412
2.399 1.5 -0.473 2.832 7.484 10 1 1 0 NACA4412 NACA4412
2.599 1.5 -0.473 2.934 7.483 10 1 1 0 NACA4412 NACA4412
2.798 1.5 -0.473 3.105 7.481 10 1 1 0 NACA4412 NACA4412
2.998 1.5 -0.473 3.324 7.478 10 1 1 0 NACA4412 NACA4412
3.198 1.5 -0.473 3.541 7.476 10 1 1 0 NACA4412 NACA4412
3.397 1.5 -0.473 3.755 7.473 10 1 1 0 NACA4412 NACA4412
3.597 1.5 -0.473 3.965 7.47 10 1 1 0 NACA4412 NACA4412
3.796 1.5 -0.473 4.172 7.467 10 1 1 0 NACA4412 NACA4412
3.996 1.5 -0.473 4.376 7.464 10 1 1 0 NACA4412 NACA4412
4.195 1.5 -0.473 4.576 7.461 10 1 1 0 NACA4412 NACA4412
4.395 1.5 -0.473 4.772 7.458 10 1 1 0 NACA4412 NACA4412
4.594 1.5 -0.473 4.964 7.455 10 1 1 0 NACA4412 NACA4412
4.793 1.5 -0.473 5.152 7.451 10 1 1 0 NACA4412 NACA4412
4.992 1.5 -0.473 5.335 7.448 10 1 1 0 NACA4412 NACA4412
5.192 1.5 -0.473 5.514 7.445 10 1 1 0 NACA4412 NACA4412
5.391 1.5 -0.473 5.688 7.441 10 1 1 0 NACA4412 NACA4412
5.59 1.5 -0.473 5.858 7.438 10 1 1 0 NACA4412 NACA4412
5.789 1.5 -0.473 6.022 7.434 10 1 1 0 NACA4412 NACA4412
5.988 1.5 -0.473 6.182 7.431 10 1 1 0 NACA4412 NACA4412
6.186 1.5 -0.473 6.337 7.427 10 1 1 0 NACA4412 NACA4412
6.385 1.5 -0.473 6.487 7.424 10 1 1 0 NACA4412 NACA4412
6.584 1.481 -0.467 6.631 7.421 10 1 1 0 NACA4412 NACA4412
6.783 1.463 -0.461 6.771 7.418 10 1 1 0 NACA4412 NACA4412
6.981 1.444 -0.455 6.906 7.415 10 1 1 0 NACA4412 NACA4412
7.18 1.425 -0.449 7.036 7.412 10 1 1 0 NACA4412 NACA4412
7.378 1.406 -0.443 7.143 7.41 10 1 1 0 NACA4412 NACA4412
7.577 1.388 -0.437 7.217 7.408 10 1 1 0 NACA4412 NACA4412
7.775 1.369 -0.431 7.293 7.407 10 1 1 0 NACA4412 NACA4412
7.973 1.35 -0.425 7.425 7.405 10 1 1 0 NACA4412 NACA4412
8.172 1.331 -0.419 7.601 7.402 10 1 1 0 NACA4412 NACA4412
8.37 1.313 -0.413 7.775 7.399 10 1 1 0 NACA4412 NACA4412
8.568 1.294 -0.408 7.951 7.396 10 1 1 0 NACA4412 NACA4412
8.766 1.275 -0.402 8.131 7.393 10 1 1 0 NACA4412 NACA4412
8.964 1.256 -0.396 8.319 7.389 10 1 1 0 NACA4412 NACA4412
9.162 1.238 -0.39 8.52 7.384 10 1 1 0 NACA4412 NACA4412
9.36 1.219 -0.384 8.739 7.38 10 1 1 0 NACA4412 NACA4412
9.558 1.2 -0.378 8.984 7.374 10 1 1 0 NACA4412 NACA4412
9.755 1.181 -0.372 9.263 7.368 10 1 1 0 NACA4412 NACA4412
9.953 1.163 -0.366 9.588 7.361 10 1 1 0 NACA4412 NACA4412
10.15 1.144 -0.36 9.948 7.352 10 1 1 0 NACA4412 NACA4412
10.347 1.125 -0.354 10.305 7.342 10 1 1 0 NACA4412 NACA4412
10.544 1.106 -0.348 10.633 7.331 10 1 1 0 NACA4412 NACA4412
10.74 1.088 -0.343 10.933 7.318 10 1 1 0 NACA4412 NACA4412
10.936 1.069 -0.337 11.202 7.302 10 1 1 0 NACA4412 NACA4412
11.133 1.05 -0.331 11.438 7.284 10 1 1 0 NACA4412 NACA4412
11.329 1.031 -0.325 11.64 7.264 10 1 1 0 NACA4412 NACA4412
11.525 1.013 -0.319 11.807 7.241 10 1 1 0 NACA4412 NACA4412
11.72 0.994 -0.313 11.907 7.222 10 1 1 0 NACA4412 NACA4412
11.916 0.975 -0.307 11.944 7.212 10 1 1 0 NACA4412 NACA4412
12.112 0.956 -0.301 11.968 7.2 10 1 1 0 NACA4412 NACA4412
12.307 0.938 -0.295 12.016 7.165 10 1 1 0 NACA4412 NACA4412
12.503 0.919 -0.289 12.06 7.123 10 1 1 0 NACA4412 NACA4412
12.699 0.9 -0.284 6.14 7.301 10 1 1 0 NACA4412 NACA4412
12.897 0.6 -0.189 6.14 7.301 10 1 1 0 NACA4412 NACA4412
Elevator
0 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
0.2 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
0.4 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
0.6 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
0.8 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
1 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
1.2 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
1.4 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
1.6 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
1.8 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
2 1 -0.283 0 0 10 1 1 0 NACA0012 NACA0012
Fin
0 0.6 -0.15 0 0 10 1 1 0 NACA0009 NACA0009
0.2 0.75 -0.188 0 0 10 1 1 0 NACA0009 NACA0009
0.4 0.9 -0.225 0 0 10 1 1 0 NACA0009 NACA0009
0.6 0.85 -0.213 0 0 10 1 1 0 NACA0009 NACA0009
0.8 0.8 -0.2 0 0 10 1 1 0 NACA0009 NACA0009
1 0.75 -0.188 0 0 10 1 1 0 NACA0009 NACA0009
1.2 0.7 -0.175 0 0 10 1 1 0 NACA0009 NACA0009
1.4 0.65 -0.163 0 0 10 1 1 0 NACA0009 NACA0009
1.6 0.6 -0.15 0 0 10 1 1 0 NACA0009 NACA0009

データの出力は以下の手順で行う

①「Wing and Plane Design」の「Plane>Current Plane>Edit」をクリック

②ウィンドウが開くので,それぞれの翼で「Define」をクリック

③「Other>Export Wing (depricated, use XML)」をクリック

④好きな名前で保存

以上

各断面ごとに11個のデータで構成されている

各データは左から順に以下の通り

名称単位
翼断面の位置(y座標)m
コード長m
前縁のx座標(機体後方を正)m
上反角deg
取り付け角(頭上げを正)deg
パネル分割数(コード方向)-
パネル分割数(スパン方向)-
パネル分割手法(コード方向)-
パネル分割手法(スパン方向)-
翼型(パネルの翼根側)-
翼型(パネルの翼端側)-
.xwimpファイルのデータ構成

今回のプログラムではパネル分割数/分割手法は使わないので気にしなくていい

翼型も1種類しか使わないので深く考える必要はない(考えたくない)

2次元翼のデータはこんな感じ

NACA4412
 1.0000000     0.0012600
 0.9928006     0.0032198
 0.9798895     0.0066835
 0.9635184     0.0109823
 0.9445469     0.0158354
 0.9234998     0.0210604
 0.9007495     0.0265227
 0.8765830     0.0321168
 0.8512328     0.0377572
 0.8248944     0.0433730
 0.7977362     0.0489045
 0.7699063     0.0543008
 0.7415373     0.0595184
 0.7127487     0.0645195
 0.6836500     0.0692712
 0.6543419     0.0737448
 0.6249182     0.0779148
 0.5954661     0.0817592
 0.5660677     0.0852584
 0.5368005     0.0883955
 0.5077378     0.0911557
 0.4789491     0.0935267
 0.4505011     0.0954983
 0.4224571     0.0970626
 0.3948782     0.0982103
 0.3678229     0.0988053
 0.3413476     0.0987893
 0.3155065     0.0981781
 0.2903524     0.0969911
 0.2659361     0.0952510
 0.2423068     0.0929845
 0.2195125     0.0902210
 0.1975998     0.0869933
 0.1766138     0.0833369
 0.1565988     0.0792897
 0.1375977     0.0748918
 0.1196525     0.0701852
 0.1028043     0.0652129
 0.0870931     0.0600189
 0.0725582     0.0546478
 0.0592379     0.0491435
 0.0471700     0.0435495
 0.0398384     0.0397909
 0.0363913     0.0379076
 0.0330915     0.0360235
 0.0299394     0.0341394
 0.0269381     0.0322577
 0.0240880     0.0303793
 0.0213896     0.0285049
 0.0188458     0.0266369
 0.0164571     0.0247759
 0.0142241     0.0229225
 0.0121494     0.0210790
 0.0102336     0.0192459
 0.0084774     0.0174236
 0.0068832     0.0156139
 0.0054516     0.0138171
 0.0041835     0.0120339
 0.0030809     0.0102667
 0.0021446     0.0085156
 0.0013756     0.0067792
 0.0007756     0.0050573
 0.0003455     0.0033493
 0.0000866     0.0016607
 0.0000000     0.0000000
 0.0000866    -0.0016261
 0.0003455    -0.0032112
 0.0007756    -0.0047474
 0.0013756    -0.0062299
 0.0021446    -0.0076600
 0.0030809    -0.0090391
 0.0041835    -0.0103693
 0.0054516    -0.0116513
 0.0068832    -0.0128843
 0.0084774    -0.0140686
 0.0102336    -0.0152049
 0.0121494    -0.0162931
 0.0142241    -0.0173340
 0.0164571    -0.0183285
 0.0188458    -0.0192762
 0.0213896    -0.0201778
 0.0240880    -0.0210342
 0.0269381    -0.0218453
 0.0299394    -0.0226118
 0.0330915    -0.0233344
 0.0363913    -0.0240133
 0.0398384    -0.0246491
 0.0471700    -0.0257940
 0.0592379    -0.0272029
 0.0725582    -0.0282569
 0.0870931    -0.0289743
 0.1028043    -0.0293755
 0.1196525    -0.0294825
 0.1375977    -0.0293193
 0.1565988    -0.0289118
 0.1766138    -0.0282876
 0.1975998    -0.0274762
 0.2195125    -0.0265089
 0.2423068    -0.0254180
 0.2659361    -0.0242376
 0.2903524    -0.0230023
 0.3155065    -0.0217477
 0.3413476    -0.0205094
 0.3678229    -0.0193230
 0.3948782    -0.0182234
 0.4224571    -0.0171746
 0.4505011    -0.0160650
 0.4789491    -0.0149118
 0.5077378    -0.0137351
 0.5368005    -0.0125542
 0.5660677    -0.0113870
 0.5954661    -0.0102497
 0.6249182    -0.0091567
 0.6543419    -0.0081203
 0.6836500    -0.0071506
 0.7127487    -0.0062555
 0.7415373    -0.0054401
 0.7699063    -0.0047076
 0.7977362    -0.0040587
 0.8248944    -0.0034919
 0.8512328    -0.0030041
 0.8765830    -0.0025904
 0.9007495    -0.0022449
 0.9234998    -0.0019608
 0.9445469    -0.0017313
 0.9635184    -0.0015496
 0.9798895    -0.0014106
 0.9928006    -0.0013115
 1.0000000    -0.0012600
NACA0012
 1.0000000     0.0012600
 0.9928006     0.0022657
 0.9798895     0.0040471
 0.9635184     0.0062660
 0.9445469     0.0087833
 0.9234998     0.0115106
 0.9007495     0.0143838
 0.8765830     0.0173536
 0.8512328     0.0203807
 0.8248944     0.0234325
 0.7977362     0.0264816
 0.7699063     0.0295042
 0.7415373     0.0324793
 0.7127487     0.0353875
 0.6836500     0.0382109
 0.6543419     0.0409325
 0.6249182     0.0435357
 0.5954661     0.0460044
 0.5660677     0.0483227
 0.5368005     0.0504749
 0.5077378     0.0524454
 0.4789491     0.0542192
 0.4505011     0.0557817
 0.4224571     0.0571186
 0.3948782     0.0582169
 0.3678229     0.0590641
 0.3413476     0.0596494
 0.3155065     0.0599629
 0.2903524     0.0599967
 0.2659361     0.0597443
 0.2423068     0.0592012
 0.2195125     0.0583649
 0.1975998     0.0572348
 0.1766138     0.0558122
 0.1565988     0.0541007
 0.1375977     0.0521056
 0.1196525     0.0498338
 0.1028043     0.0472942
 0.0870931     0.0444966
 0.0725582     0.0414523
 0.0592379     0.0381732
 0.0471700     0.0346718
 0.0363913     0.0309604
 0.0299394     0.0283756
 0.0269381     0.0270515
 0.0240880     0.0257068
 0.0213896     0.0243414
 0.0188458     0.0229565
 0.0164571     0.0215522
 0.0142241     0.0201283
 0.0121494     0.0186860
 0.0102336     0.0172254
 0.0084774     0.0157461
 0.0068832     0.0142491
 0.0054516     0.0127342
 0.0041835     0.0112016
 0.0030809     0.0096529
 0.0021446     0.0080878
 0.0013756     0.0065046
 0.0007756     0.0049023
 0.0003455     0.0032802
 0.0000866     0.0016434
 0.0000000     0.0000000
 0.0000866    -0.0016434
 0.0003455    -0.0032802
 0.0007756    -0.0049023
 0.0013756    -0.0065046
 0.0021446    -0.0080878
 0.0030809    -0.0096529
 0.0041835    -0.0112016
 0.0054516    -0.0127342
 0.0068832    -0.0142491
 0.0084774    -0.0157461
 0.0102336    -0.0172254
 0.0121494    -0.0186860
 0.0142241    -0.0201283
 0.0164571    -0.0215522
 0.0188458    -0.0229565
 0.0213896    -0.0243414
 0.0240880    -0.0257068
 0.0269381    -0.0270515
 0.0299394    -0.0283756
 0.0363913    -0.0309604
 0.0471700    -0.0346718
 0.0592379    -0.0381732
 0.0725582    -0.0414523
 0.0870931    -0.0444966
 0.1028043    -0.0472942
 0.1196525    -0.0498338
 0.1375977    -0.0521056
 0.1565988    -0.0541007
 0.1766138    -0.0558122
 0.1975998    -0.0572348
 0.2195125    -0.0583649
 0.2423068    -0.0592012
 0.2659361    -0.0597443
 0.2903524    -0.0599967
 0.3155065    -0.0599629
 0.3413476    -0.0596494
 0.3678229    -0.0590641
 0.3948782    -0.0582169
 0.4224571    -0.0571186
 0.4505011    -0.0557817
 0.4789491    -0.0542192
 0.5077378    -0.0524454
 0.5368005    -0.0504749
 0.5660677    -0.0483227
 0.5954661    -0.0460044
 0.6249182    -0.0435357
 0.6543419    -0.0409325
 0.6836500    -0.0382109
 0.7127487    -0.0353875
 0.7415373    -0.0324793
 0.7699063    -0.0295042
 0.7977362    -0.0264816
 0.8248944    -0.0234325
 0.8512328    -0.0203807
 0.8765830    -0.0173536
 0.9007495    -0.0143838
 0.9234998    -0.0115106
 0.9445469    -0.0087833
 0.9635184    -0.0062660
 0.9798895    -0.0040471
 0.9928006    -0.0022657
 1.0000000    -0.0012600
NACA0009
 1.0000000     0.0009450
 0.9901282     0.0019773
 0.9701682     0.0040276
 0.9469539     0.0063503
 0.9229830     0.0086826
 0.8988830     0.0109621
 0.8747544     0.0131812
 0.8506129     0.0153402
 0.8264621     0.0174401
 0.8023039     0.0194817
 0.7781400     0.0214655
 0.7539718     0.0233914
 0.7298010     0.0252589
 0.7056291     0.0270669
 0.6814577     0.0288141
 0.6572885     0.0304984
 0.6331231     0.0321174
 0.6089632     0.0336680
 0.5848106     0.0351466
 0.5606672     0.0365489
 0.5365351     0.0378702
 0.5124163     0.0391050
 0.4883132     0.0402470
 0.4642285     0.0412893
 0.4401651     0.0422242
 0.4161262     0.0430429
 0.3921157     0.0437356
 0.3681380     0.0442917
 0.3441984     0.0446990
 0.3203032     0.0449439
 0.2964603     0.0450110
 0.2726795     0.0448829
 0.2489733     0.0445397
 0.2253582     0.0439580
 0.2018562     0.0431109
 0.1784983     0.0419661
 0.1553293     0.0404845
 0.1324180     0.0386185
 0.1098794     0.0363101
 0.0879308     0.0334933
 0.0670466     0.0301197
 0.0482645     0.0262609
 0.0330984     0.0222613
 0.0253681     0.0197423
 0.0221517     0.0185528
 0.0193096     0.0174133
 0.0167968     0.0163198
 0.0145701     0.0152677
 0.0125870     0.0142513
 0.0108176     0.0132648
 0.0092361     0.0123031
 0.0078178     0.0113606
 0.0065463     0.0104322
 0.0054085     0.0095141
 0.0043917     0.0086017
 0.0034888     0.0076918
 0.0026950     0.0067823
 0.0020056     0.0058707
 0.0014197     0.0049561
 0.0009380     0.0040392
 0.0005596     0.0031211
 0.0002811     0.0022089
 0.0000981     0.0013119
 0.0000069     0.0004347
 0.0000069    -0.0004347
 0.0000981    -0.0013119
 0.0002811    -0.0022089
 0.0005596    -0.0031211
 0.0009380    -0.0040392
 0.0014197    -0.0049561
 0.0020056    -0.0058707
 0.0026950    -0.0067823
 0.0034888    -0.0076918
 0.0043917    -0.0086017
 0.0054085    -0.0095141
 0.0065463    -0.0104322
 0.0078178    -0.0113606
 0.0092361    -0.0123031
 0.0108176    -0.0132648
 0.0125870    -0.0142513
 0.0145701    -0.0152677
 0.0167968    -0.0163198
 0.0193096    -0.0174133
 0.0221517    -0.0185528
 0.0253681    -0.0197423
 0.0330984    -0.0222613
 0.0482645    -0.0262609
 0.0670466    -0.0301197
 0.0879308    -0.0334933
 0.1098794    -0.0363101
 0.1324180    -0.0386185
 0.1553293    -0.0404845
 0.1784983    -0.0419661
 0.2018562    -0.0431109
 0.2253582    -0.0439580
 0.2489733    -0.0445397
 0.2726795    -0.0448829
 0.2964603    -0.0450110
 0.3203032    -0.0449439
 0.3441984    -0.0446990
 0.3681380    -0.0442917
 0.3921157    -0.0437356
 0.4161262    -0.0430429
 0.4401651    -0.0422242
 0.4642285    -0.0412893
 0.4883132    -0.0402470
 0.5124163    -0.0391050
 0.5365351    -0.0378702
 0.5606672    -0.0365489
 0.5848106    -0.0351466
 0.6089632    -0.0336680
 0.6331231    -0.0321174
 0.6572885    -0.0304984
 0.6814577    -0.0288141
 0.7056291    -0.0270669
 0.7298010    -0.0252589
 0.7539718    -0.0233914
 0.7781400    -0.0214655
 0.8023039    -0.0194817
 0.8264621    -0.0174401
 0.8506129    -0.0153402
 0.8747544    -0.0131812
 0.8988830    -0.0109621
 0.9229830    -0.0086826
 0.9469539    -0.0063503
 0.9701682    -0.0040276
 0.9901282    -0.0019773
 1.0000000    -0.0009450

これら2つのテキストファイル(.xwimpと.dat)から冒頭に示したようなCADデータを作成する

フローチャートとソースコード全文

今回のプログラムのフローチャートはこんな感じ

ソースコードはこちら

import os
import math
import PySide2

import FreeCAD as App
import FreeCADGui as Gui
import importAirfoilDAT
import Draft

### Read .xwimp
# Open dialog box
DirPath = os.path.dirname(App.ActiveDocument.FileName)
ReadName, Filter = PySide2.QtWidgets.QFileDialog.getOpenFileName(None, "Read a file", DirPath, "All files (*.*);;")
# Open .xwimp
with open(ReadName) as f:
    xwimp = f.readlines()
    xwimp = xwimp[1:]
xwimp = [line.split() for line in xwimp]

### Import airfoil sections
y0 = 0
z = 0
for i, sta in enumerate(xwimp):
    
    # Import airfoil.dat at the beginning of the loop
    if i==0:
        # Get the airfoil Name (FoilName.dat)
        FoilName = sta[9]
        # Import .dat file
        importAirfoilDAT.insert(DirPath+"/"+FoilName+".dat",App.activeDocument().Name)
        # Get the imported airfoil object
        airfoilObject = App.ActiveDocument.getObject(FoilName).Group[0]
        # Set label for the object
        airfoilObject.Label = airfoilObject.Name+"_"+FoilName
    
    # Read dimensions from .xwimp
    y = float(sta[0])*1000
    chord = float(sta[1])*1000
    sweep = float(sta[2])*1000
    dihedral = float(sta[3])
    twist = float(sta[4])
    # Calcurate z dimensions
    z = z+math.tan(math.radians(dihedral))*(y-y0)
    y0 = y

    # Create clones for each aifoil sections
    cloneObject = Draft.clone(airfoilObject)
    # Move and Scale the cloned object
    cloneObject.Scale = (chord,chord,chord)
    cloneObject.Placement = App.Placement(App.Vector(sweep,z,y),App.Rotation(-twist,0,0))

### Lofting the airfoil sections
App.ActiveDocument.addObject('Part::Loft','Loft_'+FoilName)
LoftObject = App.ActiveDocument.ActiveObject
LoftObject.Sections = [obj for obj in App.ActiveDocument.Objects if FoilName+' (2D)' in obj.Label]
LoftObject.Solid = True
LoftObject.Ruled = True
LoftObject.Closed = False

### Mirror the left wing to create the right wing
Gui.runCommand('Part_Mirror',0)
App.ActiveDocument.addObject("Part::Mirroring",'Mirror_'+FoilName)
MirrorObject = App.ActiveDocument.ActiveObject
MirrorObject.Source = LoftObject
MirrorObject.Label = 'Mirror_'+FoilName
MirrorObject.Normal = (0,0,1)
MirrorObject.Base = (0,0,0)

### Run a boolean operation with the loft and the mirrored one
Gui.runCommand('Part_Boolean',0)
App.activeDocument().addObject("Part::Fuse","Fusion_"+FoilName)
FusionObject = App.ActiveDocument.getObject('Fusion_'+FoilName)
FusionObject.Base = LoftObject
FusionObject.Tool = MirrorObject
Gui.activeDocument().hide(LoftObject.Name)
Gui.activeDocument().hide(MirrorObject.Name)
FusionObject.ViewObject.ShapeColor = getattr(LoftObject.getLinkedObject(True).ViewObject,'ShapeColor',FusionObject.ViewObject.ShapeColor)
FusionObject.ViewObject.DisplayMode = getattr(LoftObject.getLinkedObject(True).ViewObject,'DisplayMode',FusionObject.ViewObject.DisplayMode)
Gui.activeDocument().getObject(FusionObject.Name).DisplayMode = "Shaded"

# Hide airfoil section objects
objectLabelList = [obj.Label for obj in App.ActiveDocument.Objects if FoilName+' (2D)' in obj.Label]
Gui.Selection.clearSelection()
for objectLabel in objectLabelList:
    App.ActiveDocument.getObjectsByLabel(objectLabel)[0].Visibility = False

FusionObject.Placement = App.Placement(App.Vector(0,0,0),App.Rotation(180,0,-90))

これだけ見てもなんのこっちゃ分からないので,1つずつ解説していく

ソースコードの解説

FreeCADにおけるプログラムの作成方法

FreeCADで行うすべての操作はPythonのスクリプトに変換されて実行される

Python Consoleを開けば自分が行った操作がPythonでどのように記述されるかをリアルタイムで知ることができる

Python console - FreeCAD Documentation

自分で何かしらのマクロを作成したい場合は

  1. マクロにしたい操作を自分で実行する
  2. Python Consoleに出てくるスクリプトをコピペする
  3. 上記のスクリプトをもとに汎化性を持たせたコードを作成する

といった手順をとることで,比較的簡単に欲しいマクロを作成することができる

左下に表示されているのがPython Console

作成したプログラムのデバッグを行う際はReport Viewを使うといい

Report view - FreeCAD Documentation

print()の出力やエラー文などが表示される

以上のことを踏まえて,フローチャートに沿って説明していく

モジュールのインポート

今回のプログラムでは以下のモジュールを使用する

import os
import math
import PySide2

import FreeCAD as App
import FreeCADGui as Gui
import importAirfoilDAT
import Draft

PySide2は「ファイルを開く」ダイアログボックスを使うのに使用する

QFileDialog — Qt for Python

FreeCADFreeCADGuiはPython Consoleに合わせてそれぞれAppGuiとしてインポートしている

.xwimpの読み込み

.xwimpファイルを読み込む

### Read .xwimp
# Open dialog box
DirPath = os.path.dirname(App.ActiveDocument.FileName)
ReadName, Filter = PySide2.QtWidgets.QFileDialog.getOpenFileName(None, "Read a file", DirPath, "All files (*.*);;")
# Open .xwimp
with open(ReadName) as f:
    xwimp = f.readlines()
    xwimp = xwimp[1:]
xwimp = [line.split() for line in xwimp]

App.ActiveDocument.FileNameで現在編集しているFreeCADのファイル名を取得し,os.path.dirname()でFreeCADのファイルが保存されているディレクトリのPathを取得する

PySide2.QtWidgets.QFileDialog.getOpenFileName()で,「ファイルを開く」のダイアログボックスを開くことができる

最初に開くのは上で取得したFreeCADのファイルが保存されているディレクトリである

「ファイルを開く」のダイアログボックス

選択したファイルはwith open()以降で読み込まれている

.xwimpファイルはスペース区切りのテキストファイルなので,split()を使って空白を削除してリストに変換している

参考
FreeCAD API - FreeCAD Documentation
Pythonで実行中のファイルの場所(パス)を取得する
QFileDialog — Qt for Python
Pythonでファイルの読み込み、書き込み(作成・追記)
Python, splitでカンマ区切り文字列を分割、空白を削除しリスト化

.datのインポート,翼断面のループ

.datファイルをインポートし,翼断面の数だけループを回して必要な断面を作成する

### Import airfoil sections
y0 = 0
z = 0
for i, sta in enumerate(xwimp):
    
    # Import airfoil.dat at the beginning of the loop
    if i==0:
        # Get the airfoil Name (FoilName.dat)
        FoilName = sta[9]
        # Import .dat file
        importAirfoilDAT.insert(DirPath+"/"+FoilName+".dat",App.activeDocument().Name)
        # Get the imported airfoil object
        airfoilObject = App.ActiveDocument.getObject(FoilName).Group[0]
        # Set label for the object
        airfoilObject.Label = airfoilObject.Name+"_"+FoilName
    
    # Read dimensions from .xwimp
    y = float(sta[0])*1000
    chord = float(sta[1])*1000
    sweep = float(sta[2])*1000
    dihedral = math.radians(float(sta[3]))
    twist = float(sta[4])
    # Calcurate z dimensions
    z = z+math.tan(dihedral)*(y-y0)
    y0 = y

    # Create clones for each aifoil sections
    cloneObject = Draft.clone(airfoilObject)
    # Move and Scale the cloned object
    cloneObject.Scale = (chord,chord,chord)
    cloneObject.Placement = App.Placement(App.Vector(sweep,z,y),App.Rotation(0,0,-twist))

FreeCADのObjectにはややこしいことにNameとLabelという2つの概念が存在する

In summary, the Name essentially acts like a unique identifier (UID) for an object. Since a unique Name is very restrictive, all objects also have a Label property which allows "renaming" the object to something more descriptive. The internal Name actually remains fixed, but the user editable Label can be used in most situations where the Name would be used. In common usage in the program and the documentation, "renaming" means changing the Label and not the actual Name of the object.

https://wiki.freecadweb.org/Object_name

NameはObjectが生成されたとき与えられるIDのようなもので変更できない

LabelはデフォルトではNameと同じだが,ユーザーが分かりやすいように変更することができる

FreeCADのTree Viewなどから変更できる名前はLabelの方である

今回のプログラムでは,わかりやすさの観点からできる限りLabelを使用することにする

ループの初めにimportAirfoilDAT.insert()を使って翼型の.datファイルをインポートしている

インポートされた翼型は「翼型名のGroupオブジェクト>Wireオブジェクト」という入れ子構造になっている

これから使うのはWireオブジェクトの方なので,App.ActiveDocument.getObject(FoilName).Group[0]でGroupオブジェクトに入っているWireオブジェクトを取得している

左上のTree Viewに注目

このWireオブジェクトに対して拡大縮小・移動回転を行って各翼断面を作成する

Draft.clone()でWireオブジェクトのCloneを作成し,Scaleで拡大縮小,Placementで移動および回転を行う

Placementにおける回転角は「Z軸回り→Y軸回り→X軸回り」の順番で指定する

Placement = [Position, Yaw-Pitch-Roll]
The second form of Placement fixes an object's location in space with a Position (as in the first form), but describes its orientation using Yaw, Pitch and Roll angles. ... Yaw-Pitch-Roll = (y,p,r) is a tuple that specifies the attitude of the object. Values for y,p,r specify degrees of rotation about each of the z,y,x axis (see note).

https://wiki.freecadweb.org/Placement

俗にいう航空機のヨー・ピッチ・ロールである

ここまで実行すると次のようになる

各翼断面を作成できた

参考
FreeCAD 翼形状の作成 - XSim
Object name - FreeCAD Documentation
Tree view - FreeCAD Documentation
FreeCAD Scripting Basics - FreeCAD Documentation
Std Group - FreeCAD Documentation
Draft Wire - FreeCAD Documentation
オブジェクトのコピー、移動、回転
FreeCAD 3次元オブジェクトの拡大縮小 - XSim
Draft Clone - FreeCAD Documentation
Draft Scale - FreeCAD Documentation
Placement - FreeCAD Documentation

翼断面をロフトでつなぐ

上で作成した翼断面をロフトでつなぐ

### Lofting the airfoil sections
App.ActiveDocument.addObject('Part::Loft','Loft_'+FoilName)
LoftObject = App.ActiveDocument.ActiveObject
LoftObject.Sections = [obj for obj in App.ActiveDocument.Objects if FoilName+' (2D)' in obj.Label]
LoftObject.Solid = True
LoftObject.Ruled = True
LoftObject.Closed = False

SectionsではロフトでつなぐObjectのリストを指定する

App.ActiveDocument.Objectsでドキュメント内のすべてのObjectのリストを取得し,ifを使って上で作成したCloneオブジェクトのみを抽出している

それ以外の設定の意味は以下の通り

The Loft has three parameters, "Ruled surface","Create solid" and "Closed" each with a value of either "true" or "false".
If "Create solid" is "true" FreeCAD creates a solid if the profiles are of closed geometry, if "false" FreeCAD creates a face or (if more than one face) a shell for either open or closed profiles.
If "Ruled surface" is "true" FreeCAD creates a face, faces or a solid from ruled surfaces. Ruled surface page on Wikipedia.
If "Closed" is "true" FreeCAD attempts to loft the last profile to the first profile to create a closed figure.

https://wiki.freecadweb.org/Part_Loft

Ruled Surfaceとは線織面のことである
線織面とは - コトバンク
線織面と可展面 - 大人になってからの再学習
FreeCAD 線織面の作成 - XSim

意味は分からない

ここまで実行すると次のようになる

片翼分しかいらないならプログラムはここまででいい

参考
Pythonで文字列のリスト(配列)の条件を満たす要素を抽出、置換
断面形状をつないで複雑な形状を作成する(ロフト機能)
Part Loft - FreeCAD Documentation

LoftをMirrorで複製する

上で作成したロフトをMirrorで複製して反対側の翼を作成する

### Mirror the left wing to create the right wing
Gui.runCommand('Part_Mirror',0)
App.ActiveDocument.addObject("Part::Mirroring",'Mirror_'+FoilName)
MirrorObject = App.ActiveDocument.ActiveObject
MirrorObject.Source = LoftObject
MirrorObject.Label = 'Mirror_'+FoilName
MirrorObject.Normal = (0,0,1)
MirrorObject.Base = (0,0,0)

Sourceでミラー元のオブジェクト(上で作成したLoftオブジェクト)を指定する

NormalとBaseで「原点中心でXY平面に対するMirroring」を指定している

ここまで実行すると次のようになる

参考
Part Mirror - FreeCAD Documentation

LoftとMirrorを結合する

Loftで作成した左翼とMirrorで作成した右翼を結合する

### Run a boolean operation with the loft and the mirrored one
Gui.runCommand('Part_Boolean',0)
App.activeDocument().addObject("Part::Fuse","Fusion_"+FoilName)
FusionObject = App.ActiveDocument.getObject('Fusion_'+FoilName)
FusionObject.Base = LoftObject
FusionObject.Tool = MirrorObject
Gui.activeDocument().hide(LoftObject.Name)
Gui.activeDocument().hide(MirrorObject.Name)
FusionObject.ViewObject.ShapeColor = getattr(LoftObject.getLinkedObject(True).ViewObject,'ShapeColor',FusionObject.ViewObject.ShapeColor)
FusionObject.ViewObject.DisplayMode = getattr(LoftObject.getLinkedObject(True).ViewObject,'DisplayMode',FusionObject.ViewObject.DisplayMode)
Gui.activeDocument().getObject(FusionObject.Name).DisplayMode = "Shaded"

そろそろノリで理解できるのではないだろうか

DisplayMode = "Shaded"で表示モードを変更している

ここまで実行すると次のようになる

左翼に見える翼断面の線はCloneオブジェクトである

参考
ソリッド間のブーリアン演算
Part Boolean - FreeCAD Documentation
Std DrawStyle - FreeCAD Documentation

不要なObjectを非表示・翼を回転させる

Loftの作成に使ったCloneオブジェクトを非表示にする

ついでに座標系を「機体前方にX軸,右翼側にY軸,機体下方にZ軸」となるように翼を回転させる

# Hide airfoil section objects
objectLabelList = [obj.Label for obj in App.ActiveDocument.Objects if FoilName+' (2D)' in obj.Label]
Gui.Selection.clearSelection()
for objectLabel in objectLabelList:
    App.ActiveDocument.getObjectsByLabel(objectLabel)[0].Visibility = False

FusionObject.Placement = App.Placement(App.Vector(0,0,0),App.Rotation(180,0,-90))

これで3次元翼の読み込みは完了である

使い方

それではこのプログラムを使って実際に主翼,水平尾翼,垂直尾翼を作成してみる

ソースコードの置き場所

上で説明したソースコードを置くべきディレクトリを調べる

「Macro>Macros ...」をクリックする

「Execute macro」のウィンドウが開くので,下の「User macros location」のパスを調べる

筆者の場合は「C:/Users/XXX/AppData/Roaming/FreeCAD/Macro」だったので,ここに作成した.pyファイルを置けばいい

マクロの実行は右上の「Execute」を実行すればいい

主翼の読み込み

「Macro>macros ...」でウィンドウを開き,.pyを選択して「Execute」をクリックする

ダイアログボックスが開くので「SampleWing.xwimp」を選択して「開く」をクリックする

「Tasks>Cansel」で読み込みが完了する

水平尾翼の読み込み

主翼と同様の手順で「SampleTail.xwimp」を読み込む

Tree Viewから「Fusion_NACA0012」を選択し,Dataタブの「Base>Placement>Position」に「x=-4500mm,z=-450mm」を入力する

水平尾翼の位置がいい感じになる

垂直尾翼の読み込み

主翼・水平尾翼と同様の手順で「SampleFin.xwimp」を読み込む

垂直尾翼にMirrorは必要ないので削除する必要がある

「Fusion_NACA0009」を選択して「Delete」キーを押す

続けて「Mirror_NACA0009」を選択して「Delete」キーを押す

「Loft_NACA0009」を選択して,Dataタブの「Base>Placement」で「Angle=180°,Axis=(0,1,0)」を入力する

続けてDataタブの「Base>Placement>Position」に「x=-3500mm,z=-150mm」を入力する

最後にViewタブの「Display Options>Display Mode」のプルダウンから「Shaded」を選択する

これですべての操作は完了である

おわりに

FreeCADで3次元翼を作成するPythonスクリプトを作成した

FreeCADはオープンソースのソフトウェアなので無料で使える上に,一般的なサブスク形式のCADソフトのように突然の改悪におびえる必要もない

これからはしばらくはFreeCADをメインにして使っていこうと思う

↓関連記事

FreeCAD
「FreeCAD」の記事一覧です。

↓おすすめ記事

コメント