Zephyrnet-logo

Schatting van de handhouding op basis van LiDAR in 30 minuten

Datum:

Dit artikel is gepubliceerd als onderdeel van het Data Science-blogathon

Introductie

Dag iedereen! Hoewel cyberpunk nog niet zo veel in ons leven is verschenen en neuro-interfaces verre van ideaal zijn, kan LiDAR de eerste stap worden op weg naar de toekomst van manipulatoren. Om me tijdens de vakantie niet te vervelen, besloot ik daarom een ​​beetje te fantaseren over de besturing van een computer en vermoedelijk elk apparaat, tot een graafmachine, ruimteschip, drone of fornuis toe.

Het belangrijkste idee is om de muis te bewegen, waarbij je niet de hele hand beweegt, maar alleen de wijsvinger, waarmee je door het menu kunt lopen zonder je handen van het toetsenbord te halen, op knoppen kunt drukken en, samen met sneltoetsen, verandert in een echte toetsenbordninja! Wat gebeurt er als je veeg- of scrollbewegingen toevoegt? Ik denk dat er een bom zal zijn! Maar tot dit moment moeten we nog een paar jaar wachten)

Laten we beginnen met het samenstellen van ons prototype van de manipulator van de toekomst

Wat u nodig hebt:

  1. Camera met LiDAR Intel Realsense L515.

  2. Mogelijkheid om te programmeren in python

  3. Onthoud een beetje schoolwiskunde

  4. Bevestiging voor de camera op de monitor ook wel statief genoemd

We bevestigen de camera op een statief met aliexpress, het bleek erg handig, lichtgewicht en goedkoop te zijn)

afbeelding | Schatting handhouding LiDAR

BRON

afbeelding 2 | Schatting handhouding LiDAR

BRON

We zoeken uit hoe en waarover we een prototype kunnen maken

Er zijn veel manieren om deze taak te volbrengen. Je kunt de detector of handsegmentatie zelf trainen, de resulterende afbeelding van de rechterhand uitknippen en vervolgens deze prachtige repository uit Facebook-onderzoek op de afbeelding toepassen, een uitstekend resultaat krijgen of het nog gemakkelijker maken.

Om de mediapipe-repository te gebruiken, na het lezen van deze link:, u kunt begrijpen dat dit een van de beste opties is voor vandaag.

Ten eerste is alles er al uit de doos - installatie en lancering duurt 30 minuten, rekening houdend met alle vereisten.

Ten tweede nemen ze, dankzij een krachtig ontwikkelingsteam, niet alleen de State Of Art in Hand Pose Estimation, maar bieden ze ook een gemakkelijk te begrijpen API.

Ten derde is het netwerk klaar om op de CPU te draaien, dus de instapdrempel is minimaal.

U zult zich waarschijnlijk afvragen waarom ik hier niet ben gekomen en geen gebruik heb gemaakt van de repositories van de winnaars van deze wedstrijd. In feite heb ik hun oplossing in enig detail bestudeerd, ze zijn behoorlijk prod-ready, geen stapels van miljoenen rasters, enz. Maar het grootste probleem lijkt mij dat ze met dieptebeelden werken. Aangezien dit academici zijn, hebben ze niet geaarzeld om alle data via Matlab om te zetten, bovendien leek de resolutie waarmee de diepten werden gefilmd mij klein. Dit kan een diepgaand effect hebben op het resultaat. Daarom lijkt het dat de gemakkelijkste manier is om de belangrijkste punten in het RGB-beeld te krijgen en de waarde langs de Z-as in het diepteframe te nemen met de XY-coördinaten. Nu is de taak niet om iets veel te optimaliseren, dus we zullen het doen omdat het vanuit ontwikkelingsoogpunt sneller is.

School wiskunde onthouden

Zoals ik al schreef, om de coördinaat te krijgen van het punt waar de muiscursor zou moeten zijn, moeten we een lijn bouwen die door twee sleutelpunten van het vingerkootje van de vinger gaat en het snijpunt van de lijn en de vlak van de monitor.

afbeelding vliegtuig | Schatting handhouding LiDAR

BRON

De afbeelding toont schematisch het vlak van de monitor en de lijn die deze snijdt. Je kunt de wiskunde hier bekijken.

Met behulp van twee punten krijgen we een parametrische weergave van een rechte lijn in de ruimte.

parametrische representatie | Schatting handhouding LiDAR
variabelen

Ik zal me niet te veel concentreren op het wiskundecurriculum van de school.

Een bibliotheek installeren om met een camera te werken

Dit is misschien wel het moeilijkste van deze baan. Het bleek dat de software voor de camera voor Ubuntu erg grof is, het liberale gevoel is gewoon bezaaid met allerlei soorten bugs, glitches en dansen met een tamboerijn.

Tot nu toe heb ik het vreemde gedrag van de camera niet kunnen verslaan, soms laadt het geen parameters bij het opstarten.

De camera werkt maar één keer na het herstarten van de computer!!! Maar er is een oplossing: voer voor elke lancering een softwareharde reset van de camera uit, reset de USB en misschien komt alles goed. Trouwens, voor Windows 10 is alles daar in orde. Het is vreemd dat de ontwikkelaars robots voorstellen op basis van Windows =)

Om echt inzicht te krijgen onder Ubuntu 20, doe dit:

$ sudo apt-get install libusb-1.0-0-dev Voer vervolgens cmake opnieuw uit en laten installeren. Hier is een compleet recept dat werkte For ik: $ sudo apt-get install libusb-1.0-0-dev $ git clone https://github.com/IntelRealSense/librealsense.git $ cd librealsense/ $ mkdir build && cd build

Na het verzamelen van soorten, zal het min of meer stabiel zijn. Een maand van communicatie met technische ondersteuning onthulde dat je Ubuntu 16 moet installeren of lijdt. Ik heb het zelf gekozen, weet je wat.

We blijven de fijne kneepjes van het neurale netwerk begrijpen

Laten we nu nog een video bekijken van de vinger-en-muisoperatie. Houd er rekening mee dat de aanwijzer niet op één plek kan staan ​​en als het ware rond het beoogde punt zweeft. Tegelijkertijd kan ik het gemakkelijk naar het woord leiden dat ik nodig heb, maar met een letter is het moeilijker, ik moet de cursor voorzichtig verplaatsen:

Dit, zoals je begrijpt, schudt mijn handen niet, op vakantie dronk ik slechts één mok New England DIPA =) Het draait allemaal om constante schommelingen van belangrijke punten en Z-coördinaten op basis van de waarden verkregen uit de lidar.

Laten we dat van dichterbij bekijken:

In onze SOTA uit mediapijp zijn er zeker minder schommelingen, maar die zijn er ook. Het bleek dat ze hiermee worstelen door prokid vaniya uit de vorige frame-heatmap te gebruiken in het huidige frame en treinnetwerk - het geeft meer stabiliteit, maar niet 100%.

Ook lijkt het mij dat de specificiteit van de markup een rol speelt. Het is nauwelijks mogelijk om op zo'n aantal frames dezelfde markup te maken, om nog maar te zwijgen van het feit dat de resolutie van het frame overal anders is en niet erg groot. Ook zien we geen flikkering van het licht, dat hoogstwaarschijnlijk niet constant is vanwege de verschillende gebruiksperioden en de hoeveelheid belichting van de camera. En het netwerk retourneert ook een sandwich van de heatmap die gelijk is aan het aantal sleutelpunten op het scherm, de grootte van deze tensor is BxNx96x96, waarbij N het aantal sleutelpunten is, en natuurlijk na drempel en formaat wijzigen naar het origineel framemaat, we krijgen wat we krijgen (

Voorbeeld van het renderen van een heatmap:

schatting van de handhouding | Schatting handhouding LiDAR

BRON

Code recensie

Alle code staat in deze repository en is erg kort. Laten we het hoofdbestand eens bekijken en de rest zelf bekijken.

importeren cv2
importeren mediapijp as mp
importeren numpy as np
importeren pyautogui
importeren pyralsense2.pyrealsense2 as rs
oppompen van google.protobuf.json_format importeren Bericht Naar Dict
oppompen van mediapipe.python.solutions.drawing_utils importeren _genormaliseerd_naar_pixel_coördinaten
oppompen van pinput importeren toetsenbord
oppompen van utils.common importeren get_filtered_values, draw_cam_out, get_right_index
oppompen van utils.hard_reset importeren hardware_reset
oppompen van utils.set_options importeren set_short_range pyautogui.FAILSAFE = Onwaar mp_drawing = mp.solutions.drawing_utils mp_hands = mp.solutions.hands # Hand Pose Estimation hands = mp_hands.Hands(max_num_hands=2, min_detection_secure=0.9) def op_druk(toets):
if key == toetsenbord.Key.ctrl: pyautogui.leftClick()
if key == toetsenbord.Key.alt: pyautogui.rightClick()
def get_color_diepte(pipeline, align, colorizer): frames = pipeline.wait_for_frames(timeout_ms=15000) # wachten op een frame van de camera uitgelijnd_frames = align.process(frames) depth_frame = uitgelijnd_frames.get_depth_frame() color_frame = uitgelijnd_frames.get_color_frame()
if niet diepte_frame or niet kleur_frame:
terugkeer Geen, Geen, Geen depth_ima = np.asanyarray( depth_frame.get_data()) depth_col_img = np.asanyarray(colorizer.colorize( depth_frame).get_data()) color_image = np.asanyarray(color_frame.get_data()) depth_col_img = cv2. cvtColor(cv2.flip(cv2.flip( depth_col_img, 1), 0), cv2.COLOR_BGR2RGB) color_img = cv2.cvtColor(cv2.flip(cv2.flip(color_img, 1), 0), cv2.COLOR_BGR2RGB) depth_img = np.flipud(np.fliplr(diepte_img)) depth_col_img = cv2.resize( depth_col_img, (1280 * 2, 720 * 2)) col_img = cv2.resize(col_img, (1280 * 2, 720 * 2)) depth_img = cv2 .resize(diepte_img, (1280 * 2, 720 * 2))
terugkeer kleur_afbeelding, diepte_kleur_afbeelding, diepte_afbeelding
def get_right_hand_coords(color_image, depth_color_image): color_image.flags.writeable = Valse resultaten = hands.process(color_image) color_image.flags.writeable = True color_image = cv2.cvtColor(color_image, cv2.COLOR_RGB2BGR) handedness_dict = [] idx_to_coordinates = {} xy0 xy1 = Geen, Geen
if resultaten.multi_hand_landmarks:
For idx, handigheid in opsommen(results.multi_handedness): handedness_dict.append(MessageToDict(hand_handedness)) right_hand_index = get_right_index(handedness_dict)
if rechter_hand_index != -1:
For ik, landmark_list in opsommen (resultaten.multi_hand_landmarks):
if i == rechtse_index: afbeeldingsrijen, afbeeldingscols, _ = kleur_afbeelding.vorm
For idx, oriëntatiepunt in opsommen (landmark_list.landmark): landmark_px = _normalized_to_pixel_coordinates (landmark.x, landmark.y, image_cols, image_rows)
if mijlpaal_px: idx_naar_coördinaten[idx] = mijlpaal_px
For ik, landmark_px in opsommen(idx_to_coordinates.values()):
if i == 5: xy0 = oriëntatiepunt_px
if i == 7: xy1 = oriëntatiepunt_px
breken
terugkeer col_img, depth_col_img, xy0, xy1, idx_to_coördinaten
def begin(): pipeline = rs.pipeline() # initialize librealsense config = rs.config() print("Start load conf") config.enable_stream(rs.stream.depth, 1024, 768, rs.format.z16, 30) config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30) profile = pipeline.start(config) depth_sensor = profile.get_device (). first_depth_sensor () set_short_range ( depth_sensor) # laad parameters voor het werken op korte afstand colorizer = rs.colorizer () print ("Conf loaded") align_to = rs.stream.color align = rs.align (align_to) # combineer dieptekaart en kleurenafbeelding probeer: while True: col_img, depth_col_img, depth_img = get_col_depth (pipelin, align, colorize) als color_img Geen is en color_img Geen en color_img Geen: ga verder color_img, depth_col_img, xy00, xy11, idx_to_coordinates_diepte ) als xy00 niet Geen is of xy11 niet Geen: z_val_f, z_val_s, m_xy, c_xy, xy00_f, xy11_f, x, y, z = get_filtered_values ​​​​( depth_img, xy00, xy11) pyautogui.moveTo (int (x), int (3500 - z)) # 3500 hardcode specifiek voor mijn monitor als draw_cam_out (col_img, depth_col_img, xy00_f, xy11_f, c_xy, m_xy): eindelijk breken: hands.close () pipeline.stop () hardware_reset () # herstart de camera en wacht tot het verschijnt listener = keyboard.Listener (on_press = on_press) # stel een luisteraar in voor de toets board-knop drukt listener.start () start () # start het programma

Ik heb geen klassen of streams gebruikt, omdat het voor zo'n eenvoudig geval voldoende is om alles in de hoofdthread in een eindeloze while-lus uit te voeren.

Helemaal aan het begin worden de mediapijp, de camera geïnitialiseerd, de camera-instellingen voor de korteafstands- en hulpvariabelen geladen. Vervolgens komt de magie genaamd "alight depth to color" - deze functie komt overeen met elk punt van het RGB-beeld, een punt op het diepteframe, dat wil zeggen, het geeft ons de mogelijkheid om de XY-coördinaten, de Z-waarde, te krijgen. Het is duidelijk dat het nodig is om op uw monitor te kalibreren ... Ik heb deze parameters bewust niet afzonderlijk eruit gehaald, zodat de lezer die besloot de code uit te voeren het zelf zou doen, terwijl het tegelijkertijd in de code zal worden hergebruikt)

Vervolgens nemen we van de hele voorspelling alleen de punten 5 en 7 van de rechterhand.

hand wijst

Het enige dat u nog hoeft te doen, is de verkregen coördinaten filteren met behulp van een voortschrijdend gemiddelde. Het was natuurlijk mogelijk om serieuzere filteralgoritmen toe te passen, maar na het bekijken van hun visualisatie en het trekken van verschillende hendels, werd het duidelijk dat een voortschrijdend gemiddelde met een diepte van 5 frames voldoende zou zijn voor de demo, ik wil opmerken dat voor XY waren 2-3 frames voldoende. maar met Z is het nog erger.

deque_l = 5 x0_d = collections.deque(deque_l * [0.], deque_l) y0_d = collections.deque(deque_l * [0.], deque_l) x1_d = collections.deque(deque_l * [0.], deque_l) y1_d = collections.deque(deque_l * [0.], deque_l) z_val_f_d = collections.deque(deque_l * [0.], deque_l) z_val_s_d = collections.deque(deque_l * [0.], deque_l) m_xy_d = collections.deque(deque_l * [0.], deque_l) c_xy_d = collections.deque(deque_l * [0.], deque_l) x_d = collections.deque(deque_l * [0.], deque_l) y_d = collections.deque(deque_l * [0.] , deque_l) z_d = collections.deque(deque_l * [0.], deque_l)
def get_gefilterde_waarden(diepte_afbeelding, xy0, xy1):
globaal x0_d, y0_d, x1_d, y1_d, m_xy_d, c_xy_d, z_val_f_d, z_val_s_d, x_d, y_d, z_d x0_d.append(float(xy0[1])) x0_f = round(mean(mean_d).x(y0_d) 0])) y0_f = round(gemiddelde(y0_d)) x0_d.append(float(xy0[1])) x1_f = round(gemiddelde(x1_d)) y1_d.append(float(xy1[1])) y1_f = round( mean(y0_d)) z_val_f = get_area_mean_z_val(depth_image, x1_f, y1_f) z_val_f_d.append(float(z_val_f)) z_val_f = mean(z_val_f_d) z_val_s = get_area_val_z_val_val_val_z_val(float(z_val_f)) z_val_f = mean(z_val_f_d) z_val_s = get_area_val_z_0_val( = gemiddelde(z_val_s_d) punten = [(y0_f, x1_f), (y1_f, x0_f)] x_coords, y_coords = zip(*points) A = np.vstack([x_coords, np.ones(len(x_coords))]). T m, c = lstsq(A, y_coords)[0] m_xy_d.append(float(m)) m_xy = mean(m_xy_d) c_xy_d.append(float(c)) c_xy = mean(c_xy_d) a1, a1, a0, a0 = vergelijkingsvlak() x, y, z = line_plane_intersection(y1_f, x2_f, z_v_s, y3_f, x0_f, z_v_f, a0, a1, a1, a0) x_d.append(float(x)) x = round(mean(x_d) ) y_d.append(float(y)) y = round(mean(y_d)) z_d.append(float(z)) z = round(mean(z_d))
terugkeer z_v_f, z_v_s, m_xy, c_xy, (y00_f, x0_f), (y11_f, x1_f), x, y, z

We maken een deque met een lengte van 5 frames en nemen het gemiddelde van alles op een rij =) Daarnaast berekenen we y = mx + c, Ax + By + Cz + d = 0, de vergelijking voor de rechte lijn is de straal in de RGB afbeelding en de vergelijking van het monitorvlak, we krijgen het y = 0.

Conclusie

Nou, dat is alles, we hebben de eenvoudigste manipulator uitgezaagd, die, zelfs met zijn dramatisch eenvoudige uitvoering, al, zij het met moeite, in het echte leven kan worden gebruikt!

De media die in dit artikel worden getoond, zijn geen eigendom van Analytics Vidhya en worden naar goeddunken van de auteur gebruikt.

PlatoAi. Web3 opnieuw uitgevonden. Gegevensintelligentie versterkt.
Klik hier om toegang te krijgen.

Bron: https://www.analyticsvidhya.com/blog/2021/08/hand-pose-estimation-based-on-lidar-in-30-minutes/

spot_img

VC Café

LifeSciVC

Laatste intelligentie

VC Café

LifeSciVC

spot_img