Instrument Control

Contrôler un multimètre Keysight

Connectez un multimètre Keysight 34461A ou 34465A à Python via PyVISA, mesurez tension, courant et résistance, et enregistrez les résultats dans TofuPilot.

JJulien Buteau
beginner12 min de lecture14 mars 2026

Connectez un multimètre Keysight 34461A ou 34465A à Python via PyVISA, envoyez des commandes SCPI pour mesurer la tension DC, la tension AC, la résistance et le courant, puis enregistrez automatiquement les résultats dans TofuPilot via un plug OpenHTF.

Prérequis

  • Multimètre Keysight 34461A ou 34465A (ou série 344xxA compatible)
  • Python 3.8+
  • OpenHTF et TofuPilot installés
  • Keysight IO Libraries Suite (pour USB) ou accès réseau (pour Ethernet)
terminal
pip install pyvisa pyvisa-py tofupilot openhtf

Étape 1 : Se connecter à l'instrument

Les multimètres Keysight prennent en charge deux interfaces principales : USB-TMC et Ethernet (LXI).

InterfaceChaîne de ressource VISACas d'utilisation
USB-TMCUSB0::0x0957::0x1A07::MY12345678::INSTRPoste unique, connexion PC directe
Ethernet (VXI-11)TCPIP0::192.168.1.100::inst0::INSTRSystèmes de test en réseau
Ethernet (HiSLIP)TCPIP0::192.168.1.100::hislip0::INSTRDébit plus élevé, recommandé pour le 34465A

Trouvez la chaîne de ressource de votre instrument :

find_instruments.py
import pyvisa

rm = pyvisa.ResourceManager()
print(rm.list_resources())

Ouvrez une connexion et vérifiez l'identité :

connect.py
import pyvisa

rm = pyvisa.ResourceManager()
dmm = rm.open_resource("USB0::0x0957::0x1A07::MY12345678::INSTR")
dmm.timeout = 5000  # ms
print(dmm.query("*IDN?"))
# Keysight Technologies,34461A,MY12345678,A.02.14-02.40-02.14-00.49-01-01

Étape 2 : Configurer les mesures SCPI

Réinitialisez toujours l'instrument à un état connu avant de configurer.

scpi_basics.py
dmm.write("*RST")
dmm.write("*CLS")
print(dmm.query("SYST:ERR?"))
# +0,"No error"

Tension DC

measure_dc_voltage.py
# Tension DC en auto-range
dmm.write("CONF:VOLT:DC AUTO,DEF")
reading = float(dmm.query("READ?"))
print(f"Tension DC : {reading:.6f} V")

# Calibre manuel : calibre 10 V
dmm.write("CONF:VOLT:DC 10,DEF")
reading = float(dmm.query("READ?"))

Tension AC

measure_ac_voltage.py
dmm.write("CONF:VOLT:AC AUTO,DEF")
reading = float(dmm.query("READ?"))
print(f"Tension AC (RMS) : {reading:.6f} V")

Résistance (2 fils et 4 fils)

measure_resistance.py
# Résistance 2 fils, auto-range
dmm.write("CONF:RES AUTO,DEF")
reading = float(dmm.query("READ?"))
print(f"Résistance 2 fils : {reading:.4f} Ohm")

# Résistance 4 fils (élimine la résistance des fils de mesure)
dmm.write("CONF:FRES AUTO,DEF")
reading = float(dmm.query("READ?"))
print(f"Résistance 4 fils : {reading:.4f} Ohm")

Courant DC

measure_current.py
dmm.write("CONF:CURR:DC AUTO,DEF")
reading = float(dmm.query("READ?"))
print(f"Courant DC : {reading:.6f} A")

Référence des commandes SCPI

MesureCommande CONFValeurs de calibre
Tension DCCONF:VOLT:DC {range},DEF0.1, 1, 10, 100, 1000 V
Tension ACCONF:VOLT:AC {range},DEF0.1, 1, 10, 100, 750 V
Résistance 2 filsCONF:RES {range},DEF100, 1k, 10k, 100k, 1M, 10M, 100M Ohm
Résistance 4 filsCONF:FRES {range},DEFUtiliser pour les valeurs inférieures à 100 Ohm
Courant DCCONF:CURR:DC {range},DEF0.0001, 0.001, 0.01, 0.1, 1, 3 A
Courant ACCONF:CURR:AC {range},DEF0.001, 0.01, 0.1, 1, 3 A

Étape 3 : Ajuster le compromis vitesse/précision avec NPLC

NPLC (Number of Power Line Cycles) contrôle le temps d'intégration. Un NPLC plus élevé moyenne davantage le bruit mais prend plus de temps.

nplc_config.py
dmm.write("CONF:VOLT:DC 10,DEF")
dmm.write("VOLT:DC:NPLC 1")    # équilibré (par défaut)
dmm.write("VOLT:DC:NPLC 10")   # haute précision, plus lent
dmm.write("VOLT:DC:NPLC 0.02") # rapide, plus de bruit
NPLCTemps d'intégration (60 Hz)Lectures/secCas d'utilisation
0.02333 us~50Tri rapide en production
0.23.3 ms~15Bon compromis vitesse/bruit
116.7 ms~5Précision standard
10167 ms~0.6Mesures haute précision
1001.67 s~0.1Niveau calibration

Le calibre manuel élimine le délai d'auto-range (~20-50 ms) et est recommandé pour le débit des tests de production.

Étape 4 : Créer un plug OpenHTF pour le multimètre Keysight

keysight_dmm_plug.py
import pyvisa
import openhtf as htf


class KeysightDMM(htf.plugs.BasePlug):
    """Plug OpenHTF pour les multimètres Keysight série 344xxA."""

    RESOURCE = "USB0::0x0957::0x1A07::MY12345678::INSTR"
    NPLC = 1.0

    def setUp(self):
        self._rm = pyvisa.ResourceManager()
        self._dmm = self._rm.open_resource(self.RESOURCE)
        self._dmm.timeout = 5000
        self._dmm.write("*RST")
        self._dmm.write("*CLS")

    def tearDown(self):
        if self._dmm:
            self._dmm.close()
        self._rm.close()

    def measure_dc_voltage(self, range_v: float = None) -> float:
        range_str = str(range_v) if range_v else "AUTO"
        self._dmm.write(f"CONF:VOLT:DC {range_str},DEF")
        self._dmm.write(f"VOLT:DC:NPLC {self.NPLC}")
        return float(self._dmm.query("READ?"))

    def measure_ac_voltage(self, range_v: float = None) -> float:
        range_str = str(range_v) if range_v else "AUTO"
        self._dmm.write(f"CONF:VOLT:AC {range_str},DEF")
        return float(self._dmm.query("READ?"))

    def measure_resistance(self, four_wire: bool = False, range_ohm: float = None) -> float:
        cmd = "FRES" if four_wire else "RES"
        range_str = str(range_ohm) if range_ohm else "AUTO"
        self._dmm.write(f"CONF:{cmd} {range_str},DEF")
        self._dmm.write(f"{cmd}:NPLC {self.NPLC}")
        return float(self._dmm.query("READ?"))

    def measure_dc_current(self, range_a: float = None) -> float:
        range_str = str(range_a) if range_a else "AUTO"
        self._dmm.write(f"CONF:CURR:DC {range_str},DEF")
        self._dmm.write(f"CURR:DC:NPLC {self.NPLC}")
        return float(self._dmm.query("READ?"))

Étape 5 : Écrire les tests de production avec TofuPilot

test_power_supply_board.py
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
from keysight_dmm_plug import KeysightDMM


@htf.plug(dmm=KeysightDMM)
@htf.measures(
    htf.Measurement("output_voltage_3v3")
    .in_range(minimum=3.267, maximum=3.333)
    .with_units(units.VOLT),
    htf.Measurement("output_voltage_5v0")
    .in_range(minimum=4.900, maximum=5.100)
    .with_units(units.VOLT),
)
def test_output_voltages(test, dmm):
    """Vérifie que les tensions de sortie régulées sont dans la tolérance."""
    test.measurements.output_voltage_3v3 = dmm.measure_dc_voltage(range_v=10)
    test.measurements.output_voltage_5v0 = dmm.measure_dc_voltage(range_v=10)


@htf.plug(dmm=KeysightDMM)
@htf.measures(
    htf.Measurement("sense_resistor_value")
    .in_range(minimum=0.099, maximum=0.101)
    .with_units(units.OHM),
)
def test_sense_resistor(test, dmm):
    """Vérifie la résistance de mesure de courant en 4 fils."""
    r = dmm.measure_resistance(four_wire=True, range_ohm=100)
    test.measurements.sense_resistor_value = r


@htf.plug(dmm=KeysightDMM)
@htf.measures(
    htf.Measurement("quiescent_current")
    .in_range(maximum=0.005)
    .with_units(units.AMPERE),
)
def test_quiescent_current(test, dmm):
    """Vérifie que le courant de repos ne dépasse pas 5 mA."""
    test.measurements.quiescent_current = dmm.measure_dc_current(range_a=0.01)


@htf.plug(dmm=KeysightDMM)
@htf.measures(
    htf.Measurement("ac_ripple")
    .in_range(maximum=0.050)
    .with_units(units.VOLT),
)
def test_output_ripple(test, dmm):
    """Vérifie l'ondulation AC sur la sortie 5 V."""
    test.measurements.ac_ripple = dmm.measure_ac_voltage(range_v=1)


def main():
    test = htf.Test(
        test_output_voltages,
        test_sense_resistor,
        test_quiescent_current,
        test_output_ripple,
        test_name="Power Supply Board Functional Test",
    )

    with TofuPilot(test):
        test.execute(test_start=lambda: input("Entrez le numéro de série du DUT : ").strip())


if __name__ == "__main__":
    main()

Étape 6 : Mode de mesure rapide multi-échantillons

Pour les lignes à haut débit, utilisez SAMP:COUN pour l'échantillonnage en rafale.

fast_sampling.py
import pyvisa
import numpy as np

rm = pyvisa.ResourceManager()
dmm = rm.open_resource("USB0::0x0957::0x1A07::MY12345678::INSTR")
dmm.timeout = 10000

dmm.write("*RST")
dmm.write("CONF:VOLT:DC 10,DEF")
dmm.write("VOLT:DC:NPLC 0.2")
dmm.write("SAMP:COUN 10")
dmm.write("TRIG:SOUR IMM")
dmm.write("TRIG:DEL 0")

dmm.write("INIT")
raw = dmm.query("FETC?")

readings = [float(x) for x in raw.split(",")]
print(f"Moyenne : {np.mean(readings):.6f} V")
print(f"Écart-type : {np.std(readings):.6f} V")

Dépannage

SymptômeCause probableSolution
VI_ERROR_RSRC_NFOUNDChaîne de ressource incorrecte ou pilote non installéExécuter rm.list_resources(), installer Keysight IO Libraries
+1,"Hardware error"Surcharge sur les bornes d'entréeVérifier les connexions des sondes, réduire le signal d'entrée
La lecture retourne +9.9E+37Le signal dépasse le calibre sélectionnéUtiliser l'auto-range ou augmenter le calibre manuel
Les lectures dérivent sur 30 secondesMultimètre pas encore en températureLaisser 30 minutes de chauffe pour une précision inférieure à 10 ppm
Périphérique USB non trouvé sous LinuxRègles udev manquantesAjouter SUBSYSTEM=="usb", ATTRS{idVendor}=="0957", MODE="0666"
Connexion HiSLIP refuséeHiSLIP non activéActiver via le panneau avant : Utilities > I/O Config > LAN > HiSLIP
La commande NPLC retourne une erreurPréfixe de fonction incorrectNPLC est par fonction : VOLT:DC:NPLC, RES:NPLC
Débit lent via LANOverhead du protocole VXI-11Passer à HiSLIP pour des transactions 10x plus rapides
La lecture 4 fils est identique à la lecture 2 filsMauvaises bornesVérifier que SENSE HI/LO sont connectés aux bornes séparées

Plus de guides

Mettez ce guide en pratique