{"id":400950,"date":"2024-06-29T15:33:45","date_gmt":"2024-06-29T15:33:45","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=400950"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T15:33:45","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=400950","title":{"rendered":"<span>\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u043e\u0431\u043e\u0442\u0430\u043c\u0438 \u0438\u0437 VR. \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 2<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f <strong>\u2013<\/strong> <a href=\"https:\/\/habr.com\/ru\/articles\/730164\/\" rel=\"noopener noreferrer nofollow\">\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u043e\u0431\u043e\u0442\u0430\u043c\u0438 \u0438\u0437 VR<\/a><\/p>\n<p>\u0412\u0442\u043e\u0440\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f <strong>\u2013<\/strong> <a href=\"https:\/\/habr.com\/ru\/articles\/730450\/\" rel=\"noopener noreferrer nofollow\">\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u043e\u0431\u043e\u0442\u0430\u043c\u0438 \u0438\u0437 VR. \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 1. <\/a><\/p>\n<p>\u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0441\u0435\u0442\u0435\u0432\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438 DataChannel \u0438 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u043c\u0435\u0434\u0438\u0430\u0442\u0440\u0430\u043a\u0430\u043c\u0438 \u0438\u0437 JS\/Unity\/Python. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0430\u043c\u0435\u0440\u043e\u0439 \u0438 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u043e\u043c(\u0427\u0430\u0441\u0442\u0438 5, 6).<\/p>\n<h2>\u0427\u0430\u0441\u0442\u044c 5. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u0430\u043c\u0435\u0440\u043e\u0439<\/h2>\n<p>\u041d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u043e \u0441 \u043c\u0430\u043b\u043e\u0433\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f LED \u0438 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430\u043c\u0438 \u0433\u043e\u043b\u043e\u0432\u044b, \u044d\u0442\u0438\u043c \u0443 \u043d\u0430\u0441 \u0437\u0430\u043d\u0438\u043c\u0430\u044e\u0442\u0441\u044f 2 \u0441\u0435\u0440\u0432\u043e SG90 \u043d\u0430 \u043a\u0440\u043e\u043d\u0448\u0442\u0435\u0439\u043d\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 datachannel \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441\u0430 LED \u0438 \u043f\u043e\u0437\u0438\u0446\u0438\u0438, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0443\u0433\u043e\u043b \u0441\u0435\u0440\u0432\u043e\u043c\u043e\u0442\u043e\u0440\u0430, \u0430 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 Python \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u0438\u0445 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c.<\/p>\n<h3>\u0421\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442<\/h3>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0443 \u043d\u043e\u0432\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>main.html<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"xml\">...   &lt;div class=\"row-md-6 mt-3 mb-3\">     &lt;form class=\"form-inline\">       &lt;div class=\"form-group\">         &lt;div class=\"custom-control custom-switch\">           &lt;input type=\"checkbox\" class=\"custom-control-input\" id=\"ledSwitch\" disabled>           &lt;label class=\"custom-control-label\" for=\"ledSwitch\" id=\"ledSwitchLabel\">LED control (LED Off)&lt;\/label>         &lt;\/div>       &lt;\/div>     &lt;\/form>   &lt;\/div>   &lt;div class=\"row-md-6 mt-3 mb-3\">     &lt;form class=\"form-inline\">       &lt;div class=\"form-group\">         &lt;label for=\"servoRangeX\" class=\"form-label\">SG90 X-angle:&lt;\/label>         &lt;input type=\"range\" class=\"form-range\" min=\"0\" max=\"180\" value=\"90\" step=\"1\" style=\"width: 500px;\" id=\"servoRangeX\" disabled>         &lt;span id=\"servoRangeXInfo\">&lt;\/span>       &lt;\/div>     &lt;\/form>   &lt;\/div>   &lt;div class=\"row-md-6 mt-3 mb-3\">     &lt;form class=\"form-inline\">       &lt;div class=\"form-group\">         &lt;label for=\"servoRangeY\" class=\"form-label\">SG90 Y-angle:&lt;\/label>         &lt;input type=\"range\" class=\"form-range\" min=\"0\" max=\"180\" value=\"90\" step=\"1\" style=\"width: 500px;\" id=\"servoRangeY\" disabled>         &lt;span id=\"servoRangeYInfo\">&lt;\/span>       &lt;\/div>     &lt;\/form>   &lt;\/div> ...<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0448 \u0441\u043a\u0440\u0438\u043f\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<details class=\"spoiler\">\n<summary>robo.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">\/\/ \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u044e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0438 datachannel: \/\/ open handling dataChannel.onopen = function(){   console.log(\"Data channel is open!\");   $( \"#ledSwitch\" ).prop( \"disabled\", false );   $( \"#servoRangeX\" ).prop( \"disabled\", false );   $( \"#servoRangeY\" ).prop( \"disabled\", false ); }  \/\/ \u0418 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u043b\u0430\u044e\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0438\u043d\u0444\u043e: function switchLed(){   if(dataChannel != null){     if(blink == \"ON\"){       dataChannel.send(\"BLINK_OFF\");       console.log(\"LED is OFF\");       blink = \"OFF\";       $(\"#ledSwitchLabel\").text(\"LED control (LED OFF)\");     } else{       dataChannel.send(\"BLINK_ON\");       console.log(\"LED is ON\");       blink = \"ON\";       $(\"#ledSwitchLabel\").text(\"LED control (LED ON)\");     }   } }  function servoRangeX(){   if(dataChannel != null){     servoAngleX = $(\"#servoRangeX\").val();     dataChannel.send(\"X-ANG#\"+servoAngleX);     console.log(\"Sending new servo X angle = \"+ servoAngleX);     $(\"#servoRangeXInfo\").text(\"\" + servoAngleX);   } }  function servoRangeY(){   if(dataChannel != null){     servoAngleY = $(\"#servoRangeY\").val();     dataChannel.send(\"Y-ANG#\"+servoAngleY);     console.log(\"Sending new servo Y angle = \"+ servoAngleY);     $(\"#servoRangeYInfo\").text(\"\" + servoAngleY);   } } ... $(\"#ledSwitch\").change(function() {switchLed(); }); $(\"#servoRangeX\").change(function() {servoRangeX(); }); $(\"#servoRangeY\").change(function() {servoRangeY(); });<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0418\u0442\u043e\u0433\u043e, \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 datachannel \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441 \u043d\u043e\u0432\u044b\u043c \u0443\u0433\u043b\u043e\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0441\u0435\u0440\u0432\u043e \u0438\u043b\u0438 \u0432\u043a\u043b\/\u0432\u044b\u043a\u043b LED.<\/p>\n<h3>\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442(Python-\u0441\u043a\u0440\u0438\u043f\u0442 \u043d\u0430 RPI)<\/h3>\n<p>\u0421\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u044d\u0442\u0438\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439.<\/p>\n<details class=\"spoiler\">\n<summary>part4.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\"> ## \u0412\u0441\u043f\u043e\u043c\u043d\u0438\u043c \u043d\u0430\u0448\u0443 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0443\u0436\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438: import RPi.GPIO as GPIO import busio from board import SCL, SDA from adafruit_motor import servo from adafruit_pca9685 import PCA9685  ## \u0418\u043d\u0438\u0446\u0438\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e: GPIO.setmode(GPIO.BCM) GPIO.setup(21, GPIO.OUT) i2c = busio.I2C(SCL, SDA) pca = PCA9685(i2c) pca.frequency = 60 servoX = servo.Servo(pca.channels[0], min_pulse=500, max_pulse=2400) servoY = servo.Servo(pca.channels[1], min_pulse=500, max_pulse=2400) servoX.angle = 90 servoY.angle = 90  ## \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e on message: @channel.on(\"message\") async def on_message(rtc_message):   if isinstance(rtc_message, str):   print(\"New message from datachannel \" + rtc_message)   #channel.send(\"Reply from PyPi - \" + rtc_message)   await consume_message(rtc_message)  ## \u0418 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0435\u0435: async def consume_message(rtc_message: str) -> None:   if rtc_message.startswith(\"BLINK_ON\"):   GPIO.output(21,True)   if rtc_message.startswith(\"BLINK_OFF\"):   GPIO.output(21,False)   if rtc_message.startswith(\"X-ANG\"):   xServoAngle = int(rtc_message.split(\"#\")[1])   servoX.angle = xServoAngle   if rtc_message.startswith(\"Y-ANG\"):   yServoAngle = int(rtc_message.split(\"#\")[1])   servoY.angle = yServoAngle<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043c\u0435\u0436\u0434\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u043c \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u043c:<\/p>\n<details class=\"spoiler\">\n<summary>\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430:<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/44c\/92f\/c6f\/44c92fc6f66846d4b16fd356ca3169da.png\" width=\"794\" height=\"424\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/44c\/92f\/c6f\/44c92fc6f66846d4b16fd356ca3169da.png\"\/><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fa5\/2e9\/778\/fa52e9778d7fad48186ff4b2f2e5c9af.png\" width=\"794\" height=\"507\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/fa5\/2e9\/778\/fa52e9778d7fad48186ff4b2f2e5c9af.png\"\/><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u043a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, LED \u0438 \u0441\u0435\u0440\u0432\u043e \u043e\u0442\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442 \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u043a VR \u0447\u0430\u0441\u0442\u0438.<\/p>\n<h3>\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442(Unity VR)<\/h3>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430 \u0433\u043e\u043b\u043e\u0432\u044b \u043d\u0443\u0436\u043d\u043e \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0443.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0443\u0441\u0442\u043e\u0439 \u043e\u0431\u044a\u0435\u043a\u0442, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 \u043d\u0435\u043c \u043d\u043e\u0432\u0443\u044e \u043a\u0430\u043c\u0435\u0440\u0443 Robo Camera(\u0442\u044d\u0433 RoboCamera) \u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043c \u043d\u0430\u0448 Panel \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 Robo Camera. \u041f\u0440\u0438\u0447\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u043c \u0438\u0445 \u043d\u0430 \u0432\u044b\u0441\u043e\u0442\u0435 3 \u043d\u0430\u0434 ground. \u0418 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0442\u0434\u0430\u043b\u0438\u043c Image \u043e\u0442 \u043a\u0430\u043c\u0435\u0440\u044b.(+ \u0443 Robo Camera \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u043c AudioListener). \u0422\u0430\u043a \u0436\u0435 \u0432 Robo Camera  \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u00abTracked Pose Driver(Input System)\u00bb \u0438 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u043c \u0435\u0433\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Position Input\/ Rotation Input \u0441 Main Camera.<\/p>\n<details class=\"spoiler\">\n<summary>\u0414\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432\u043e\u0442 \u0442\u0430\u043a:<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/571\/fab\/28c\/571fab28cf581cd58391f8a64558377b.png\" alt=\"\" title=\"\" width=\"372\" height=\"301\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/571\/fab\/28c\/571fab28cf581cd58391f8a64558377b.png\"\/><\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/367\/7cc\/093\/3677cc0933ad19fd5da43258c23fc0ad.png\" width=\"794\" height=\"347\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/367\/7cc\/093\/3677cc0933ad19fd5da43258c23fc0ad.png\"\/><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0442\u043e\u043c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u043a\u0430\u043c\u0435\u0440\u0430\u043c\u0438 Main \u0438 Robo. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043d\u043e\u043f\u043e\u043a \u043d\u0430 \u043f\u0440\u0430\u0432\u043e\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435 Oculus. \u0421\u0434\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0432 \u0441\u043a\u0440\u0438\u043f\u0442 RightControllerButtonWatcher.cs<\/p>\n<details class=\"spoiler\">\n<summary>RightControllerButtonWatcher.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.XR; public class RightControllerButtonWatcher : MonoBehaviour {   public static bool triggerValue;   private List&lt;InputDevice> rightHandDevices;   InputDevice rightHandCantroller;    void Awake()   {       rightHandDevices = new List&lt;InputDevice>();   }      void Update()   {       if (rightHandDevices.Count == 0)       {           InputDevices.GetDevicesAtXRNode(XRNode.RightHand, rightHandDevices);           Debug.Log(\"Try to get Right controller!\");       }          if (rightHandDevices.Count > 0)       {           rightHandCantroller = rightHandDevices[0];           if (rightHandCantroller.TryGetFeatureValue(CommonUsages.secondaryButton, out triggerValue) &amp;&amp; triggerValue)           {               gameObject.SendMessage(\"MakeVideoInFullScreen\", triggerValue);           }       }   }  } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0418 \u043f\u0440\u0438\u0430\u0442\u0442\u0430\u0447\u0438\u043c \u0435\u0433\u043e \u043a \u0442\u043e\u043c\u0443 \u0436\u0435 GameObject, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u0441\u043a\u0440\u0438\u043f\u0442 Connection.cs.<\/p>\n<p>\u0412 \u0441\u0430\u043c\u043e\u043c Connection.cs \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u043d\u0430\u043a\u043e\u043f\u0438\u0442\u0435\u043b\u044f.<\/p>\n<details class=\"spoiler\">\n<summary>Connection.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">... private bool fullScreenOn = false; private short fullScreenCounter = 0; private Camera mainCamera; private Camera roboCamera; ... void Start() { mainCamera = GameObject.FindGameObjectWithTag(\"MainCamera\").GetComponent&lt;Camera>(); roboCamera = GameObject.FindGameObjectWithTag(\"RoboCamera\").GetComponent&lt;Camera>(); roboCamera.enabled = false; CameraTrackingChanger(roboCamera, false); CameraAudioChanger(roboCamera, false); ... ... void CameraTrackingChanger(Camera camera, bool status) {   Component[] components = camera.GetComponents(typeof(MonoBehaviour));   foreach (Component comp in components)   {     if (comp.ToString().Contains(\"TrackedPoseDriver\"))     {       MonoBehaviour mb = (MonoBehaviour)comp;       mb.enabled = status;     }   } }  void CameraAudioChanger(Camera camera, bool status) {   var audio = camera.GetComponent&lt;AudioListener>();   audio.enabled = status; }    \/\/ \u0418 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u0435\u0442\u043e\u0434  MakeVideoInFullScreen public void MakeVideoInFullScreen(bool trigger) {   if (trigger &amp;&amp; !fullScreenOn)   {     fullScreenCounter++;     if (fullScreenCounter >= 100)     {       fullScreenOn = true;       mainCamera.enabled = false;       CameraTrackingChanger(mainCamera, false);       CameraAudioChanger(mainCamera, false);       roboCamera.enabled = true;       CameraTrackingChanger(roboCamera, true);       CameraAudioChanger(roboCamera, true);       Debug.Log(\"Robo cam switch On\");     }   }   else if (trigger &amp;amp;&amp;amp; fullScreenOn)   {   fullScreenCounter--;   if (fullScreenCounter &amp;lt;= 0)   {     fullScreenOn = false;     roboCamera.enabled = false;     CameraTrackingChanger(roboCamera, false);     CameraAudioChanger(roboCamera, false);     mainCamera.enabled = true;     CameraTrackingChanger(mainCamera, true);     CameraAudioChanger(mainCamera, true);     Debug.Log(\"Robo cam switch Off\");   }   } }  <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041c\u043e\u0436\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443. \u041f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443 \u043d\u0430\u0441 \u0430\u043a\u0442\u0438\u0432\u043d\u0430 Main Camera. \u041f\u0440\u0438 \u0437\u0430\u0436\u0430\u0442\u0438\u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \u00abB\u00bb (SecondaryButton) \u043f\u0440\u0430\u0432\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u043d\u0430\u0441 \u043f\u0435\u0440\u0435\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u043d\u0430 Robo Camera. \u0418 \u043e\u0431\u0440\u0430\u0442\u043d\u043e.<\/p>\n<p>\u041f\u043e\u0442\u043e\u043c \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0435 \u0433\u043e\u043b\u043e\u0432\u044b \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0443\u0433\u043e\u043b \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430 \u0448\u043b\u0435\u043c\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0443\u0433\u043b\u043e\u0432 \u0438 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u044e \u0438\u0445 \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u0435\u043c\u043b\u0435\u043c\u044b \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0441\u0435\u0440\u0432\u043e:<\/p>\n<details class=\"spoiler\">\n<summary>HeadRotation.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">public class HeadRotation : MonoBehaviour {   public static int valHeadX;   public static int valHeadY;   void Update()   {       if (Camera.current != null)       {           valHeadX = (int) Camera.current.transform.localEulerAngles.x;           valHeadY = (int) Camera.current.transform.localEulerAngles.y;              \/\/ angle casting           if (valHeadX &amp;gt;= 0 &amp;amp;&amp;amp; valHeadX &amp;lt; 180) valHeadX = 90 - valHeadX;           else if (valHeadX &amp;gt; 180) valHeadX = 360 - valHeadX + 90;              if (valHeadY &amp;gt;= 0 &amp;amp;&amp;amp; valHeadY &amp;lt; 180) valHeadY = 90 - valHeadY;           else if (valHeadY &amp;gt; 180) valHeadY = 360 - valHeadY + 90;       }   } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0438 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0435 Connection.cs \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u044d\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f(\u0442.\u043a. \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 valHeadX valHeadY \u2013 public static) \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432 datachannel 20 \u0440\u0430\u0437 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443.<\/p>\n<details class=\"spoiler\">\n<summary>Connection.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">... private int headX; private int headY; private float elapsed = 0f; ...  void Update() {   if (dataChannel != null)   {   elapsed += Time.deltaTime;   if (elapsed >= 0.05f)   {     elapsed = 0f;     if (headX != HeadRotation.valHeadX)     {       headX = HeadRotation.valHeadX;       var message = \"X-ANG#\" + headX;       dataChannel.Send(message);     }     if (headY != HeadRotation.valHeadY)     {       headY = HeadRotation.valHeadY;       var message = \"Y-ANG#\" + headY;       dataChannel.Send(message);     }   } } ... <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u2013 \u043f\u043e\u0432\u043e\u0440\u043e\u0442 \u0433\u043e\u043b\u043e\u0432\u044b \u0432 Unity \u0438\u0434\u0435\u0442 \u0432 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 0-360 \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0437 \u043e\u0441\u0438 \u0440\u043e\u0442\u0430\u0446\u0438\u0438, \u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0443 \u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0441\u0435\u0440\u0432\u043e\u043c\u043e\u0442\u043e\u0440\u0430 0-180, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043a\u0430\u0441\u0442 \u043e\u0442 0-360 \u043a 0-180, \u0441\u0434\u0435\u043b\u0430\u043d \u043e\u043d \u0433\u0440\u0443\u0431\u043e, \u043d\u043e \u044d\u0442\u043e\u0433\u043e \u0432\u043f\u043e\u043b\u043d\u0435 \u0445\u0432\u0430\u0442\u0438\u0442.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u043e ws \u0438 webrtc, \u043d\u0430\u0436\u0430\u0442\u0438\u0435\u043c \u043a\u043b\u0430\u0432\u0438\u0448\u0438 \u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435 \u043d\u0430\u0441 \u043f\u0435\u0440\u0435\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u043d\u0430 \u043d\u043e\u0432\u0443\u044e \u043a\u0430\u043c\u0435\u0440\u0443, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u0432\u0438\u0434\u0438\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441 \u043a\u0430\u043c\u0435\u0440\u044b, \u0430 HeadRotation.cs \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430 \u0433\u043e\u043b\u043e\u0432\u044b \u043f\u043e \u043e\u0441\u044f\u043c \u0438 \u043a\u0430\u0441\u0442\u0443\u0435\u0442 \u0438\u0445 \u043a \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u043c\u0443 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0443.<\/p>\n<h4>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432<\/h4>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c Python \u0441\u043a\u0440\u0438\u043f\u0442 \u0438 Unity, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435, \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u043a\u0430\u043c\u0435\u0440\u0430\u043c\u0438 \u0438 \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043f\u0440\u0438 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0435 \u0433\u043e\u043b\u043e\u0432\u044b \u0441\u0435\u0440\u0432\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043f\u043e\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u044e\u0442 \u043a\u0430\u043c\u0435\u0440\u0443, \u043c\u043e\u0436\u0435\u043c \u0432\u0435\u0440\u0442\u0435\u0442\u044c \u0433\u043e\u043b\u043e\u0432\u043e\u0439 \u0438 \u043d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u0442\u044c\u0441\u044f.<\/p>\n<hr\/>\n<h2>\u0427\u0430\u0441\u0442\u044c 6. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u043e\u043c<\/h2>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u043e\u043c \u0441\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043a 4 \u044d\u0442\u0430\u043f\u0430\u043c:<\/p>\n<ul>\n<li>\n<p>\u0418\u043d\u0438\u0446\u0438\u0430\u0446\u0438\u044e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u044d\u0442\u043e \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Grip Button \u043f\u0440\u0430\u0432\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0438 \u043d\u0430\u043a\u043e\u043f\u0438\u0442\u0435\u043b\u044f, \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043a\u0430\u043c\u0435\u0440\u044b, \u0442.\u00a0\u0435. \u0435\u0441\u043b\u0438 \u0437\u0430\u0436\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443 Grip \u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435 \u2013 \u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u043a \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u0440\u0443\u043a\u043e\u0439. \u0415\u0441\u043b\u0438 \u0437\u0430\u0436\u0430\u0442\u044c \u0435\u0449\u0435 \u0440\u0430\u0437 \u2013 \u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043e\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0445\u0432\u0430\u0442\u043e\u043c(\u043a\u043b\u0435\u0448\u043d\u0435\u0439). \u042d\u0442\u043e\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0431\u0443\u0434\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0436\u043e\u0439\u0441\u0442\u0438\u043a\u0430 \u043d\u0430 \u043f\u0440\u0430\u0432\u043e\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435 \u043a\u0443\u0440\u0441\u043e\u0440 \u043b\u0435\u0432\u043e-\u043f\u0440\u0430\u0432\u043e.<\/p>\n<\/li>\n<li>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u043e\u043c \u043a\u043b\u0435\u0448\u043d\u0438. \u042d\u0442\u043e\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0431\u0443\u0434\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0440\u043e\u0442\u0430\u0446\u0438\u0438 \u043f\u0440\u0430\u0432\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u043a\u043b\u0435\u0448\u043d\u0438 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435. \u0422\u0443\u0442 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u043f\u043e \u043e\u0441\u044f\u043c X-Y-Z \u0432 unity.<\/p>\n<\/li>\n<\/ul>\n<p>\u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043f\u0443\u043d\u043a\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0430\u043c\u044b\u043c \u0441\u043b\u043e\u0436\u043d\u044b\u043c \u0438\u0437 4\u0445. \u041c\u044b \u0445\u043e\u0442\u0438\u043c \u0438\u0437 Unity \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e Vector3 \u0441 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430(\u0438\u0445 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c), \u0430 \u0441\u0435\u0440\u0432\u043e\u043c\u043e\u0442\u043e\u0440\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043d\u0430 \u043d\u0443\u0436\u043d\u044b\u0439 \u0443\u0433\u043e\u043b \u0432 \u043d\u0443\u0436\u043d\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043a \u0438\u043d\u0432\u0435\u0440\u0441\u043d\u043e\u0439 \u043a\u0438\u043d\u0435\u043c\u0430\u0442\u0438\u043a\u0435.<\/p>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u043f\u0443\u0442\u044f\u043c\u0438:<\/p>\n<ul>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c IK solver \u0438\u0437 Unity \u2013 \u0442.\u00a0\u0435. \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0430\u0448\u0435\u0439 \u0440\u043e\u0431\u043e\u0440\u0443\u043a\u0438 \u0432 Unity \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0443\u0433\u043b\u043e\u0432 \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u0443\u044e \u0440\u043e\u0431\u043e\u0440\u0443\u043a\u0443. \u042f \u0435\u0433\u043e \u043e\u0442\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430, \u0442.\u00a0\u043a. \u044d\u0442\u043e \u0436\u0435\u0441\u0442\u043a\u043e \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u043c\u0443 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0443.<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 IK solver \u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u043e\u0432 Python, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <a href=\"https:\/\/pypi.org\/project\/tinyik\/\" rel=\"noopener noreferrer nofollow\">tinyik<\/a>, <a href=\"https:\/\/pypi.org\/project\/pybotics\/\" rel=\"noopener noreferrer nofollow\">pybotics <\/a>\u0438\u043b\u0438 <a href=\"https:\/\/pypi.org\/project\/ikpy\/\" rel=\"noopener noreferrer nofollow\">ikpy<\/a>, \u0442.\u00a0\u0435. \u0418\u043d\u0438\u0446\u0438\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440, \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432 default position, \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c\u0443, \u0438 \u043f\u0440\u0438 \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0438 \u043d\u043e\u0432\u044b\u0445 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 EndEffector \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0433\u043b\u044b \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0441\u0435\u0440\u0432\u043e. \u042f \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u043b tinyik, \u043d\u043e \u043d\u0430 RPI \u043e\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e, 0,3s-0,5s \u043d\u0430 \u0440\u0430\u0441\u0447\u0435\u0442, \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u043e\u0444\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043c\u043d\u0435 \u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c, \u043a\u0430\u043a \u0438 \u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435, \u0442.\u043a. \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u043e PoC<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e, \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430 \u0432 default position \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0441\u0442\u0438 \u0441\u0435\u0440\u0438\u044e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0443\u0433\u043b\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u043e\u043f\u0440\u0438\u0432\u043e\u0434\u0430\u0445, \u0437\u0430\u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f EndEffector \u043d\u0430 \u043e\u0441\u044f\u0445 X-Y-Z, \u0438 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0441\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0441\u0432\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435(\u0438\u0437 \u0433\u043b\u0438\u043d\u044b \u0438 \u043f\u0430\u043b\u043e\u043a, \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u0431\u0435\u0437 \u043f\u0430\u043b\u043e\u043a). \u0422\u0430\u043a \u044f \u0438 \u043f\u043e\u0441\u0442\u0443\u043f\u0438\u043b.<\/p>\n<\/li>\n<\/ul>\n<blockquote>\n<p>\u0432 ROS \u0437\u0430\u0434\u0430\u0447\u0438 \u0418\u041a \u0440\u0435\u0448\u0430\u044e\u0442\u0441\u044f \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u0435\u0435, \u0441 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0438 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c UDRF-\u043c\u043e\u0434\u0435\u043b\u0438, \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u043c\u044b \u044d\u0442\u043e \u0443\u0432\u0438\u0434\u0438\u043c.<\/p>\n<\/blockquote>\n<p>\u041c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0438 \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u0441\u0431\u043e\u0440\u043a\u0435 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430. \u0422\u043e\u043b\u043a\u043e\u0432\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0439 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0443 \u043d\u0435 \u043c\u043d\u043e\u0433\u043e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <a href=\"https:\/\/www.youtube.com\/watch?v=NqL9n3avBbY\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a> \u0438\u043b\u0438 <a href=\"https:\/\/automaticaddison.com\/how-to-build-a-diy-aluminium-6-dof-robotic-arm-from-scratch\/\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a>, \u043d\u043e \u043a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430 \u0435\u0441\u0442\u044c \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0435\u0442\u0430\u043b\u0438. \u0412\u0441\u0435\u0433\u0434\u0430 \u043f\u043e\u043c\u043d\u0438\u0442\u0435, \u0447\u0442\u043e \u0443 \u0441\u0435\u0440\u0432\u043e\u043c\u043e\u0442\u043e\u0440\u0430 \u0435\u0441\u0442\u044c \u0443\u0433\u043e\u043b \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c\u0441\u044f, \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043d\u0430\u0448\u0438\u0445 \u0441\u0435\u0440\u0432 \u2013 SG-90, MG996R \u0438 YF-6125MG \u043e\u043d \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 180 \u0433\u0440\u0430\u0434\u0443\u0441\u043e\u0432. \u0421\u0431\u043e\u0440\u043a\u0443 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u0441 \u0443\u0447\u0435\u0442\u043e\u043c \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u043e\u043c\u043e\u0442\u043e\u0440\u043e\u0432 \u0438 \u0436\u0435\u043b\u0430\u0435\u043c\u043e\u0433\u043e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u00ab\u043f\u043e-\u0443\u043c\u043e\u043b\u0447\u043d\u0438\u0438\u044e\u00bb, \u0442\u043e\u0439 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0430\u0447\u043d\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u0448\u0430 \u0440\u0443\u043a\u0430 \u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043f\u0440\u0438\u0434\u0435\u0442 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b, \u0430 \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0433\u0440\u0430\u043d\u0438\u0446 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0435\u0440\u0432\u043e\u043c\u043e\u0442\u043e\u0440\u043e\u0432 \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f \u0440\u0430\u0431\u043e\u0447\u0430\u044f \u0437\u043e\u043d\u0430 \u0434\u043b\u044f \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043a \u0441\u0431\u043e\u0440\u043a\u0435 \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438 \u0441\u0431\u043e\u0440\u043a\u0435 \u0438 \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0443\u0433\u043b\u043e\u0432:<\/p>\n<details class=\"spoiler\">\n<summary>test.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import time import busio from board import SCL, SDA from adafruit_motor import servo from adafruit_pca9685 import PCA9685  i2c = busio.I2C(SCL, SDA) pca = PCA9685(i2c) pca.frequency = 50 servoR1 = servo.Servo(pca.channels[2], min_pulse=500, max_pulse=2500) servoR2 = servo.Servo(pca.channels[3], min_pulse=500, max_pulse=2500) servoS1 = servo.Servo(pca.channels[4], min_pulse=550, max_pulse=2300) servoS2 = servo.Servo(pca.channels[5], min_pulse=550, max_pulse=2300) servoS3 = servo.Servo(pca.channels[6], min_pulse=550, max_pulse=2300) servoS4 = servo.Servo(pca.channels[7], min_pulse=550, max_pulse=2300)  print(\"Assembling test\")  try:     # R1 calibration     servoR1.angle = 180     time.sleep(10)     servoR1.angle = 0          # R2 calibration     #servoR2.angle = 180     #time.sleep(10)     #servoR2.angle = 0          # S1 calibration     #servoS1.angle = 180     #time.sleep(10)     #servoS1.angle = 0          # S2 calibration     #servoS2.angle = 180     #time.sleep(10)     #servoS2.angle = 0          # S3 calibration     #servoS3.angle = 180     #time.sleep(10)     #servoS3.angle = 0          # S4 calibration     #servoS4.angle = 180     #time.sleep(10)     #servoS4.angle = 110          # set Def position     #servoS4.angle = 150     #time.sleep(0.5)     #servoS3.angle = 60     #time.sleep(0.5)     #servoS2.angle = 90     #time.sleep(0.5)     #servoS1.angle = 100     #time.sleep(0.5)     #servoR2.angle = 100     #time.sleep(0.5)     #servoR1.angle = 90     #print(\"Def position setted\")      except KeyboardInterrupt:     pca.deinit()  print(\"Assembling done\") pca.deinit()<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0435\u0433\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0440\u0438 \u0441\u0431\u043e\u0440\u043a\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430 \u0441\u043d\u0438\u043c\u0430\u044f \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u044b \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438.<\/p>\n<\/div>\n<\/details>\n<p>\u041c\u044b \u043f\u043e \u043e\u0447\u0435\u0440\u0435\u0434\u0438 \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0430 \u0441\u0435\u0440\u0432\u043e \u0443\u0433\u043e\u043b 0 \u0438 180 \u0433\u0440\u0430\u0434\u0443\u0441\u043e\u0432 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u044f, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c \u0440\u0430\u0431\u043e\u0447\u0438\u0435 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0441\u0435\u0440\u0432\u043e\u043c\u043e\u0442\u043e\u0440\u043e\u0432, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c min-max \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445  \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u0443\u0433\u043b\u0430 \u0441\u0435\u0440\u0432\u043e. \u0412 \u043a\u043e\u043d\u0446\u0435 \u0434\u0435\u043b\u0430\u0435\u043c \u043c\u043e\u043d\u0442\u0430\u0436 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0437\u0430\u043d\u044f\u0442\u044c default position.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0435\u043b\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0418\u041a \u2014 testKin.py<\/p>\n<details class=\"spoiler\">\n<summary>testKin.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import time import busio from board import SCL, SDA from adafruit_motor import servo from adafruit_pca9685 import PCA9685   i2c = busio.I2C(SCL, SDA) pca = PCA9685(i2c) pca.frequency = 50 servoR1 = servo.Servo(pca.channels[2], min_pulse=500, max_pulse=2500) servoR2 = servo.Servo(pca.channels[3], min_pulse=500, max_pulse=2500) servoS1 = servo.Servo(pca.channels[4], min_pulse=550, max_pulse=2300) servoS2 = servo.Servo(pca.channels[5], min_pulse=550, max_pulse=2300) servoS3 = servo.Servo(pca.channels[6], min_pulse=550, max_pulse=2300) servoS4 = servo.Servo(pca.channels[7], min_pulse=550, max_pulse=2300) ik_servos = [servoR1, servoR2, servoS1, servoS2] servo_ang = [90, 100, 100, 90] ee_x = 0 ee_y = 5 ee_z = 0   def set_home_position():   global servo_ang   servoR1.angle = 90   time.sleep(0.1)   servoR2.angle = 100   time.sleep(0.1)   servoS1.angle = 100   time.sleep(0.1)   servoS2.angle = 90   time.sleep(0.1)   servoS3.angle = 60   time.sleep(0.1)   servoS4.angle = 150   time.sleep(0.1)   print('Home position setted')   servo_ang = [90, 100, 100, 90]     def add_arm_pos_x():   global ee_x   global ee_y   global servo_ang   if ee_x != 0 and ee_x % 10 == 0 and ee_y &lt; 20:     correction_add_y()   if ee_x != 0 and ee_x % 10 == 0 and ee_y > 20:     correction_sub_y()   if ee_x &lt;= 80:     servo_ang[3] += 1     servoS2.angle = servo_ang[3]     servo_ang[2] -= 1     servoS1.angle = servo_ang[2]   if ee_x &lt; 50:     servo_ang[1] += 1     servoR2.angle = servo_ang[1]   else:     servo_ang[1] += 2     servoR2.angle = servo_ang[1]     ee_x += 1  def correction_add_x():   global ee_x   global servo_ang   servo_ang[3] += 1   servoS2.angle = servo_ang[3]   servo_ang[2] -= 1   servoS1.angle = servo_ang[2]   if ee_x &lt; 50:     servo_ang[1] += 1     servoR2.angle = servo_ang[1]   else:     servo_ang[1] += 2     servoR2.angle = servo_ang[1]    def sub_arm_pos_x():   global ee_x   global ee_y   global servo_ang   if ee_x != 0 and ee_x % 10 == 0 and ee_y &lt; 20:     correction_sub_y()   if ee_x != 0 and ee_x % 10 == 0 and ee_y > 20:     correction_add_y()   if ee_x &lt;= 80:     servo_ang[3] -= 1     servoS2.angle = servo_ang[3]     servo_ang[2] += 1     servoS1.angle = servo_ang[2]   if ee_x &lt; 50:     servo_ang[1] -= 1     servoR2.angle = servo_ang[1]   else:     servo_ang[1] -= 2     servoR2.angle = servo_ang[1]     ee_x -= 1     def correction_sub_x():   global ee_x   global servo_ang   servo_ang[3] -= 1   servoS2.angle = servo_ang[3]   servo_ang[2] += 1   servoS1.angle = servo_ang[2]   if ee_x &lt; 50:     servo_ang[1] -= 1     servoR2.angle = servo_ang[1]   else:     servo_ang[1] -= 2     servoR2.angle = servo_ang[1]     def add_arm_pos_y():   global ee_x   global ee_y   global servo_ang   if ee_y % 10 == 0:     correction_add_x()   if ee_y &lt;= 50:     servo_ang[1] -= 1     servoR2.angle = servo_ang[1]   if ee_y &lt;= 25:     servo_ang[2] -= 1     servoS1.angle = servo_ang[2]     servo_ang[3] -= 1     servoS2.angle = servo_ang[3]     ee_y += 1   def correction_add_y():   global ee_y   global servo_ang   servo_ang[1] -= 1   servoR2.angle = servo_ang[1]   servo_ang[2] -= 1   servoS1.angle = servo_ang[2]   servo_ang[3] -= 1   servoS2.angle = servo_ang[3]   def sub_arm_pos_y():   global ee_x   global ee_y   global servo_ang   if ee_y % 10 == 0:     correction_sub_x()   if ee_y &lt;= 50:     print(f'short ee_y={ee_y}')     servo_ang[1] += 1     servoR2.angle = servo_ang[1]   if ee_y &lt;= 25:     servo_ang[2] += 1     servoS1.angle = servo_ang[2]     servo_ang[3] += 1     servoS2.angle = servo_ang[3]     ee_y -= 1     def correction_sub_y():   global ee_y   global servo_ang   servo_ang[1] += 1   servoR2.angle = servo_ang[1]   servo_ang[2] += 1   servoS1.angle = servo_ang[2]   servo_ang[3] += 1   servoS2.angle = servo_ang[3]   def add_arm_pos_z():   global ee_z   global servo_ang   servo_ang[0] += 1   servoR1.angle = servo_ang[0]   ee_z += 1     def sub_arm_pos_z():   global ee_z   global servo_ang   servo_ang[0] -= 1   servoR1.angle = servo_ang[0]   ee_z -= 1   try:   print('Servo Kinematics test')   set_home_position()   print(f'IK servo angles:{servo_ang}')   print(f'EE position: X:{ee_x}, Y:{ee_y}, Z:{ee_z}')   time.sleep(2)   for i in range(20):       add_arm_pos_y()       time.sleep(0.1)          time.sleep(2)      for i in range(60):       add_arm_pos_x()       time.sleep(0.1)          time.sleep(2)      for i in range(30):       add_arm_pos_z()       time.sleep(0.1)          time.sleep(2)      for i in range(60):       sub_arm_pos_x()       time.sleep(0.1)          time.sleep(2)      for i in range(30):       sub_arm_pos_z()       time.sleep(0.1)          time.sleep(2)      for i in range(20):       sub_arm_pos_y()       time.sleep(0.1)          time.sleep(2)      print(f'IK servo angles:{servo_ang}')   print(f'EE position: X:{ee_x}, Y:{ee_y}, Z:{ee_z}')      set_home_position()  except KeyboardInterrupt:   pca.deinit()   print(\"Kinematics test done\")   pca.deinit()<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u042d\u0442\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438\u043d\u0442\u0443\u0438\u0442\u0438\u0432\u043d\u043e\u0439 \u0438\u043c\u043f\u0440\u043e\u0432\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u0447\u0438\u0441\u0442\u043e\u043c \u0432\u0438\u0434\u0435, \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043c \u0442\u0435\u0441\u0442\u044b, \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u0432\u0441\u0435 \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0445\u043e\u0442\u044f \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043b\u043e\u0445\u043e, \u043f\u043e\u0433\u0440\u0435\u0448\u043d\u043e\u0441\u0442\u0438 \u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430 \u043d\u0430 100\u043c\u043c +\/-5-10\u043c\u043c, \u0441\u043e\u0432\u0441\u0435\u043c \u043f\u043b\u043e\u0445\u043e, \u0447\u0442\u043e \u043d\u0435\u0442 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0435 \u0441 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u044c\u044e \u043f\u043e\u043b\u0430(collision detection), \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043d\u0430 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043d\u043e\u0435 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0443\u0433\u043b\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u043e (self-collision detection). \u041e\u043f\u044f\u0442\u044c \u0442\u0430\u043a\u0438 \u0432\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u043c \u043f\u0440\u043e ROS&#8230; \u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u2013 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043a\u043e\u0435-\u043a\u0430\u043a\u043e\u0439 IK, \u043e\u043d \u0431\u044b\u0441\u0442\u0440\u044b\u0439, \u0438 \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d \u0438\u0437 \u0442\u0435\u0441\u0442\u0430 \u0432 \u0438\u0442\u043e\u0433\u043e\u0432\u043e\u043c \u0441\u043a\u0440\u0438\u043f\u0442\u0435.<\/p>\n<details class=\"spoiler\">\n<summary>\u0418 \u0442\u0430\u043a \u0441\u043e\u0439\u0434\u0435\u0442&#8230;<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/523\/89d\/b52\/52389db526ce97709b79d05ee693c347.png\" width=\"500\" height=\"375\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/523\/89d\/b52\/52389db526ce97709b79d05ee693c347.png\"\/><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u0421\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442<\/h3>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0441\u0442\u0435\u043f\u0435\u043d\u0438 \u0437\u0430\u0445\u0432\u0430\u0442\u0430, \u0443\u0433\u043b\u0430 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430 \u0438 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0438 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u0435 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u0435\u0440\u0432\u043e\u043c\u043e\u0442\u043e\u0440\u043e\u0432. \u0414\u043b\u044f \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430 \u2013 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 150 \u044d\u0442\u043e \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f(\u043f\u043e\u0442\u043e\u043c \u0438\u0437\u043c\u0435\u043d\u0438\u043c) \u0438 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0437\u0430\u0445\u0432\u0430\u0442\u0430 180. \u0414\u043b\u044f \u0440\u043e\u0442\u0430\u0446\u0438\u0438 &#8212; \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 0, \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 180. \u0414\u043b\u044f \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0432\u0435\u043a\u0442\u043e\u0440 \u0441\u0434\u0432\u0438\u0433\u0430 EE. \u0422\u0430\u043a \u0436\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u0435 \u0432 datachannel, \u0442\u0435\u043f\u0435\u0440\u044c \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0442\u0443\u0434\u0430 \u0432 \u0432\u0438\u0434\u0435 JSON:<\/p>\n<details class=\"spoiler\">\n<summary>main.html<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"xml\">... &lt;div class=\"row-md-6 mt-3 mb-3\">   &lt;form class=\"form-inline\">     &lt;div class=\"form-group\">       &lt;label for=\"servoS4\" class=\"form-label\">Grab:&lt;\/label>       &lt;input type=\"range\" class=\"form-range\" min=\"90\" max=\"180\" value=\"150\" step=\"1\" style=\"width: 500px;\" id=\"servoS4\" disabled>       &lt;span id=\"servoS4Info\">&lt;\/span>     &lt;\/div>   &lt;\/form> &lt;\/div>  &lt;div class=\"row-md-6 mt-3 mb-3\">   &lt;form class=\"form-inline\">     &lt;div class=\"form-group\">       &lt;label for=\"servoS3\" class=\"form-label\">EE rotation:&lt;\/label>       &lt;input type=\"range\" class=\"form-range\" min=\"0\" max=\"180\" value=\"0\" step=\"1\" style=\"width: 500px;\" id=\"servoS3\" disabled>       &lt;span id=\"servoS3Info\">&lt;\/span>     &lt;\/div>   &lt;\/form> &lt;\/div>  &lt;div class=\"row-md-6 mt-3 mb-3\">   &lt;form class=\"form-inline\">     &lt;div class=\"input-group\">       &lt;span class=\"input-group-text\">EE changer:&lt;\/span>       &lt;input type=\"text\" aria-label=\"ee-x-pos\" class=\"form-control\" placeholder=\"X (mm)\" id=\"eePosX\">       &lt;input type=\"text\" aria-label=\"ee-y-pos\" class=\"form-control\" placeholder=\"Y (mm)\" id=\"eePosY\">       &lt;input type=\"text\" aria-label=\"ee-z-pos\" class=\"form-control\" placeholder=\"Z (mm)\" id=\"eePosZ\">       &lt;button id=\"changeEE\" class=\"btn btn-primary\" type=\"submit\" disabled>Change EE position&lt;\/button>     &lt;\/div>   &lt;\/form> &lt;\/div> ...<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>robo.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">... var servoH1angle = 0; var servoH2angle = 0; var servoS4angle = 0; var servoS3angle = 0; ... function servoEEgrab(){ if(dataChannel != null){ servoS4angle = $(\"#servoS4\").val(); dataChannel.send(JSON.stringify({'grabEE' : servoS4angle})); console.log(\"Sending EE grab angle = \"+ servoS4angle); $(\"#servoS4Info\").text(\"\" + servoS4angle); } }  function servoEErotate(){ if(dataChannel != null){ servoS3angle = $(\"#servoS3\").val(); dataChannel.send(JSON.stringify({'rotateEE' : servoS3angle})); console.log(\"Sending EE rotate angle = \"+ servoS3angle); $(\"#servoS3Info\").text(\"\" + servoS3angle); } }  function changeEEposition(){ if(dataChannel != null){ var xPosition = $(\"#eePosX\").val(); var yPosition = $(\"#eePosY\").val(); var zPosition = $(\"#eePosZ\").val(); dataChannel.send(JSON.stringify({'positionEE' : [xPosition, yPosition, zPosition]})); console.log(\"Sending EE position = x:\" + xPosition + \" y:\" + yPosition +\" z:\"+ zPosition); } } ...<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442(Python-\u0441\u043a\u0440\u0438\u043f\u0442 \u043d\u0430 RPI)<\/h3>\n<p>\u0414\u043e\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043d\u0430\u0448 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043a\u0440\u0438\u043f\u0442:<\/p>\n<details class=\"spoiler\">\n<summary>part5.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\"># \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u0437 testIK.py # add_arm_pos_x() \/ correction_add_x() # sub_arm_pos_x() \/ correction_sub_x() # add_arm_pos_y() \/ correction_add_y(): # sub_arm_pos_y() \/ correction_add_y() # add_arm_pos_z() # sub_arm_pos_z()  # \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438\u0437 datachannel async def consume_message(rtc_message: str) -> None:     rtc_msg = json.loads(rtc_message)     if rtc_msg.get(\"blink\"):         if rtc_msg.get(\"blink\").startswith(\"ON\"):             GPIO.output(21,True)         if rtc_msg.get(\"blink\").startswith(\"OFF\"):              GPIO.output(21,False)     if rtc_msg.get(\"headX\"):         xServoAngle = int(rtc_msg.get(\"headX\"))         servoX.angle = xServoAngle     if rtc_msg.get(\"headY\"):         yServoAngle = int(rtc_msg.get(\"headY\"))         servoY.angle = yServoAngle     if rtc_msg.get(\"grabEE\"):         eeGrabAngle = int(rtc_msg.get(\"grabEE\"))         servoS4.angle = eeGrabAngle     if rtc_msg.get(\"rotateEE\"):         eeRotateAngle = int(rtc_msg.get(\"rotateEE\"))         servoS3.angle = eeRotateAngle     if rtc_msg.get(\"positionEE\"):         xyz = [int(i or 0) for i in list(rtc_msg.get(\"positionEE\"))]         if xyz[0] > 0:             while xyz[0] > 0:                 add_arm_pos_x()                 xyz[0] -= 1         if xyz[0] &lt; 0:             while xyz[0] &lt; 0:                 sub_arm_pos_x()                 xyz[0] += 1         if xyz[1] > 0:             while xyz[1] > 0:                 add_arm_pos_y()                 xyz[1] -= 1         if xyz[1] &lt; 0:             while xyz[1] &lt; 0:                 sub_arm_pos_y()                 xyz[1] += 1         if xyz[2] > 0:             while xyz[2] > 0:                 add_arm_pos_z()                 xyz[2] -= 1         if xyz[2] &lt; 0:             while xyz[2] &lt; 0:                 sub_arm_pos_z()                 xyz[2] += 1<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u043a\u0440\u0430\u0442\u043a\u043e \u2013 \u0442\u043e \u043c\u044b \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u043b\u0438 \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u0438\u0437 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u0442\u0435\u0441\u0442\u0430 IK \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0432 datachannel(\u0442\u0435\u043f\u0435\u0440\u044c \u043e\u043d\u0438 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u044e\u0442\u0441\u044f \u0438\u0437 JSON). <\/p>\n<\/div>\n<\/details>\n<p>\u0412\u0441\u0435, \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u0442\u0435\u0441\u0442 \u0438 \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c EE \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435.<\/p>\n<h3>\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442(Unity VR)<\/h3>\n<p>\u0414\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u043c\u043d\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u043f\u043e RTT\/Jitter. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e \u0435\u0435, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430 Robo Video Panel \u0434\u0432\u0430 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0441 \u0442\u044d\u0433\u0430\u043c\u0438, \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0435<\/p>\n<details class=\"spoiler\">\n<summary>Connection.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">... private Text rttVal; private Text jtrVal; private double rtt; private double jtr; ...    void Start() {   ...     rttVal = GameObject.FindGameObjectWithTag(\"rttVal\").GetComponent&lt;Text>();     jtrVal = GameObject.FindGameObjectWithTag(\"jtrVal\").GetComponent&lt;Text>();   ...    void Update() { ...   if (webrtcConnection != null)   {     elapsedStatistics += Time.deltaTime;     if (elapsedStatistics >= 1f)     {       elapsedStatistics = 0f;       StartCoroutine(GetStatistics());     }   }   rttVal.text = \"RTT=\" + rtt * 1000 + \" ms\";   jtrVal.text = \"Jitter=\" + jtr * 1000 + \" ms\";  ... }  IEnumerator GetStatistics() {   var statsOperation = webrtcConnection.GetStats();   yield return statsOperation;   var statsReport = statsOperation.Value;      foreach (var x in statsReport.Stats)   {   if (x.Value is RTCInboundRTPStreamStats)   {     x.Value.Dict.TryGetValue(\"jitter\", out object jitter);     if (jitter != null) jtr = (double) jitter;   }    if (x.Value is RTCIceCandidatePairStats)   {     x.Value.Dict.TryGetValue(\"currentRoundTripTime\", out object currentRoundTripTime);     if (currentRoundTripTime != null) rtt = (double) currentRoundTripTime;   }   } }  <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0443\u0442 \u043c\u044b \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0432 \u0430\u043f\u0434\u0435\u0439\u0442\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c GetStatistics, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443 \u043f\u043e WebRTC, \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f <strong>jitter<\/strong> \u0438\u0437  <strong>RTCInboundRTPStreamStats<\/strong> \u0438 <strong>currentRoundTripTime<\/strong> \u0438\u0437  <strong>RTCIceCandidatePairStats<\/strong>. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438 \u043f\u0440\u0438\u0435\u043c\u0435 \u0432\u0438\u0434\u0435\u043e\u0442\u0440\u0430\u043a\u0430 \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f RTT\/Jitter \u0432\u043d\u0438\u0437\u0443 \u044d\u043a\u0440\u0430\u043d\u0430 VR.<\/p>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043d\u0430 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a\u0430\u043c\u0435\u0440\u044b. \u0412 \u0441\u043a\u0440\u0438\u043f\u0442\u0435 Connection.ws<\/p>\n<details class=\"spoiler\">\n<summary>Connection.ws<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">...  private bool controllerTrackingOn = false; private short controllerTrackingCounter = 0; private int grabEE; private int rotateEE; public Vector3 positionEE; private float elapsedStatistics = 0f;  ...    public void ControllerTracking(bool trigger) {     if (trigger &amp;&amp; !controllerTrackingOn)     {         controllerTrackingCounter++;         if (controllerTrackingCounter >= 100)         {             controllerTrackingOn = true;             Debug.Log(\"Controller tracking enable!\");         }     }     else if (trigger &amp;&amp; controllerTrackingOn)     {         controllerTrackingCounter--;         if (controllerTrackingCounter &lt;= 0)         {             controllerTrackingOn = false;             Debug.Log(\"Controller tracking disable!\");         }     } } ...  <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0410 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0435 RightControllerButtonWatcher.\u0441s \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043d\u0430\u0436\u0430\u0442\u0438\u044f GripButton, 2D \u0432\u0435\u043a\u0442\u043e\u0440 \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0434\u0436\u043e\u0439\u0441\u0442\u0438\u043a\u0430, \u043a\u0432\u0430\u0442\u0435\u0440\u043d\u0438\u043e\u043d \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0440\u043e\u0442\u0430\u0446\u0438\u0438 \u0438 3D \u0432\u0435\u043a\u0442\u043e\u0440 \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435:<\/p>\n<details class=\"spoiler\">\n<summary>RightControllerButtonWatcher.\u0441s<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">... public static Vector2 rightController2DAxisPosition; public static Quaternion rightControllerRotation; public static Vector3 rightControllerPosition; ...    void Update() {   ...   if ((rightHandCantroller.TryGetFeatureValue(CommonUsages.gripButton, out gripValue) &amp;&amp; gripValue))   {     gameObject.SendMessage(\"ControllerTracking\", gripValue);   }      rightHandCantroller.TryGetFeatureValue(CommonUsages.primary2DAxis, out rightController2DAxisPosition);   rightHandCantroller.TryGetFeatureValue(CommonUsages.deviceRotation, out rightControllerRotation);   rightHandCantroller.TryGetFeatureValue(CommonUsages.devicePosition, out rightControllerPosition);   ... }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u0430\u043b\u0435\u0435, \u043d\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0433\u043e\u043b\u043e\u0432\u044b\/\u043d\u0430\u043a\u043e\u043f\u0438\u0442\u0435\u043b\u044f \u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430\/\u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430 \u0438 \u0441\u043c\u0435\u043d\u044b \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>Connection.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">void Update() {   if (dataChannel != null)   {     elapsed += Time.deltaTime;     if (elapsed >= 0.1f)     {       elapsed = 0f;       int xheadbuf = HeadRotation.valHeadX;       int yheadbuf = HeadRotation.valHeadY;       float joystickbuf = RightControllerButtonWatcher.rightController2DAxisPosition.x;       float rotbuff = RightControllerButtonWatcher.rightControllerRotation.z;       Vector3 posbuf = RightControllerButtonWatcher.rightControllerPosition;        var message = new DcMessage();        if (fullScreenOn)       {         if (headX != xheadbuf)         {           message.headX = xheadbuf.ToString();           headX = xheadbuf;         }            if (headY != yheadbuf)         {           message.headY = yheadbuf.ToString();           headY = yheadbuf;         }       }        if (controllerTrackingOn)       {         if (joystickbuf > 0.3 &amp;&amp; grabEE &lt; 180)         {             grabEE += 5;             message.grabEE = grabEE.ToString();         }         if (joystickbuf &lt; -0.3 &amp;&amp; grabEE > 60)         {           grabEE -= 5;           message.grabEE = grabEE.ToString();         }            if (rotbuff > 0.3 &amp;&amp; rotationEE > 0)         {           rotationEE -= 5;           message.rotateEE = rotationEE.ToString();         }         if (rotbuff &lt; -0.3 &amp;&amp; rotationEE &lt; 180)         {           rotationEE += 5;           message.rotateEE = rotationEE.ToString();         }         if (posbuf != positionEE)         {           var delta = posbuf - positionEE;           message.positionEE = new List&lt;int>() { (int)(delta.z * 125), (int)(delta.y * 125), (int)(delta.x * 125) };           positionEE = posbuf;         }       }       else       {         positionEE = posbuf;       }              dataChannel.Send(JsonUtility.ToJson(message));              } } ...  ... [Serializable] public class DcMessage {     public string headX;     public string headY;     public string grabEE;     public string rotateEE;     public List&lt;int> positionEE; }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h4>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432<\/h4>\n<p>\u0418\u0442\u0430\u043a, \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043a\u0430\u043a \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430\u0448 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440 \u0438\u0437 Unity.<\/p>\n<details class=\"spoiler\">\n<summary>\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430:<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/54e\/e9c\/bad\/54ee9cbad18882693461c97b2dca2a59.png\" width=\"547\" height=\"324\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/54e\/e9c\/bad\/54ee9cbad18882693461c97b2dca2a59.png\"\/><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 Oculus<\/h3>\n<p>\u0420\u0430\u043d\u0435\u0435 \u043c\u044b \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043b\u0438 \u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u044b \u0447\u0435\u0440\u0435\u0437 Oculus Link + Unity, \u043d\u043e \u0432 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u0430\u0448\u0438\u043c \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u043e\u043c \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0441\u0438\u0434\u044f \u043d\u0430 \u0434\u0438\u0432\u0430\u043d\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435  \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0435\u0433\u043e, \u043d\u043e \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430, \u0435\u0441\u0442\u044c \u043e\u0434\u043d\u0430 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430\u044f \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0441 \u043f\u043e\u0440\u0442\u043e\u043c 9000 &#8212; \u043d\u0430 \u043d\u0435\u043c \u043d\u0435 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442(\u0443 \u043c\u0435\u043d\u044f \u0441\u043c\u0443\u0442\u043d\u043e\u0435 \u043e\u0449\u0443\u0449\u0435\u043d\u0438\u0435, \u0447\u0442\u043e \u044f \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0441\u044f \u0441 \u0442\u0430\u043a\u043e\u0439 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c\u044e \u0432 Android, \u043d\u043e \u0445\u043e\u0442\u044c \u0443\u0431\u0435\u0439 \u043d\u0435 \u0441\u043c\u043e\u0433 \u0432\u0441\u043f\u043e\u043c\u043d\u0438\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u0438), \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u0441\u043c\u0435\u043d\u0438\u043c \u043f\u043e\u0440\u0442 websocket \u043d\u0430 :9090 \u0432\u043e \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445!<\/p>\n<details class=\"spoiler\">\n<summary>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0441\u0431\u043e\u0440\u043a\u0438 \u0432 Unity<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b4c\/877\/fdf\/b4c877fdf9a94a52e66df7bfb415b6b3.png\" width=\"642\" height=\"474\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b4c\/877\/fdf\/b4c877fdf9a94a52e66df7bfb415b6b3.png\"\/><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0418 \u0432\u0441\u0435, \u043f\u043e \u043d\u0430\u0436\u0430\u0442\u0438\u044e Build&amp;Run Unity \u0441\u0434\u0435\u043b\u0430\u0435\u0442 \u0441\u0431\u043e\u0440\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442 \u0435\u0433\u043e \u043d\u0430 Oculus \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u041c\u043e\u0436\u0435\u0442\u0435 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0432 \u043a\u0440\u0435\u0441\u043b\u0435 \u0438 \u043f\u0440\u0438\u0432\u044b\u043a\u0430\u0442\u044c \u0443 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u0434\u0435\u0432\u0430\u0439\u0441\u043e\u043c \u0438\u0437 VR, \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0432\u0438\u0434\u0435\u043e \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438. <\/p>\n<p>\u041f\u0440\u0438\u0432\u044b\u043a\u043d\u0443\u0442\u044c \u043a \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0437\u0430 10-15 \u043c\u0438\u043d\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u043e\u0434\u043d\u0430\u043a\u043e \u043d\u0438\u0437\u043a\u043e\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043a\u0430\u043c\u0435\u0440\u044b \u0438\u0437\u0440\u044f\u0434\u043d\u043e \u043f\u043e\u0440\u0442\u0438\u0442 \u0432\u043f\u0435\u0447\u0430\u0442\u043b\u0435\u043d\u0438\u0435, \u0442\u0430\u043a \u0436\u0435 \u0447\u0443\u0432\u0441\u0442\u0432\u0443\u0435\u0442\u0441\u044f \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u043a \u0441\u0442\u0435\u0440\u0435\u043e \u0432\u043e\u0441\u043f\u0440\u0438\u044f\u0442\u0438\u044f \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0434\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0430\u044f \u0440\u0430\u0431\u043e\u0447\u0430\u044f \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430.<\/p>\n<h3>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0438<\/h3>\n<p>\u0415\u0449\u0435 \u043e\u0434\u0438\u043d \u0432\u043e\u043f\u0440\u043e\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u0435\u043d\u044f \u043e\u0447\u0435\u043d\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043b \u2013 \u043a\u0430\u043a \u044d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430\u043c\u0438\/\u0434\u0436\u0438\u0442\u0442\u0435\u0440\u043e\u043c \u0432 \u0441\u0435\u0442\u0438, \u0438 \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043d\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u043e\u0441\u0442\u0438 \u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u0432\u043f\u0435\u0447\u0430\u0442\u043b\u0435\u043d\u0438\u0435 \u043e\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f. \u0414\u0430\u0431\u044b \u043d\u0435 \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0432\u0448\u0435\u0435\u0441\u044f \u0434\u043e\u0431\u0440\u043e \u0432 \u043f\u0430\u0431\u043b\u0438\u043a \u0441\u0435\u0439\u0447\u0430\u0441, \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u043e\u0443\u0442\u0435\u0440 \u0441 OWRT \u0441 \u043f\u0430\u043a\u0435\u0442\u0430\u043c\u0438 <a href=\"https:\/\/openwrt.org\/docs\/guide-user\/network\/traffic-shaping\/sch_netem\" rel=\"noopener noreferrer nofollow\">tc+netem<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f RTT\/Loss\/Jitter.<\/p>\n<p>RPI \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043a \u044d\u0442\u043e\u043c\u0443 \u0440\u043e\u0443\u0442\u0435\u0440\u0443 \u0441 OWRT, \u0440\u043e\u0443\u0442\u0435\u0440 \u0436\u0435 wan-\u043e\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043a \u0434\u043e\u043c\u0430\u0448\u043d\u0435\u0439 \u0441\u0435\u0442\u0438, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f Quest \u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u0438\u0433\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u0430 wan owrt \u0438 \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u044d\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0438. \u041d\u0443 \u0438\u043b\u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c netem \u043d\u0430 RPI.<\/p>\n<h4>\u0421\u0430\u043c\u0438 \u0442\u0435\u0441\u0442\u044b<\/h4>\n<p>\u0417\u0430 \u0438\u0434\u0435\u0430\u043b \u044f \u0431\u0440\u0430\u043b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u0438, \u0431\u0435\u0437 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u0434\u0435\u0440\u0436\u0435\u043a. RTT = 5ms. \u041e\u0449\u0443\u0449\u0435\u043d\u0438\u044f \u043e\u0442 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435, \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435 \u0447\u0443\u0432\u0441\u0442\u0432\u0443\u0435\u0442\u0441\u044f \u043e\u0442\u0437\u044b\u0432\u0447\u0438\u0432\u043e\u0441\u0442\u044c \u0432\u0435\u043b\u0438\u043a\u043e\u043b\u0435\u043f\u043d\u0430\u044f, \u043b\u044e\u0431\u044b\u0435 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0438 \u0441 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430\u043c\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u0432\u043f\u043e\u043b\u043d\u0435 \u0443\u0432\u0435\u0440\u0435\u043d\u043d\u043e.<\/p>\n<p><code>tc qdisc add dev eth0.2 root netem delay 50ms<\/code><\/p>\n<p>\u0438\u043b\u0438<\/p>\n<p><code>tc qdisc change dev eth0.2 root netem delay 75ms<\/code><\/p>\n<p>\u041d\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438  RTT = 100-150ms \u0433\u0434\u0435-\u0442\u043e \u0441\u0435\u0440\u0435\u0434\u0438\u043d\u0430 \u043f\u043e \u043e\u0449\u0443\u0449\u0435\u043d\u0438\u044f\u043c, \u043d\u0435 \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e \u043e\u0442\u043b\u0438\u0447\u043d\u043e, \u043d\u043e \u0432\u0435\u0440\u0442\u0435\u0442\u044c \u0433\u043e\u043b\u043e\u0432\u043e\u0439, \u0432\u044b\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u043a\u0443\u0431\u0438\u043a\u0438 \u0438\u043b\u0438 \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0438 \u0441 \u043f\u0440\u0435\u0434\u043c\u0435\u0442\u0430\u043c\u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e.<\/p>\n<p><code>tc qdisc change dev eth0.2 root netem delay 100ms<\/code><\/p>\n<p>\u041d\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445 \u0432 RTT = 200ms \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u0443\u0436\u0435 \u043e\u0449\u0443\u0449\u0430\u0435\u0442\u0441\u044f \u0441\u0438\u043b\u044c\u043d\u043e, \u0447\u0443\u0432\u0441\u0442\u0432\u0443\u0435\u0442\u0441\u044f \u043b\u0430\u0433 \u043c\u0435\u0436\u0434\u0443 \u0441\u0430\u043c\u0438\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c \u0438 \u0440\u0435\u0430\u043a\u0446\u0438\u0435\u0439 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0430, \u043a \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0438\u0432\u044b\u043a\u0430\u0442\u044c, \u043d\u043e \u0435\u0449\u0435 \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u0432\u043f\u043e\u043b\u043d\u0435 \u043c\u043e\u0436\u043d\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c, \u043f\u0440\u043e\u0441\u0442\u043e \u044d\u0442\u043e \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0434\u043e\u043b\u044c\u0448\u0435 \u0438 \u043a\u0430\u0436\u0434\u043e\u0435 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u043e \u0441\u0438\u043b\u044c\u043d\u0435\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p><code>tc qdisc change dev eth0.2 root netem delay 150ms<\/code><\/p>\n<p>\u041d\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445 \u0432  RTT = 300 \u044f \u0440\u0435\u0448\u0438\u043b \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c, \u0442\u0443\u0442 \u0443\u0436\u0435 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u043d\u0435 \u043f\u0440\u0438\u0435\u043c\u043b\u0435\u043c\u0430\u044f \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u0430\u043a \u0435\u0441\u0442\u044c, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u043f\u0440\u0438 \u0442\u0430\u043a\u0438\u0445 \u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u0445 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u043c\u0435\u0445\u0430\u043d\u0438\u043a\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u0443 \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435.<\/p>\n<p>\u0422\u0430\u043a \u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c jitter:<\/p>\n<p><code>tc qdisc add dev eth0.2 root netem delay 50ms 10ms<\/code><\/p>\n<p>\u041d\u0430 \u0441\u0447\u0435\u0442 \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044f jitter \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f \u043f\u0435\u0447\u0430\u043b\u044c\u043d\u0435\u0435, \u043f\u043e\u0432\u044b\u0448\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u0435\u0442\u0435\u0432\u043e\u0433\u043e jitter \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0435 10ms \u043a\u0440\u0430\u0442\u043d\u043e \u0443\u0445\u0443\u0434\u0448\u0430\u0435\u0442 \u0432\u043e\u0441\u043f\u0440\u0438\u044f\u0442\u0438\u0435, \u043f\u0440\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438 >40\u043c\u0441 \u0432\u0438\u0434\u0435\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u0437\u043d\u0430\u0442\u043d\u043e \u0441\u044b\u043f\u0430\u0442\u044c\u0441\u044f \u0438 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0438. Loss \u0438 \u0440\u0435\u043e\u0440\u0434\u0435\u0440\u0438\u043d\u0433 \u043d\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043b, \u043e\u0441\u0442\u0430\u0432\u0438\u043b \u043d\u0430 \u043f\u043e\u0442\u043e\u043c.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430 \u043d\u0430 ROS \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u0443\u044e \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0443 \u043c\u0435\u043d\u044f \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u0438\u043f\u0430\u0441\u0435\u043d RPi4. \u0422\u0430\u043a \u0436\u0435 \u0435\u0441\u0442\u044c \u0436\u0435\u043b\u0430\u043d\u0438\u0435 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u00ab\u043e\u0434\u043d\u043e\u0440\u0443\u043a\u043e\u0433\u043e \u0431\u0430\u043d\u0434\u0438\u0442\u0430\u00bb \u043d\u0430 \u043a\u043e\u043b\u0435\u0441\u0430 \u0438\u043b\u0438 \u0445\u043e\u0434\u0443\u043b\u0438, \u043f\u0443\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u043a\u043e\u043c\u043d\u0430\u0442\u0435, \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043d\u043e\u0441\u0438\u0442\u044c \u043a\u043e\u0444\u0435 (\u0445\u043e\u0442\u044f \u0441 \u044d\u0442\u0438\u043c \u043f\u0443\u043d\u043a\u0442\u043e\u043c \u0443 \u043c\u0435\u043d\u044f \u043f\u043e\u043a\u0430 \u0431<strong>\u043e<\/strong>\u043b\u044c\u0448\u0438\u0435 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0432 \u0432\u044b\u0431\u043e\u0440\u0435 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f). <\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/730522\/\"> https:\/\/habr.com\/ru\/articles\/730522\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f <strong>\u2013<\/strong> <a href=\"https:\/\/habr.com\/ru\/articles\/730164\/\" rel=\"noopener noreferrer nofollow\">\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u043e\u0431\u043e\u0442\u0430\u043c\u0438 \u0438\u0437 VR<\/a><\/p>\n<p>\u0412\u0442\u043e\u0440\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f <strong>\u2013<\/strong> <a href=\"https:\/\/habr.com\/ru\/articles\/730450\/\" rel=\"noopener noreferrer nofollow\">\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u043e\u0431\u043e\u0442\u0430\u043c\u0438 \u0438\u0437 VR. \u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 1. <\/a><\/p>\n<p>\u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0441\u0435\u0442\u0435\u0432\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043b\u0438 DataChannel \u0438 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u043c\u0435\u0434\u0438\u0430\u0442\u0440\u0430\u043a\u0430\u043c\u0438 \u0438\u0437 JS\/Unity\/Python. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0430\u043c\u0435\u0440\u043e\u0439 \u0438 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0442\u043e\u0440\u043e\u043c(\u0427\u0430\u0441\u0442\u0438 5, 6).<\/p>\n<h2>\u0427\u0430\u0441\u0442\u044c 5. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a\u0430\u043c\u0435\u0440\u043e\u0439<\/h2>\n<p>\u041d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u043e \u0441 \u043c\u0430\u043b\u043e\u0433\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f LED \u0438 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430\u043c\u0438 \u0433\u043e\u043b\u043e\u0432\u044b, \u044d\u0442\u0438\u043c \u0443 \u043d\u0430\u0441 \u0437\u0430\u043d\u0438\u043c\u0430\u044e\u0442\u0441\u044f 2 \u0441\u0435\u0440\u0432\u043e SG90 \u043d\u0430 \u043a\u0440\u043e\u043d\u0448\u0442\u0435\u0439\u043d\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 datachannel \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u0438\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441\u0430 LED \u0438 \u043f\u043e\u0437\u0438\u0446\u0438\u0438, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u0443\u0433\u043e\u043b \u0441\u0435\u0440\u0432\u043e\u043c\u043e\u0442\u043e\u0440\u0430, \u0430 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 Python \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u0438\u0445 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c.<\/p>\n<h3>\u0421\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442<\/h3>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0443 \u043d\u043e\u0432\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>main.html<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"xml\">...   &lt;div class=\"row-md-6 mt-3 mb-3\">     &lt;form class=\"form-inline\">       &lt;div class=\"form-group\">         &lt;div class=\"custom-control custom-switch\">           &lt;input type=\"checkbox\" class=\"custom-control-input\" id=\"ledSwitch\" disabled>           &lt;label class=\"custom-control-label\" for=\"ledSwitch\" id=\"ledSwitchLabel\">LED control (LED Off)&lt;\/label>         &lt;\/div>       &lt;\/div>     &lt;\/form>   &lt;\/div>   &lt;div class=\"row-md-6 mt-3 mb-3\">     &lt;form class=\"form-inline\">       &lt;div class=\"form-group\">         &lt;label for=\"servoRangeX\" class=\"form-label\">SG90 X-angle:&lt;\/label>         &lt;input type=\"range\" class=\"form-range\" min=\"0\" max=\"180\" value=\"90\" step=\"1\" style=\"width: 500px;\" id=\"servoRangeX\" disabled>         &lt;span id=\"servoRangeXInfo\">&lt;\/span>       &lt;\/div>     &lt;\/form>   &lt;\/div>   &lt;div class=\"row-md-6 mt-3 mb-3\">     &lt;form class=\"form-inline\">       &lt;div class=\"form-group\">         &lt;label for=\"servoRangeY\" class=\"form-label\">SG90 Y-angle:&lt;\/label>         &lt;input type=\"range\" class=\"form-range\" min=\"0\" max=\"180\" value=\"90\" step=\"1\" style=\"width: 500px;\" id=\"servoRangeY\" disabled>         &lt;span id=\"servoRangeYInfo\">&lt;\/span>       &lt;\/div>     &lt;\/form>   &lt;\/div> ...<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0414\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0448 \u0441\u043a\u0440\u0438\u043f\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<details class=\"spoiler\">\n<summary>robo.js<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"javascript\">\/\/ \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u044e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u0438 datachannel: \/\/ open handling dataChannel.onopen = function(){   console.log(\"Data channel is open!\");   $( \"#ledSwitch\" ).prop( \"disabled\", false );   $( \"#servoRangeX\" ).prop( \"disabled\", false );   $( \"#servoRangeY\" ).prop( \"disabled\", false ); }  \/\/ \u0418 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u043b\u0430\u044e\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0443 \u0438\u043d\u0444\u043e: function switchLed(){   if(dataChannel != null){     if(blink == \"ON\"){       dataChannel.send(\"BLINK_OFF\");       console.log(\"LED is OFF\");       blink = \"OFF\";       $(\"#ledSwitchLabel\").text(\"LED control (LED OFF)\");     } else{       dataChannel.send(\"BLINK_ON\");       console.log(\"LED is ON\");       blink = \"ON\";       $(\"#ledSwitchLabel\").text(\"LED control (LED ON)\");     }   } }  function servoRangeX(){   if(dataChannel != null){     servoAngleX = $(\"#servoRangeX\").val();     dataChannel.send(\"X-ANG#\"+servoAngleX);     console.log(\"Sending new servo X angle = \"+ servoAngleX);     $(\"#servoRangeXInfo\").text(\"\" + servoAngleX);   } }  function servoRangeY(){   if(dataChannel != null){     servoAngleY = $(\"#servoRangeY\").val();     dataChannel.send(\"Y-ANG#\"+servoAngleY);     console.log(\"Sending new servo Y angle = \"+ servoAngleY);     $(\"#servoRangeYInfo\").text(\"\" + servoAngleY);   } } ... $(\"#ledSwitch\").change(function() {switchLed(); }); $(\"#servoRangeX\").change(function() {servoRangeX(); }); $(\"#servoRangeY\").change(function() {servoRangeY(); });<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0418\u0442\u043e\u0433\u043e, \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 datachannel \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0441 \u043d\u043e\u0432\u044b\u043c \u0443\u0433\u043b\u043e\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0441\u0435\u0440\u0432\u043e \u0438\u043b\u0438 \u0432\u043a\u043b\/\u0432\u044b\u043a\u043b LED.<\/p>\n<h3>\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442(Python-\u0441\u043a\u0440\u0438\u043f\u0442 \u043d\u0430 RPI)<\/h3>\n<p>\u0421\u043e \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u044d\u0442\u0438\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439.<\/p>\n<details class=\"spoiler\">\n<summary>part4.py<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\"> ## \u0412\u0441\u043f\u043e\u043c\u043d\u0438\u043c \u043d\u0430\u0448\u0443 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0443\u0436\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438: import RPi.GPIO as GPIO import busio from board import SCL, SDA from adafruit_motor import servo from adafruit_pca9685 import PCA9685  ## \u0418\u043d\u0438\u0446\u0438\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e: GPIO.setmode(GPIO.BCM) GPIO.setup(21, GPIO.OUT) i2c = busio.I2C(SCL, SDA) pca = PCA9685(i2c) pca.frequency = 60 servoX = servo.Servo(pca.channels[0], min_pulse=500, max_pulse=2400) servoY = servo.Servo(pca.channels[1], min_pulse=500, max_pulse=2400) servoX.angle = 90 servoY.angle = 90  ## \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e on message: @channel.on(\"message\") async def on_message(rtc_message):   if isinstance(rtc_message, str):   print(\"New message from datachannel \" + rtc_message)   #channel.send(\"Reply from PyPi - \" + rtc_message)   await consume_message(rtc_message)  ## \u0418 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0435\u0435: async def consume_message(rtc_message: str) -> None:   if rtc_message.startswith(\"BLINK_ON\"):   GPIO.output(21,True)   if rtc_message.startswith(\"BLINK_OFF\"):   GPIO.output(21,False)   if rtc_message.startswith(\"X-ANG\"):   xServoAngle = int(rtc_message.split(\"#\")[1])   servoX.angle = xServoAngle   if rtc_message.startswith(\"Y-ANG\"):   yServoAngle = int(rtc_message.split(\"#\")[1])   servoY.angle = yServoAngle<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043c\u0435\u0436\u0434\u0443 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u043c \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u043c:<\/p>\n<details class=\"spoiler\">\n<summary>\u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430:<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><\/figure>\n<figure class=\"full-width\"><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u043a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, LED \u0438 \u0441\u0435\u0440\u0432\u043e \u043e\u0442\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442 \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u043a VR \u0447\u0430\u0441\u0442\u0438.<\/p>\n<h3>\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442(Unity VR)<\/h3>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430 \u0433\u043e\u043b\u043e\u0432\u044b \u043d\u0443\u0436\u043d\u043e \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0443.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0443\u0441\u0442\u043e\u0439 \u043e\u0431\u044a\u0435\u043a\u0442, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 \u043d\u0435\u043c \u043d\u043e\u0432\u0443\u044e \u043a\u0430\u043c\u0435\u0440\u0443 Robo Camera(\u0442\u044d\u0433 RoboCamera) \u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043c \u043d\u0430\u0448 Panel \u0432 \u043e\u0431\u044a\u0435\u043a\u0442 Robo Camera. \u041f\u0440\u0438\u0447\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u043c \u0438\u0445 \u043d\u0430 \u0432\u044b\u0441\u043e\u0442\u0435 3 \u043d\u0430\u0434 ground. \u0418 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0442\u0434\u0430\u043b\u0438\u043c Image \u043e\u0442 \u043a\u0430\u043c\u0435\u0440\u044b.(+ \u0443 Robo Camera \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u043c AudioListener). \u0422\u0430\u043a \u0436\u0435 \u0432 Robo Camera  \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u00abTracked Pose Driver(Input System)\u00bb \u0438 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u043c \u0435\u0433\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 Position Input\/ Rotation Input \u0441 Main Camera.<\/p>\n<details class=\"spoiler\">\n<summary>\u0414\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432\u043e\u0442 \u0442\u0430\u043a:<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"\"><\/figure>\n<figure class=\"full-width\"><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0442\u043e\u043c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u043a\u0430\u043c\u0435\u0440\u0430\u043c\u0438 Main \u0438 Robo. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043d\u043e\u043f\u043e\u043a \u043d\u0430 \u043f\u0440\u0430\u0432\u043e\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0435 Oculus. \u0421\u0434\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0432 \u0441\u043a\u0440\u0438\u043f\u0442 RightControllerButtonWatcher.cs<\/p>\n<details class=\"spoiler\">\n<summary>RightControllerButtonWatcher.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.XR; public class RightControllerButtonWatcher : MonoBehaviour {   public static bool triggerValue;   private List&lt;InputDevice> rightHandDevices;   InputDevice rightHandCantroller;    void Awake()   {       rightHandDevices = new List&lt;InputDevice>();   }      void Update()   {       if (rightHandDevices.Count == 0)       {           InputDevices.GetDevicesAtXRNode(XRNode.RightHand, rightHandDevices);           Debug.Log(\"Try to get Right controller!\");       }          if (rightHandDevices.Count > 0)       {           rightHandCantroller = rightHandDevices[0];           if (rightHandCantroller.TryGetFeatureValue(CommonUsages.secondaryButton, out triggerValue) &amp;&amp; triggerValue)           {               gameObject.SendMessage(\"MakeVideoInFullScreen\", triggerValue);           }       }   }  } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0418 \u043f\u0440\u0438\u0430\u0442\u0442\u0430\u0447\u0438\u043c \u0435\u0433\u043e \u043a \u0442\u043e\u043c\u0443 \u0436\u0435 GameObject, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u0441\u043a\u0440\u0438\u043f\u0442 Connection.cs.<\/p>\n<p>\u0412 \u0441\u0430\u043c\u043e\u043c Connection.cs \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0436\u0430\u0442\u0438\u0435 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u043d\u0430\u043a\u043e\u043f\u0438\u0442\u0435\u043b\u044f.<\/p>\n<details class=\"spoiler\">\n<summary>Connection.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">... private bool fullScreenOn = false; private short fullScreenCounter = 0; private Camera mainCamera; private Camera roboCamera; ... void Start() { mainCamera = GameObject.FindGameObjectWithTag(\"MainCamera\").GetComponent&lt;Camera>(); roboCamera = GameObject.FindGameObjectWithTag(\"RoboCamera\").GetComponent&lt;Camera>(); roboCamera.enabled = false; CameraTrackingChanger(roboCamera, false); CameraAudioChanger(roboCamera, false); ... ... void CameraTrackingChanger(Camera camera, bool status) {   Component[] components = camera.GetComponents(typeof(MonoBehaviour));   foreach (Component comp in components)   {     if (comp.ToString().Contains(\"TrackedPoseDriver\"))     {       MonoBehaviour mb = (MonoBehaviour)comp;       mb.enabled = status;     }   } }  void CameraAudioChanger(Camera camera, bool status) {   var audio = camera.GetComponent&lt;AudioListener>();   audio.enabled = status; }    \/\/ \u0418 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u0435\u0442\u043e\u0434  MakeVideoInFullScreen public void MakeVideoInFullScreen(bool trigger) {   if (trigger &amp;&amp; !fullScreenOn)   {     fullScreenCounter++;     if (fullScreenCounter >= 100)     {       fullScreenOn = true;       mainCamera.enabled = false;       CameraTrackingChanger(mainCamera, false);       CameraAudioChanger(mainCamera, false);       roboCamera.enabled = true;       CameraTrackingChanger(roboCamera, true);       CameraAudioChanger(roboCamera, true);       Debug.Log(\"Robo cam switch On\");     }   }   else if (trigger &amp;amp;&amp;amp; fullScreenOn)   {   fullScreenCounter--;   if (fullScreenCounter &amp;lt;= 0)   {     fullScreenOn = false;     roboCamera.enabled = false;     CameraTrackingChanger(roboCamera, false);     CameraAudioChanger(roboCamera, false);     mainCamera.enabled = true;     CameraTrackingChanger(mainCamera, true);     CameraAudioChanger(mainCamera, true);     Debug.Log(\"Robo cam switch Off\");   }   } }  <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041c\u043e\u0436\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443. \u041f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443 \u043d\u0430\u0441 \u0430\u043a\u0442\u0438\u0432\u043d\u0430 Main Camera. \u041f\u0440\u0438 \u0437\u0430\u0436\u0430\u0442\u0438\u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \u00abB\u00bb (SecondaryButton) \u043f\u0440\u0430\u0432\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u043d\u0430\u0441 \u043f\u0435\u0440\u0435\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u043d\u0430 Robo Camera. \u0418 \u043e\u0431\u0440\u0430\u0442\u043d\u043e.<\/p>\n<p>\u041f\u043e\u0442\u043e\u043c \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438 \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0435 \u0433\u043e\u043b\u043e\u0432\u044b \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0443\u0433\u043e\u043b \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430 \u0448\u043b\u0435\u043c\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0443\u0433\u043b\u043e\u0432 \u0438 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u044e \u0438\u0445 \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u0435\u043c\u043b\u0435\u043c\u044b \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0441\u0435\u0440\u0432\u043e:<\/p>\n<details class=\"spoiler\">\n<summary>HeadRotation.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">public class HeadRotation : MonoBehaviour {   public static int valHeadX;   public static int valHeadY;   void Update()   {       if (Camera.current != null)       {           valHeadX = (int) Camera.current.transform.localEulerAngles.x;           valHeadY = (int) Camera.current.transform.localEulerAngles.y;              \/\/ angle casting           if (valHeadX &amp;gt;= 0 &amp;amp;&amp;amp; valHeadX &amp;lt; 180) valHeadX = 90 - valHeadX;           else if (valHeadX &amp;gt; 180) valHeadX = 360 - valHeadX + 90;              if (valHeadY &amp;gt;= 0 &amp;amp;&amp;amp; valHeadY &amp;lt; 180) valHeadY = 90 - valHeadY;           else if (valHeadY &amp;gt; 180) valHeadY = 360 - valHeadY + 90;       }   } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0438 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0435 Connection.cs \u0431\u0443\u0434\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u044d\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f(\u0442.\u043a. \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 valHeadX valHeadY \u2013 public static) \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432 datachannel 20 \u0440\u0430\u0437 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443.<\/p>\n<details class=\"spoiler\">\n<summary>Connection.cs<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cs\">... private int headX; private int headY; private float elapsed = <\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-400950","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400950","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=400950"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400950\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=400950"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=400950"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=400950"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}