Generative AI를 사용하여 IoT 기능으로 실험실 기기 개조하기

Ari Mahpour
|  작성 날짜: 사월 4, 2024  |  업데이트 날짜: 칠월 1, 2024
IoT 기능을 사용하여 실험실 기기를 개조하는 방법

스마트폰으로 제어 가능한 구형 파워 서플라이로 개조하기에서는 구형 파워 서플라이에 IoT 기능을 추가하여 새로운 생명을 불어넣었습니다. 이번 기사에서는 실험실 장비들(특히 VISA를 사용하여 제어되는 장비들)에 IoT 기능을 보다 간소화된 방법으로 도입하는 방법을 살펴볼 것입니다. 처음부터 구축하는 대신 대부분의 중요 작업을 수행하기 위해 생성 AI를 활용할 것입니다. 이 튜토리얼을 통해 이러한 개념을 실험실 장비의 웹 활성화 드라이버를 구축하고 전반적인 개발 속도를 높일 수 있어야 합니다.

기본 설정

이 기사에서는 장비 자체와 인터넷 사이의 중개자 역할을 하는 웹 서비스를 구성하는 데 중점을 둘 것입니다. 다음은 장비와 인터넷(즉, 웹사이트를 통한 접근) 사이의 종단 간 연결을 보여주는 일러스트레이션입니다.

블록 다이어그램

그림 1: 장비와 인터넷 사이의 종단 간 통신

그러나 그렇게 하기 전에 모든 장비 드라이버를 직접 작성할 필요는 없다는 것을 명확히 하는 것이 중요합니다. 제조업체에서 이미 완성된 것을 온라인에서 활용하거나 오픈 소스 저장소를 활용할 수 있습니다. 간결함을 위해 이미 온라인에서 찾은 것을 활용하지만, 제가 만족하는 프레임워크를 구성하기 위해 생성 AI를 활용할 것입니다.

저는 Rigol의 DP832 파워 서플라이와 DL3021 전자 부하를 사용하고 있습니다. GitHub에서 빠른 검색을 통해 DP832 파워 서플라이에 필요한 모든 SCPI 명령을 포함한 Python 라이브러리를 찾았습니다. 대안적인 접근 방식은 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"{self.usb_conn_type}에 대한 쿼리 메소드를 찾을 수 없습니다.") ``` ``` ``` ``` 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. 각 함수에 대한 docstring 스타일의 주석 블록 추가하기
  3. 이러한 함수를 검증하는 테스트 작성하기 (별도의 클래스로)

이 작업을 위해 생성형 AI를 활용하면 수시간 걸리는 작업을 60초로 단축할 수 있었습니다. 또한, ChatGPT가 작성해 준 자동 생성 테스트를 실행함으로써 온라인에서 찾은 드라이버를 매우 빠르게 검증할 수 있었습니다. 예를 들어, 전자 부하의 작동 모드를 설정하는 DL3021의 SCPI 명령 중 하나가 실제로 잘못되었다는 것을 매우 빠르게 발견했습니다. 테스트가 실시간으로 실행되는 것을 보면서 내 기기에서 모드가 변경되지 않는 것을 알아차렸습니다. SCPI 매뉴얼을 빠르게 조회한 후에 수정할 수 있었습니다.

웹 서비스

이제 우리는 기기를 제어하기 위한 Python 라이브러리에 대한 좋은 기반을 갖추었습니다. 이제 목표는 기기 라이브러리 앞에 웹 서비스를 배치하여 웹을 통해 제어할 수 있는 기능을 제공하는 것입니다. 웹 프레임워크에 대해 아는 것이 없다고 가정하고, ChatGPT(또는 다른 LLM)에게 전체 기능을 수행하도록 요청할 수 있습니다. 시작하기 위해 사용한 프롬프트는 다음과 같습니다:

기본 URL(웹 브라우저를 통해)을 사용하여 내 기기를 제어할 웹 서비스를 만들어야 합니다. 웹 서비스나 프레임워크에 대해 잘 모르지만 Python에는 익숙합니다. 이 작업을 수행하기 위한 전체 Python 코드를 작성하세요.

내 기기를 제어하는 클래스는 다음과 같습니다:

<pre><code>```

{위의 코드}

```

</code></pre>

그리고 (부분 응답):

ChatGPT 프롬프트 응답

그림 2: ChatGPT 프롬프트로부터의 응답

고급(유료) LLM들에 대해 가장 마음에 드는 부분은 그들이 정말로 작업을 세분화하여 교육하고, 꽤 좋은 해결책을 첫 번째 반복으로 제공한다는 것입니다. Gemini vs. ChatGPT: 누가 더 나은 코드를 작성하는가?에서 저는 두 LLM을 서로 비교했고 (스포일러 경고) Google의 최신, 가장 진보된 버전인 Gemini가 실제로 꽤 좋았다는 것을 발견했습니다(만약 ChatGPT 4와 동등하지 않다면). 이러한 LLM 중 어느 하나를 사용하면 위에 보여진 것과 같은 결과를 얻을 수 있습니다.

이것을 클래스에 넣거나 다시 포맷하고 싶다면, 채팅에 응답하여 요청할 수 있습니다. 코드에 주석을 달아달라고요? 문제 없습니다, 그냥 요청하세요!

프롬프트: 각 함수에 docstring 스타일의 주석 블록을 추가하세요. 결과적으로 전체 클래스를 다시 작성해서 코드 편집기에 복사하여 붙여넣을 수 있도록 해주세요.

ChatGPT 프롬프트

그림 3: ChatGPT 프롬프트로부터의 다음 응답

테스팅

이제 우리는 생성 AI가 Python 모듈을 만들었으니, 우리에게 테스트를 작성하도록 요청하거나 수동으로 테스트하는 방법을 보여줄 수 있습니다:

테스트 URL

그림 4: ChatGPT로부터의 수동 테스트 URL

자동화된 테스트

 그림 5: ChatGPT에 의해 작성된 자동화된 테스트

이 테스트들을 실행하거나 수정한 후 실행하면, 웹과 우리의 기기 사이를 인터페이스하는 새로운 Python 모듈을 검증할 수 있어야 합니다.

결론

이 기사에서는 웹상의 기존 코드와 생성 AI를 활용하여 우리의 기기를 제어하는 파이썬 모듈을 함께 구성했습니다. LLM이 모듈을 완성한 후에는 인터넷과 기기 자체(웹 서비스를 통해) 사이에서 중개자 역할을 하는 새로운 모듈을 도입했습니다. 또한 생성 AI가 수동 또는 자동 테스트를 포함한 전체 과정을 안내하는 방법에 대해서도 빠르게 살펴보았습니다. 이 연습의 가장 좋은 점은, 올바르게 수행되었다면, 함께 구성하는 데 거의 시간이 걸리지 않았어야 한다는 것입니다. 처음에는 약간의 추가 노력이 필요할 수 있지만, 많은 기기에 대해 이 과정을 반복하는 것은 숨통이 트일 것이며(그리고 거의 개발 시간이 들지 않을 것입니다). 생성 AI가 우리 모두에게 풍경을 완전히 바꾸었다는 것은 분명하며, 이 기사는 그것을 정말로 보여줍니다. 생성 AI를 활용하여 다음 세트의 기기 라이브러리를 만들고 그것을 커뮤니티의 나머지 부분에 기여하게 되기를 바랍니다.

이 프로젝트에서 사용된 모든 소스 코드는 다음에서 찾을 수 있습니다: https://gitlab.com/ai-examples/instrument-controllables.

작성자 정보

작성자 정보

Ari is an engineer with broad experience in designing, manufacturing, testing, and integrating electrical, mechanical, and software systems. He is passionate about bringing design, verification, and test engineers together to work as a cohesive unit.

관련 자료

관련 기술 문서

홈으로 돌아가기
Thank you, you are now subscribed to updates.