[PYTHON] Versuchen Sie es mit billigem LiDAR (Camsense X1)

Einführung

Als ich in AliExpress herumwanderte, verkaufte LiDAR für satte 19 US-Dollar (einschließlich Versand). https://ja.aliexpress.com/item/4000632680397.html Diesmal habe ich mir den billigen LiDAR gekauft. Es scheint, dass Kommunikationsspezifikationen und Bibliotheken nicht offiziell veröffentlicht werden.

Die Identität des Super-Rabatt-LiDAR

Die Hersteller etc. sind nicht auf der AliExpress Seite geschrieben, Es gab einen Aufkleber "Camsense X1" im Bild, also scheint dies das Modell des Sensors zu sein. Wenn Sie nach Camsense X1 suchen, sieht es aus wie ein Produkt, das 2017 von der Marke Camsense auf den Markt gebracht wurde. http://en.camsense.cn/index.php?c=article&id=116

Spezifikation

Die Camsense-Seite enthält die folgenden einfachen Spezifikationen.

Spezifikationselemente Spezifikationswert
Entfernungsmessbereich 0.08 ~ 8m
Wiederholen Sie die Genauigkeit 0.5%Weniger als
Absolute Genauigkeit 2%Weniger als
Messfrequenz Über 2080Hz

Die Messfrequenz ist nicht die Rotationsfrequenz, sondern die Anzahl der Schritte, mit denen Entfernungsdaten pro Sekunde ausgeführt werden können. Andere Spezifikationen, die ich nach der Verwendung gefunden habe, sind wie folgt.

Spezifikationselemente Spezifikationswert
Stromspannung 5V
Derzeitiger Verbrauch 0.4A oder weniger
Winkelauflösung 0.Über 75 Grad
Rotationszyklus 0.Ca. 2 Sekunden
Kommunikation UARTKommunikation(115200bps)
UART-Ebene 3.3V

Die Winkelauflösung ist nicht konstant. Wenn Sie sie mit der Hand berühren, um die Motordrehung zu verlangsamen, wird die Winkelauflösung bis zu 0,5 Grad. Der Datenerfassungszyklus ist nicht mit der Drehung synchronisiert, und es scheint, dass die Zeit fest ist.

Verbindung

Es gibt drei Leitungen für die Verbindung nach außen: VCC, GND und TX. Ich hatte keinen Stecker und versuchte mein Bestes, um ihn anzuschließen.

Wenn Sie mit einem PC kommunizieren möchten, benötigen Sie einen USB-UART-Konverter, da die UART-Kommunikation nicht direkt möglich ist. Ich habe so etwas benutzt. (Ich habe es vor langer Zeit gekauft und vergessen, wo ich es gekauft habe)

Raspberry Pi hat einen 3,3-V-UART, daher sollten Sie ihn so verwenden können, wie er ist.

So wird der Camsense X1 mit Ihrem PC verbunden.

Camsense X1 Seite PC-Seite
VCC 5.0V
GND GND
TX RX

Hinweis

Wenn unter Windows Daten kontinuierlich von der seriellen Schnittstelle stammen, werden sie als serielle Maus erkannt, und der Mauszeiger bewegt sich hier und da, und Vorgänge, die nicht ausgeführt werden sollten, können ausgeführt werden. Daher ist es im Fall von Windows besser, die Funktion zu deaktivieren, die es als serielle Maus erkennt. Eine serielle Maus ist eine sehr alte Maus, die vor USB und PS / 2 verwendet wurde. Dies bedeutet nicht, dass die aktuelle Maus nicht verwendet werden kann. Mit einem Registrierungseditor ist das ganz einfach HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\sermouse Ich denke, der Wert der Starttaste von ist 3, also habe ich ihn einfach auf 4 gesetzt. https://stackoverflow.com/questions/9226082/device-misdetected-as-serial-mouse

Kommunikationsprotokoll

Die Person, die das Kommunikationsprotokoll analysiert hat, hat es auf Github erklärt, daher habe ich es als Referenz verwendet. https://github.com/Vidicon/camsense-X1

Es handelt sich um ein 36-Byte-Paket, das die folgenden Entfernungs- und Stärkedaten in jeweils 8 Schritten enthält. Es wird endlos ausgegeben.

Artikel Anzahl der Bytes Bemerkungen
Header 4 0x55, 0xAA, 0x03,0x08 behoben
Drehzahl 2(Kleiner Inder) value / 64 = rpm
Startwinkel 2(Kleiner Inder) (value - 40960) / 64 = degree
Entfernung 0 2(Kleiner Inder) mm
Stärke 0 1 Das Gerät ist unbekannt
Entfernung 1 2(Kleiner Inder) mm
Stärke 1 1 Das Gerät ist unbekannt
... ... ...
Entfernung 7 2(Kleiner Inder) mm
Stärke 7 1 Das Gerät ist unbekannt
Endwinkel 2(Kleiner Inder) (value - 40960) / 64 = degree
CRC? 2 ?

Die letzten 2 Bytes scheinen CRC zu sein, aber ich habe versucht, mit einigen generierten Polynomen zu berechnen, und den Header aus dem Berechnungsbereich entfernt, aber ich konnte keine Übereinstimmung finden.

Datenerfassungsprogramm (Python)

Ich habe den Code geschrieben, um die Daten von Camsense X1 abzurufen und mit Python zu zeichnen. Ich habe es auch auf Github gelegt. https://github.com/j-fujimoto/CamsenseX1

Da sich die Winkelauflösung in Abhängigkeit von der Drehzahl ändert, wird die Anzahl der Daten für eine Runde nicht bestimmt, sondern die Anzahl der Daten für eine Runde der Winkelauflösung zum Zeitpunkt der Stabilität ist gesichert. Es erfordert Pyserial und Matplotlib, um zu arbeiten.

Betriebsüberprüfungsumgebung

Sanft Ausführung
python 3.8.2
pyserial 3.4
matplotlib 3.2.2

camsenseX1.py


import matplotlib.pyplot as plt
import math
import serial
import sys
import struct
import time
import threading

class Capture:
    def __init__(self, serialPort, dataSize = 460, isInvert = True):
        self.theta = [0] * dataSize
        self.distance = [0] * dataSize
        self.intensity = [0] * dataSize
        self.writePos = 0
        self.serial = serial.Serial(port = serialPort, baudrate = 115200)
        self.dataSize = dataSize
        self.thread = threading.Thread(target = self.getData)
        self.lock = threading.Lock()
        self.isInvert = isInvert
        self.dataObtained = False
        self.rpm = 0

    def getDataUnit(self):
        #Lesen Sie zuerst den Header herunter
        header = b"\x55\xAA\x03\x08"
        headerPos = 0
        while True:
            tmp = self.serial.read(1)
            if tmp[0] == header[headerPos]:
                headerPos += 1
                if headerPos == len(header):
                    break
            else:
                headerPos = 0

        tmp = self.serial.read(4)
        # "<" :Kleiner Inder, "H" :2-Byte-Daten ohne Vorzeichen, "B" :1-Byte-Daten ohne Vorzeichen
        (rotationSpeedTmp, startAngleTmp) = struct.unpack_from("<2H", tmp)
        self.rpm = rotationSpeedTmp / 64
        startAngle = (startAngleTmp - 0xa000) / 64
        #Bereiten Sie ein Array zum Speichern von Entfernungs- und Stärkedaten vor
        distanceTmp = [0] * 8
        intensityTmp = [0] * 8
        for i in range(8):
            tmp = self.serial.read(3)
            (distanceTmp[i], intensityTmp[i]) = struct.unpack_from("<HB", tmp)
        tmp = self.serial.read(4)
        (endAngleTmp, crc) = struct.unpack_from("<2H", tmp)
        endAngle = (endAngleTmp - 0xa000) / 64

        return (distanceTmp, intensityTmp, startAngle, endAngle)

    def getData(self):
        preStartAngle = 0
        while True:
            (distanceTmp, intensityTmp, startAngle, endAngle) = self.getDataUnit()

            #Wenn es nahe 0 Grad liegt, kann die Größenbeziehung zwischen dem Startwinkel und dem Endwinkel umgekehrt werden. Fügen Sie daher dem Endwinkel 360 Grad hinzu, um die Größenbeziehung beizubehalten.
            if endAngle < startAngle:
                endAngle += 360

            #Wenn der Startwinkel klein wird, ist dies eine Stelle von 0 Grad. Setzen Sie daher das Datenaktualisierungsflag
            if (startAngle - preStartAngle < 0):
                self.dataObtained = True
            preStartAngle = startAngle

            #Winkel in Bogenmaß umrechnen
            startAngleRad = startAngle * math.pi / 180 * (-1 if self.isInvert else 1)
            endAngleRad = endAngle * math.pi / 180 * (-1 if self.isInvert else 1)
            #Berechnen Sie den Winkel pro Schritt
            angleIncrement = (endAngleRad - startAngleRad) / len(distanceTmp)
            #Exklusiver Kontrollstart
            self.lock.acquire()
            for i in range(len(distanceTmp)):
                self.theta[self.writePos] = startAngleRad + angleIncrement * i
                self.distance[self.writePos] = distanceTmp[i]
                self.intensity[self.writePos] = intensityTmp[i]
                self.writePos += 1
                if self.writePos >= self.dataSize:
                    self.writePos = 0
            #Exklusive Kontrolle Ende
            self.lock.release()

    def run(self):
        self.thread.start()

    def stop(self):
        self.thread.stop()

if __name__ == "__main__":
    if len(sys.argv) == 1:
        print("You must specify serial port! ex) " + sys.argv[0] + " COM2")
        quit()

    #Generieren Sie ein Diagramm der Polarkoordinaten
    dist = plt.subplot(111, polar = True)
    #Stellen Sie den Anfangswert der maximal anzuzeigenden Entfernung auf 2000 ein
    rmax = 2000

    capture = Capture(sys.argv[1], dataSize = 480, isInvert = True)
    capture.run()
    preTime = time.time()

    while True:
        if capture.dataObtained:
            #Zeichnung initialisieren
            plt.cla()

            #Exklusiver Kontrollstart
            capture.lock.acquire()
            #Entfernungsdaten zeichnen(Zeichnen Sie als Streudiagramm)
            dist.scatter(list(capture.theta), list(capture.distance), c = "blue", s = 5)
            #Intensitätsdaten zeichnen(Zeichnen Sie durch Verbinden mit Linien)
            dist.plot(list(capture.theta), list(capture.intensity), c = "orange", linewidth = 1)
            #Da die Daten gezeichnet wurden, senken Sie das Datenerfassungsflag.
            capture.dataObtained = False
            #Exklusiver Kontrollstart
            capture.lock.release()

            #Stellen Sie den oberen Bildschirmrand auf 0 Grad ein
            dist.set_theta_offset(math.pi / 2)
            #Stellen Sie den Maximalwert des anzuzeigenden Abstandswerts ein
            dist.set_rmax(rmax)
            #Zeichenprozess
            plt.pause(0.01)
            #Ruft den aktuell eingestellten maximalen Anzeigeabstand ab
            rmax = dist.get_rmax()

        else:
            time.sleep(0.01)

Der Zustand des Zeichnens der Daten ist wie folgt. Entfernungsdaten werden mit blauen Punkten gezeichnet, und Intensitätsdaten werden mit orangefarbenen Linien angezeigt. camsenseX1_data.png

Andere öffentliche Programme

Knoten für ROS1: https://github.com/Vidicon/camsense_driver Erstellt von Vidicon (Bram Fenijn) Knoten für ROS2: https://github.com/rossihwang/ros2_camsense_x1 Hergestellt von rossihwang Programm für STM32: https://github.com/anhui1995/Camsense_X1

abschließend

Es ist erstaunlich, dass LiDAR mit dieser Leistung für 19 US-Dollar erworben werden kann. Mit LiDAR, das über Ethernet verbunden ist, ist die Verarbeitung mit einem kostengünstigen Mikrocomputer jedoch recht schwierig Da es sich bei Camsense X1 um einen UART handelt, kann er auch mit einem kostengünstigen Mikrocomputer problemlos verarbeitet werden. Der Rotationszyklus ist ziemlich langsam und die Entfernung, die erreicht werden kann, ist kurz. Ich denke, es ist schwierig, sie für Roboter zu verwenden, die sich schnell bewegen, aber Ich denke, es ist gut genug für Hobby

Recommended Posts

Versuchen Sie es mit billigem LiDAR (Camsense X1)
Versuchen Sie es mit dem $ 6 Rabatt LiDAR (Camsense X1)
Versuchen Sie es mit Tkinter
Versuchen Sie es mit Docker-Py
Versuchen Sie es mit PDFMiner
Versuchen Sie es mit Geopandas
Versuchen Sie es mit Selen
Versuchen Sie es mit scipy
Versuchen Sie es mit pandas.DataFrame
Versuchen Sie es mit Django-Swiftbrowser
Versuchen Sie es mit matplotlib
Versuchen Sie es mit tf.metrics
Versuchen Sie es mit PyODE
Versuchen Sie es mit virtualenv (virtualenvwrapper)
Versuchen Sie es mit E-Cell 4 unter Windows 7 oder Mac OS X.
[Azure] Versuchen Sie, Azure-Funktionen zu verwenden
Versuchen Sie es jetzt mit virtualenv
Versuchen Sie es mit W & B.
Versuchen Sie es mit Django templates.html
[Kaggle] Versuchen Sie es mit LGBM
Versuchen Sie es mit dem Feed-Parser von Python.
Versuchen Sie es mit Pythons Tkinter
Versuchen Sie es mit Tweepy [Python2.7]
Versuchen Sie es mit Pytorchs collate_fn
Verwenden von X11 mit ubuntu18.04 (C-Sprache)
Versuchen Sie, PythonTex mit Texpad zu verwenden.
[Python] Versuchen Sie, Tkinters Leinwand zu verwenden
Versuchen Sie es mit Jupyters Docker-Image
Versuchen Sie es mit Scikit-Learn (1) - K-Clustering nach Durchschnittsmethode
Versuchen Sie die Funktionsoptimierung mit Hyperopt
Versuchen Sie es mit Azure Logic Apps
[Kaggle] Versuchen Sie es mit xg boost
Versuchen Sie es mit der Twitter-API
Versuchen Sie es mit OpenCV unter Windows
Versuchen Sie, Jupyter Notebook dynamisch zu verwenden
Versuchen Sie, automatisch mit Selen zu twittern.
Versuchen Sie es mit SQLAlchemy + MySQL (Teil 1)
Versuchen Sie es mit der Twitter-API
Versuchen Sie es mit SQLAlchemy + MySQL (Teil 2)
Versuchen Sie es mit der Vorlagenfunktion von Django
Versuchen Sie es mit der PeeringDB 2.0-API
Versuchen Sie es mit der Entwurfsfunktion von Pelican
Versuchen Sie es mit pytest-Overview und Samples-
Versuchen Sie es mit Folium mit Anakonda