PCプラットフォームによる PCプラットフォームによる 産業用ソリューションプロバイダー 産業用ソリューションプロバイダー


採用情報


お問合せ

JapaneseEnglish

  • PP931
ホーム > RT-Python
Python + Realtime
RT-Python

1. RT-Pythonとは

RT-Pythonは、Windows上で動作するPythonをリアルタイムOS INtime向けに移植したソフトウェアです。Python本来の言語仕様をそのまま活かしてプログラミングできます。また、外部機器の制御、コントローラ間通信、上位サーバーへのアクセスなど、さまざまな機能と連携できます。
ハードリアルタイム制御を、扱いやすいPython言語で記述した処理によって、実現できることが大きな特長です。

2. 特長

① INtimeによるハードリアルタイム制御

動作プラットフォームがINtimeであるため、ハードリアルタイム制御を実現できます。さらに、同一PC上で動作するINtime/Windowsアプリケーションとの連携も容易です。

② Python言語の利用が可能

開発言語には、C/C++言語に比べ学習コストの低いPython言語を採用しています。動的型付けやクラスによるオブジェクト指向プログラミングなど、Pythonの言語仕様をそのまま活用できます。
また、コンパイル不要のインタプリタ型言語であるため、テキストエディタで直接プログラミングできます。

③ 豊富なインターフェース

RT-Pythonは、コントローラ上のNICやPCI/PCIeデバイスといった外部インターフェースを制御できるため、アクチュエーター、サーボモーター、PLC、上位サーバー、各種センサーやスイッチなど、多様な機器との制御・連携を実現します。
豊富なインターフェース イメージ

3. 製品仕様

3-1. 基本仕様

No 項目 詳細
1 動作環境 INtime 7 以降
Windows 10以降
2 プログラミング言語 Python3標準構文準拠
3 制御周期 最速100usec
4 実行モード インタラクティブモード / スクリプトモード

3-2. 実装モジュール

No 項目 詳細
1 連携機能用モジュール 様々な機能を扱うためのRT-Python専用モジュールです。
利用できる機能については 連携機能 を参照ください。
2 リアルタイム処理用モジュール RT-Python専用モジュールです。制御周期を生成するためのSleep機能を提供します。
3 mathモジュール 数学演算関数機能を提供します。
4 sysモジュール インタプリタや実行環境に関する情報を扱うためのモジュールデです。以下の3つの機能に対応しています。
sys.path / sys.argv / sys.version

3-3. 連携機能

No 項目 内容
1 モーション制御 Pythonプログラムからモーション制御が行なえます。
2 データベース接続 制御データをデータベースへ保存できます。
3 HMI連携 市販のHMIや弊社のHMIとの連携が容易に出来ます。
4 挙動監視 ※1 映像や入出力を監視し、条件に応じてイベントを検知し、アクションを実行する機能です。
5 マルチメディア ※1 動画、静止画、音声で通知、メール送受信、遠隔操作の3つの機能を提供します。
6 INtimeアプリケーション INtimeアプリケーション側から連携機能を利用することで、RT-Python処理との通信が可能になります。
7 Windowsアプリケーション ユーザーが作成するWindowsアプリケーション側から連携機能を利用することで、RT-Python処理との通信が可能になります。
また、DDE通信やOPC UA通信などに対応している市販アプリケーションからも接続可能です。
8 Pythonアプリケーション Windows上で動作するPythonアプリケーションから連携機能を利用することで、RT-Python処理との通信が可能になります。
※1…今後対応予定の機能です。

3-4. 各種I/F

No 項目 対応方式
1 外部I/O PCI/PCIeボード (デジタル、アナログなど)
EtherCAT
2 コントローラ間通信 FL-net
EtherNet/IP
CC-LinkIE Control
三菱MCプロトコル通信
オムロンFINS通信
3 外部通信 Ethernet
シリアル通信
OPC UA
MQTT
FTP

4. デモプログラム: 1軸サーボモーター制御

Panasonic社製サーボアンプを用い、EtherCAT通信経由でサーボモーターを制御します。
制御周期は125µsecで、Python言語で作成したスクリプトファイル内の処理にて目標位置を更新し、精密な位置制御を行っています。
これはINtimeのタスクスケジューリングに基づく正確な周期指令によって実現しています。

4-1. デモ動作環境

PC PHOENIC CONTACT VL2 BPC 9000
CPU Intel Core i7 6822EQ
INtime 7.1.25030.3
サーボアンプ/モーター Panasonic社 MINAS A6B
サーボアンプ  : MADLN11BE
サーボモーター : MHMF011L1A2

4-2. 制御条件

通信 EtherCAT
制御周期 125usec (polling mode)
制御モード CiA402 CSP(Cyclic Synchronous Position) mode

4-3. デモプログラム構成

デモプログラム構成
【処理内容】
  1. (1) Pythonプログラム
    1. ① サーボモーターを励磁状態へ遷移
    2. ② 制御周期毎の位置情報を更新
    3. ③ HMIアプリへサーボモーターの位置/速度を通知
      (共有メモリ経由)
  2. (2) HMIアプリ(Windowsアプリ)
    1. ① サーボモーターの位置/速度情報を取得
      (共有メモリ経由)
    2. ② 取得情報を画面へ表示
  3. (3) 外部I/O(EtherCAT通信処理
    1. ① EtherCAT通信の初期化
    2. ② サイクル周期毎のEtherCAT通信処理

4-4. 計測結果

INtime付属ユーティリティINscopeにて、RT-Pythonの処理時間及び、動作周期の計測結果を下図に示します。
紫色で示される処理時間がPython言語で記述したプログラム処理です。制御周期毎にプログラム処理が、約10usecで実行されている様子が確認できます。
計測結果

4-5. 動作の様子

デモプログラム動作時の様子です。

4-6. デモプログラム ソースコード

サーボモーター制御に利用したPythonスクリプトファイルは以下になります。
import INtime
import RTedge
import math

g_Interval = 0.125

"""
CiA402 サーボドライバ制御プログラム(Python版)
"""

# 目標位置を順次生成する
#--------------------------------------
def generate_wave():

    ctrl =  [
        ( 1000,  1), #/*  0 */ 
        ( 2000,  0), #/*  1 */ 
        ( 3000,  2), #/*  2 */ 
        ( 4000,  0), #/*  3 */ 
        ( 5000, -1), #/*  4 */ 
        ( 6000,  0), #/*  5 */ 
        ( 7000,  3), #/*  6 */ 
        ( 9000,  0), #/*  7 */ 
        (10000, -5), #/*  8 */ 
        (11500, -6), #/*  9 */ 
        (13000,  0), #/* 10 */ 
        (14500,  6), #/* 11 */ 
    ]

    phase = 0
    vel = 0
    pos = 0
    t_ms = 0
    
    while (phase < len(ctrl)):
        while (t_ms < (ctrl[phase])[0]):
            vel += ( (ctrl[phase])[1] * g_Interval)
            pos += vel*g_Interval
            t_ms += g_Interval
            yield int(pos)
        phase += 1

#  CSPモードへの変更処理
#--------------------------------------
def ReqChangeCSPmode():
    val = 8                 # 8:CSP mode
    RTedge.EgTagWrite("Motor1_ModesOfOperation", bytes([val]))

#  現在のサーボステートの取得
#--------------------------------------
def GetServoState():
    data = RTedge.EgTagRead("Motor1_Statusword", 2)
    status_word = int.from_bytes(data, byteorder='little')
    return status_word

#  サーボステート変更要求
#--------------------------------------
def ReqServoState(newval):
    data = newval.to_bytes(2, byteorder='little')
    RTedge.EgTagWrite("Motor1_Controlword", data)

#  目標位置変更要求
#--------------------------------------
def UpdateTargetPos(pos):
    data = pos.to_bytes(4, byteorder='little', signed=True)
    RTedge.EgTagWrite("Motor1_TargetPosition", data)

#  動作開始要求確認
#--------------------------------------
def IsReqStartRun():
    data = RTedge.EgTagRead("DEMO_RUN", 1)    
    isRun = int.from_bytes(data, byteorder='little')
    return True if isRun else False

#  動作終了要求の確認
#--------------------------------------
def IsReqStop():
    data = RTedge.EgTagRead("Stop_set", 1)    
    isRun = int.from_bytes(data, byteorder='little')
    data = RTedge.EgTagRead("DEMO_SHUTDOWN", 1)    
    isRun |= int.from_bytes(data, byteorder='little')
    return True if isRun else False

def IsReqShutdown():
    data = RTedge.EgTagRead("DEMO_SHUTDOWN", 1)    
    isRun = int.from_bytes(data, byteorder='little')
    return True if isRun else False

def ClearReqStartRun():
    data = 0
    RTedge.EgTagWrite("DEMO_RUN", bytes([data])) # タグへの書き込み
def ClearReqStop():
    data = 0
    RTedge.EgTagWrite("Stop_set", bytes([data])) # タグへの書き込み
def ClearReqShutdown():
    data = 0
    RTedge.EgTagWrite("DEMO_SHUTDOWN", bytes([data])) # タグへの書き込み

#  HMI 表示情報の更新
#--------------------------------------
def UpdateDispTotalMoment(newval):
    data = newval.to_bytes(4, byteorder='little', signed=True)
    RTedge.EgTagWrite("TotalMovement", data)

#  HMI 表示情報の更新
#--------------------------------------
def UpdateDispSpeed(newval):
    data = newval.to_bytes(4, byteorder='little', signed=True)
    RTedge.EgTagWrite("Speed", data)

#  現在のステートから、次のステート要求値を算出
#--------------------------------------
def ChangeMotionState(status_word, control_word):
    """
    CiA402 サーボドライバ状態遷移処理
    
    Args:
        status_word (unsigned short): 現在のステータスワード
        control_word (unsigned short): 制御ワード(参照渡し相当)
        
    Returns:
        tuple: (新しい制御ワード, 状態)
    """
    state = status_word & 0x6F
    new_control_word = control_word
    
    if state == 0x00:      # 未初期化状態 -> 初期化要求
        new_control_word = (control_word & 0xFF70) | 0x00
    elif state == 0x40:    # 初期化完了状態 -> 主回路電源OFF要求
        new_control_word = 0x06
    elif state == 0x60:    # 初期化完了状態 -> 主回路電源OFF要求
        new_control_word = (control_word & 0xFF70) | 0x06
    elif state == 0x21:    # 主回路電源OFF状態 -> サーボレディ要求
        new_control_word = (control_word & 0xFF70) | 0x07
    elif state == 0x23:    # サーボレディ状態 -> サーボON要求
        new_control_word = (control_word & 0xFF70) | 0x0F
    elif state == 0x27:    # サーボON状態 (運転開始OK状態)
        pass
    elif state == 0x2F:    # 異常処理動作中状態
        pass
    elif state == 0x28:    # 異常状態 -> 初期化要求
        new_control_word = (control_word & 0xFF70) | 0x80
        
    return new_control_word, state

# 開始要求の待機
#--------------------------------------
def WaitForReqStart():
    while True:
        if IsReqStartRun():
            break
        INtime.RtSleep(100)

#  (デバッグ用関数)
#--------------------------------------
def Break(prompt):
    print(prompt)
    input()

#  メインエントリー
#--------------------------------------
def main():
        
    # CiA402 動作モードの変更
    #  サイクリック同期位置モード へ変更要求
    ReqChangeCSPmode();
       
    # CiA402 サーボドライバ状態を、サーボONに遷移
    control_word = 0
    status_word = 0

    while True:
        # ポーリングモード: 1msec周期
        INtime.RtSleep(1)
                
        # 現在のサーボドライバ状態取得
        status_word = GetServoState()
              
        # 現在のサーボドライバ状態から、次のサーボドライバ状態へ遷移
        control_word, state = ChangeMotionState(status_word, control_word)
        
        if state == 0x27:  # サーボON状態
            break
        
        # 次のサーボドライバ遷移値を書き込み
        ReqServoState(control_word)
        
    
    # サーボON後 100msec待機する(メーカー仕様)
    INtime.RtSleep(100)
    
    # モーター動作処理
    target_pos = 0
    target_pos_old = 0
    counter = 0
    is_req_shutdown = False

    # モーション制御処理ループ
    while is_req_shutdown == False:
        while True:
            
            # 終了要求の確認
            if IsReqStop():
                break
            
            for next_target_pos in generate_wave():
                
                # ループ毎に100分移動要求
                UpdateTargetPos(target_pos)

                # HMI表示データ更新
                UpdateDispTotalMoment(next_target_pos)
                UpdateDispSpeed(int((next_target_pos - target_pos) / g_Interval))  

                # ポーリングモード: 1tick周期
                INtime.knRtSleep(1)
                
                target_pos = next_target_pos

        # クリア処理
        ClearReqStartRun()
        ClearReqStop()
        
        # 開始要求の待機
        while True:
            # デモプログラム終了要求の確認
            if IsReqShutdown():
                is_req_shutdown = True
                break
            if IsReqStartRun():
                break
            INtime.RtSleep(100)
    
    # サーボOFF
    INtime.RtSleep(100)     # ちょっと待つ
    ReqServoState(0)

    # 終了
    UpdateDispTotalMoment(0)
    UpdateDispSpeed(0)
    ClearReqShutdown()

if __name__ == "__main__":
    main()