PMXサーボを使ってみよう PCから制御「MemREAD編」
今回の記事から具体的にPMXサーボを制御する方法を解説します。まずはサーボから情報を読み出す「MemREAD」コマンドについて解説します。
【関連記事】
この記事で解説するPython向けライブラリ、及びサンプルプログラムは、下記のページからダウンロードしてください。
PMXシリーズのサーボモータを制御するためのオンラインマニュアルを公開しています。PMXサーボを制御するための詳しい情報が記載されていますので、下記の解説と一緒にご一読ください。
■MemREADコマンドについて
MemREADコマンドは、メモリマップのRAM領域から情報を読み出すためのコマンドです。PMXサーボのメモリマップにはRAM領域とROM領域があります。サーボ起動時にROM領域からRAM領域にデータが展開され、サーボの制御にはRAM領域のデータが使用されます。
このコマンドを使用することで下記の情報を読み出すことができます。
・サーボの現在の状態(現在位置、速度、消費電流、入力電圧など)
・PIDゲインなど制御に関連する設定値
・入力電圧最小値設定など制限値の設定値
・トルクON/OFFの状態、現在の制御モードなど
メモリマップの内容や、各設定値の解説はオンラインマニュアルの『6.メモリマップ』をご参照ください。
なお、サーボIDや通信速度、パリティの読み出しはSystemREADコマンドを使用します。こちらの使用方法については別の記事で解説します。
■コマンドの使用方法
MemREADコマンドは、「先頭アドレス」と「読出データ数」を指定してデータを読み出します。
「先頭アドレス」とは、メモリマップのアドレスを指し、読み出すデータの先頭のアドレスを指定することができます。下図はメモリマップの一部を抜粋した表です。たとえば「現在位置」を読み出す場合、アドレスは300ですので、先頭アドレスとして黄色いセルの300を指定します。
「読出データ数」は、先頭アドレスから何バイトのデータを読み出すかを指定することができます。「現在位置」のみを読み出す場合は、「現在位置」のデータは2バイトですので、「読出データ数」は2となります。また「現在位置」から「現在電流値」までを一気に読み出す場合は、各2バイトのデータが3つ選択しますので6と指定します。つまり、下図の緑のセルの内容が読み出されます。
メモリマップの詳細は、オンラインマニュアルの『メモリマップ一覧』をご参照ください。こちらに各データのアドレスとデータバイト数が一覧になっています。
■リトルエンディアンについて
PMXサーボの2バイト以上のデータは、リトルエンディアンに分割されて格納されています。データを読み出す場合は、読み出したデータをリトルエンディアン方式で変換してご利用ください。詳しくはオンラインマニュアルの『データ構造』にある「データ」の解説をご参照ください。
■プログラム解説
ここからPythonライブラリのサンプルプログラム「MemREAD_Sample」を使用してMemREADコマンドを実行し、PMXサーボからデータを読み出す方法をご紹介します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import sys import os import time # PmxLib import # ライブラリのパスはフォルダの構成によって変わります # 現在のスクリプトのディレクトリを取得 current_dir = os.path.dirname(__file__) # 1つ上のフォルダを取得(sanpleのフォルダの同じディレクトリにPmxLibがあるため) parent_dir = os.path.dirname(current_dir) # Pmxのライブラリのパスを登録 pmxLibPath = parent_dir + '\PmxLib' sys.path.append(pmxLibPath) from PmxPySerialClass import PmxPySerialClass # pySerialでPMXを通信するクラスをつかるようにする from PmxLib import PmxLib as PMX # PMXの固有値が定義してある「PmxLib」をPMXという名前で使えるようにする # クラスをインスタンス化 pmx = PmxPySerialClass() |
まずは必要なファイルやライブラリを参照し、ライブラリを"pmx"の名前でインスタンス化して使用できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 通信の設定項目 #comName = "/dev/ttyUSB0" comName = "COM3" pmxBaudrate = 115200 pmxTimeout = 1.0 ServoID = 0 """ Main処理 """ def main(): # 通信ログを表示させる(表示:True,非表示:False) pmx.logOutput = False # COMを開く openFlag = pmx.open(comName, baudrate=pmxBaudrate, timeout=pmxTimeout) if openFlag == False: print("Serial not open. Exit program") exit() else: print("Serial Connect success") |
PCに接続しているUSBアダプターのCOM番号、通信速度、タイムアウトを指定し、COMポートをopen()関数で開きます。上記の内容で正常にCOMが開けますと、コンソールに「Serial Connect success」と表示されます。
COM番号はWindowsの「デバイスマネージャ」から確認することができます。詳しくは、KO Driverに付属しているマニュアルをご参照ください。Linuxの場合は、dmesgで「/dev/ttyUSB*」(*は認識した番号)で認識できますので、そのまま「comName」に代入してください。
通信速度は、サーボに設定されている通信速度です。工場出荷状態では115200bpsに設定されています。
タイムアウトは、なんらかのトラブルによりサーボからの返事が返ってこない場合の待ち時間です。pySerialを使用する場合は、単位は[秒]ですので1.0は1秒になります。短くしすぎると正常にパケットを受け取れない場合がありますのでご注意ください。
pmx.open()の前に書いてある「pmx.logOutput = False 」は、サーボとPC間で送受信しているパケットを表示するかを指定するプログラムです。Trueを指定すると、コンソールに送受信したパケットの内容がすべて表示されます。表示しない場合はFalseを代入します。こちらは表示の有無のみでプログラムには影響ありません。パケットの内容にご興味がありましたらTrueを選択して表示してください。※短時間に連続して送受信する場合は、通信の遅延の原因になる場合があります。
●基本のMemREAD()関数
1 2 |
flag, dataArray = pmx.MemREAD(ServoID, PMX.RamAddrList.NowPosition, 6) # id , Addr ,dataSize print(hex(flag), dataArray) |
この行からMemREADコマンドを実行します。PMXサーボのPythonライブラリには、MemREADコマンドを実行するために多彩な関数を用意しました。上記の関数はベースとなる基本の関数です。
第一引数はサーボIDです。「ServoID」は、MemREADコマンドの命令の対象になるサーボを指定しています。前のプログラムで「ServoID = 0」と代入していますので、この関数はID0のサーボに対して命令を送信しています。
第二引数は先頭アドレスです。「PMX.RamAddrList.NowPosition」は、ライブラリ内で定義された変数で、メモリマップのアドレスを指定しています。「PMX.RamAddrList.NowPosition」には300が代入されていますので、所持の際は300に変換されます。また数値で300と指定しても同じ番地からデータを読み出すことができます。
第三引数は読出データ数です。6が指定されていますので、先頭アドレス300から6バイトのデータを読み出します。
戻り値の「flag」は、サーボとの通信状態とサーボの状態が正常かどうかのステータスを足したものが返ってきます。データが0であれば問題ありません。0以外の場合は、なんらかの問題が発生していますので、ステータス部がエラーの場合はオンラインマニュアルの『5.エラー状態』を参照してください。
「dataArray」にMemREADコマンドで読み出したデータが代入されます。前述の通り、2バイト以上のデータはリトルエンディアンで分割されていますので連結する必要があります。Pythonには、リトルエンディアンで分割されたデータを組み合わせるために便利な関数が用意されています。
1 2 3 |
posArray = [dataArray[0], dataArray[1]] # 現在位置のデータは300, 301番地のため[0], [1]が該当 pos = int.from_bytes(posArray, byteorder="little", signed=True) print("現在位置", pos) |
int.from_bytes()でデータを連結することができます。第一引数に連結するデータを渡します。今回は例として6バイトのデータを読み出しました。「現在位置」は先頭の2バイトのみですので、別のリストに2バイトのみ分けで代入しましたが、pmx.MemREAD()で2バイトのみ読み出した場合は、そのまま第一引数に渡しても問題ありません。
第二引数に"big"か"little"を指定します。今回はリトルエンディアンなので"little"を指定しました。
第三引数のsighedは、符号の有無を指定できます。データが符号付きの-32768~32767までの範囲でしたらTrue、符号なしの0~65535までの範囲でしたらFalseを指定します。「現在位置」の型はInt16で符号付きなのでsignedをTrueとしました。
データの符号の有無についていは、オンラインマニュアルの「メモリマップ一覧」で確認することができます。Uint16、Uint32は符号なし(False)、他は符号あり(True)です。
連結した結果をpos変数に代入してコンソールに表示しています。サーボの軸を指で回転させながら角度が変わるか確認してみてください。
●1つにまとまったデータを受け取ることができるMemREADコマンドの便利な関数
基本的な使い方は以上ですが、MemREAD()で読み出したデータを毎度int.from_bytes()で連結するとプログラムの行数が増えてしまいます。そこで、戻り値として連結したデータを受け取ることができる関数を用意しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# MemREAD関数を使ってを現在位置(単位[1/100度])(2byte符号あり)を取得する print('2byte(signed)の位置データを取得します') flag, data = pmx.MemREADToInt16(ServoID, PMX.RamAddrList.NowPosition) # id, Addr print(hex(flag), data/100, "[°]") # MemRead関数を使って電圧(単位[mV])(2byte符号なし)を取得する print('2byte(unsigned)の電圧データを取得します') flag, data = pmx.MemREADToUint16(ServoID, PMX.RamAddrList.InputVoltage) # id, Addr print(hex(flag), data/1000, "[V]") # MemRead関数を使って位置制御のPゲインを取得する print('4byte(unsigned)の位置制御のPゲインを取得します') flag, data = pmx.MemREADToUint32(ServoID, PMX.RamAddrList.PositionKp) # id, Addr print(hex(flag), data) # MemRead関数を使ってトルクスイッチの状態を取得する print('1byte(unsigned)のトルクスイッチの状態を取得します') # TorqueSwitch Data: 0x01=TorqueON, 0x02=Free, 0x04=Brake, 0x08=Hold flag, data = pmx.MemREADToByte(ServoID, PMX.RamAddrList.TorqueSwitch) # id, Addr print(hex(flag), data) |
関数は下記の5種類です。
MemREADToByte(id, address) =Byte型(1バイト)符号なし
MemREADToInt16(id, address) =Int型(2バイト)符号あり
MemREADToUint16(id, address) =Int型(2バイト)符号なし
MemREADToInt32(id, address) =Int型(4バイト)符号あり
MemREADToUint32(id, address) =Int型(4バイト)符号なし
いずれも引数にサーボIDとアドレスを渡します。オンラインマニュアルの『メモリマップ一覧』を参照し、型と符号が一致する関数を使用してください。
●各パラメータの専用関数でプログラムを読みやすく
また、読み出すデータによっては、アドレスの指定や読出データ数の指定を省くために専用の関数が用意されています。こちらを使用するとサーボIDのみで機能を実行できますので便利です。関数の種類はライブラリをご参照ください。
1 2 3 4 5 |
# トルクスイッチの状態を取得する print('トルクスイッチの状態を読み出します') # TorqueSwitch Data: 0x01=TorqueON, 0x02=Free, 0x04=Brake, 0x08=Hold flag, data = pmx.getTorqueSwitch(ServoID) # id print(hex(flag), hex(data)) |
■エラーの解除方法
最後に、サーボにエラーが発生した場合のフラグの解除方法を解説します。受信パケットのステータスにエラーが発生した場合は、受信パケットのステータスがエラー(0以外)になります。同時にアドレス400番台の該当のデータが変化しています。エラーの種類のよっては、安全のためエラーを解除しない限り動作が停止する場合がありますので解除が必要です。
エラーは、アドレス400番台をMemREADコマンドで読み出しますことでエラーを解除することができます。下記の通り、getFullStatus()関数を使用するとステータスの情報を一気に読む出すことができます。
読みだしたデータはfullStatusに順番に各エラーをリストとして代入していますので、print()でコンソールに表示します。
1 2 3 |
print('400番地(エラーステータス)からエラー情報を読み出します') status, fullStatus = pmx.getFullStatus(ServoID) print(fullStatus) |
ただし、電源電圧が低い、高いなど原因が解消していない状態でエラー情報を読み出してもエラーを解除することはできません。エラーが発生した場合は、エラーの内容をよく確認し安全にご利用ください。
エラー情報については、オンラインマニュアルの『5.エラー状態』をご参照ください。
以上でMemREADコマンドの解説を終わります。次回はメモリマップの情報を書き換えることができるMemWRITEコマンドについて解説します。
次の記事『PMXサーボを使ってみよう PCから制御「MemWRITE編」』
前の記事『PMXサーボを使ってみよう PCから制御「準備編」』
PMX-SCR-9204HVの詳細をみる PMX-SCR-9203HVの詳細をみる PMX-SCR-5204HVの詳細をみる PMX-SCR-5203HVの詳細をみる