arduino-cli自作クラスの他スケッチでの再利用(ライブラリ化?)
前提
arduino-cliをvscodeで利用している。下記ページ参照。 aka1022.hatenablog.com
ファイル構成は以下の前提
/myWorkSpace /.vscode arduino.json c_cpp_properties.json /ArduinoOutput /mySketch1 mySketch1.ino myClass.cpp myClass.h /mySketch2 mySketch2.ino
arduino.jsonのsketchにmySketch1\mySketch1.inoが設定され、そのスケッチ内でmyClassをコーディングした。 新しく、mySketch2を作成し、そこでもmyClassを使いたくなった。単純にヘッダファイルをインクルードするだけではリンクされないのでmyClassのソースとヘッダをライブラリとして認識させる必要がある。
下準備
ユーザーディレクトリを確認
arduino-cli config dump
のdirectoriesのuserがユーザーディレクトリとなる。このディレクトリのlibrariesディレクトリにクラスのソースファイルとヘッダファイルを置くとそれがライブラリとして認識される。今のファイル構成ではユーザーディレクトとしては/myWorkSpaceを充てる必要がある。 一致していない場合、
arduino-cli config set directories.user [myWorkSpaceのパス]
を実行する。
コーディング
順番が前後してしまったが、クラスのソースファイルとヘッダファイルについて。
1. ソースの拡張子は.cpp
2. ソースでArduino.hをインクルードする。
をしておけば、通常のcppの記法でソース、ヘッダを記述できる。
利用する側はmyClass.hをインクルードすればいい。
明示的には、
#include <myClass.h>
すればOK。(次項目で移動するが、../libraries/myClass/myClass.hとしなくてもいい。)
ファイル構成
myWorkSpace直下にlibrariesフォルダを作成し、その直下にヘッダファイル名と同じmyClassフォルダを作成し、その直下にヘッダファイルとソースファイルを移動する。 明示すると、
/myWorkSpace ... /libraries /myClass myClass.cpp myClass.h /mySketch1 mySketch1.ino myClass.cpp myClass.h ...
となる。git を利用している人はgit mvコマンドで移動させるのがよい。
まとめ
以上の手順で、新しいスケッチmySketch2でもmyClassを利用できるはず。
visual studio code でarduino-cli を使用する
visual studio code でarduino-cli を使用する
仕事でvscodeの便利さを思い知り、arduinoもvscode上で開発できないかと思い調べてみると出来そうだった。 日本語ドキュメントが少なかったり、古かったりだったので、備忘録を兼ねてログを残しておく。(2023年3月現在)
注意
筆者はvscode初心者です。なんか色々やったら出来たという状況です。ここに書いてない事で必須な事があるかもしれません。バージョンアップで以下の方法が使えなくなる可能性もあります、あらかじめご了承ください。
目標
vscodeのarduino拡張機能を入れるのが本題になるが、調べてみると対応がLegacy Arduino IDEとArduino CLIなるものだった。バージョン1.8.19以前のIDEがLegacy Arduino IDEになるらしく、これのリリースが2021/12/20である。その次のリリースは2.x.xとなっている。つまり、GUIのIDEを使っている場合、古いバージョンしか対応してないらしい。
(筆者は久しぶりにarduinoを使おうと最近IDEをインストールしたため、2.0.3になっていた。)
レガシーもまだダウンロードできるのでそっちでもいいが、レガシーと名の付く通り、そのうちダウンロードできなくなる事と、CLIの方が拡張性が高そうな事を考慮してCLIを選択する。
そういうわけで、本目標はvscodeでarduino-cliを使う事になる。
ダウンロード/インストール
vscodeのダウンロードからインストールについてはごまんと記事があるので、そちらを参照してください。 CLIの公式サイトによると、インストール方法がいろいろあるらしい。 インストールページのビルド済みバイナリをダウンロードしてくる方法が手っ取り早そう。 arduino-cli.exeが入った圧縮ファイルがダウンロードできるので、展開する。 この時点でarduino-cli.exeをコマンドプロンプトなりで実行することで、ファイルのビルド、アップロードはできるようになっている。 今後の利用のために、パスを通しておきましょう。 自分はC:\Program Files直下にダウンロードし展開したフォルダを置き、パスを通した。
core のインストール(2023/8/24追記)
arduinoといっても様々な種類があるが、全てに対応しているわけではなく、それぞれに適応するcoreというファイルのインストールが必要となる。 まず以下コマンドで利用可能なプラットフォームとライブラリのローカルキャッシュを更新する。
$ arduino-cli core update-index
PCにarduinoを接続し、必要なcoreを判別する。
$ arduino-cli board list
出力のCore欄が必要なcoreになる。 以下コマンドでcoreをインストールする。
$ arduino-cli core install [必要core]
以上でcoreのインストールができる。
拡張機能インストール
一応この時点でPowerShellターミナルからビルド、アップロードはできるが(そのうちバッチで運用するかも)、せっかくなので拡張機能を導入する。
導入するのは
- vsciot-vscode.vscode-arduino
- ms-vscode.cpptools, ms-vscode.cpptools-extension-pack (IntelliSense用、どっちかでもいいと思うけどどっちが何なのかわからん。cppファイル用の拡張機能をインストールしておけばいいと思う。)
作業フォルダ構成について
ソースコードの配置の仕方に注意点がある。 setup, loop関数があるファイルは同名のフォルダに格納する必要がある。 つまり、hoge.inoにsetup関数、loop関数を書いた場合、そのファイルは -/hoge/hoge.ino に置かなければならない。ファイル名とフォルダ名が違った場合、setup, loop関数のあるファイル(=mainファイル?)を見つけられず、ビルドエラーが出る。
VScode設定
vscodeの設定(ctrl+,)で必要なのは、Arduino: Use Arduino Cliをチェックするだけ(だと思う)。IDEの場合、arduino: Pathとかを設定する必要があるらしいが、環境変数PATHが通っていればいい。 また、VScode画面下の\
c_cpp_properties.json
この時点ではincludeなどが設定されておらず、関数が認識されない。 ctrl+alt+iを押すとc_cpp_properties.jsonにインクルードなどが自動的に設定され、関数やマクロ定数が認識されるようになる。もしそのままの場合、VScode画面下の{} C++の{}にカーソルを合わせると(C++ではなく、{}に合わせるので注意)メニューが出るので構成の変更を選択しArduinoを選択すると認識されるようになる。
以上
この手順を踏めばコーディング、検証、アップロードができるはず。 今回はIDEでも最初から使えるUNOが対象だったが、arduino互換ボードの場合、別途設定が必要になると思う。 使う機会があれば、更新したい。
2023/8/24 追記 Arduino UNO R4 Minima対応
基本的には上記と一緒だが、intelliSenseの自動設定が不十分なのでc_cpp_properties.jsonのincludePathに "C:/Users/[ユーザー名]/AppData/Local/Arduino15/packages/arduino/hardware/renesas_uno/1.0.2/variants/MINIMA/includes/**" を追加し、拡張機能設定のDisable Intelli Sense Auto GenをONする。
3Dプリンターをpythonで制御してみた transitions編
前回
緒言
この記事で3Dプリンターをシリアル通信経由でPC状のpythonから制御した。
制御といっても、エクストルーダを任意の方向へ動かすモードとレベリング用に事前設定した四隅にキー入力に応じて移動するレベリングモードの2モードに応じてエクストルーダの移動命令を送信するだけである。
備え付けの操作盤が非常に使いづらかったので、エクストルーダを頻繁に動かす際には有用だった。
しかし、エクストルーダ移動モードからレベリングモードへのモード切替は可能だが、その逆は不可能だったりと少しばかり不満があったので、改良した。
主な改良は
- transitionsを使った状態遷移モデル設計
- クラス化
の2点。改良といいつつ、設計から見直している。
クラス化については、通信系をPrinterDriverクラスとしてまとめ、キーボード入力からプリンターの制御量を決定する機能をクラスにまとめる。
transitionというのはpythonで状態遷移図を扱うためのモジュールである。
状態と遷移を定め遷移を呼び出すと、遷移に対応した処理を行い、状態を切り替えることができる。
今回はそこまで複雑な状態遷移を作成するつもりはなかったが、ほかにも応用が効きそうなモジュールであったため、練習がてら今回使用する。
作成する状態遷移図
以下の状態遷移図をtransitionに実装する。
ほとんど前回の記事と一緒だが、緒言で書いた通り、LevelingModeからMoveExtへの遷移が追加されている。
トリガー名が遷移の名前で処理が遷移時に実行される処理関数である。
プログラム実行時の初期状態はEntranceであり、最初にStartStateを実行しMoveExtへ遷移する。その際に3Dプリンターとの通信のためのコムポートを開いたり、ホームポジションへの移動をしたりなどする。
以降の遷移は、キーボード入力を待ち、入力に対応する遷移を実行することを繰り返す。 Exitへの遷移時のExitProcでコムポートを閉じ、プログラムを終了する。
実装
以下が前章で示した状態遷移図を実装したコードである。
transitionsの具体的な使い方はググればわかりやすい日本語記事が出てくるのでそちらを参照してください。
簡単にはMachineクラスを持つクラス(ここではStateMachine)を用意し、コンストラクタを実行時に状態(states)を渡す。
その後にadd_transitionで遷移を定義する。その際にtriggerと遷移先元、遷移時の処理(before, after)を指定する。遷移時の処理はStateMachineのメソッド関数の名前を渡す。
遷移をするときは、trigger([トリガー名])で遷移できる。
from transitions import Machine from msvcrt import getch #key input import time import serial #serial from itertools import cycle class PrinterDriver: debug = True corner=cycle([ [20, 20], [270, 20], [270, 270], [20, 270] ]) # serial port COM = 'COM7' #gcode checktemp = b"M105" autohome = b"G28" moveX = b"G0 X " moveY = b"G0 Y " moveZ = b"G0 Z " setAbsolute = b"G90" setRelative = b"G91" comport = None def __init__(self, commpoart): self.COM = commpoart def openComport(self): if self.debug == False: self.comport = serial.Serial(self.COM, baudrate=115200, parity=serial.PARITY_NONE, timeout=0.1) #相手の再起動待ち time.sleep(5) #念のためバッファクリア self.comport.reset_input_buffer() def moveExtruder(self, dim, val): comm = str('') if dim == 'X': comm = self.moveX+str(val).encode() pass elif dim == 'Y': comm = self.moveY+str(val).encode() pass elif dim == 'Z': comm = self.moveZ+str(val).encode() pass else: pass self.sendcommand_waitless(comm) def sendcommand_waitless(self, command): if self.debug == False: self.comport.write(command)#commandを送る。commandはバイナリ文字列 self.comport.write(b"\n") print("send: "+command.decode()) time.sleep(0.5) #print("return: "+comport.readline().decode())#返答表示 return self.comport.readline().decode() else: print("send: "+command.decode()) return "recived message" #gcodeを送り、返信を表示 #okが帰るまでwait秒待つ。 #wait秒以内に戻らなければ、-1、そうでなければ、0を返す。 def sendcommand_wait(self, command, wait): echo=0 count=0 if self.debug == False: self.comport.write(command) self.comport.write(b"\n") print("send: "+command.decode()) while not ( (echo == b"ok\n") or (count >wait)): echo=self.comport.read(3) print("wait "+command.decode()+" "+str(count)+"/"+str(wait)) time.sleep(1) count=count+1 else: if echo == b"ok\n": print("finished") return 0 else: print("troubled") return -1 else: print("send: "+command.decode()) while not count >wait: print("wait "+command.decode()+" "+str(count)+"/"+str(wait)) time.sleep(1) count=count+1 print("finished") return 0 def closeComport(self): if self.debug == False: self.comport.close() pass class StateMachine(object): # 状態遷移を管理するMachineクラスのインスタンス machine=None # PrinterDriverクラスのインスタンス Printer = None #状態の定義 states = ['Entrance', 'MoveExt', 'Leveling', 'Exit'] # キー入力バッファ(長押し対応予定) keyBuffer = 0 """ keyInput: キー入力に対する処理を記載する辞書 トリガーをaddする際に追加する。 { [ステート名]:{ [キー番号]:{ 'trigger':[トリガー名], 'Informaiton':[表示用の説明文] },... },... } """ keyInput = {} # コンストラクタ def __init__(self, name): # 名前(使わないと思う) self.name = name # machineのコンストラクト self.machine = Machine(model=self, states=self.states, initial='Entrance', auto_transitions=False) # 初期処理実行用遷移の追加 self.machine.add_transition(trigger='StartState', source='Entrance', dest='MoveExt', before= 'InitProc', after= ['enterMoveExt', 'PrintKeyInput']) # keyInput辞書の初期化 for st in self.states: self.keyInput[st] = {} # 各モードでの遷移の追加 self.setMoveExtTrans() self.setLevelingTrans() # 初期処理 def InitProc(self): print('3DPrinter Initting ...') # プリンタードライバーの実体化 self.Printer = PrinterDriver('COM7') # ポートオープン self.Printer.openComport() # ( 本当はエラー処理をするべき 例;コムポートがない ) #print(self.state) #ホームポジションに移動 self.Printer.sendcommand_wait(self.Printer.autohome,10) # エクストルーダ移動モードの遷移の追加 def setMoveExtTrans(self): # ステート名の定義 state = 'MoveExt' # 終了処理への遷移 Exit self.machine.add_transition(trigger='Exit', source=state, dest='Exit', before= 'ExitProc') self.keyInput[state][27]={'trigger':'Exit', 'Information':'Exit Process'} # レベリングモードへの遷移 switchMode self.machine.add_transition(trigger='switchMode', source=state, dest='Leveling', before= 'SwitchToLevel', after= 'PrintKeyInput') self.keyInput[state][ord('b')]={'trigger':'switchMode', 'Information':'change to Leveling Mode'} # キー入力処理用遷移 MoveProcess self.machine.add_transition(trigger='MoveProcess', source=state, dest=state, before= 'MoveProc') self.keyInput[state][ord('a')]={'trigger':'MoveProcess', 'Information':'Move to X <'} # X < self.keyInput[state][ord('d')]={'trigger':'MoveProcess', 'Information':'Move to X >'} # X > self.keyInput[state][ord('w')]={'trigger':'MoveProcess', 'Information':'Move to Y ^'} # Y ^ self.keyInput[state][ord('s')]={'trigger':'MoveProcess', 'Information':'Move to Y v'} # Y v self.keyInput[state][ord('q')]={'trigger':'MoveProcess', 'Information':'Move to Z ^'} # Z ^ self.keyInput[state][ord('e')]={'trigger':'MoveProcess', 'Information':'Move to Z v'} # Z v self.keyInput[state][ord('h')]={'trigger':'MoveProcess', 'Information':'Move to home position'} # home position # エクストルーダ移動モードの初回処理 def enterMoveExt(self): self.Printer.sendcommand_waitless(self.Printer.setRelative) print('set relateve') pass # エクストルーダ移動モードから抜ける時の処理 def escapeMoveExt(self): self.Printer.sendcommand_waitless(self.Printer.setAbsolute) print('set absolute') pass # 移動時処理 def MoveProc(self): key = self.keyBuffer if key == 97:#a self.Printer.moveExtruder('X', -5) #sendcommand_waitless(moveX+b"-5") elif key ==100:#d -> self.Printer.moveExtruder('X', 5) #sendcommand_waitless(moveX+b"5") elif key ==119:#w self.Printer.moveExtruder('Y', 5) #sendcommand_waitless(moveY+b"5") elif key ==115:#s self.Printer.moveExtruder('Y', -5) #sendcommand_waitless(moveY+b"-5") elif key ==113:#q self.Printer.moveExtruder('Z', 5) #sendcommand_waitless(moveZ+b"5") elif key ==101:#e self.Printer.moveExtruder('Z', -5) #sendcommand_waitless(moveZ+b"-5") elif key ==ord('h'):#h self.Printer.sendcommand_wait(self.Printer.autohome,10) # レベリングモードへの切り替え def SwitchToLevel(self): self.escapeMoveExt() print('Switch to Leveling Mode') self.enterLevelingMode() # レベリングモードの遷移の設定 def setLevelingTrans(self): state = 'Leveling' self.machine.add_transition(trigger='Exit', source=state, dest='Exit', before= 'ExitProc') self.keyInput[state][27]={'trigger':'Exit', 'Information':'Exit Process'} self.machine.add_transition(trigger='switchMode', source=state, dest='MoveExt', before= 'SwitchToMoveExt', after= 'PrintKeyInput') self.keyInput[state][ord('b')]={'trigger':'switchMode', 'Information':'change to Move Extruder Mode'} self.machine.add_transition(trigger='TGoToCorner', source=state, dest=state, before= 'GoToCorner') self.keyInput[state][32]={'trigger':'TGoToCorner', 'Information':'Go to Next Leveling Corner'} # レベリングモードの初期処理 def enterLevelingMode(self): self.Printer.sendcommand_waitless(self.Printer.setAbsolute) print('set relateve') pass # レベリングモードの終了時処理 def escapeLevelingMode(self): #self.Printer.sendcommand_waitless(self.Printer.setRelative) #print('set absolute') pass # 角位置の遷移 def GoToCorner(self): if self.keyBuffer == ord(' '): cr = next(self.Printer.corner) print('Go to X '+str(cr[0])+' Y '+str(cr[1])) self.Printer.moveExtruder('Z', 5) self.Printer.moveExtruder('X', cr[0]) self.Printer.moveExtruder('Y', cr[1]) self.Printer.moveExtruder('Z', 0) pass pass # 移動モードの遷移 def SwitchToMoveExt(self): print('Swtich to Exetoruder Move Mode') self.escapeLevelingMode() self.enterMoveExt() # キー入力表示 def PrintKeyInput(self): print('') print('Now state : '+self.state) for k, v in self.keyInput[self.state].items(): if k == 27: key = 'Esc' elif k == 32: key = 'space' else: key = str(chr(k)) print('key '+key+' : '+v['Information']) pass # 終了時処理 def ExitProc(self): print('Exit Prog') self.Printer.closeComport() exit() # メイン処理 def mainProc(self): self.keyBuffer = ord(getch()) #print(key) dict=self.keyInput[self.state].get(self.keyBuffer) #print(trig) if dict != None: #print(dict['trigger']) self.trigger(dict['trigger']) else: print('key input '+str(self.keyBuffer)+' is invalid') # 状態遷移図の実体化 STM = StateMachine('buzz') # 初期処理の実行 STM.trigger('StartState') while True: STM.mainProc()
プログラムの流れとしては、まず、コード下方でStateMachineクラスのインスタンスを生成する。 StateMachineのコンストラクタではMachineクラスのインスタンス生成と各遷移の定義を行う。
# StateMachineコンストラクタ def __init__(self, name): # 名前(使わないと思う) self.name = name # machineのコンストラクト self.machine = Machine(model=self, states=self.states, initial='Entrance', auto_transitions=False) # 初期処理実行用遷移の追加 self.machine.add_transition(trigger='StartState', source='Entrance', dest='MoveExt', before= 'InitProc', after= ['enterMoveExt', 'PrintKeyInput']) # keyInput辞書の初期化 for st in self.states: self.keyInput[st] = {} # 各モードでの遷移の追加 self.setMoveExtTrans() self.setLevelingTrans()
setMoveExtTrans関数では遷移の定義と、キー入力に対する応答をまとめるkeyInput辞書の定義を行う。
# エクストルーダ移動モードの遷移の追加 def setMoveExtTrans(self): # ステート名の定義 state = 'MoveExt' # 終了処理への遷移 Exit self.machine.add_transition(trigger='Exit', source=state, dest='Exit', before= 'ExitProc') self.keyInput[state][27]={'trigger':'Exit', 'Information':'Exit Process'} # レベリングモードへの遷移 switchMode self.machine.add_transition(trigger='switchMode', source=state, dest='Leveling', before= 'SwitchToLevel', after= 'PrintKeyInput') self.keyInput[state][ord('b')]={'trigger':'switchMode', 'Information':'change to Leveling Mode'} # キー入力処理用遷移 MoveProcess self.machine.add_transition(trigger='MoveProcess', source=state, dest=state, before= 'MoveProc') self.keyInput[state][ord('a')]={'trigger':'MoveProcess', 'Information':'Move to X <'} # X < self.keyInput[state][ord('d')]={'trigger':'MoveProcess', 'Information':'Move to X >'} # X > self.keyInput[state][ord('w')]={'trigger':'MoveProcess', 'Information':'Move to Y ^'} # Y ^ self.keyInput[state][ord('s')]={'trigger':'MoveProcess', 'Information':'Move to Y v'} # Y v self.keyInput[state][ord('q')]={'trigger':'MoveProcess', 'Information':'Move to Z ^'} # Z ^ self.keyInput[state][ord('e')]={'trigger':'MoveProcess', 'Information':'Move to Z v'} # Z v self.keyInput[state][ord('h')]={'trigger':'MoveProcess', 'Information':'Move to home position'} # home position
遷移の追加はadd_transition関数で行う。
keyInput辞書は以下のような構造になっている。入力を受けるとキー入力からトリガー名を調べ、trigger関数で遷移を呼び出すことで、キー入力を介して遷移を実行する。
操作説明用の表示文はInformationに記載する。
{
[ステート名]:{
[キー番号]:{
'trigger':[トリガー名],
'Informaiton':[表示用の説明文]
},...
},...
}
基本的に1つのキー入力に対し1つの遷移が対応するので、遷移を1つ定義したときに同時に追加するようにしている。
移動モードは複数キー入力それぞれに遷移を作ると面倒だったので、呼び出される処理関数で改めてキーを判別し移動方向を決定する。
レベリングモードも同様にして遷移を追加する。
遷移を追加し終わったら、StartStateの遷移し、初期処理を実行する。
その後、無限ループ内で、mainProcを実行する。
# メイン処理 def mainProc(self): self.keyBuffer = ord(getch()) #print(key) dict=self.keyInput[self.state].get(self.keyBuffer) #print(trig) if dict != None: #print(dict['trigger']) self.trigger(dict['trigger']) else: print('key input '+chr(self.keyBuffer)+' is invalid')
mainProcではgetchでキー入力を取得し、keyInputから入力に対応するトリガーで遷移する。
まとめ
上のコードで期待通りのプログラムを実装できた。 transitionを使うことで状態遷移が簡単に記述できた。 add_transitionとkeyInputを追加することで、新しい機能を追加できるので、拡張しやすいと思う。 贅沢をいうと、状態ごとにクラスを定義して、そのメソッド関数を呼び出すようにしたかったが、少し面倒だったのであきらめた。
3次元曲線の離散化について
ちょげん
曲線を離散化する際によく使われる方法は折れ線で近似する方法である.
もっと良さげな方法を思いついたのでメモする
0. フレネの公式
離散化はフレネの公式をベースにするためその簡単な解説から始める. フレネの公式は曲線に関する3つのベクトルの変化についての公式である. 3つのベクトルの1つ目は接線ベクトルである.接線と同じ方向で大きさが1となるベクトルである.2つ目は主法線ベクトルである.これはの変化方向を向き大きさが1となるベクトルである.3つ目は従法線ベクトルであり,これは,の両者に直交し大きさ1のベクトルである.この3つのベクトルは基本的に直交しており(直交を説明するにはより厳密な定義が必要),少なくとも独立している.フレネの公式は曲線上の各点で
が成り立つ法則である.ここでダッシュ記号はについての微分を示し,はそれぞれtについての実数値関数で速度,曲率,捩じれ率と呼ばれる.
1.曲線のテイラー展開
曲線がで定義されており,この区間を速度,曲率,捩じれ率がそれぞれ一定と見なせる程細かく区分する.その区間の幅をとする.
この時,曲線を周りにテイラー展開すると,
となる.ここではのk回微分係数であり,これを求めることでが求まる.その導出にフレネの公式を用いる.
2.の導出
は大きさがで向きがのベクトルなので
となる.検討している区間ではがそれぞれ一定なので
である.はフレネの公式より
となる.同様にして
となる.これをそれぞれ代入して
となる.Nの係数は
とでき,として,
である.の係数は
である.まとめると,
が得られる.
3.でのフレーム
の計算では媒介係数でのフレームが既知である前提で議論した.実際には求めたをに代入しを求め,これを繰り返すことで目的の曲線を得る.そのためには媒介係数の段階でにおけるフレームが得られなければならない.本節ではそれを求める.
前節では以下の式を得た.
これは媒介係数をとした曲線と見ることができる.またの係数を取り出し,
は座標軸をとした座標系における曲線と見ることができる.
はを通り,接線方向を向き,大きさが1のベクトルである.
接線方向は
である.大きさは
よりである.これよりが前提である「領域内での速度はで一定」と矛盾しない事が確認された.またを大きさ1にするため,をで割って,は
である.はの変化方向で大きさが1のベクトルであるため,の微分は
である.大きさは
よりであり,として,
である. ちなみに大きさは
と確かに1である.また,との内積は
より直交している事が確認できる. はとの両方に直交しているベクトルなので外積を使い,
である.ちなみに大きさは
より確かに1である.
4.まとめ
以上の議論をまとめると,は
であり,は,
である.
これを曲線を求める計算に利用する事を考える.そのためにフレームの式の係数をまとめて,
とする.ちなみにこれは特殊直交行列となる.これよりフレームの式は
となる.ここでフレームをまとめてとした.また,
とすると,は
と表現できる. 区間をそれぞれ速度,曲率,捩じれ率がそれぞれ一定と見なせるような領域になるように媒介係数を個に等分割する.1つの領域の幅は
である.ここでである.領域の境界点は
である.この時微小領域は
内 と表記できる.内でのパラメータを,,で与えられるとする.また,初期位置,初期フレームは与えられるとする. i番目境界点でのフレームは
と書ける. また曲線は
となる.これをi=1からnまで繰り返し計算することで離散化された曲線を得られる.
謝辞
このブログページを作製するにあたり,自前の環境での数式からはてなブログの数式へ変換するためano3様のこちらのツールを使用させて頂きました.非常に有用なツールをありがとうございます.
参考文献
微分幾何の勉強はこれでしました.
Neill, Barrett. Elementary differential geometry. Burlington, MA San Diego, CA London: Academic Press, an imprint of Elsevier, 2006. Print.
https://www.amazon.co.jp/Elementary-Differential-Geometry-Revised-Second/dp/0120887355
英語で長く比較的丁寧に書いているのでフレネの公式だけについて知りたい方はwikipedia等のページを参照することをお勧めします.
一方で微分幾何について応用するのに十分に理解できるので時間がある方には一読をお勧めします.
非情報系ガチ勢のためのFusion360のScriptメモ
ちょげん
無料で高機能な3DCADであるFusion360はpython,C++,JavaScript1でAPI(Script,Add-on)が提供されています.
本ページではそのpythonでのScriptを書く上で調べたことの備忘録を残しておきます.随時更新予定です.参考にしてください.
またより簡単な方法があれば教えていただければ幸いです.
(筆者は非情報系ガチ勢ではないので素っ頓狂で当たり前な事を言うかもしれません.ご了承ください.また手探りで勉強しながら書いているので詳細な解説はありません.)
公式
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-1C665B4D-7BF7-4FDF-98B0-AA7EE12B5AC2
このサイトの左メニューの
Programming Interface/Fusion 360 API Reference Manual/Objects
に正確な情報がある.
下準備
Scriptを書き始めるまでの手順については公式情報等を参考にしてください.特に詰まる事は無いと思います. ここではそれ以外の事を書きます.
テキストコマンド
scriptのコマンドを実行できそうなターミナル. エラーコードはここに吐かれるので開いておくのが吉. Fusion360画面右上,ファイル→表示→テキストコマンドを表示.
hello world
テキスト「hello world」を表示するだけのスクリプト.
import adsk.core, adsk.fusion, traceback def run(context): ui = None#いらないっぽい try: app = adsk.core.Application.get() ui = app.userInterface product = app.activeProduct design = adsk.fusion.Design.cast(product) title = 'hello Fusion360 Script world' if not design: ui.messageBox('No active Fusion design', title) return #処理 #例 メッセージボックスの表示(OK押すと処理が進む) ui.messageBox('!hello world!', title) except: if ui: ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
Fusion360のモジュールはadsk.coreとadsk.fusionの2つ(tracebackはエラー処理系?)らしいのでインポート. run関数がc言語で言うところのmain関数にあたる関数.無ければ「SCRIPT ERROR」が出て動かないので注意. 基本的な初動は
app = adsk.core.Application.get() ui = app.userInterface product = app.activeProduct design = adsk.fusion.Design.cast(product)
の4文を実行する.ここでできるdesignオブジェクトの中にコンポーネントやフィーチャ等の機能が入っている. uiにはメッセージボックスやダイアログなどのユーザーインターフェースの機能が入っている. titleはメッセージボックス用の文字列なので基本必要なし.
if not design: ui.messageBox('No active Fusion design', title) return
は何を目的としているのか分からない.サンプルコードでもよく見るのでとりあえずおまじない.
ui.messageBox('!hello world!', title)
はメッセージボックスを表示する関数.ウインドウが出てきてokボタンを押すと処理が進む. 引数titleは無くてもいい. 基本的にはこの当たりにやってほしい処理を書いていく事でスクリプトを実装していく. 例外処理(try,except)でエラーコードをメッセージボックスに表示するようにしてある.テキストコマンドを表示しているなら無くてもいいと思う.
自作モジュール(複数ファイル利用)
スクリプトの保存場所と実行場所が違うのでパスを通す必要がある. パスさえ通ればどんな手段でもいいが,一例としては
import sys sys.path.append(r"モジュールファイルが保存されているディレクトリパス") import mymodule
が楽.これで追加されたパスはFusion360が起動している間保存され,一度落とすとパスは消えるので注意. (逆にいえば必要な時だけパスが追加されるので適当にモジュールを作ってパスがごちゃごちゃするという事はない.)
コンポーネントの作成
#ルートコンポーネントのオカランスを取得 allOccs = design.activeComponent.occurrences #コンポーネントを作成 newOcc = allOccs.addNewComponent(adsk.core.Matrix3D.create()) #コンポーネントの参照を取得 newComp=newOcc.component #コンポーネントに名前を付ける newComp.name='NewComponent'
design.activeComponentは現在選択しているcomponetオブジェクト.これによりactiveComponent内に新しいコンポーネントが生成される.
スケッチ+ソリッド押出し作成
#ルートコンポーネントのオブジェクトを取得 root = design.rootComponent #xy平面のスケッチを作成 sketch = root.sketches.add(root.xYConstructionPlane) #線分を開始 sketchlines = sketch.sketchCurves.sketchLines #点を生成 p1 = adsk.core.Point3D.create(0, 0, 0) p2 = adsk.core.Point3D.create(1, 0, 0) p3 = adsk.core.Point3D.create(1, 1, 0) p4 = adsk.core.Point3D.create(0, 1, 0) #線分を作成 sketchlines.addByTwoPoints(p1, p2) sketchlines.addByTwoPoints(p2, p3) sketchlines.addByTwoPoints(p3, p4) sketchlines.addByTwoPoints(p4, p1) #プロファイルを取得 prof1 = sketch.profiles.item(0) #押出しinputを生成 extrudes1 = root.features.extrudeFeatures extInput1 = extrudes1.createInput(prof1, adsk.fusion.FeatureOperations.NewBodyFeatureOperation) #distanceオブジェクトを生成 distance1 = adsk.core.ValueInput.createByReal(2.0) #distanceオブジェクトをInputにセット extInput1.setDistanceExtent(False, distance1) #ソリッドフラグをinputにセット extInput1.isSolid = True #押出しを実行 ext1 = extrudes1.add(extInput1)
デフォルトだとcm単位なので注意.コード中では長さ1の線をcreateしているが,これは長さ1cmの線として読まれ,表示上は10mmの線として表示される.
プロファイルとはスケッチで作られた領域?
フィーチャーはその設定をinputというオブジェクトにまとめ,addメソッドに渡すことで実行される.
-
JavaScriptは2020年12月の更新でサポートされなくなりました.↩
ジャンクlatex数式コード置き場
別件でまとめてたのが没ったので供養.
自由に使ってもらってかまいませんが自己責任でお願いします.
ジャンクなので動作確認(検算)してません.
可読性のためにコード中に=だけの行をおいてますが,環境によってはここでバグる可能性があります.適宜改行を消して下さい.
ジャンクlatex数式コード置き場
別件でまとめてたのが没ったので供養. 自由に使ってもらってかまいませんが自己責任でお願いします. ジャンクなので動作確認(検算)してません. 可読性のためにコード中に=だけの行をおいてますが,環境によってはここでバグる可能性があります.適宜改行を消して下さい.
回転行列
x軸回転
$
R_x(\theta_x)=
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)
$
R_x(\theta_x)= \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right)
y軸回転
$R_y(\theta_y)=
\left(\begin{matrix}
C\theta_y & & S\theta_y \\
& 1 & \\
-S\theta_y & & C\theta_y
\end{matrix}\right)$
R_y(\theta_y)= \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right)
z軸回転
$R_z(\theta_z)=
\left(\begin{matrix}
C\theta_z & -S\theta_z & \\
S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)$
R_z(\theta_z)= \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right)
${R'}^{T}R$
$\theta_i=\theta_i(s)$,$s$での微分を'で表記する.
${R}^{T}$は行列$R$の転置を示す.
${R'}_x^{{T}}R_x=$$\theta_x'
\left(\begin{matrix}
0 & & \\
& -S\theta_x & C\theta_x \\
& -C\theta_x & -S\theta_x
\end{matrix}\right)
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)
$$=\theta_x'\left(
\begin{matrix}
0 & & \\
& 0 & 1 \\
& -1 & 0
\end{matrix}
\right)
$
{R'}_x^{T}R_x= \theta_x' \left(\begin{matrix} 0 & & \\\\ & -S\theta_x & C\theta_x \\\\ & -C\theta_x & -S\theta_x \end{matrix}\right) \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right) = \theta_x'\left( \begin{matrix} 0 & & \\\\ & 0 & 1 \\\\ & -1 & 0 \end{matrix} \right)
${R'}_y^{T}R_y= \theta_y' \left(\begin{matrix} -S\theta_y & & -C\theta_y \\ & 0 & \\ C\theta_y & & -S\theta_y \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\ & 1 & \\ -S\theta_y & & C\theta_y \end{matrix}\right)= \theta_y' \left(\begin{matrix} 0 & & -1 \\ & 0 & \\ 1 & & 0 \end{matrix}\right)$
{R'}_y^{T}R_y= \theta_y' \left(\begin{matrix} -S\theta_y & & -C\theta_y \\\\ & 0 & \\\\ C\theta_y & & -S\theta_y \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right) = \theta_y' \left(\begin{matrix} 0 & & -1 \\\\ & 0 & \\\\ 1 & & 0 \end{matrix}\right)
${R'}_z^{T}R_z= \theta_z' \left(\begin{matrix} -S\theta_z & C\theta_z & \\ -C\theta_z & -S\theta_z & \\ & & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\ S\theta_z & C\theta_z & \\ & & 1 \end{matrix}\right) =\theta_z' \left(\begin{matrix} 0 & 1 & \\ -1 & 0 & \\ & & 0 \end{matrix}\right)$
{R'}_z^{T}R_z= \theta_z' \left(\begin{matrix} -S\theta_z & C\theta_z & \\\\ -C\theta_z & -S\theta_z & \\\\ & & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) = \theta_z' \left(\begin{matrix} 0 & 1 & \\\\ -1 & 0 & \\\\ & & 0 \end{matrix}\right)
$R_2^{T}{R_1'}^{T}R_1{R_2}$
1=x,2=y
$R_y^{T}{R_x'}^{T}R_x{R_y}=
\theta_x'
\left(\begin{matrix}
C\theta_y & & -S\theta_y \\
& 1 & \\
S\theta_y & & C\theta_y
\end{matrix}\right)
\left(\begin{matrix}
0 & & \\
& 0 & 1 \\
& -1 & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_y & & S\theta_y \\
& 1 & \\
-S\theta_y & & C\theta_y
\end{matrix}\right)
$
$R_y^{T}{R_x'}^{T}R_x{R_y}=
\theta_x'
\left(\begin{matrix}
0 & S\theta_y & \\
& 0 & 1 \\
& -C\theta_y & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_y & & S\theta_y \\
& 1 & \\
-S\theta_y & & C\theta_y
\end{matrix}\right)
=\theta_x'
\left(\begin{matrix}
0 & S\theta_y & \\
-S\theta_y & 0 & C\theta_y \\
& -C\theta_y & 0
\end{matrix}\right)$
R_y^{T}{R_x'}^{T}R_x{R_y}= \theta_x' \left(\begin{matrix} C\theta_y & & -S\theta_y \\\\ & 1 & \\\\ S\theta_y & & C\theta_y \end{matrix}\right) \left(\begin{matrix} 0 & & \\\\ & 0 & 1 \\\\ & -1 & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right) = \theta_x' \left(\begin{matrix} 0 & S\theta_y & \\\\ & 0 & 1 \\\\ & -C\theta_y & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right) = \theta_x' \left(\begin{matrix} 0 & S\theta_y & \\\\ -S\theta_y & 0 & C\theta_y \\\\ & -C\theta_y & 0 \end{matrix}\right)
1=x,2=z
$R_z^{T}{R_x'}^{T}R_x{R_z}=
\theta_x'
\left(\begin{matrix}
C\theta_z & S\theta_z & \\
-S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
\left(\begin{matrix}
0 & & \\
& 0 & 1 \\
& -1 & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_z & -S\theta_z & \\
S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
$
$
R_z^{T}{R_x'}^{T}R_x{R_z}=
\theta_x'
\left(\begin{matrix}
0 & & S\theta_z \\
& 0 & C\theta_z \\
& -1 & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_z & -S\theta_z & \\
S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
=\theta_x'\left(\begin{matrix}
0 & & S\theta_z \\
0 & 0 & C\theta_z \\
-S\theta_z & -C\theta_z & 0
\end{matrix}\right)
$
R_z^{T}{R_x'}^{T}R_x{R_z}= \theta_x' \left(\begin{matrix} C\theta_z & S\theta_z & \\\\ -S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) \left(\begin{matrix} 0 & & \\\\ & 0 & 1 \\\\ & -1 & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) = \theta_x' \left(\begin{matrix} 0 & & S\theta_z \\\\ & 0 & C\theta_z \\\\ & -1 & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) =\theta_x'\left(\begin{matrix} 0 & & S\theta_z \\\\ 0 & 0 & C\theta_z \\\\ -S\theta_z & -C\theta_z & 0 \end{matrix}\right)
1=y,2=x
$R_x^{T}{R_y'}^{T}R_y{R_x}=
\theta_y'
\left(\begin{matrix}
1 & & \\
& C\theta_x & S\theta_x \\
& -S\theta_x & C\theta_x
\end{matrix}\right)
\left(\begin{matrix}
0 & & -1 \\
& 0 & \\
1 & & 0
\end{matrix}\right)
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)$
$
R_x^{T}{R_y'}^{T}R_y{R_x}=
\theta_y'
\left(\begin{matrix}
0 & & -1 \\
S\theta_x & 0 & \\
C\theta_x & & 0
\end{matrix}\right)
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)
=\theta_y'
\left(\begin{matrix}
0 & -S\theta_x & -C\theta_x \\
S\theta_x & 0 & \\
C\theta_x & & 0
\end{matrix}\right)$
R_x^{T}{R_y'}^{T}R_y{R_x}= \theta_y' \left(\begin{matrix} 1 & & \\\\ & C\theta_x & S\theta_x \\\\ & -S\theta_x & C\theta_x \end{matrix}\right) \left(\begin{matrix} 0 & & -1 \\\\ & 0 & \\\\ 1 & & 0 \end{matrix}\right) \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right) = \theta_y' \left(\begin{matrix} 0 & & -1 \\\\ S\theta_x & 0 & \\\\ C\theta_x & & 0 \end{matrix}\right) \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right) = \theta_y' \left(\begin{matrix} 0 & -S\theta_x & -C\theta_x \\\\ S\theta_x & 0 & \\\\ C\theta_x & & 0 \end{matrix}\right)
1=y,2=z
$R_z^{T}{R_y'}^{T}R_y{R_z}=
\theta_y'
\left(\begin{matrix}
C\theta_z & S\theta_z & \\
-S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
\left(\begin{matrix}
0 & & -1 \\
& 0 & \\
1 & & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_z & -S\theta_z & \\
S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
$
$R_z^{T}{R_y'}^{T}R_y{R_z}=
\theta_y'
\left(\begin{matrix}
0 & & -C\theta_z \\
& 0 & S\theta_z \\
1 & & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_z & -S\theta_z & \\
S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
=\theta_y'
\left(\begin{matrix}
0 & & -C\theta_z \\
& 0 & S\theta_z \\
C\theta_z & -S\theta_z & 0
\end{matrix}\right)$
R_z^{T}{R_y'}^{T}R_y{R_z}= \theta_y' \left(\begin{matrix} C\theta_z & S\theta_z & \\\\ -S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) \left(\begin{matrix} 0 & & -1 \\\\ & 0 & \\\\ 1 & & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) = \theta_y' \left(\begin{matrix} 0 & & -C\theta_z \\\\ & 0 & S\theta_z \\\\ 1 & & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) = \theta_y' \left(\begin{matrix} 0 & & -C\theta_z \\\\ & 0 & S\theta_z \\\\ C\theta_z & -S\theta_z & 0 \end{matrix}\right)
1=z,2=x
$R_x^{T}{R_z'}^{T}R_z{R_x}=
\theta_z'
\left(\begin{matrix}
1 & & \\
& C\theta_x & S\theta_x \\
& -S\theta_x & C\theta_x
\end{matrix}\right)
\left(\begin{matrix}
0 & 1 & \\
-1 & 0 & \\
& & 0
\end{matrix}\right)
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)$
$
R_x^{T}{R_z'}^{T}R_z{R_x}=
\theta_z'
\left(\begin{matrix}
0 & 1 & \\
-C\theta_x & 0 & \\
S\theta_x & & 0
\end{matrix}\right)
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)
=\theta_z'
\left(\begin{matrix}
0 & C\theta_x & -S\theta_x \\
-C\theta_x & 0 & \\
S\theta_x & & 0
\end{matrix}\right)$
R_x^{T}{R_z'}^{T}R_z{R_x}= \theta_z' \left(\begin{matrix} 1 & & \\\\ & C\theta_x & S\theta_x \\\\ & -S\theta_x & C\theta_x \end{matrix}\right) \left(\begin{matrix} 0 & 1 & \\\\ -1 & 0 & \\\\ & & 0 \end{matrix}\right) \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right) = \theta_z' \left(\begin{matrix} 0 & 1 & \\\\ -C\theta_x & 0 & \\\\ S\theta_x & & 0 \end{matrix}\right) \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right) =\theta_z' \left(\begin{matrix} 0 & C\theta_x & -S\theta_x \\\\ -C\theta_x & 0 & \\\\ S\theta_x & & 0 \end{matrix}\right)
1=z,2=y
$R_y^{T}{R_z'}^{T}R_z{R_y}=
\theta_z'
\left(\begin{matrix}
C\theta_y & & -S\theta_y \\
& 1 & \\
S\theta_y & & C\theta_y
\end{matrix}\right)
\left(\begin{matrix}
0 & 1 & \\
-1 & 0 & \\
& & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_y & & S\theta_y \\
& 1 & \\
-S\theta_y & & C\theta_y
\end{matrix}\right)
$
$R_y^{T}{R_x'}^{T}R_x{R_y}=
\theta_z'
\left(\begin{matrix}
0 & C\theta_y & \\
-1 & 0 & \\
& S\theta_y & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_y & & S\theta_y \\
& 1 & \\
-S\theta_y & & C\theta_y
\end{matrix}\right)
=\theta_z'
\left(\begin{matrix}
0 & C\theta_y & \\
-C\theta_y & 0 & -S\theta_y \\
& S\theta_y & 0
\end{matrix}\right)$
R_y^{T}{R_z'}^{T}R_z{R_y}= \theta_z' \left(\begin{matrix} C\theta_y & & -S\theta_y \\\\ & 1 & \\\\ S\theta_y & & C\theta_y \end{matrix}\right) \left(\begin{matrix} 0 & 1 & \\\\ -1 & 0 & \\\\ & & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right) = \theta_z' \left(\begin{matrix} 0 & C\theta_y & \\\\ -1 & 0 & \\\\ & S\theta_y & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right) = \theta_z' \left(\begin{matrix} 0 & C\theta_y & \\\\ -C\theta_y & 0 & -S\theta_y \\\\ & S\theta_y & 0 \end{matrix}\right)
$R_3^{T}R_2^{T}{R_1'}^{T}R_1{R_2}R_3$
1=x,2=y,3=z
$R_z^{T}R_y^{T}{R_x'}^{T}R_x{R_y}R_z=
\theta_x'
\left(\begin{matrix}
C\theta_z & S\theta_z & \\
-S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
\left(\begin{matrix}
0 & S\theta_y & \\
-S\theta_y & 0 & C\theta_y \\
& -C\theta_y & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_z & -S\theta_z & \\
S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
$
$R_y^{T}{R_x'}^{T}R_x{R_y}=
\theta_x'
\left(\begin{matrix}
-S\theta_yS\theta_z & S\theta_yC\theta_z & C\theta_yS\theta_z \\
-S\theta_yC\theta_z & -S\theta_yS\theta_z & C\theta_yC\theta_z \\
& -C\theta_y & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_z & -S\theta_z & \\
S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
=\theta_x'
\left(\begin{matrix}
0 & S\theta_y & C\theta_yS\theta_z \\
-S\theta_y & 0 & C\theta_yC\theta_z \\
-C\theta_yS\theta_z & -C\theta_yC\theta_z & 0
\end{matrix}\right)$
R_z^{T}R_y^{T}{R_x'}^{T}R_x{R_y}R_z= \theta_x' \left(\begin{matrix} C\theta_z & S\theta_z & \\\\ -S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) \left(\begin{matrix} 0 & S\theta_y & \\\\ -S\theta_y & 0 & C\theta_y \\\\ & -C\theta_y & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) = \theta_x' \left(\begin{matrix} -S\theta_yS\theta_z & S\theta_yC\theta_z & C\theta_yS\theta_z \\\\ -S\theta_yC\theta_z & -S\theta_yS\theta_z & C\theta_yC\theta_z \\\\ & -C\theta_y & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) = \theta_x' \left(\begin{matrix} 0 & S\theta_y & C\theta_yS\theta_z \\\\ -S\theta_y & 0 & C\theta_yC\theta_z \\\\ -C\theta_yS\theta_z & -C\theta_yC\theta_z & 0 \end{matrix}\right)
1=x,2=z,3=y
$R_y^{T}R_z^{T}{R_x'}^{T}R_x{R_z}R_y=
\theta_x'
\left(\begin{matrix}
C\theta_y & & -S\theta_y \\
& 1 & \\
S\theta_y & & C\theta_y
\end{matrix}\right)
\left(\begin{matrix}
0 & & S\theta_z \\
0 & 0 & C\theta_z \\
-S\theta_z & -C\theta_z & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_y & & S\theta_y \\
& 1 & \\
-S\theta_y & & C\theta_y
\end{matrix}\right)
$
$
R_z^{T}{R_x'}^{T}R_x{R_z}=
\theta_x'
\left(\begin{matrix}
S\theta_yS\theta_z & S\theta_yC\theta_z & C\theta_yS\theta_z \\
& 0 & C\theta_z \\
-C\theta_yS\theta_z & -C\theta_yC\theta_z & S\theta_yS\theta_z
\end{matrix}\right)
\left(\begin{matrix}
C\theta_y & & S\theta_y \\
& 1 & \\
-S\theta_y & & C\theta_y
\end{matrix}\right)
=\theta_x'
\left(\begin{matrix}
0 & S\theta_yC\theta_z & S\theta_z \\
-S\theta_yC\theta_z & 0 & C\theta_yC\theta_z \\
-S\theta_z & -C\theta_yC\theta_z & 0
\end{matrix}\right)
$
R_y^{T}R_z^{T}{R_x'}^{T}R_x{R_z}R_y= \theta_x' \left(\begin{matrix} C\theta_y & & -S\theta_y \\\\ & 1 & \\\\ S\theta_y & & C\theta_y \end{matrix}\right) \left(\begin{matrix} 0 & & S\theta_z \\\\ 0 & 0 & C\theta_z \\\\ -S\theta_z & -C\theta_z & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right) = \theta_x' \left(\begin{matrix} S\theta_yS\theta_z & S\theta_yC\theta_z & C\theta_yS\theta_z \\\\ & 0 & C\theta_z \\\\ -C\theta_yS\theta_z & -C\theta_yC\theta_z & S\theta_yS\theta_z \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right) = \theta_x' \left(\begin{matrix} 0 & S\theta_yC\theta_z & S\theta_z \\\\ -S\theta_yC\theta_z & 0 & C\theta_yC\theta_z \\\\ -S\theta_z & -C\theta_yC\theta_z & 0 \end{matrix}\right)
1=y,2=x,3=z
$R_z^{T}R_x^{T}{R_y'}^{T}R_y{R_x}R_z=
\theta_y'
\left(\begin{matrix}
C\theta_z & S\theta_z & \\
-S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
\left(\begin{matrix}
0 & -S\theta_x & -C\theta_x \\
S\theta_x & 0 & \\
C\theta_x & & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_z & -S\theta_z & \\
S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)$
$R_z^{T}R_x^{T}{R_y'}^{T}R_y{R_x}R_z=
\theta_y'
\left(\begin{matrix}
S\theta_xS\theta_z & -S\theta_xC\theta_z & -C\theta_xC\theta_z \\
S\theta_xC\theta_z & S\theta_xS\theta_z & C\theta_xS\theta_z\\
C\theta_x & & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_z & -S\theta_z & \\
S\theta_z & C\theta_z & \\
& & 1
\end{matrix}\right)
=\theta_y'
\left(\begin{matrix}
0 & -S\theta_x & -C\theta_xC\theta_z \\
S\theta_x & 0 & C\theta_xS\theta_z\\
C\theta_xC\theta_z & -C\theta_xS\theta_z & 0
\end{matrix}\right)
$
R_z^{T}R_x^{T}{R_y'}^{T}R_y{R_x}R_z= \theta_y' \left(\begin{matrix} C\theta_z & S\theta_z & \\\\ -S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) \left(\begin{matrix} 0 & -S\theta_x & -C\theta_x \\\\ S\theta_x & 0 & \\\\ C\theta_x & & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) = \theta_y' \left(\begin{matrix} S\theta_xS\theta_z & -S\theta_xC\theta_z & -C\theta_xC\theta_z \\\\ S\theta_xC\theta_z & S\theta_xS\theta_z & C\theta_xS\theta_z\\\\ C\theta_x & & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_z & -S\theta_z & \\\\ S\theta_z & C\theta_z & \\\\ & & 1 \end{matrix}\right) = \theta_y' \left(\begin{matrix} 0 & -S\theta_x & -C\theta_xC\theta_z \\\\ S\theta_x & 0 & C\theta_xS\theta_z\\\\ C\theta_xC\theta_z & -C\theta_xS\theta_z & 0 \end{matrix}\right)
1=y,2=z,3=x
$R_x^{T}{R_z}^{T}{R_y'}^{T}R_y{R_z}{R_x}=
\theta_y'
\left(\begin{matrix}
1 & & \\
& C\theta_x & S\theta_x \\
& -S\theta_x & C\theta_x
\end{matrix}\right)
\left(\begin{matrix}
0 & & -C\theta_z \\
& 0 & S\theta_z \\
C\theta_z & -S\theta_z & 0
\end{matrix}\right)
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)$
$
R_x^{T}{R_z}^{T}{R_y'}^{T}R_y{R_z}{R_x}=
\theta_y'
\left(\begin{matrix}
0 & & -C\theta_z \\
S\theta_xC\theta_z & -S\theta_xS\theta_z & C\theta_xS\theta_z \\
C\theta_xC\theta_z & -C\theta_xS\theta_z & -S\theta_xS\theta_z
\end{matrix}\right)
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)
=\theta_y'
\left(\begin{matrix}
0 & -S\theta_xC\theta_z & -C\theta_xC\theta_z \\
S\theta_xC\theta_z & 0 & S\theta_z \\
C\theta_xC\theta_z & -S\theta_z & 0
\end{matrix}\right)$
R_x^{T}{R_z}^{T}{R_y'}^{T}R_y{R_z}{R_x}= \theta_y' \left(\begin{matrix} 1 & & \\\\ & C\theta_x & S\theta_x \\\\ & -S\theta_x & C\theta_x \end{matrix}\right) \left(\begin{matrix} 0 & & -C\theta_z \\\\ & 0 & S\theta_z \\\\ C\theta_z & -S\theta_z & 0 \end{matrix}\right) \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right)$ $ R_x^{T}{R_z}^{T}{R_y'}^{T}R_y{R_z}{R_x}= \theta_y' \left(\begin{matrix} 0 & & -C\theta_z \\\\ S\theta_xC\theta_z & -S\theta_xS\theta_z & C\theta_xS\theta_z \\\\ C\theta_xC\theta_z & -C\theta_xS\theta_z & -S\theta_xS\theta_z \end{matrix}\right) \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right) =\theta_y' \left(\begin{matrix} 0 & -S\theta_xC\theta_z & -C\theta_xC\theta_z \\\\ S\theta_xC\theta_z & 0 & S\theta_z \\\\ C\theta_xC\theta_z & -S\theta_z & 0 \end{matrix}\right)
1=z,2=x,3=y
$R_y^{T}R_x^{T}{R_z'}^{T}R_z{R_x}R_y=
\theta_z'
\left(\begin{matrix}
C\theta_y & & -S\theta_y \\
& 1 & \\
S\theta_y & & C\theta_y
\end{matrix}\right)
\left(\begin{matrix}
0 & C\theta_x & -S\theta_x \\
-C\theta_x & 0 & \\
S\theta_x & & 0
\end{matrix}\right)
\left(\begin{matrix}
C\theta_y & & S\theta_y \\
& 1 & \\
-S\theta_y & & C\theta_y
\end{matrix}\right)
$
$
R_y^{T}R_x^{T}{R_z'}^{T}R_z{R_x}R_y=
\theta_z'
\left(\begin{matrix}
-S\theta_xS\theta_y & C\theta_xC\theta_y & -S\theta_xC\theta_y \\
-C\theta_x & 0 & \\
S\theta_xC\theta_y & C\theta_xS\theta_y & -S\theta_xS\theta_y
\end{matrix}\right)
\left(\begin{matrix}
C\theta_y & & S\theta_y \\
& 1 & \\
-S\theta_y & & C\theta_y
\end{matrix}\right)
=\theta_z'
\left(\begin{matrix}
0 & C\theta_xC\theta_y & -S\theta_x \\
-C\theta_xC\theta_y & 0 & -C\theta_xS\theta_y \\
S\theta_x & C\theta_xS\theta_y & 0
\end{matrix}\right)
$
R_y^{T}R_x^{T}{R_z'}^{T}R_z{R_x}R_y= \theta_z' \left(\begin{matrix} C\theta_y & & -S\theta_y \\\\ & 1 & \\\\ S\theta_y & & C\theta_y \end{matrix}\right) \left(\begin{matrix} 0 & C\theta_x & -S\theta_x \\\\ -C\theta_x & 0 & \\\\ S\theta_x & & 0 \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right) = \theta_z' \left(\begin{matrix} -S\theta_xS\theta_y & C\theta_xC\theta_y & -S\theta_xC\theta_y \\\\ -C\theta_x & 0 & \\\\ S\theta_xC\theta_y & C\theta_xS\theta_y & -S\theta_xS\theta_y \end{matrix}\right) \left(\begin{matrix} C\theta_y & & S\theta_y \\\\ & 1 & \\\\ -S\theta_y & & C\theta_y \end{matrix}\right) = \theta_z' \left(\begin{matrix} 0 & C\theta_xC\theta_y & -S\theta_x \\\\ -C\theta_xC\theta_y & 0 & -C\theta_xS\theta_y \\\\ S\theta_x & C\theta_xS\theta_y & 0 \end{matrix}\right)
1=z,2=y,3=x
$R_x^{T}{R_y}^{T}{R_z'}^{T}{R_y}R_z{R_x}=
\theta_z'
\left(\begin{matrix}
1 & & \\
& C\theta_x & S\theta_x \\
& -S\theta_x & C\theta_x
\end{matrix}\right)
\left(\begin{matrix}
0 & C\theta_y & \\
-C\theta_y & 0 & -S\theta_y \\
& S\theta_y & 0
\end{matrix}\right)
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)$
$
R_x^{T}{R_y}^{T}{R_z'}^{T}{R_y}R_z{R_x}=
\theta_z'
\left(\begin{matrix}
0 & C\theta_y & \\
-C\theta_xC\theta_y & S\theta_xS\theta_y & -C\theta_xS\theta_y \\
S\theta_xC\theta_y & C\theta_xS\theta_y & S\theta_xS\theta_y
\end{matrix}\right)
\left(\begin{matrix}
1 & & \\
& C\theta_x & -S\theta_x \\
& S\theta_x & C\theta_x
\end{matrix}\right)
=\theta_z'
\left(\begin{matrix}
0 & C\theta_xC\theta_y & -S\theta_xC\theta_y \\
-C\theta_xC\theta_y & 0 & -S\theta_y \\
S\theta_xC\theta_y & S\theta_y & 0
\end{matrix}\right)$
R_x^{T}{R_y}^{T}{R_z'}^{T}{R_y}R_z{R_x}= \theta_z' \left(\begin{matrix} 1 & & \\\\ & C\theta_x & S\theta_x \\\\ & -S\theta_x & C\theta_x \end{matrix}\right) \left(\begin{matrix} 0 & C\theta_y & \\\\ -C\theta_y & 0 & -S\theta_y \\\\ & S\theta_y & 0 \end{matrix}\right) \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right) = \theta_z' \left(\begin{matrix} 0 & C\theta_y & \\\\ -C\theta_xC\theta_y & S\theta_xS\theta_y & -C\theta_xS\theta_y \\\\ S\theta_xC\theta_y & C\theta_xS\theta_y & S\theta_xS\theta_y \end{matrix}\right) \left(\begin{matrix} 1 & & \\\\ & C\theta_x & -S\theta_x \\\\ & S\theta_x & C\theta_x \end{matrix}\right) =\theta_z' \left(\begin{matrix} 0 & C\theta_xC\theta_y & -S\theta_xC\theta_y \\\\ -C\theta_xC\theta_y & 0 & -S\theta_y \\\\ S\theta_xC\theta_y & S\theta_y & 0 \end{matrix}\right)
随時更新予定
pythonで3Dプリンター制御してみた
緒言
自宅の3DプリンターはPCレスで動かせるのはいいが、操作がダイヤルを回す+押すで非常にめんどい。
どうやらうちの3DプリンターはFT232系の規格でPCとシリアル通信できるっぽい。
キー入力で動かせれば比較的楽に操作できそうなのでやってみた。
(プリンター標準のスライサがCuraでCuraからプリンターを操作する機能があるので、多分多くのプリンターが同じ通信)
環境
プリンター:CR-10
PC:windows10
python3.6.5をAnaconda上で起動
コード
とりあえず、キー入力からエクストルーダの移動ができるようにした。
後、レベリングする時四隅に移動させるのが面倒なので、それ用のコードも書いた。
import serial #serial import time #wait from msvcrt import getch #key input from itertools import cycle #gcode checktemp=b"M105" autohome=b"G28" moveX=b"G0 X " moveY="G0 Y ".encode() moveZ="G0 Z ".encode() setAbsolute="G90".encode() setRelative="G91".encode() #functions #gcodeを送り、返信を表示 #okは無視 def sendcommand_waitless(command): comport.write(command)#commandを送る。commandはバイナリ文字列 comport.write(b"\n") print("send: "+command.decode()) time.sleep(0.5) print("return: "+comport.readline().decode())#返答表示 #gcodeを送り、返信を表示 #okが帰るまでwait秒待つ。 #wait秒以内に戻らなければ、-1、そうでなければ、0を返す。 def sendcommand_wait(command, wait): comport.write(command) comport.write(b"\n") print("send: "+command.decode()) echo=0 count=0 while not ( (echo == b"ok\n") or (count >wait)): echo=comport.read(3) print("wait "+command.decode()+" "+str(count)+"/"+str(wait)) time.sleep(1) count=count+1 else: if echo == b"ok\n": print("finished") return 0 else: print("troubled") return -1 #open comport comport = serial.Serial('COM4', baudrate=115200, parity=serial.PARITY_NONE, timeout=0.1) #相手の再起動待ち time.sleep(5) #念のためバッファクリア comport.reset_input_buffer() #温度確認 sendcommand_waitless(checktemp) #ホームポジションに移動 sendcommand_wait(autohome,10) #エクストルーダ移動モード #相対座標モードへ移行 sendcommand_waitless(setRelative) while True: print("move Extruder") print("Q:up W:+y E:down") print("A:-x S:-y D:+x") print("Esc: Escape this mode") key = ord(getch()) print(key) if key == 27: break elif key == 97:#a sendcommand_waitless(moveX+b"-5") elif key ==100:#d -> sendcommand_waitless(moveX+b"5") elif key ==119:#w sendcommand_waitless(moveY+b"5") elif key ==115:#s sendcommand_waitless(moveY+b"-5") elif key ==113:#q sendcommand_waitless(moveZ+b"5") elif key ==101:#e sendcommand_waitless(moveZ+b"-5") sendcommand_waitless(setAbsolute) corner=cycle([ [10, 10], [280, 10], [280, 280], [10, 280] ]) #leveling mode #四隅をスペースキーが押されるごとに移動する。 while True: print("Leveling Mode") print("Space: move next corner") print("Esc: Escape this mode") key = ord(getch()) print(key) if key == 27: break elif key == 32:#space tmp=next(corner) sendcommand_waitless(b"G0 Z5") sendcommand_waitless(b"G0 X"+ str(tmp[0]).encode()) sendcommand_waitless(b"G0 Y"+ str(tmp[1]).encode()) sendcommand_waitless(b"G0 Z0") comport.close() #print(comport.readline())
結果
動いた
展望
使いずらいのでなんかいい感じにまとめたい
タイマーで決まった時間に出力開始させたらいろいろ楽