TOPに戻る

ラズパイ5 +Python+CANopenでモータを回す ⑭ モーション CiA 402の規格<Profile Velocity Mode (PVM)> campvm.py

 この連載で使っているモータ・コントローラBLVD-KRDは、次の三つのモードをサポートしています(最新のバージョンではProfile Torque Mode (tq)をサポート)。

  • プロファイル位置モード(pp)
  • プロファイル速度モード(pv)
  • 原点復帰モード(hm)

  初期状態では、「プロファイル速度モード(pv)」が設定されていて、前回まで、位置決めのできる「プロファイル位置モード(pp)」に設定を変更して利用しました。ここでは、「プロファイル速度モード(pv)」を利用します。

(※)同じ動作モードで、異なる表現が混在しています。

  • プロファイル位置モード(pp)は、「Profile position mode;位置制御モード」と同じ
  • プロファイル速度モード(pv)は、「Profile velocity mode;速度制御モード」と同じ

プロファイル速度モード(pv)とは

 目標位置の定義なしでオブジェクトTarget velocityに書かれた速度設定ポイントに追従します。

 設定すべきパラメタ(オブジェクト)です。

  • Target velocity (60FFh) 初期値0
  • Profile acceleration (6083h) 初期値1000
  • Profile deceleration (6084h) 初期値1000

 そのほかの設定パラメタです。

  • Quick stop deceleration (6085h) 初期値1000
  • Quick stop option code (605Ah) 初期値2
  • Max torque (6072h) 初期値10000
  • Halt option code (605Dh) 初期値1
  • Velocity window (606Dh) 初期値15
  • Velocity threshold (606Fh) 初期値15

 モータ側から返ってくる値です。

  • Target reached in statusword
  • Speed in statusword
  • Velocity actual value (606Ch)
  • Torque actual value (6077h)


 なじみのないオブジェクト;

◆Velocity window (606Dh) 初期値15
 現在の目標速度 (Target velocity) + (Velocity offset) に対して、現在速度 (Velocity actual value)が到達したとみなす速度の差を設定

◆Velocity threshold (606Fh) 初期値15
 停止しているとみなす速度の閾値を設定

MEXE02を用いてプロファイル速度モード(pv)に変更する

 接続OFF状態で、「(p5) CANopen object通信初期値・設定」を見ます。

 Profile velocity mode = プロファイル速度モード(pv)ですね。なぜか、Profile position modeに変更していた状態から初期状態に戻っています。理由は不明です。

 接続をONにし、「(m14) CANopenオブジェクトディクショナリモニタ」を見ます。

 Modes of operation、Modes of operation displayが'1'のProfile Position Modeになっています。

 つまり、(p5) CANopen object通信初期値・設定で、 Profile Position Mode(1)に変更にして保存した状態は保たれているけど、画面は、あくまでも初期値なので、Profile Velocity Modeになっていたということですね。

 「(p5) CANopen object通信初期値・設定」で、接続OFF状態、Modes of operationがProfile velocity mode の状態で、データの書き込みを行います。

Profile Velocity Modeには2種類の動作がある

 Profile Velocity Modeには、Controlwordのbit12の状態によって、2種類の動作があると説明されています。

 Controlwordのbit12の説明です。

  「(m14) CANopenオブジェクトディクショナリモニタ」の6040h=Controlwordは0x0004です。bit12は'0'ですね。

 動作の違いが図示されています。

 trajectory generator for positioningは、位置決めの際に使われる軌道ジェネレータのことのようです。bit12が'1'の場合、目標位置(607Ah)の絶対値を相対移動距離として扱うという説明です。軌道ジェネレータは、振動などが起こらないように素早い計算をするという目的で使われるようです。図では、減速時の事例が出ているので、そのような用途に使うときにメリットがあるのかもしれません。

canppm2.pyを修正してcampvm.py

 プログラムcampvm.pyです。move()関数は、引数に速度を入れます。mainでは、CWに10秒、CCWに10秒回転して止まるプログラムです。

 「(m16) トレースモニタ」を使うために、最初のresetNmt()は使わず、node.nmt.send_command(0x1)   # NMT startに変更してあります。

#node.sdo[0x6060].raw = 0x0003 #  Modes of operation <- Profile Velocity Mode

は、MEXE02で'3'へ変更ができていなくても、この1行を生かしておけば、プログラムはProfile Velocity Modeで動作します。

import canopen
import time
import logging
# logging.basicConfig(level=logging.DEBUG)

# Start with creating a network representing one CAN bus
network = canopen.Network()

# Connect to the CAN bus
# sudo ip link set can0 up type can bitrate 1000000
network.connect(bustype='socketcan', channel='can0')
print("\n===start  ID=12 OrientalMotor===\n")

# Add some nodes with corresponding Object Dictionaries
node = canopen.BaseNode402(12,'Downloads/BLVD-KRD_CANopen_V200.eds')
network.add_node(node)

def resetNmt():
    # all nodes simulaneously as a broadcast message
    network.nmt.state = 'RESET'
    time.sleep(0.5)
    # Reset this node
    node.nmt.state = 'RESET'
    node.nmt.wait_for_bootup(15)
    node.nmt.state = 'RESET COMMUNICATION'
    node.nmt.wait_for_bootup(15)
    node.nmt.send_command(0x1)   # NMT start
    network.check()
    print('---node state = {0}\n'.format(node.nmt.state))

def startState_machine():
    node.setup_402_state_machine()
    node.sdo[0x6040].raw = 0x0010  # Fault Reset
    time.sleep(0.5)
    node.state = 'SWITCH ON DISABLED'
    time.sleep(0.1)  
    timeout = time.time() + 15
    node.state = 'READY TO SWITCH ON'
    while node.state != 'READY TO SWITCH ON':
        if time.time() > timeout:
            raise Exception('Timeout when trying to change state')
        time.sleep(0.01)
    time.sleep(0.1)  
    timeout = time.time() + 15
    node.state = 'SWITCHED ON'
    while node.state != 'SWITCHED ON':
        if time.time() > timeout:
            raise Exception('Timeout when trying to change state')
        time.sleep(0.001)
    time.sleep(0.1)  
    timeout = time.time() + 15
    node.state = 'OPERATION ENABLED'
    while node.state != 'OPERATION ENABLED':
        if time.time() > timeout:
            raise Exception('Timeout when trying to change state')
        time.sleep(0.001)
    time.sleep(0.1) 
    print("---SWITCH state ready\n") 
    network.check()
    print('---switch state = {0}\n'.format(node.state))

def move(targetVelocity):
    node.rpdo[1]['Controlword'].phys  = 0x000f
    node.rpdo[1].transmit()
    time.sleep(0.02)
    node.rpdo[4]['Controlword'].phys  = 0x200f
    node.rpdo[4]['Target velocity'].phys  = targetVelocity
    node.rpdo[4].transmit()
    time.sleep(0.02)

def moveStop():
    #node.rpdo[1]['Controlword'].phys  = 0x000f
    #node.rpdo[1].transmit()
    #time.sleep(0.02)
    node.rpdo[4]['Controlword'].phys  = 0x010f
    #node.rpdo[4]['Target velocity'].phys  = targetVelocity
    node.rpdo[4].transmit()
    time.sleep(0.02)

def dispParameter():
    Modes_of_Operation = node.sdo[0x6060].raw
    print('#Modes of Operation',Modes_of_Operation)
    Modes_of_Operation_Display  = node.sdo[0x6061].raw
    print('#Modes of Operation Display',Modes_of_Operation_Display,'\n')
    Target_Position = node.sdo[0x607a].raw
    print('#Target Position',Target_Position)
    Target_Velocity = node.sdo[0x60ff].raw
    print('#Target Velocity', Target_Velocity,'\n')
    Position_Actual_Value = node.sdo[0x6040].raw
    print('#Position Actual Value',Position_Actual_Value)
    Velocity_Actual_Value = node.sdo[0x606c].raw
    print('#Velocity Actual Value', Velocity_Actual_Value,'\n')
    Profile_velocity = node.sdo[0x6081].raw
    print('#Profile velocity',Profile_velocity)
    Profile_acceleration = node.sdo[0x6083].raw
    print('#Profile acceleration',Profile_acceleration)
    Profile_deceleration = node.sdo[0x6084].raw
    print('#Profile deceleration', Profile_deceleration,'\n')

def dispPosition():
    node.tpdo[3].wait_for_reception()
    print('Target position {:4d}'.format(node.tpdo[3]['Position actual value'].phys))

#----main----------------

#resetNmt()
node.nmt.send_command(0x1)   # NMT start
network.sync.start(0.1)  # 100ms
startState_machine()
time.sleep(0.2)

#node.sdo[0x6060].raw = 0x0003 #  Modes of operation <- Profile Velocity Mode
node.sdo[0x607a].raw = 0       # Target position
node.sdo[0x6081].raw = 1000    # Profile velocity
node.sdo[0x6083].raw = 1000    # Profile acceleration
node.sdo[0x6084].raw = 1000    # Profile deceleration

dispParameter()
dispPosition()

print(' ===start move===\n')

move(1000)
time.sleep(10)
dispPosition()

move(-1000)
time.sleep(10)
dispPosition()

moveStop()

#-------------------
print("\n---closed---")
node.sdo[0x6040].raw = 0x0080  # Fault Reset
node.nmt.send_command(0x02)  # NMT remote stop
node.nmt.send_command(0x81)  # NMT reset
time.sleep(0.5)
node.nmt.send_command(0x82)  # NMT Reset Communication
network.sync.stop()
network.disconnect()

 「(m16) トレースモニタ」の画面です。プログラム実行時の最後のほうで、モータの軸を手でつまんでいるのでトルクが変化しています。

連載 ラズパイ5 +Python+CANopenでモータを回す

(1) 構成と環境(オリエンタルモーター の「BLMR5100K-A-B」 + 「BLVD-KRD」)

(2) サポート・ソフト MEXE02

(3) PythonでSDOの読み出し(仮)caninfo.py

(4) CANopenのベーシックな規格とSDO/PDO、オブジェクト・ディクショナリ<前編>

(5) CANopenのベーシックな規格とSDO/PDO、オブジェクト・ディクショナリ<後編>

(6) 二つの状態遷移(NMTとStatus Machine)

(7) CANバス信号を見る<前編>canreset.py candump0.py

(8) CANバス信号を見る<中編>Arduino

(9) CANバス信号を見る<後編>CANopenのデコードができ無償で使えるツールAnalog Devices TMCL-IDE

(10) モーション CiA 402の規格<Homing mode> canHome.py

(11) モーション CiA 402の規格<Profile Position Mode (PPM)-前編> canPPMread.py

(12) モーション CiA 402の規格<Profile Position Mode (PPM)-中編> caninfo2.py canppm.py

(13) モーション CiA 402の規格<Profile Position Mode (PPM)-後編> canppm2.py

(14) モーション CiA 402の規格<Profile Velocity Mode (PVM)> campvm.py