この記事ではラズパイと三菱PLCをネットワーク経由で連携させる方法を紹介します。FAでもIoTニーズが高まってきていますよね。といってもPLCと連動してくれないと出来ることは限られる。でもIO配線はめんどくさい。というわけでラズパイをネットワーク経由でPLCと連携させる方法を紹介します。トリガーをPLCからラズパイへ送り、画像処理させて、結果をPLCへ返すような動きに使えます。記事ではLAN直結でテストしていますが、工場に生産工程用のWiFiがあれば電源配線だけで済むのでかなり使えそうですね。
イーサネットユニットでの接続はこちら。
ラズパイPythonと三菱PLCの連携【イーサネットユニット編】この記事でわかること
ラズパイと三菱PLC(シーケンサ)をネットワーク接続し、ラズパイ上のPythonからMCプロトコルでPLCのIOを読み書きする方法がわかります。動作確認のサンプルとしてオルタネート回路をラズパイのPythonで走らせます。三菱PLC側のラダーを書く必要がありますが数行程度です。ラズパイ側はサンプルコードコピペでOKです。
- 三菱PLC設定方法
- ラズパイIP設定方法
- Pythonライブラリ pymcprotocolインストール方法
- pymcprotocolを使った通信方法
前提条件
まず前提条件となる私の環境です。
Raspberry Pi | 4 Model B 8GB |
---|---|
Raspberry Pi OS | Raspbian GNU/Linux 10 (buster) |
VSCode | 1.55.2 |
Python | 3.7.3 |
pymcprotocol | 0.3.0 |
三菱PLC | Q03UDECPU CPU-LANポート使用 |
GX Works2 | 1.586L |
ラズパイのPython開発環境はVisual Studio Codeを使用します。インストールはこちらを参照してください。
【IoT×キャンプ|Python開発環境VSCode編】意地でもラズパイとキャンプへ!らずキャン△プロジェクト第6回 RealSense L515をラズパイ4 Raspberry Pi OSのPythonから起動する最速手順&サンプルコード通信設定
三菱PLCパラメータ設定
まずはPLCを設定します。PLCのIPアドレスはローカル192.168.1.2を使用します。MCプロトコルはTCPポートを使用します。ラズパイとPLCをLANケーブル直結する場合はIPアドレスの設定だけでOKです。IPとMCプロトコルの設定方法はこちらを参照してください。
GX Works2でSLMP通信を試すための内蔵Ethernet設定 - Qiitaテストに使用するIPとポート
PLCのIPアドレス | 192.168.1.2 |
---|---|
MCプロトコルのポート | TCP 1026 |
設定が完了したらPLCを再起動してください。再起動しないとパラメータ設定が反映されません。
ラズパイのIP設定
ラズパイのIPを192.168.1.3に設定します。 こちらもLANでPLC直結の場合はIPアドレスだけセットすればOKです。右上のネットワーク設定からeth0を選択してIPを入力するだけでOKです。
ラズパイのIPアドレス | 192.168.1.3 |
---|
pymcprotocolのインストール
PythonでMCプロトコルを扱うためのライブラリをインストールします。LXTerminalをひらいて次のコマンドを実行してください。
pip install pymcprotocol
インストールは以上です。ドキュメントはこちら。
Welcome to pymcprotocol's documentation! - pymcprotocol '0.3.0' documentationLXTerminalでPythonを立ち上げてimportすることで動作確認できます。
python3
import pymcprotocol
import後にエラーがなければインストールに問題はありません。
pymcprotocolを使った通信テスト
テスト準備
準備できましたので実際にテストしていきます。ラズパイとPLCをLANケーブルで直接接続しましょう。
テストの動作パターンですが、PLC側でY0出力ON/OFFを繰り返し、それをラズパイ側で読み取ってオルタネート出力をPLCのX0に書き込むようにします。オルタネートについてはこちらを参照してください。
シーケンサのオルタネイト回路についてラダー記述
テスト用のラダーを記述します。出力Y0をON/OFF繰り返す回路です。とりあえず1秒ON、1秒OFFにします。
M1、M2はただのモニタ用です。編集が終わったらPLCに書き込んでRUNになっているか確認してください。MCプロトコルでの通信確認が目的ですので、ラダーは正直なんでも良いです。面倒であればY0を強制ON/OFFでもかまいません。
Pythonコード実行
VSCodeで新しくpyファイルを作成し、そこに以下のコードをコピペしてください。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from logging import getLogger, StreamHandler, DEBUG, INFO, FileHandler, Formatter
from logging.handlers import TimedRotatingFileHandler
import datetime
import time
import os
import pymcprotocol
# 接続設定
TARGET_PLC_IP = '192.168.1.2'
PORT = 1026
# 先頭デバイスアドレス
HEAD_X = 'X0'
HEAD_Y = 'Y0'
def main():
# Mitsubishi Qシリーズに接続
pymc = pymcprotocol.Type3E()
connect_plc(pymc)
# フラグ初期化
flag_work_done = False
try:
while True:
try:
# 配列初期化
bitunits_Xs = []
bitunits_Ys = []
# PLCのXとYをそれぞれ0から16点読み出し
bitunits_Xs = pymc.batchread_bitunits(headdevice= HEAD_X, readsize=16)
bitunits_Ys = pymc.batchread_bitunits(headdevice= HEAD_Y, readsize=16)
# オルタネート回路
alternate_circuit(pymc, bitunits_Xs, bitunits_Ys)
except Exception as e:
logger.error(f'def main: {e}')
pymc.close()
time.sleep(5)
logger.info('再接続開始')
connect_plc(pymc)
except Exception as e:
logger.error(f'main: {e}')
finally:
pymc.close()
logger.info('終了')
def connect_plc(pymc):
try:
pymc.connect(TARGET_PLC_IP, PORT)
logger.info('PLC接続完了')
except Exception as e:
logger.error(f'def connect_plc: {e}')
def alternate_circuit(pymc, bitunits_Xs, bitunits_Ys):
global flag_work_done
if bitunits_Ys[0] == 0:
flag_work_done = False # ワンショットメモリ
# XがOFFなら1回だけX0をONにしてX0から16点書き込み
if bitunits_Ys[0] == 1 and bitunits_Xs[0] == 0 and not flag_work_done:
flag_work_done = True
logger.debug(f'Y0 ON検出: {bitunits_Ys}')
bitunits_Xs[0] = 1
pymc.batchwrite_bitunits(headdevice=HEAD_X, values=bitunits_Xs)
logger.debug(f'X0 ON書き込み: {bitunits_Xs}')
# X0がONなら1回だけX0をOFFにしてX0から16点書き込み
if bitunits_Ys[0] == 1 and bitunits_Xs[0] == 1 and not flag_work_done:
flag_work_done = True
logger.debug(f'Y0 ON検出: {bitunits_Ys}')
bitunits_Xs[0] = 0
pymc.batchwrite_bitunits(headdevice=HEAD_X, values=bitunits_Xs)
logger.debug(f'X0 OFF書き込み: {bitunits_Xs}')
def set_log_config(logger):
# Log出力先フォルダ
LOG_PATH = 'MCProtocol/Log/'
os.makedirs(LOG_PATH, exist_ok=True) #フォルダが無い場合は作成
# handler1 ターミナル用
handler1 = StreamHandler()
handler1.setLevel(DEBUG)
handler1.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s"))
# handler2 ファイル出力用
filename = LOG_PATH + "MCProtocol.log"
handler2 = TimedRotatingFileHandler(filename = filename, encoding='utf-8', when='midnight', backupCount=30)
handler2.setLevel(INFO)
handler2.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s"))
logger.info(f'set_log_config: Path = {filename}')
# loggerにハンドラ設定
logger.setLevel(DEBUG)
logger.addHandler(handler1)
logger.addHandler(handler2)
logger.propagate = False
return logger
#### Log Config ####
logger = getLogger(__name__)
logger = set_log_config(logger)
## End of Log config
#### Start ####
if __name__ == '__main__':
main()
LAN接続状態で実行するとPLCのY0がONするたびにX0の状態が切り替わります。
ポイントは、PLCとの通信に失敗した場合5秒後に自動的に再接続を試みるところです。このようなコードにしておかないと通信が切れた場合ソフトを再起動しないといけません。再起動が必要ではFA用途としては全然ダメですよね。
停止したいときはターミナルにカーソルを合わせCtrl+Cです。
Pythonコードの簡単な解説
PLCへの接続
ドキュメントにあるとおりまずはPLCのシリーズを指定してインスタンスを作ります。AとFXは非対応ですのでご注意を。
Welcome to pymcprotocol's documentation! - pymcprotocol '0.3.0' documentationconnectメソッドを実行するとPLCに接続します。引数に、上で設定したPLCのIPアドレスとMCプロトコルのTCPポート番号を渡します。
PLCのIO読み書き
読み出しは先頭アドレスと読み出し点数を指定します。
# PLCのXとYをそれぞれ0から16点読み出し
bitunits_Xs = pymc.batchread_bitunits(headdevice= HEAD_X, readsize=16)
bitunits_Ys = pymc.batchread_bitunits(headdevice= HEAD_Y, readsize=16)
書き込みは先頭アドレスを指定し、値にはリストを渡します。先頭アドレスから連続した領域にリストのサイズ分が書き込まれます。
pymc.batchwrite_bitunits(headdevice=HEAD_X, values=bitunits_Xs)
ログ出力
常時稼働することを想定してログ出力を組んでいます。0時に自動的に新しいログファイルに切り替わります。30ファイルたまると古いものから自動削除。logger.debug()はログ記録されずターミナルに表示するだけ。logger.info()以上はログに記録されます。
#ログ出力
from logging import getLogger, StreamHandler, DEBUG, INFO, FileHandler, Formatter
from logging.handlers import TimedRotatingFileHandler
def set_log_config(logger):
# Log出力先フォルダ
LOG_PATH = 'MCProtocol/Log/'
os.makedirs(LOG_PATH, exist_ok=True) #フォルダが無い場合は作成
# handler1 ターミナル用
handler1 = StreamHandler()
handler1.setLevel(DEBUG)
handler1.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s"))
# handler2 ファイル出力用
filename = LOG_PATH + "MCProtocol.log"
handler2 = TimedRotatingFileHandler(filename = filename, encoding='utf-8', when='midnight', backupCount=30)
handler2.setLevel(INFO)
handler2.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s"))
logger.info(f'set_log_config: Path = {filename}')
# loggerにハンドラ設定
logger.setLevel(DEBUG)
logger.addHandler(handler1)
logger.addHandler(handler2)
logger.propagate = False
return logger
#### Log Config ####
logger = getLogger(__name__)
logger = set_log_config(logger)
logger.debug("Test_debug")
logger.info("Test_info")
logger.error("Test_error")
最後に
いかがでしたか。これでPLCからトリガーをネットワーク経由でラズパイに送ることができますね。LiDAR×IoT×AIは響き的にはゲームチェンジャーの予感しかしない!RealSenseをラズパイOSのPythonから起動するための記事はこちら。
RealSense L515をラズパイ4 Raspberry Pi OSのPythonから起動する最速手順&サンプルコード ラズパイのCPUクロックをPythonでPID制御しCPU温度コントロールイーサネットユニットでの接続はこちら。
ラズパイPythonと三菱PLCの連携【イーサネットユニット編】
0 件のコメント:
コメントを投稿