Dans Retrofitting your Old Power Supply to be Smartphone Controllable, nous avons insufflé une nouvelle vie à une ancienne alimentation électrique en lui donnant des capacités IoT. Dans cet article, nous allons examiner une manière plus rationalisée d'apporter des capacités IoT à tous vos instruments de laboratoire (spécifiquement ceux contrôlés en utilisant VISA). Plutôt que de construire cela à partir de zéro, nous allons tirer parti de l'IA générative pour effectuer la majeure partie du travail difficile. Après avoir suivi ce tutoriel, vous devriez être capable d'appliquer ces concepts pour construire des pilotes web pour tous vos instruments de laboratoire et accélérer également votre développement global.
Dans cet article, nous allons nous concentrer sur la mise en place du service web qui sert d'intermédiaire entre l'instrument lui-même et Internet. Ce qui suit est une illustration de la connexion de bout en bout entre l'instrument et internet (c'est-à-dire l'accès via un site web).
Figure 1 : Communication de bout en bout entre l'instrument et Internet
Avant cela, il est important d'établir qu'il n'est pas complètement nécessaire d'écrire tous les pilotes d'instruments nous-mêmes. Nous pouvons tirer parti de ce qui a déjà été fait en ligne par les fabricants ou nous pouvons utiliser des dépôts open source. Pour des raisons de brièveté, je vais utiliser ce que j'ai déjà trouvé en ligne mais utiliser l'IA générative pour assembler un cadre qui me convient.
J'utilise l'alimentation DP832 et la charge électronique DL3021 de Rigol. Après une rapide recherche sur GitHub, j'ai trouvé des bibliothèques Python pour l'alimentation DP832 qui contiennent toutes les commandes SCPI nécessaires pour commencer. Une approche alternative serait de prendre une liste de commandes SCPI du manuel du DP832, de la remettre à un modèle de langage de grande taille (LLM), par exemple ChatGPT ou Gemini, et de lui faire générer les fonctions pour moi. Je suis un peu exigeant donc je vais définir mon propre processus de configuration PyVISA puis tirer parti de la majorité de ce qui a déjà été fait. Voici mon processus de configuration PyVISA générique (encapsulé dans une classe séparée):
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"Type de connexion USB invalide : {usb_conn_type}. Les types valides sont {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'Aucun appareil VISA trouvé avec vid "{vid}" et pid "{pid}" mais les appareils VISA suivants ont été trouvés : {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"Méthode de requête pour {self.usb_conn_type} non trouvée.")
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],
}
```
J'ai supprimé tous les commentaires, les espaces supplémentaires, et même certaines séquences de configuration (comme les journaux) pour plus de concision. Comme vous pouvez le voir, j'ai quelques fonctions génériques en plus du support pour PyVISA et USBTMC. La plupart des bibliothèques Python basées sur SCPI que vous trouverez en ligne n'auront pas cette fonctionnalité de base. C'est bien parce que je peux étendre cette classe dans ma nouvelle classe :
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()
# … Le code a été supprimé pour plus de concision
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]),
```
"puissance": float(meas[2]),
}
class DP712(DP):
def channel_check(self, channel):
assert channel in [1, ""], f"Canal de sortie {channel} non pris en charge"
class DP821(DP):
def channel_check(self, channel):
assert channel in [1, 2, ""], f"Canal de sortie {channel} non pris en charge"
class DP832(DP):
def channel_check(self, channel):
assert channel in [1, 2, 3, ""], f"Canal de sortie {channel} non pris en charge"
J'ai pris quelques exemples différents en ligne et les ai passés dans ChatGPT. Je l'ai sollicité pour :
Utiliser l'IA générative pour cette tâche a réduit un travail de plusieurs heures à 60 secondes. J'ai également pu, très rapidement, valider les pilotes que j'ai trouvés en ligne en exécutant les tests générés automatiquement que ChatGPT a écrits pour moi. Par exemple, j'ai découvert, très rapidement, qu'une des commandes SCPI pour le DL3021 pour définir le mode de fonctionnement de la charge électronique était en fait incorrecte. En regardant les tests s'exécuter en temps réel, j'ai remarqué que le mode ne changeait pas sur mon instrument. Une recherche rapide dans le manuel SCPI et j'ai pu le corriger.
À ce stade, nous avons une bonne base pour notre bibliothèque Python pour contrôler nos instruments. L'objectif est maintenant de placer un service web devant la bibliothèque d'instruments, nous donnant la capacité de les contrôler via le web. En supposant que je ne sais rien sur les cadres web, je peux simplement demander à ChatGPT (ou à tout autre LLM) d'effectuer la fonction entière pour moi. Voici l'invite que j'ai utilisée pour commencer :
J'ai besoin de créer un service web qui contrôlera mon instrument en utilisant des URL basiques (via un navigateur web). Je ne connais pas grand-chose aux services web ou aux cadres, mais je suis familier avec Python. Écrivez le code Python complet pour réaliser cette tâche.
Voici la classe qui contrôle mon instrument :
<pre><code>```
{code from above}
```
</code></pre>
Et la (réponse partielle) :
```
Figure 2 : Réponse à l'invite de ChatGPT
Ma partie préférée concernant les LLM sophistiqués (payants) est qu'ils décomposent vraiment la tâche pour vous, vous éduquent et fournissent une solution assez agréable comme première itération. Dans Gemini vs. ChatGPT : Qui écrit le meilleur code ? J'ai comparé les deux LLM l'un contre l'autre et (alerte spoiler) découvert que la version la plus récente et la plus avancée de Gemini de Google était en fait assez bonne (si ce n'est au niveau de ChatGPT 4). Utiliser l'un ou l'autre de ces LLM donnera un résultat similaire à celui montré ci-dessus.
Si je veux mettre cela dans une classe ou le reformater, je peux simplement répondre au chat et faire ma demande. Vous voulez que votre code soit commenté ? Pas de problème, demandez juste !
Invite : Ajoutez des blocs de commentaires de style docstring à chaque fonction. Réécrivez toute la classe en conséquence pour que je puisse la copier et la coller de retour dans mon éditeur de code.
Figure 3 : Prochaine réponse à l'invite de ChatGPT
Maintenant que nous avons fait créer le module Python par l'IA générative, nous pouvons soit lui demander d'écrire des tests pour nous, soit nous montrer comment tester manuellement :
Figure 4 : URL de test manuel de ChatGPT
Figure 5 : Test automatisé écrit par ChatGPT
Après avoir exécuté (ou ajusté puis exécuté) ces tests, nous devrions être en mesure de valider notre nouveau module Python qui fait l'interface entre le web et notre instrument.
Dans cet article, nous avons assemblé un module Python qui contrôlait notre instrument en tirant parti du code existant sur le web et de l'IA générative. Après que notre LLM ait complété le module, nous avons ensuite introduit un nouveau module qui agissait comme intermédiaire entre Internet et l'instrument lui-même (via un service web). Nous avons également jeté un coup d'œil rapide sur comment l'IA générative peut nous guider à travers tout le processus, y compris les tests manuels ou automatisés. La meilleure partie de cet exercice est que, si bien fait, cela aurait dû vous prendre très peu de temps à assembler. La première fois pourrait nécessiter un effort supplémentaire mais répéter ce processus pour de nombreux instruments devrait être un jeu d'enfant (et nécessiter très peu de temps de développement). Il est clair que l'IA générative a complètement changé le paysage pour nous tous et cet article le démontre vraiment. Nous espérons que vous serez inspiré pour créer le prochain ensemble de bibliothèques d'instruments en tirant parti de l'IA générative et les contribuer en retour au reste de la communauté.
Tout le code source utilisé dans ce projet peut être trouvé à : https://gitlab.com/ai-examples/instrument-controllables.