Generative AIを使用したIoT機能を備えたラボ機器の改修

Ari Mahpour
|  投稿日 2024/04/4, 木曜日  |  更新日 2024/07/1, 月曜日
IoT機能を備えたラボ機器の改造に、Generative AIを使用

古い電源をスマートフォンで制御可能に改造するでは、古い電源にIoT機能を追加して新たな命を吹き込みました。この記事では、ラボ機器(特にVISAを使用して制御されるもの)にIoT機能をもたらすための、より合理的な方法を見ていきます。最初から構築するのではなく、Generative AIを活用して重労働の大部分を行います。このチュートリアルを通じて、ラボ機器のためのWeb対応ドライバーを構築し、全体的な開発を加速するためのコンセプトを身につけることができるはずです。

基本

この記事では、機器自体とインターネットの間の仲介役となるWebサービスを組み立てることに焦点を当てます。以下は、機器とインターネット(つまり、ウェブサイトを介したアクセス)とのエンドツーエンドの接続を示した図です。

ブロック図

図1: 機器とインターネットとのエンドツーエンド通信

それを行う前に、すべての機器ドライバーを自分たちで書く必要は完全にはないということを確立しておくことが重要です。メーカーから提供されているものや、オープンソースのリポジトリを活用することができます。簡潔にするため、私はオンラインで見つけたものを活用しますが、Generative AIを使用して自分が満足するフレームワークを組み立てます。

私はRigolのDP832電源とDL3021電子負荷を使用しています。GitHubで簡単に検索したところ、DP832電源用のPythonライブラリが見つかり、始めるために必要なすべてのSCPIコマンドが含まれていました。別のアプローチとしては、DP832マニュアルからSCPIコマンドのリストを取り、それを大規模言語モデル(LLM)、例えばChatGPTやGeminiに渡し、関数を生成してもらうこともできます。私は少し選り好みするので、自分で定義したPyVISAのセットアッププロセスを使用し、既に行われていることの大部分を活用するつもりです。以下は私の一般的なPyVISAセットアッププロセスです(別のクラスにカプセル化されています): 

class CommBase:

   USBConnType = Literal["USBTMC", "VISA", "Socket"]

    def __init__(self, usb_conn_type: USBConnType, vid: int = None, pid: int = None, visa_resource_prefix: str = None):

        self.visa_resource_prefix = visa_resource_prefix

        self.usb_conn_type = usb_conn_type

        if usb_conn_type == "USBTMC":

            self.configure_usbtmc(vid, pid)

        elif usb_conn_type == "VISA":

```             self.configure_visa(vid, pid) ``` ```         elif usb_conn_type == "Socket": ``` ```             pass ``` ```         else: ``` ```             raise ValueError(f"Invalid USB connection type: {usb_conn_type}. Valid types are {self.VALID_USB_CONN_TYPES}") ``` ```   ``` ```     def configure_usbtmc(self, vid: int, pid: int): ``` ```         self.inst = usbtmc.Instrument(vid, pid) ``` ```   ``` ```     def configure_visa(self, vid: int, pid: int): ``` ```         self.rm = pyvisa.ResourceManager() ``` ```         instrument_list = self.rm.list_resources() ``` ```         visa_address = self.find_visa_resource(vid, pid, instrument_list, prefix=self.visa_resource_prefix) ``` ```         if visa_address is not None: ``` ```             self.inst = self.rm.open_resource(visa_address, read_termination="\n") ``` ```         else: ``` ```             raise IOError(f'No VISA devices found using vid "{vid}" and pid "{pid}" but the following VISA devices have been found: {instrument_list}') ``` ```   ``` ```     @staticmethod ``` ```     def find_visa_resource(vid: int, pid: int, resource_strings: list, prefix: str) -> str: ``` ```         hex_vid, hex_pid = f"0x{vid:X}", f"0x{pid:X}" ``` ```         dec_vid, dec_pid = str(vid), str(pid) ``` ```         for resource in resource_strings: ``` ```             parts = resource.split("::") ``` ```             if len(parts) >= 4: ``` ```                 serial_and_more = parts[3] ``` ```                 if (any(x in resource for x in (hex_vid, dec_vid)) and any(x in resource for x in (hex_pid, dec_pid)) and serial_and_more.startswith(prefix)):

 

                    return resource

        return None

 

    def query_device(self, command: str) -> str:

        if self.usb_conn_type == "USBTMC":

            return self.inst.ask(command)

        elif self.usb_conn_type == "VISA":

            return self.inst.query(command)

        else:

            raise NotImplementedError(f"Query method for {self.usb_conn_type} not found.")

 

    def write_device(self, command: str):

        self.inst.write(command)

 

    def close(self):

        self.inst.close()

        if self.usb_conn_type == "VISA":

            self.rm.close()

 

    def id(self) -> dict:

        id_str = self.query_device("*IDN?").strip().split(",")

        return {

            "manufacturer": id_str[0],

            "model": id_str[1],

            "serial_number": id_str[2],

            "version": id_str[3],

        }

```

コメント、余分なスペース、さらにはいくつかの設定シーケンス(ログ記録など)を全て削除しました。簡潔にするためです。ご覧の通り、いくつかの汎用関数とPyVISAおよびUSBTMCの両方をサポートしています。オンラインで見つかるほとんどのSCPIベースのPythonライブラリには、この基本機能がありません。それは問題ありません、なぜならこのクラスを私の新しいクラスに拡張できるからです:

 

from base.CommBase import CommBase

class DP(CommBase):

    def channel_check(self, channel):

        assert NotImplementedError

 

    def get_output_mode(self, channel: int) -> str:

        self.channel_check(channel)

        return self.query_device(f":OUTP:MODE? CH{channel}").strip()

 

    # … コードは簡潔にするために削除されました

 

    def measure_current(self, channel):

        self.channel_check(channel)

        meas = self.query_device(f":MEAS:CURR? CH{channel}").strip()

        return float(meas)

 

    def measure_voltage(self, channel):

        self.channel_check(channel)

        meas = self.query_device(f":MEAS? CH{channel}").strip()

        return float(meas)

 

    def measure_all(self, channel):

        self.channel_check(channel)

        meas = self.query_device(f":MEAS:ALL? CH{channel}").strip().split(",")

        return {

            "voltage": float(meas[0]),

            "current": float(meas[1]),

            "power": float(meas[2]),

        }

 

class DP712(DP):

    def channel_check(self, channel):

        assert channel in [1, ""], f"出力チャネル {channel} はサポートされていません"

 

class DP821(DP):

    def channel_check(self, channel):

        assert channel in [1, 2, ""], f"出力チャネル {channel} はサポートされていません"

 

class DP832(DP):

    def channel_check(self, channel):

        assert channel in [1, 2, 3, ""], f"出力チャネル {channel} はサポートされていません"

オンラインで様々な例をいくつか取り上げ、それらをChatGPTに渡しました。私がそれに求めたのは以下の通りです:

  1. ファイルをクラスに変換する
  2. 各関数にドックストリングスタイルのコメントブロックを追加する
  3. これらの関数を検証するテストを書く(別のクラスとして)

このタスクに対して生成AIを活用することで、数時間の作業を60秒に短縮することができました。また、非常に迅速にオンラインで見つけたドライバーを自動生成されたテストを実行することで検証することができました。例えば、電子負荷のDL3021の操作モードを設定するためのSCPIコマンドが実際には間違っていることを非常に迅速に発見しました。テストをリアルタイムで実行していると、私の機器上でモードが変更されていないことに気づきました。SCPIマニュアルを素早く調べることで、それを修正することができました。

Webサービス

この時点で、私たちは機器を制御するためのPythonライブラリに良い基盤を持っています。次の目標は、Web上でそれらを制御できるようにするために、機器ライブラリの前にWebサービスを配置することです。Webフレームワークについて何も知らないと仮定しても、ChatGPT(または任意のLLM)に全機能を実行してもらうことができます。これが私が使ったプロンプトです:

基本的なURL(Webブラウザ経由)を使用して私の機器を制御するWebサービスを作成する必要があります。Webサービスやフレームワークについてあまり知りませんが、Pythonには慣れています。このタスクを達成するための完全なPythonコードを書いてください。

これが私の機器を制御するクラスです:

<pre><code>```

{上記のコード}

```

</code></pre>

そして(部分的な応答):

ChatGPTプロンプトの応答

図2: ChatGPTプロンプトからの応答

高度な(有料の)LLMが好きな理由は、それらが本当にタスクを細分化し、教育し、第一の反復としてかなり良い解決策を提供してくれることです。Gemini vs. ChatGPT: どちらがより良いコードを書くか?では、2つのLLMを比較し、(ネタバレ注意)Googleの最新で最も進んだバージョンのGeminiが実際にはかなり良い(ChatGPT 4と同等である)ことを発見しました。これらのLLMのいずれかを使用すると、上に示したような結果が得られます。

これをクラスに入れたり、再フォーマットしたりしたい場合は、チャットに返信してリクエストをするだけです。コードにコメントを付けたいですか?問題ありません、ただ尋ねてください!

プロンプト: 各関数にdocstringスタイルのコメントブロックを追加します。その結果、クラス全体を書き直して、コードエディタにコピー&ペーストできるようにしてください。

ChatGPTプロンプト

図3: ChatGPTプロンプトからの次の応答

テスト

Generative AIにPythonモジュールを作成してもらった今、テストの書き方を教えてもらうか、手動でテストする方法を示してもらうことができます:

テストURL

図4: ChatGPTからの手動テストURL

自動テスト

図5: ChatGPTによって書かれた自動テスト

これらのテストを実行(または微調整してから実行)した後、ウェブと私たちの機器の間のインターフェースとなる新しいPythonモジュールを検証できるはずです。

結論

この記事では、ウェブ上の既存のコードと生成型AIを活用して、私たちの機器を制御するPythonモジュールを組み立てました。LLMがモジュールを完成させた後、インターネットと機器自体(ウェブサービスを介して)の間に仲介する新しいモジュールを導入しました。また、生成型AIがマニュアルまたは自動テストを含む全プロセスをどのように案内してくれるかについても簡単に見ていきました。この演習の最良の部分は、正しく行えば、組み立てるのにほとんど時間がかからないはずだということです。最初は少し余分な努力が必要かもしれませんが、多くの機器にこのプロセスを繰り返すことは簡単であり(そして非常に少ない開発時間を要するはずです)。生成型AIが私たち全員のための風景を完全に変えたことは明らかで、この記事はそれを本当に示しています。願わくば、生成型AIを活用した次のセットの機器ライブラリを作成し、それをコミュニティに貢献することにインスパイアされるでしょう。

このプロジェクトで使用されたすべてのソースコードは、https://gitlab.com/ai-examples/instrument-controllablesで見つけることができます。

筆者について

筆者について

Ariは、設計、デバイスパッケージ、テスト、および電気、機械、およびソフトウェアシステムの統合において幅広い経験を持つエンジニアです。彼は、設計/デザイン、検証、テストのエンジニアをまとめて団結したグループとして機能させることに情熱を注いでいます。

関連リソース

関連する技術文書

ホームに戻る
Thank you, you are now subscribed to updates.