【実機解説】PythonでKeyence PLCのデータをSQL Serverに自動保存する方法|設備IoTの土台づくり

fa-engineer

導入:結論から先に

PLCから取得したデータは、Pythonと無料のSQL Serverを使えば、データベースに自動で溜め続けることができます。

これまでの記事で、PythonでKeyence PLCのデバイス値を読み出す方法や、FANUCロボットの電流値・位置座標をPLCで常時取得する方法を解説してきました。値が「取れる」ようになると、次に必ずぶつかるのがこの疑問です。

取った値を、どこに・どうやって溜めればいいのか?

CSVファイルに書き出す方法もありますが、データが増えると管理しきれなくなり、あとから「グラフ化したい」「異常検知したい」と思っても加工が大変です。最初からデータベース(DB)に溜めておくのが、設備IoTの王道です。

この記事では、私が実際に構築した流れをそのまま、次の3点に絞って解説します。

  • 無料で使えるSQL Server Expressの導入(お金はかかりません)
  • 設備が増えても困らないテーブル設計(実務で効いた設計の勘所)
  • コピペで動くPythonコード(1行ずつコメント付き)

専門用語はできるだけ補足しながら進めます。データベースが初めての方でも、上から順にやれば動くところまで書きます。

全体構成:今回作るのはどこか

設備IoTの全体像の中で、今回作るのは「Python → SQL Server」の部分です。

PLC(電流値・座標などの値)
   │  ← 読み出し:前回までの記事で解説済み
   ▼
Python(値を受け取って整形)
   │  ← ★今回はここを作る
   ▼
SQL Server(データベースに蓄積)
   │
   ▼
グラフで見える化/異常検知(次のステップ)

PLCからの読み出し部分は別記事で解説済みなので、この記事では「受け取った値をデータベースに保存する」部分に集中します。そのため、動作確認ではPLCの値を模擬した数値を使います(読み出しと組み合わせれば、そのまま実機構成になります)。

この記事で使う環境

役割ソフト費用
データベース本体SQL Server 2022 Express無料
DB管理ツールSSMS 22(SQL Server Management Studio)無料
プログラムPython 3(今回は3.12で確認)無料
PythonからDBへ接続する部品pyodbc+ODBC Driver 18無料

SQL Serverを選ぶ理由:製造業の現場システムでは、Windowsとの相性が良いSQL Serverが事実上の標準です。無料のExpress版でも、設備数台ぶんのデータ収集には十分すぎる性能があります。

準備①:SQL Server 2022 Express を入れる(無料)

Microsoft公式ページから、無料のExpress版をダウンロードします。

👉 Microsoft公式:SQL Server 2022 Express ダウンロードページ

  1. ページの「ダウンロード」から SQL2022-SSEI-Expr.exe(約4MB)を入手して実行
  2. 最初の選択画面で「基本(Basic)」を選ぶ(今回はこれで十分です)
  3. あとは同意して進めるだけ。本体をダウンロードしながらインストールされます

インストールが完了すると、接続に必要な情報が表示されます。この画面の「インスタンス名」(標準では SQLEXPRESS)を覚えておいてください。あとでPythonからの接続に使います。

準備②:SSMS(管理ツール)を入れる

次に、データベースの中身を見たりテーブルを作ったりする管理ツール「SSMS」を入れます。こちらも無料です。

👉 Microsoft公式:SSMSのインストールページ(「Download the SQL Server Management Studio 22 installer」からダウンロード)

  1. vs_SSMS.exe を実行
  2. 「Visual Studio Installer」という画面が開くが、チェックは何も追加せずそのまま「インストール」でOK(コア機能だけで十分)

準備③:SSMSでSQL Serverに接続する

SSMSを起動すると接続画面が出ます。次のように入力します。

  • サーバー名localhost\SQLEXPRESS(自分のPCの中のSQL Server、という意味)
  • 認証:Windows認証(パスワード入力は不要)
  • 「サーバー証明書を信頼する」に必ずチェック ← ここが最初のつまずきポイント!

なぜチェックが必要?:最近のSSMSは通信の暗号化が標準でオンになっています。自分のPC内の接続では簡易的な証明書が使われるため、チェックを入れないと「証明書チェーンは信頼されていない機関によって発行されました」というエラーで接続できません。ローカル接続なので信頼して問題ありません。

【設計の勘所】設備が増えても困らないテーブル設計

ここがこの記事でいちばん伝えたいことです。コードよりも大事です。

データを溜めるテーブル(表)を作るとき、初心者がやりがちな設計がこれです。

  • ❌ 設備が増えるたびに、新しいテーブルを作る(robot1_log、robot2_log…)
  • ❌ 設備が増えるたびに、列を増やす(robot1_x、robot2_x…)

これをやると、設備が1台増えるたびにテーブルもプログラムも作り直しになり、運用が破綻します。

私が実務のデータ収集システムで採用して効果があったのは、次の形です。

  • 「設備リスト」のテーブルを別に持ち、各設備にIDを振る
  • ✅ データ収集テーブルには「どの設備の値か(設備ID)」を1列持たせるだけ
  • ✅ 2つのテーブルを設備IDでリレーション(関連付け)する
テーブル役割主な列
equipment(設備リスト)設備の台帳equipment_id(主キー)/equipment_name/line
robot_log(データ収集)値を溜める本体equipment_id(設備リストと紐づく外部キー)/recorded_at/pos_x・pos_y・pos_z/current_j1

2つのテーブルは equipment_idリレーション(関連付け)されています。

この形にしておくと、設備が3台目、4台目と増えても「設備リストに1行追加するだけ」。データ収集テーブルもプログラムも一切変更不要です。逆に、最初にこの設計をサボると、後からの変更は本当に大変です。

データベースとテーブルを作る(コピペで完成)

SSMSの「新しいクエリ」を開き、次のSQLを貼り付けてF5(実行)してください。データベース作成からテーブル2つ、サンプル設備の登録まで一度に完了します。

-- ① データ収集用のデータベースを作成
IF DB_ID('plc_iot') IS NULL
    CREATE DATABASE plc_iot;
GO

USE plc_iot;
GO

-- ② 設備リスト(台帳):設備IDを主キーに持つ
CREATE TABLE equipment (
    equipment_id   INT           PRIMARY KEY,   -- 設備ID(主キー)
    equipment_name NVARCHAR(50)  NOT NULL,      -- 設備名
    line           NVARCHAR(20)  NULL           -- ライン名
);
GO

-- ③ データ収集(本体):設備IDで設備リストと紐づく
CREATE TABLE robot_log (
    id           BIGINT        IDENTITY(1,1) PRIMARY KEY,  -- 連番(自動採番)
    equipment_id INT           NOT NULL,                   -- どの設備の値か
    recorded_at  DATETIME2(0)  NOT NULL,                   -- 記録時刻
    pos_x        FLOAT         NULL,                       -- 現在位置X
    pos_y        FLOAT         NULL,                       -- 現在位置Y
    pos_z        FLOAT         NULL,                       -- 現在位置Z
    current_j1   FLOAT         NULL,                       -- J1軸の電流値
    CONSTRAINT fk_robot_log_equipment                      -- 設備リストとのリレーション
        FOREIGN KEY (equipment_id) REFERENCES equipment(equipment_id)
);
GO

-- ④ 設備リストに設備を登録(増えたらここに足すだけ)
INSERT INTO equipment (equipment_id, equipment_name, line) VALUES
    (1, N'FANUC R-30iB #1', N'A'),
    (2, N'FANUC R-30iB #2', N'A');
GO

「コマンドは正常に完了しました」と出れば成功です。左のツリーを更新すると、plc_iot の中に equipmentrobot_log の2テーブルができています。

列の内容(pos_xやcurrent_j1)は例です。ご自身が集めたい値に合わせて増減してください。前回の記事のように、ロボットの座標・電流値をPLCに取得している場合は、そのデバイス値をそのまま入れる想定です。

Python側の準備:pyodbcを入れる

PythonからSQL Serverに接続するための部品「pyodbc」を入れます。コマンドプロンプトで1行です。

pip install pyodbc

接続には「ODBCドライバ」も必要ですが、SQL Serverを入れると「ODBC Driver 18 for SQL Server」が一緒に入っていることが多いです。次の1行で、自分のPCに入っているドライバを確認できます。

python -c "import pyodbc; print(pyodbc.drivers())"

表示された一覧に ODBC Driver 18 for SQL Server(または17)があればOKです。

Pythonで保存する(コピペで動くコード)

いよいよ本体です。値を読み取り、設備IDと一緒にSQL Serverへ保存するコードです。1行ずつコメントを付けています。

# -*- coding: utf-8 -*-
import pyodbc                        # SQL Serverに接続するための部品
import random                        # 今回はPLCの代わりに模擬値を作る
from datetime import datetime        # 記録時刻を入れる

# --- SQL Serverへの接続設定 ---
CONN_STR = (
    "DRIVER={ODBC Driver 18 for SQL Server};"  # 使うODBCドライバ
    "SERVER=localhost\\SQLEXPRESS;"            # サーバー名(\は2つ重ねる)
    "DATABASE=plc_iot;"                        # 使うデータベース
    "Trusted_Connection=yes;"                  # Windows認証(パスワード不要)
    "Encrypt=yes;TrustServerCertificate=yes;"  # ローカルの証明書を信頼する
)

def read_plc_values():
    # 本来はPLCから読み出す部分(読み出し方法は別記事参照)。
    # ここでは動作確認のため、それらしい模擬値を返す。
    return {
        "pos_x": round(random.uniform(90, 110), 1),     # 現在位置X
        "pos_y": round(random.uniform(-10, 10), 1),     # 現在位置Y
        "pos_z": round(random.uniform(400, 420), 1),    # 現在位置Z
        "current_j1": round(random.uniform(1.0, 5.0), 2),  # J1軸電流値
    }

def save(equipment_id, values):
    conn = pyodbc.connect(CONN_STR)            # DBに接続する
    cur = conn.cursor()                        # 命令を出す係を用意
    cur.execute(                               # 1行ぶんをINSERT(保存)
        "INSERT INTO robot_log "
        "(equipment_id, recorded_at, pos_x, pos_y, pos_z, current_j1) "
        "VALUES (?, ?, ?, ?, ?, ?)",           # ?の場所に下の値が順に入る
        equipment_id, datetime.now(),          # 設備IDと現在時刻
        values["pos_x"], values["pos_y"],      # 位置X・Y
        values["pos_z"], values["current_j1"], # 位置Z・電流値
    )
    conn.commit()                              # 確定(忘れると保存されない!)
    conn.close()                               # 接続を閉じる

# --- メイン:60秒ごとに設備1と設備2の値を保存し続ける ---
import time                                    # 待ち時間用
while True:                                    # ずっと繰り返す
    for eq_id in (1, 2):                       # 設備リストにある設備ID分ループ
        save(eq_id, read_plc_values())         # 値を読んで保存
    time.sleep(60)                             # 60秒待って次の記録へ

ポイント:設備が増えたら、SQL側は設備リストに1行足し、コードは (1, 2)(1, 2, 3) にするだけです(設備リストから自動で読む形にもできます)。テーブルは何も変えなくてよいのが、先ほどの設計の効果です。

動作確認:データが溜まっていく様子を見る

スクリプトを動かした状態で、SSMSで robot_log を右クリック →「上位1000行の選択」を実行すると、行がどんどん増えていくのが確認できます。

設備ID(equipment_id)ごとに値が振り分けられて記録されているのがわかります。これで「取る → 溜める」までの流れが完成しました。

つまずきポイント集(実際にハマった所)

今回の構築で実際に遭遇した・遭遇しやすいポイントです。

  • ①「証明書チェーンは信頼されていない機関によって…」エラー
    SSMSなら接続画面の「サーバー証明書を信頼する」にチェック。Pythonなら接続文字列に TrustServerCertificate=yes; を入れます。
  • ② サーバー名の「\」はPythonでは2つ重ねる
    localhost\SQLEXPRESS をPythonの文字列にそのまま書くと警告が出ます。"localhost\\SQLEXPRESS" のように2つ重ねてください。
  • ③ commit() を忘れると保存されない
    エラーも出ないのにデータが増えないときは、まずここを疑ってください。
  • ④ 接続できないときはサービスを確認
    Windowsの「サービス」で SQL Server (SQLEXPRESS) が「実行中」になっているか確認します。
  • ⑤ ODBCドライバ名の不一致
    接続文字列のドライバ名は、先ほどの pyodbc.drivers() で表示された名前と一致させてください。

【締め】「取る→溜める」の次は「活かす」

これで、PLCから取得した値をデータベースに溜め続ける土台ができました。

データベースに溜まったデータは、ここからが本領です。

  • 稼働状況のグラフ化:時系列で並べれば、いつ・どれだけ動いたかが見える
  • 電流値の監視:いつもと違う負荷の傾向を早期に察知
  • 設備間の比較:設備IDで紐づけてあるので、同型設備の比較も簡単

取るのは前回まで、溜めるのが今回。次は「活かす」——可視化・異常検知です。このシリーズで、設備IoTの一気通貫の流れを引き続き解説していきます。

関連記事

お問い合わせ

「設備のデータを溜めて見える化したい」「PLCデータの収集システムを作りたい」

そんなご相談を承っています。PLC側の設定から、データベース設計・蓄積・活用の仕組みづくりまで一気通貫で対応できます。お気軽にお問い合わせください。

ABOUT ME
Yu
Yu
設備エンジニア(フリーランス)
生産技術の現場で10年以上の経験を持ち、制御設計を中心に生産設備の開発全般に携わってきました。 特に、産業用ロボットを活用した自動化設備の開発を得意としており、機械・電気・制御の知識を横断的に活かして業務に取り組んでいます。 近年では、PLCと連携したIoTシステムの構築にも注力しており、PythonやSQL Serverなどを活用したデータ収集・可視化・分析の仕組みづくりにも対応しています。 現場のリアルな課題を解決するための実践的なノウハウを発信していきます。
記事URLをコピーしました