Загрузка данных


import socket
import json
import threading

import cv2
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import TwistStamped
from sensor_msgs.msg import LaserScan, Image
from cv_bridge import CvBridge

OPERATOR_IP = '192.168.1.100'
CMD_PORT = 5001
SCAN_PORT = 5002
VIDEO_PORT = 5000
CAMERA_TOPIC = '/camera/image_raw'


def recv_exact(sock, n):
    buf = b''
    while len(buf) < n:
        chunk = sock.recv(n - len(buf))
        if not chunk:
            return None
        buf += chunk
    return buf


class CmdVelBridge(Node):

    def __init__(self):
        super().__init__('socket_cmd_bridge')
        self.pub = self.create_publisher(TwistStamped, '/cmd_vel', 10)
        self.create_subscription(LaserScan, '/scan', self.scan_cb, 10)
        self.min_distance = None

        self.bridge = CvBridge()
        self.last_frame = None
        self.create_subscription(Image, CAMERA_TOPIC, self.image_cb, 10)

    def image_cb(self, msg):
        self.last_frame = self.bridge.imgmsg_to_cv2(msg, 'bgr8')

    def send(self, linear_x, angular_z):
        msg = TwistStamped()
        msg.header.stamp = self.get_clock().now().to_msg()
        msg.header.frame_id = 'base_link'
        msg.twist.linear.x = float(linear_x)
        msg.twist.angular.z = float(angular_z)
        self.pub.publish(msg)

    def stop(self):
        self.send(0.0, 0.0)

    def scan_cb(self, msg):
        valid = [r for r in msg.ranges if r > 0.0 and r < float('inf')]
        if valid:
            self.min_distance = min(valid)


def scan_reply_loop(node):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((OPERATOR_IP, SCAN_PORT))
    print(f"подключено к оператору {OPERATOR_IP}:{SCAN_PORT} (лидар)")

    try:
        while True:
            request = sock.recv(16)
            if not request:
                break
            distance = node.min_distance if node.min_distance is not None else -1.0
            payload = json.dumps({"distance": distance}).encode('utf-8')
            sock.sendall(len(payload).to_bytes(4, 'big') + payload)
    except (ConnectionResetError, OSError) as e:
        print(f"канал лидара оборван: {e}")
    finally:
        sock.close()


def video_send_loop(node):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((OPERATOR_IP, VIDEO_PORT))
    print(f"подключено к оператору {OPERATOR_IP}:{VIDEO_PORT} (видео)")

    try:
        while True:
            if node.last_frame is None:
                continue
            ok, buffer = cv2.imencode('.jpg', node.last_frame, [cv2.IMWRITE_JPEG_QUALITY, 70])
            if not ok:
                continue
            data = buffer.tobytes()
            sock.sendall(len(data).to_bytes(4, 'big') + data)
    except (ConnectionResetError, BrokenPipeError, OSError) as e:
        print(f"канал видео оборван: {e}")
    finally:
        sock.close()


def main():
    rclpy.init()
    node = CmdVelBridge()

    ros_thread = threading.Thread(target=rclpy.spin, args=(node,), daemon=True)
    ros_thread.start()

    scan_thread = threading.Thread(target=scan_reply_loop, args=(node,), daemon=True)
    scan_thread.start()

    video_thread = threading.Thread(target=video_send_loop, args=(node,), daemon=True)
    video_thread.start()

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((OPERATOR_IP, CMD_PORT))
    print(f"подключено к оператору {OPERATOR_IP}:{CMD_PORT}")

    try:
        while True:
            size_bytes = recv_exact(sock, 4)
            if size_bytes is None:
                break
            msg_len = int.from_bytes(size_bytes, 'big')

            payload = recv_exact(sock, msg_len)
            if payload is None:
                break

            command = json.loads(payload.decode('utf-8'))
            node.send(command.get("linear_x", 0.0), command.get("angular_z", 0.0))
    except (ConnectionResetError, OSError) as e:
        print(f"соединение оборвано: {e}")
    except KeyboardInterrupt:
        pass
    finally:
        node.stop()
        sock.close()
        node.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()