मेटा क्वेस्ट 3 वीआर टेलोप सेटअप
नंगे क्वेस्ट 3 से लाइव रोबोट आर्म टेलीऑपरेशन तक चरण-दर-चरण पथ पूरा करें। नेटवर्क सेटअप, यूनिटी कॉन्फ़िगरेशन, पायथन यूडीपी सर्वर, Piper_controller.py और किसी भी आर्म के लिए एडाप्टर पैटर्न को कवर करता है।
नेटवर्क और पूर्वावश्यकताएँ
कोई भी कोड लिखने से पहले, पुष्टि करें कि मेटा क्वेस्ट 3 और नियंत्रण पीसी एक ही स्थानीय नेटवर्क पर एक दूसरे तक पहुंच सकते हैं। सिस्टम यूडीपी का उपयोग करता है - दोनों डिवाइसों को एक ही सबनेट साझा करना होगा, और यूडीपी पोर्ट 8888 और 8889 पीसी फ़ायरवॉल में खुले होने चाहिए।
आवश्यकताएँ चेकलिस्ट:
- हैंड-ट्रैकिंग सक्षम के साथ मेटा क्वेस्ट 3 हेडसेट (सेटिंग्स → मूवमेंट ट्रैकिंग → हैंड ट्रैकिंग)
- वाई-फ़ाई 6 एक्सेस पॉइंट - यूडीपी ट्रांज़िट विलंबता को 10 एमएस से नीचे रखने की पुरजोर अनुशंसा की जाती है
- Linux (Ubuntu 22.04+) या macOS पर चलने वाले PC को Python 3.10+ से नियंत्रित करें
- एजाइलएक्स पाइपर के लिए: यूएसबी-टू-कैन एडाप्टर (जैसे क्वासर या पीक), कैन केबल बांह से जुड़ा हुआ है
- यूडीपी पोर्ट 8888 और 8889 पीसी फ़ायरवॉल पर इनबाउंड खुलते हैं
कनेक्टिविटी सत्यापित करें:
# On the control PC — find your IP address ip addr show # Linux ipconfig getifaddr en0 # macOS (Wi-Fi) # Quick UDP listener test (run on PC, send any packet from Quest) python3 -c "सॉकेट आयात करें; s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM); s.bind(('0.0.0.0',8888)); print('सुनना...'); print(s.recvfrom(256))"
ip addr पीसी पर और सत्यापित करें कि पहले तीन ऑक्टेट क्वेस्ट 3 के आईपी से मेल खाते हैं (क्वेस्ट सेटिंग्स → वाई-फाई → गियर आइकन में दिखाई देता है)। एक सामान्य मुद्दा यह है कि कॉर्पोरेट वाई-फाई नेटवर्क ग्राहकों को एक-दूसरे से अलग करते हैं - रोबोटिक्स कार्य के लिए एक समर्पित एक्सेस प्वाइंट का उपयोग करें।
एकता ऐप कॉन्फ़िगरेशन
क्वेस्ट 3 पर चलने वाला यूनिटी ऐप हैंड-ट्रैकिंग के लिए एक्सआर हैंड्स पैकेज का उपयोग करके यूनिटी 2022.3 एलटीएस या बाद के संस्करण के साथ बनाया गया है। तीन स्क्रिप्ट टेलीऑपरेशन पक्ष को संभालती हैं:
- VRHandPoseSender.cs - एक्सआर हैंड्स सबसिस्टम से हैंड पोज़ पढ़ता है, 45-बाइट बाइनरी पैकेट को क्रमबद्ध करता है, यूडीपी के माध्यम से भेजता है
- VRGripperController.cs - सामान्यीकृत ग्रिपर मान के लिए पिंच ताकत को मैप करता है [0, 1]
- VRTeleoperationManager.cs - जीवनचक्र प्रबंधन, कनेक्शन स्थिति यूआई, ऑटो-रीकनेक्ट
आप कर नहीं रोबोट हथियारों के बीच स्विच करने के लिए यूनिटी ऐप को फिर से संकलित करने की आवश्यकता है। निम्नलिखित फ़ील्ड को क्रमबद्ध इंस्पेक्टर फ़ील्ड के रूप में प्रदर्शित करें और उन्हें यूनिटी एडिटर से या कॉन्फ़िगरेशन फ़ाइल के माध्यम से ट्यून करें:
| इंस्पेक्टर फील्ड | एजाइलएक्स पाइपर शुरुआती मूल्य | टिप्पणियाँ |
|---|---|---|
| लक्ष्य आईपी | आपके पीसी का आईपी पता | दौड़ना ip addr इसे ढूंढने के लिए पीसी पर |
| स्थितिऑफ़सेट (एम) | (0, 0, 0.3) | आभासी रोबोट की उत्पत्ति को बदलता है; पाइपर की पहुंच xArm6 से कम है |
| रोटेशनऑफ़सेट (डिग्री) | (0, 90, 0) | पाइपर कैन फ़्रेम के लिए 90° Y सुधार; प्रति माउंट ओरिएंटेशन समायोजित करें |
| पैमाने का कारक | 0.75 | पाइपर कार्यक्षेत्र में फिट होने के लिए हाथ की गति सीमा को कम करता है (~600 मिमी पहुंच) |
| कार्यक्षेत्र एक्स (मिमी) | ±400 | भौतिक सीमा के अंदर 50 मिमी का मार्जिन छोड़ें |
| कार्यक्षेत्र Z (मिमी) | 50 – 700 | टकराव से बचने के लिए टेबल की सतह से Z मिनट ऊपर रखें |
पायथन यूडीपी सर्वर सेटअप
पायथन सर्वर तीन समवर्ती थ्रेड चलाता है: a रिसीवर धागा जो कच्चे यूडीपी डेटाग्राम को पढ़ता है, a सुरक्षा धागा जो पैकेटों को मान्य करता है और उन्हें कतारबद्ध करता है, और a रोबोट नियंत्रण धागा जो कतार को ख़त्म कर देता है और रोबोट नियंत्रक को कॉल करता है। यह पृथक्करण सुनिश्चित करता है कि धीमी रोबोट एसडीके कॉल कभी भी यूडीपी रिसेप्शन को अवरुद्ध न करें।
निर्भरताएँ स्थापित करें:
pip install python-can piper_sdk # Activate the CAN interface (run once per boot, requires root or dialout group) bash can_activate.sh can0 1000000 # Verify the interface is UP ifconfig can0
टेलीऑपरेशन सर्वर चलाएँ:
python3 teleoperation_main.py
सर्वर से जुड़ता है 0.0.0.0:8888 (दाहिना हाथ) और वैकल्पिक रूप से 0.0.0.0:8889 (बाएं हाथ). जब कोई पैकेट आता है valid ध्वज सेट, नियंत्रण थ्रेड कॉल robot.set_pose() और robot.set_gripper(). प्रेस Ctrl-सी आपातकालीन रोक और साफ़ शटडाउन को ट्रिगर करने के लिए।
सरलीकृत सर्वर संरचना दिखाती है कि सभी तीन थ्रेड कैसे इंटरैक्ट करते हैं:
# teleoperation_main.py (simplified structure) आयात socket, struct, queue, threading, signal, time से piper_controller आयात PiperController # swap this to change arms HOST = "0.0.0.0" RIGHT_PORT = 8888 LEFT_PORT = 8889 QUEUE_MAXSIZE = 3 # drop stale frames if robot is slow CONTROL_HZ = 30 # robot command rate pose_queue = queue.Queue(maxsize=QUEUE_MAXSIZE) shutdown_event = threading.Event() डीईएफ़ udp_रिसीवर(port: int): """थ्रेड 1 - कच्चे यूडीपी डेटाग्राम प्राप्त करें।""" sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((HOST, port)) sock.settimeout(1.0) जबकि नहीं shutdown_event.is_set(): कोशिश: data, _ = sock.recvfrom(256) pose = parse_packet(data) अगर pose और pose["वैध"]: कोशिश: pose_queue.put_nowait(pose) के अलावा queue.Full: pose_queue.get_nowait() # drop oldest frame pose_queue.put_nowait(pose) के अलावा socket.timeout: जारी रखना sock.close() डीईएफ़ रोबोट_कंट्रोल_लूप(robot: PiperController): """थ्रेड 3 - CONTROL_HZ पर ड्रेन कतार और कमांड रोबोट।""" period = 1.0 / CONTROL_HZ last_pose = None जबकि नहीं shutdown_event.is_set(): t0 = time.monotonic() कोशिश: pose = pose_queue.get(timeout=0.1) अगर pose["रुकना"]: robot.emergency_stop() जारी रखना अगर pose["वैध"]: last_pose = pose x, y, z = transform_position(pose["पद"]) roll, pitch, yaw = quat_to_euler(pose["रोटेशन"]) robot.set_pose(x, y, z, roll, pitch, yaw) robot.set_gripper(pose["ग्रिपर"]) # tracking lost — hold last known position (do not send zero) के अलावा queue.Empty: उत्तीर्ण time.sleep(max(0, period - (time.monotonic() - t0)))
QUEUE_MAXSIZE 1 का न्यूनतम विलंबता देता है लेकिन झटकेदार महसूस हो सकता है। 3-5 का मान पैकेट हानि पर गति को सुचारू करता है लेकिन 30 हर्ट्ज पर 100 एमएस अतिरिक्त विलंबता जोड़ता है। 3 से शुरू करें और स्वाद के अनुसार ट्यून करें।
Piper_controller.py वॉकथ्रू
The piper_controller.py मॉड्यूल AgileX को लपेटता है piper_sdk पायथन लाइब्रेरी. यह अपेक्षित पांच-विधि इंटरफ़ेस को कार्यान्वित करता है teleoperation_main.py. मुख्य डिज़ाइन निर्णय:
- USB के माध्यम से कर सकते हैं: पाइपर भुजा CAN बस पर संचार करती है। SDK एक CAN इंटरफ़ेस नाम लेता है (
can0) और कनेक्ट करने से पहले इंटरफ़ेस को सक्रिय करना आवश्यक है। - गुलाम मोड: कॉलिंग
MasterSlaveConfig(0xFC, 0, 0, 0)आर्म को स्लेव मोड में डालता है ताकि यह स्ट्रीमिंग पोजीशन कमांड स्वीकार कर सके। - कार्यक्षेत्र क्लैम्पिंग: प्रत्येक एसडीके कॉल से पहले स्थिति को फ़ाइल के शीर्ष पर स्थिरांक से सख्ती से जोड़ा जाता है - ये फर्मवेयर से पहले सॉफ़्टवेयर सुरक्षा की अंतिम पंक्ति हैं।
- एसडीके इकाइयाँ: स्थिति माइक्रोमीटर (पूर्णांक) में है, अभिविन्यास मिलिडिग्री में है - पास होने से पहले फ्लोट मिमी/डिग्री मान को 1000 से गुणा करें
EndEffectorCtrl.
# piper_controller.py से piper_sdk आयात C_PiperInterface आयात math, logging logger = logging.getLogger(__name__) # Piper workspace limits (millimetres) — keep 50 mm inside physical limits X_MIN, X_MAX = -400, 400 Y_MIN, Y_MAX = -400, 400 Z_MIN, Z_MAX = 50, 700 # Maximum joint speed (0–100 %; keep conservative for teleop) SPEED_PERCENT = 25 कक्षा पाइपरकंट्रोलर: """XArmController के लिए ड्रॉप-इन प्रतिस्थापन - वही पांच-विधि इंटरफ़ेस।"" डीईएफ़ __इस में__(self, can_interface: str = "कर सकते हैं"): self.can_interface = can_interface self._piper: C_PiperInterface | None = None self.connected = False डीईएफ़ जोड़ना(self) -> bool: """कैन पोर्ट खोलें और स्लेव मोड सक्षम करें।"" कोशिश: self._piper = C_PiperInterface( can_name=self.can_interface, judge_flag=False, # allow 3rd-party CAN adapters can_auto_init=True, dh_is_offset=1, # firmware >= V1.6-3 ) self._piper.ConnectPort() self._piper.MasterSlaveConfig(0xFC, 0, 0, 0) # enter slave mode self.connected = True logger.info("पाइपर %s पर कनेक्ट हुआ", self.can_interface) वापस करना True के अलावा Exception जैसा e: logger.error("पाइपर कनेक्ट विफल: %s", e) वापस करना False डीईएफ़ डिस्कनेक्ट(self): """सर्वो को अक्षम करें और पोर्ट बंद करें।"" अगर self._piper: कोशिश: self._piper.DisableArm(7) # disable all joints के अलावा Exception: उत्तीर्ण self.connected = False डीईएफ़ set_pose(self, x: float, y: float, z: float, roll: float, pitch: float, yaw: float): """एंड-इफ़ेक्टर को ओरिएंटेशन (रोल, पिच, यॉ) डिग्री के साथ (x,y,z) मिमी पर ले जाएं।"" यदि नहीं self.connected: वापस करना x = max(X_MIN, min(X_MAX, x)) y = max(Y_MIN, min(Y_MAX, y)) z = max(Z_MIN, min(Z_MAX, z)) कोशिश: self._piper.EndEffectorCtrl( int यहाँ(x * 1000), int यहाँ(y * 1000), int यहाँ(z * 1000), int यहाँ(roll * 1000), int यहाँ(pitch * 1000), int यहाँ(yaw * 1000), SPEED_PERCENT, ) के अलावा Exception जैसा e: logger.warning("set_pose त्रुटि: %s", e) डीईएफ़ सेट_ग्रिपर(self, value: float): """ग्रिपर खुलापन सेट करें। 0.0 = पूरी तरह से बंद, 1.0 = पूरी तरह से खुला।"" यदि नहीं self.connected: वापस करना GRIPPER_MAX_UM = 70_000 # 70 mm max opening in µm target_um = int यहाँ(value * GRIPPER_MAX_UM) कोशिश: self._piper.GripperCtrl(target_um, SPEED_PERCENT, 0x01, 0) के अलावा Exception जैसा e: logger.warning("सेट_ग्रिपर त्रुटि: %s", e) डीईएफ़ आपातकालीन_रोकें(self): """सभी जोड़ों को तुरंत अक्षम करें। किसी भी थ्रेड से कॉल करना सुरक्षित है।""" अगर self._piper: कोशिश: self._piper.DisableArm(7) के अलावा Exception: उत्तीर्ण
piper.GetPiperFirmwareVersion() और किसी भी प्रस्ताव का आदेश देने से पहले AgileX डेवलपर दस्तावेज़ों से सत्यापित करें।
सुरक्षा सत्यापन और पहला सत्र
ड्राई-रन चेकलिस्ट (बिजली बंद):
- पायथन सर्वर प्रारंभ करें और क्वेस्ट 3 कनेक्ट करें। देखें
transform_positionआउटपुट टर्मिनल पर मुद्रित होता है। - अपना हाथ इच्छित कार्यक्षेत्र के केंद्र में रखें। पुष्टि करें कि मुद्रित XYZ मान रोबोट की घरेलू स्थिति के पास हैं (पाइपर के लिए लगभग 0, 0, 300 मिमी)।
- अपने हाथ को कार्यस्थल के प्रत्येक किनारे पर ले जाएँ। पुष्टि करें कि मान निर्धारित सीमा के भीतर रहें और Z पर कभी भी नकारात्मक न हों।
- सॉफ़्टवेयर ई-स्टॉप को ट्रिगर करने के लिए क्वेस्ट पर मेनू बटन दबाएँ। पुष्टि करना
emergency_stop()कहा जाता है और लूप रुक जाता है।
पहला लाइव सत्र:
- पर शुरू करें
SPEED_PERCENT = 20— यह लगभग 40°/s अधिकतम संयुक्त गति है। - सर्वो पावर सक्षम करें. पहले मिनट तक हाथ की घरेलू स्थिति के पास रहकर धीरे-धीरे आगे बढ़ें।
- सत्यापित करें कि हाथ की गति और रोबोट की गति एक ही दिशा में है। यदि कलाई पीछे की ओर घूमती है, तो समायोजित करें
rotationOffsetयूनिटी इंस्पेक्टर में Y पर ±90° से। - दिशा मानचित्रण सही होने की पुष्टि हो जाने पर धीरे-धीरे गति सीमा का विस्तार करें।
- भौतिक आपातकालीन स्टॉप (पावर रिले) को हर समय पहुंच के भीतर रखें।
दो सॉफ़्टवेयर ई-स्टॉप पथ कार्यान्वित किए गए हैं:
- क्वेस्ट 3 मेनू बटन: प्रत्येक आगामी यूडीपी पैकेट में फ़्लैग बाइट का बिट 1 सेट करता है। पायथन सर्वर कॉल करता है
robot.emergency_stop()तुरंत। - Ctrl-सी (हस्ताक्षर): सिग्नल हैंडलर शटडाउन इवेंट सेट करता है, जिससे कंट्रोल लूप कॉल होता है
emergency_stop()और सफाई से बाहर निकलें.
valid यूडीपी पैकेट में ध्वज 0 पर गिर जाता है। पायथन सर्वर हाथ को शून्य स्थिति में खींचने के बजाय अंतिम ज्ञात स्थिति रखता है।
एडॉप्टर इंटरफ़ेस - किसी भी आर्म पर पोर्ट
नियंत्रक-स्वैप पैटर्न पायथन एसडीके के साथ किसी भी हाथ के लिए सामान्यीकृत होता है। एकमात्र आवश्यकता यह है कि आपका नियंत्रक वर्ग इन पांच विधियों को बिल्कुल इसी हस्ताक्षर के साथ लागू करता है:
| तरीका | हस्ताक्षर | अनुबंध |
|---|---|---|
| जोड़ना() | () → बूल | संचार चैनल खोलता है; सफलता पर True लौटाता है |
| डिस्कनेक्ट() | ()→ कोई नहीं | सर्वो पावर को अक्षम करता है और पोर्ट या सॉकेट को बंद कर देता है |
| set_pose(x, y, z, रोल, पिच, yaw) | (फ्लोट×6) → कोई नहीं | मिमी + डिग्री में कार्टेशियन अंत-प्रभावक लक्ष्य; आंतरिक रूप से दबाना चाहिए |
| सेट_ग्रिपर(मान) | (फ्लोट) → कोई नहीं | सामान्यीकृत खुलापन 0.0-1.0; आंतरिक रूप से हाथ-विशिष्ट इकाइयों के लिए मानचित्र |
| आपातकालीन_स्टॉप() | ()→ कोई नहीं | किसी भी समय किसी भी थ्रेड से कॉल करना सुरक्षित होना चाहिए |
नया हाथ जोड़ने के चरण:
- लिखना
myarm_controller.pyअपने हाथ के एसडीके का उपयोग करके उपरोक्त पाँच विधियों को लागू करना। मॉड्यूल-स्तरीय स्थिरांक के रूप में हार्डकोड कार्यक्षेत्र सीमा और गति सीमा। - अलगाव में इकाई-परीक्षण: कॉल करें
connect(), तबset_poseघर से 5 सेमी सुरक्षित स्थान पर। सत्यापित करें कि हाथ हिलता है और अपेक्षित स्थिति लौटाता है। - आयात को स्वैप करें
teleoperation_main.py: प्रतिस्थापित करेंfrom piper_controller import PiperControllerअपने नए नियंत्रक के साथ. किसी अन्य परिवर्तन की आवश्यकता नहीं है. - यूनिटी इंस्पेक्टर मापदंडों को कैलिब्रेट करें (
positionOffset,rotationOffset,scaleFactor) नई शाखा के कार्यक्षेत्र के लिए। - किसी भी ऑपरेटर सत्र से पहले ई-स्टॉप व्यवहार, ट्रैकिंग-लॉस होल्ड और वर्कस्पेस क्लैंप को मान्य करें।
geometry_msgs/PoseStamped अंदर का विषय set_pose - बाकी वास्तुकला समान रहती है। कतार-आधारित डिज़ाइन स्वाभाविक रूप से ROS 2 पब/उप चक्र की विलंबता को अवशोषित करता है।