Modernizacja przyrządów laboratoryjnych o możliwości IoT za pomocą generatywnego AI

Ari Mahpour
|  Utworzono: kwiecień 4, 2024  |  Zaktualizowano: lipiec 1, 2024
Modernizacja przyrządów laboratoryjnych o możliwości IoT za pomocą generatywnego AI

W artykule Przystosowanie starego zasilacza do sterowania smartfonem tchnęliśmy nowe życie w stary zasilacz, nadając mu możliwości IoT. W tym artykule przyjrzymy się bardziej uproszczonemu sposobowi dodawania możliwości IoT do wszystkich instrumentów laboratoryjnych (szczególnie tych sterowanych za pomocą VISA). Zamiast budować wszystko od podstaw, wykorzystamy generatywne AI do wykonania większości ciężkiej pracy. Po przejściu przez ten poradnik powinieneś być w stanie zastosować te koncepcje do budowania sterowników z obsługą sieci web dla wszystkich swoich instrumentów laboratoryjnych i również przyspieszyć ogólny rozwój.

Podstawa

W tym artykule skupimy się na zbudowaniu usługi internetowej, która służy jako pośrednik między samym instrumentem a Internetem. Poniżej znajduje się ilustracja połączenia końcowego między instrumentem a internetem (tj. dostęp przez stronę internetową).

Diagram blokowy

Rysunek 1: Komunikacja końcowo-końcowa między instrumentem a Internetem

Zanim to zrobimy, ważne jest, aby ustalić, że nie jest całkowicie konieczne samodzielne pisanie wszystkich sterowników instrumentów. Możemy wykorzystać to, co już zostało zrobione online przez producentów, lub możemy skorzystać z repozytoriów open source. Dla zwięzłości skorzystam z tego, co już znalazłem online, ale użyję generatywnego AI, aby złożyć ramy, które mnie satysfakcjonują.

Używam zasilacza DP832 i obciążenia elektronicznego DL3021 od Rigol. Po szybkim przeszukaniu GitHuba znalazłem biblioteki Pythona dla zasilacza DP832, które zawierają wszystkie niezbędne polecenia SCPI, aby zacząć. Alternatywnym podejściem byłoby wzięcie listy poleceń SCPI z instrukcji DP832, przekazanie jej dużemu modelowi językowemu (LLM), na przykład ChatGPT lub Gemini, i pozwolenie mu na wygenerowanie funkcji dla mnie. Jestem trochę wybredny, więc zdefiniuję własny proces konfiguracji PyVISA, a następnie wykorzystam większość tego, co już zostało zrobione. Oto mój ogólny proces konfiguracji PyVISA (zawarty w oddzielnej klasie): 

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":

```html

            self.configure_visa(vid, pid)

        elif usb_conn_type == "Socket":

            pass

        else:

            raise ValueError(f"Nieprawidłowy typ połączenia USB: {usb_conn_type}. Dostępne typy to {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'Nie znaleziono urządzeń VISA używając vid "{vid}" i pid "{pid}", ale znaleziono następujące urządzenia VISA: {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"Metoda zapytania dla {self.usb_conn_type} nie została znaleziona.")

 

    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],

        }

Usunąłem wszystkie komentarze, dodatkowe odstępy, a nawet niektóre sekwencje konfiguracyjne (takie jak logowanie) dla zwięzłości. Jak widać, mam kilka ogólnych funkcji oraz wsparcie zarówno dla PyVISA, jak i USBTMC. Większość bibliotek Pythona opartych na SCPI, które znajdziesz online, nie będzie miała tej podstawowej funkcjonalności. To w porządku, ponieważ mogę rozszerzyć tę klasę do mojej nowej klasy:   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()       # … Kod został usunięty dla zwięzłości       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"Kanał wyjściowy {channel} nie jest obsługiwany"

 

class DP821(DP):

    def channel_check(self, channel):

        assert channel in [1, 2, ""], f"Kanał wyjściowy {channel} nie jest obsługiwany"

 

class DP832(DP):

    def channel_check(self, channel):

        assert channel in [1, 2, 3, ""], f"Kanał wyjściowy {channel} nie jest obsługiwany"

Przetestowałem kilka różnych przykładów znalezionych w internecie i przekazałem je do ChatGPT. Poprosiłem go o:

  1. Zamianę pliku na klasę
  2. Dodanie bloków komentarzy w stylu docstring dla każdej funkcji
  3. Napisanie testów do walidacji tych funkcji (jako oddzielną klasę)

Wykorzystanie AI generatywnej do tego zadania zmniejszyło wielogodzinną pracę do 60 sekund. Bardzo szybko byłem również w stanie zweryfikować sterowniki, które znalazłem w internecie, uruchamiając automatycznie generowane testy, które napisał dla mnie ChatGPT. Na przykład, bardzo szybko odkryłem, że jedno z poleceń SCPI dla DL3021 do ustawienia trybu pracy obciążenia elektronicznego było faktycznie niepoprawne. Obserwując testy uruchamiane w czasie rzeczywistym, zauważyłem, że tryb na moim instrumencie się nie zmienia. Szybkie sprawdzenie w instrukcji SCPI i byłem w stanie to poprawić.

Usługa sieciowa

W tym momencie mamy solidną podstawę dla naszej biblioteki Pythona do sterowania naszymi instrumentami. Celem teraz jest umieszczenie usługi sieciowej przed biblioteką instrumentów, dając nam możliwość sterowania nimi przez sieć. Zakładając, że nic nie wiem o frameworkach sieciowych, mogę po prostu poprosić ChatGPT (lub dowolny LLM) o wykonanie całej funkcji za mnie. Oto polecenie, które użyłem, aby zacząć:

Potrzebuję stworzyć usługę sieciową, która będzie sterować moim instrumentem za pomocą podstawowych adresów URL (przez przeglądarkę internetową). Nie wiem wiele o usługach sieciowych ani frameworkach, ale znam się na Pythonie. Napisz pełny kod Pythona, aby osiągnąć ten cel.

Oto klasa, która steruje moim instrumentem:

<pre><code>```

{kod z powyżej}

```

</code></pre>

I odpowiedź (częściowa):

Odpowiedź ChatGPT na polecenie

Rysunek 2: Odpowiedź na polecenie ChatGPT

Moim ulubionym aspektem zaawansowanych (płatnych) LLM jest to, że naprawdę pomagają one rozłożyć zadanie na części, edukują i dostarczają całkiem niezłe rozwiązanie jako pierwszą iterację. W Gemini kontra ChatGPT: Kto pisze lepszy kod? porównałem oba LLM i (spoiler) odkryłem, że najnowsza, najbardziej zaawansowana wersja Gemini od Google była naprawdę dobra (jeśli nie na równi z ChatGPT 4). Użycie któregokolwiek z tych LLM przyniesie podobny wynik, jak pokazano powyżej.

Jeśli chcę to umieścić w klasie lub zmienić format, mogę po prostu odpowiedzieć na czacie i złożyć moją prośbę. Chcesz skomentować swój kod? Żaden problem, wystarczy zapytać!

Polecenie: Dodaj bloki komentarzy w stylu docstring do każdej funkcji. Przepisz całą klasę jako wynik, abym mógł skopiować ją i wkleić z powrotem do mojego edytora kodu.

Polecenie ChatGPT

Rysunek 3: Kolejna odpowiedź na polecenie ChatGPT

Testowanie

Teraz, gdy Generatywna AI stworzyła moduł Pythona, możemy poprosić ją o napisanie testów dla nas lub pokazanie, jak testować ręcznie:

URL testu

Rysunek 4: URL testu manualnego od ChatGPT

Test automatyczny

 Rysunek 5: Test automatyczny napisany przez ChatGPT

Po uruchomieniu (lub dostosowaniu i uruchomieniu) tych testów powinniśmy być w stanie zweryfikować nasz nowy moduł Pythona, który służy jako interfejs między siecią a naszym instrumentem. 

Podsumowanie

W tym artykule zebraliśmy moduł Pythona, który kontrolował nasze urządzenie, wykorzystując istniejący kod w sieci oraz Sztuczną Inteligencję Generatywną. Po zakończeniu pracy naszego LLM nad modułem wprowadziliśmy nowy moduł, który działał jako pośrednik między Internetem a samym urządzeniem (za pośrednictwem usługi sieciowej). Rzuciliśmy również szybkie spojrzenie na to, jak Sztuczna Inteligencja Generatywna może przeprowadzić nas przez cały proces, włączając w to testowanie ręczne lub automatyczne. Najlepszą częścią tego ćwiczenia jest to, że jeśli zostało wykonane prawidłowo, powinno zająć bardzo mało czasu na jego złożenie. Pierwszy raz może wymagać trochę więcej wysiłku, ale powtarzanie tego procesu dla wielu urządzeń powinno być łatwe (i wymagać bardzo mało czasu na rozwój). Jest jasne, że Sztuczna Inteligencja Generatywna całkowicie zmieniła krajobraz dla nas wszystkich i ten artykuł naprawdę to pokazuje. Mamy nadzieję, że zostaniesz zainspirowany do stworzenia kolejnego zestawu bibliotek urządzeń, wykorzystując Sztuczną Inteligencję Generatywną i przyczynisz się do ich udostępnienia reszcie społeczności.

Cały kod źródłowy użyty w tym projekcie można znaleźć pod adresem: https://gitlab.com/ai-examples/instrument-controllables.

About Author

About Author

Ari jest inżynierem z rozległym doświadczeniem w projektowaniu, produkcji, testowaniu i integracji systemów elektrycznych, mechanicznych i oprogramowania. Jego pasją jest łączenie inżynierów zajmujących się projektowaniem, weryfikacją i testowaniem, aby pracowali jako jeden zespół.

Powiązane zasoby

Powiązana dokumentacja techniczna

Powrót do strony głównej
Thank you, you are now subscribed to updates.