esp32 webteleop 서정_동제 - dingdongdengdong/astra_ws GitHub Wiki

추가 의견
같은 망에서 사용하는가
카메라 스트림 분석
원격웹캠동작
제어명령을 로봇으로 전달

과제1-1. esp없이 코드로 teleop <-> web <-> 시뮬레이션 동작

핵심 구성 요소:

연결 과정 상세 설명

  1. 웹 브라우저 (Web Browser): 사용자가 로봇을 조작하는 웹 인터페이스입니다. 이 인터페이스는 웹 소켓(WebSocket) 또는 WebRTC 데이터 채널(DataChannel)을 통해 teleop_web_node와 통신합니다. astra_teleop_web/src/astra_teleop_web/static/index.js와 같은 프론트엔드 자바스크립트 파일들이 이 역할을 담당합니다.

  2. WebServer (webserver.py): teleop_web_node 내부에서 실행되는 웹 서버입니다. aiohttp.webaiortc 라이브러리를 사용하여 웹 브라우저와의 WebRTC 연결을 관리하고, 데이터 채널을 통해 원격 조작 명령(예: hand, pedal, control 이벤트)을 수신하며, 로봇의 카메라 영상 등을 웹으로 스트리밍합니다. Teleopoperator 객체는 이 WebServer 객체를 멤버 변수로 가지고 있습니다.

  3. Teleopoperator (teleoprator.py): 사용자의 원격 조작 입력(웹으로부터 받은 hand, pedal, control 이벤트)을 처리하고, 이를 로봇의 제어 명령으로 변환하는 핵심 로직을 담고 있는 클래스입니다. 이 클래스 내부의 on_pub_goal, on_pub_gripper, on_pub_head, on_cmd_vel과 같은 콜백 함수들은 초기에는 None으로 설정되어 있습니다.

  4. Teleop Web Node (teleop_web_node.py): 이 ROS 2 노드는 Teleopoperator 객체를 인스턴스화하고, Teleopoperatoron_pub_ 콜백 함수들에 실제 ROS 2 Publisher를 할당합니다. 즉, Teleopoperator에서 생성된 로봇 제어 명령(목표 포즈, 그리퍼 위치 등)이 이 노드를 통해 ROS 2 토픽으로 발행됩니다. 또한, ROS 2로부터 로봇의 상태 정보(예: ik_error, arm/error)를 구독하여 Teleopoperator 객체의 error_cb와 같은 콜백을 통해 웹 인터페이스로 다시 전달하기도 합니다.

  5. ROS 2 System (Robot Device): teleop_web_node가 발행하는 ROS 2 토픽을 구독하는 로봇 제어 시스템의 다른 노드들입니다. 예를 들어, arm_controller는 목표 포즈를 받아 팔을 제어하고, head_controller는 머리 움직임을 제어합니다. 이러한 노드들은 실제 로봇 하드웨어와 통신하여 물리적인 동작을 수행합니다.

연결 과정 (Mermaid Diagram)

graph TD
    A[Web Browser] -- WebRTC Data Channel --> B(WebServer in teleop_web_node)
    B -- Callbacks (on_hand, on_pedal, on_control) --> C(Teleopoperator Object)
    C -- Publishes Commands via assigned functions (on_pub_goal, on_pub_gripper, etc.) --> D(teleop_web_node ROS 2 Node)
    D -- ROS 2 Topics (e.g., /goal_pose, /gripper_command) --> E(ROS 2 Robot Control Nodes)
    E -- Controls --> F(Robot Hardware)

    F -- Sensor Data --> E
    E -- ROS 2 Topics (e.g., /robot_state, /errors) --> D
    D -- Subscribes to ROS 2 Topics & calls Teleopoperator error_cb --> C
    C -- Passes data to WebServer control_datachannel_log --> B
    B -- WebRTC Data Channel --> A
Loading

코드에서의 구체적인 연결

teleoprator.pywebserver.py를 보면 이 연결의 핵심 부분이 드러납니다.

teleoprator.py

Teleopoperator 클래스의 __init__ 메서드를 보면 WebServer 객체를 생성하고, 웹 서버로부터 오는 이벤트에 대한 콜백 함수를 할당하는 것을 볼 수 있습니다:

# astra_teleop_web/src/astra_teleop_web/teleoprator.py
from astra_teleop_web.webserver import WebServer

class Teleopoperator:
    def __init__(self):        
        self.webserver = WebServer()
        self.webserver.on_hand = self.hand_cb        # 웹에서 받은 손 움직임 데이터 처리
        self.webserver.on_pedal = self.pedal_cb      # 웹에서 받은 페달 데이터 처리
        self.webserver.on_control = self.control_cb  # 웹에서 받은 제어 명령 처리
        
        self.on_pub_goal = None
        self.on_pub_gripper = None
        self.on_pub_head = None
        self.on_cmd_vel = None
        # ... (생략) ...

여기서 self.webserver.on_hand, self.webserver.on_pedal, self.webserver.on_controlTeleopoperator 클래스 내의 메서드(hand_cb, pedal_cb, control_cb)가 할당됩니다. 따라서 웹 브라우저에서 특정 조작이 발생하면, webserver.py가 이를 받아 Teleopoperator의 해당 콜백 메서드를 호출하게 됩니다.

Teleopoperator 내에서 로봇 제어 명령을 발행하는 부분(self.on_pub_goal, self.on_pub_gripper)은 다음과 같이 초기화됩니다:

        self.on_pub_goal = None
        self.on_pub_gripper = None
        self.on_pub_head = None
        self.on_cmd_vel = None

이들은 초기에는 None이며, 실제 teleop_web_node.py에서 ROS 2 Publisher 객체가 할당됩니다.

teleop_web_node.py

teleop_web_node.py 파일은 ROS 2 노드로서 Teleopoperator 객체를 생성하고, 이 객체의 on_pub_ 콜백들에 ROS 2 Publisher를 연결하는 핵심적인 역할을 합니다.

# astra_ws.zip/src/astra_controller/astra_controller/teleop_web_node.py
import rclpy
import rclpy.node
import rclpy.publisher

from astra_teleop_web.teleoprator import Teleopoperator # <-- Teleopoperator 임포트

import sensor_msgs.msg
import std_msgs.msg
import geometry_msgs.msg
import astra_controller_interfaces.msg

def main(args=None):
    rclpy.init(args=args)
    node = rclpy.node.Node('teleop_web_node')
    teleopoperator = Teleopoperator() # <-- Teleopoperator 객체 생성

    # 목표 포즈 Publisher
    pub_goal = node.create_publisher(
        geometry_msgs.msg.PoseStamped, 'goal_pose', rclpy.qos.qos_profile_system_default
    )
    # gripper 명령 Publisher
    pub_gripper = node.create_publisher(
        std_msgs.msg.Float64, 'gripper_command', rclpy.qos.qos_profile_system_default
    )
    # head 명령 Publisher
    pub_head = node.create_publisher(
        geometry_msgs.msg.PointStamped, 'head_command', rclpy.qos.qos_profile_system_default
    )
    # base 속도 명령 Publisher
    cmd_vel_publisher = node.create_publisher(
        geometry_msgs.msg.Twist, 'cmd_vel', rclpy.qos.qos_profile_sensor_data
    )

    # Teleopoperator 객체의 콜백 함수에 ROS 2 Publisher 할당
    teleopoperator.on_pub_goal = lambda side, goal_pose: pub_goal.publish(goal_pose)
    teleopoperator.on_pub_gripper = lambda side, gripper_pos: pub_gripper.publish(std_msgs.msg.Float64(data=gripper_pos))
    teleopoperator.on_pub_head = lambda head_pose: pub_head.publish(head_pose)
    teleopoperator.on_cmd_vel = lambda twist: cmd_vel_publisher.publish(twist)

    # ... (생략) ...
    # ROS 2 Subscribe (로봇 상태 및 에러 메시지 수신하여 웹으로 전달)
    def cb(msg):
        teleopoperator.error_cb(f"[IK Left]\n{msg.data}")
    node.create_subscription(
        std_msgs.msg.String, 'left/ik_error', cb, rclpy.qos.qos_profile_sensor_data 
    )
    # ... (다른 구독자들 생략) ...

위 코드에서 볼 수 있듯이, teleop_web_node.pyTeleopoperator 객체를 생성한 후, on_pub_goal, on_pub_gripper 등의 Teleopoperator 내부 콜백에 실제 pub_goal.publish, pub_gripper.publish와 같은 ROS 2 발행 함수를 람다(lambda) 함수 형태로 연결합니다.

따라서 Teleopoperatorself.on_pub_goal(side, goal_pose[side]) 라인이 호출되면, 이는 실제로는 teleop_web_node.py에서 할당된 pub_goal.publish(goal_pose)를 실행하게 되고, 이로 인해 goal_pose 메시지가 goal_pose ROS 2 토픽으로 발행되어 로봇 제어 시스템으로 전달되는 것입니다.

결론적으로, 웹 인터페이스에서 시작된 사용자의 조작 명령은 WebServer를 통해 Teleopoperator로 전달되고, Teleopoperator는 이를 로봇 제어 명령으로 가공한 후, teleop_web_node에 의해 ROS 2 토픽으로 발행되어 최종적으로 로봇 하드웨어를 제어하게 됩니다. 로봇의 상태나 에러 메시지는 역방향으로 ROS 2 토픽을 통해 teleop_web_nodeTeleopoperator를 거쳐 웹 인터페이스로 피드백됩니다.

과제 1-2. <-> web <->esp <-> 실제 로봇 동작

데이터 송수신 및 구동 방식 다이어그램:

아래 다이어그램은 두 가지 주요 시나리오를 보여줍니다.

  • 시나리오 1: 웹 브라우저 -> teleop_web_node.py (ROS2) -> 로봇 제어 노드 -> ESP32 (또는 직접 하드웨어) (ROS2 시스템에 통합된 경우)
  • 시나리오 2: 웹 브라우저 -> astra_teleop_web/webserver.py -> ESP32 (독립적인 웹 조종 시스템의 경우, 또는 teleop_web_node.py가 이 역할을 겸하는 경우)
graph TD
    %% 전체 그래프 방향 설정 (Set overall graph direction)
    direction TB

    %% 사용자 인터페이스 서브그래프 (User Interface Subgraph)
    subgraph "사용자 인터페이스 (User Interface)"
        direction LR
        A[웹 브라우저 - index.html, JS]
    end

    %% 네트워크 통신 서브그래프 (Network Communication Subgraph)
    subgraph "네트워크 통신 (Network Communication)"
        direction LR
        B[Websocket, HTTP]
    end

    %% 서버 및 ROS2 통합 서브그래프 (Server-Side/ROS2 Integration Subgraph)
    subgraph "서버/ROS2 통합 (Server-Side/ROS2 Integration)"
        direction LR
        C[teleop_web_node.py - ROS2]
        D[webserver.py - Non-ROS]
    end

    %% ROS2 시스템 서브그래프 (ROS2 System Subgraph)
    subgraph "ROS2 시스템 (ROS2 System)"
        direction LR
        E[ROS2 노드 - head_node, arm_node, base_node]
        F[ROS2 토픽 - cmd_vel, joint_command]
    end

    %% 마이크로컨트롤러 및 하드웨어 서브그래프 (Microcontroller/Hardware Subgraph)
    subgraph "마이크로컨트롤러/하드웨어 (Microcontroller/Hardware)"
        direction LR
        G[ESP32]
        H[로봇 모터, 센서]
    end

    %% 연결 (Connections)
    A -->|"제어 명령 - JSON, Text"| B
    B -->|"웹 명령"| C
    B -->|"웹 명령"| D

    %% 시나리오 1: teleop_web_node.py를 통한 ROS2 통합 (Scenario 1: ROS2 Integration via teleop_web_node.py)
    C -->|"파싱된 명령"| F
    F --> E
    E -->|"저수준 명령 - Serial, CAN, I2C"| G
    %% 대체 경로: teleop_web_node에서 직접 제어 (Alternative path: Direct control from teleop_web_node)
    C -->|"직접 명령 - Serial, TCP, UDP, Websocket"| G

    %% 시나리오 2: 독립 웹 서버 (Scenario 2: Standalone Web Server)
    D -->|"파싱된 명령 - Serial, TCP, UDP, Websocket"| G

    %% 하드웨어 상호작용 (Hardware Interactions)
    G -->|"제어 신호"| H
    H -->|"센서 데이터"| G
    G -->|"상태, 센서 데이터 - Websocket, TCP, UDP, Serial"| C
    G -->|"상태, 센서 데이터 - Websocket, HTTP"| D
    D -->|"상태, 센서 데이터"| B
    C -->|"상태, 센서 데이터 - Websocket 또는 ROS2 토픽"| B
    B -->|"화면 업데이트, 로봇 상태"| A

    %% 스타일링 (Styling)
    classDef ui fill:#D5F5E3,stroke:#28B463,stroke-width:2px
    classDef network fill:#E8DAEF,stroke:#8E44AD,stroke-width:2px
    classDef ros fill:#D6EAF8,stroke:#2E86C1,stroke-width:2px
    classDef hardware fill:#FDEDEC,stroke:#CB4335,stroke-width:2px
    classDef topic fill:#FEF9E7,stroke:#F39C12,stroke-width:1px

    class A ui
    class B network
    class C,E ros
    class D ros
    class F topic
    class G,H hardware
Loading

데이터 송수신 및 구동 상세 설명:

  1. 사용자 입력 (A -> B):

    • 사용자는 웹 브라우저(A)에 로드된 index.html 페이지의 버튼(전진, 후진, 헤드 올림 등)을 클릭하거나 슬라이더를 조작합니다.
    • 브라우저의 JavaScript (index.js 등)는 이 입력을 감지하여 특정 형식(주로 JSON 또는 간단한 문자열)의 제어 메시지를 생성합니다.
    • 생성된 메시지는 웹소켓(실시간 양방향 통신에 유리) 또는 HTTP POST/GET 요청(B)을 통해 서버로 전송됩니다. 웹소켓 주소나 HTTP URL은 JavaScript 코드에 명시되어 있습니다.
  2. 서버에서의 명령 수신 및 처리 (B -> C 또는 B -> D):

    • teleop_web_node.py (C)의 경우 (ROS2 통합):
      • 이 Python 스크립트는 웹 서버(예: aiohttp 서버)를 내장하고 있어 특정 포트에서 웹소켓 연결 또는 HTTP 요청을 수신 대기합니다.
      • 웹 브라우저로부터 제어 메시지를 받으면, 이를 파싱하여 로봇의 어떤 부분을 어떻게 움직일지에 대한 정보(예: 선속도, 각속도, 목표 조인트 각도)를 추출합니다.
      • 추출된 정보를 바탕으로 적절한 ROS2 메시지 타입(예: geometry_msgs/msg/Twist 또는 astra_controller_interfaces/msg/JointCommand)을 생성합니다.
      • 생성된 ROS2 메시지를 해당 제어 토픽(F) (예: /cmd_vel 또는 /joint_command)으로 발행(publish)합니다.
      • 만약 ESP32와 직접 통신해야 하는 부분이 있다면 (예: 특정 GPIO 핀 제어), ROS2 토픽 발행 대신 또는 추가적으로 ESP32로 직접 명령을 전송할 수도 있습니다. (이 경우 teleop_web_node.py가 ESP32와의 통신 로직도 담당)
    • astra_teleop_web/webserver.py (D)의 경우 (독립 실행 또는 teleop_web_node.py가 이 역할 수행):
      • 이 Python 스크립트가 웹 서버 역할을 하며 웹 브라우저로부터 직접 명령을 수신합니다.
      • 수신된 명령을 ESP32가 이해할 수 있는 프로토콜(예: 간단한 문자열 명령 "FORWARD", "LEFT", 또는 JSON 객체)로 변환합니다.
      • 변환된 명령을 Wi-Fi를 통해 ESP32(G)로 전송합니다. 통신 방식은 ESP32 펌웨어에 맞춰 웹소켓 클라이언트, TCP 클라이언트, UDP 패킷 등으로 구현될 수 있습니다.
  3. ROS2 시스템 내에서의 제어 (C -> F -> E -> G, 또는 C -> G):

    • teleop_web_node.py(C)가 ROS2 토픽(F)으로 명령을 발행하면, 해당 토픽을 구독하고 있는 로봇의 실제 제어 노드들(E) (예: head_node.py, arm_node.py, base_node.py)이 이 명령을 수신합니다.
    • 각 제어 노드는 수신된 명령에 따라 자신의 담당 부분(헤드, 팔, 베이스 등)을 제어하기 위해 head_controller.py, arm_controller.py, base_controller.py 등의 저수준 컨트롤러 클래스를 호출합니다.
    • 이 저수준 컨트롤러들은 시리얼, CAN, I2C 등의 통신 방식을 통해 실제 하드웨어(모터 드라이버 등) 또는 ESP32(G)와 통신하여 명령을 전달합니다.
    • 만약 teleop_web_node.py가 ESP32와 직접 통신하도록 설계되었다면, 이 단계에서 바로 ESP32로 명령을 전달합니다.
  4. ESP32에서의 명령 실행 (G -> H):

    • ESP32(G)는 Wi-Fi를 통해 웹 서버(teleop_web_node.py 또는 astra_teleop_web/webserver.py)로부터 제어 명령을 수신합니다.
    • ESP32에 프로그래밍된 펌웨어는 수신된 명령을 해석합니다 (예: "FORWARD" 명령을 받으면 특정 GPIO 핀들을 제어하여 모터 드라이버에 신호를 보냄).
    • 해석된 명령에 따라 연결된 모터(H)를 구동시키거나, LED를 켜거나, 다른 액추에이터를 작동시킵니다.
  5. 상태 정보 송신 (H -> G -> D/C -> B -> A) (선택 사항):

    • 로봇의 센서(H) (예: 배터리 전압 센서, 거리 센서, 엔코더) 값은 ESP32(G)에서 읽힐 수 있습니다.
    • ESP32는 이 상태 정보를 주기적으로 또는 요청에 따라 Wi-Fi를 통해 웹 서버(D 또는 C)로 전송할 수 있습니다.
    • 웹 서버(D 또는 C)는 수신된 상태 정보를 웹소켓이나 HTTP 응답을 통해 웹 브라우저(A)로 전달합니다.
    • 웹 브라우저의 JavaScript는 이 정보를 받아 화면에 표시하여 사용자에게 로봇의 현재 상태를 알려줍니다.
    • teleop_web_node.py(C)의 경우, ESP32로부터 받은 센서 데이터를 다른 ROS2 노드에서 활용할 수 있도록 특정 ROS2 토픽으로 발행할 수도 있습니다.

실행 단계 (일반적인 시나리오):

  1. ESP32 준비:

    • ESP32에 Wi-Fi 접속 정보(SSID, 비밀번호)와 웹 서버 주소(IP, 포트)를 설정하고, 제어 명령 수신 및 하드웨어 제어 로직이 포함된 펌웨어를 업로드합니다.
    • ESP32가 네트워크에 연결되고 웹 서버로부터 명령을 받을 준비를 합니다.
  2. 웹 원격 조종 서버 실행:

    • ROS2 환경:
      source /opt/ros/<your_ros_distro>/setup.bash
      source <path_to_your_astra_ws>/install/setup.bash
      ros2 run astra_controller teleop_web_node
      (필요에 따라 launch 파일을 통해 관련 노드들과 함께 실행될 수 있습니다: 예: ros2 launch astra_controller start.launch.py)
    • 독립 실행 환경 (Non-ROS, astra_teleop_web 사용 시):
      cd <path_to_your_astra_ws>/non_ros_src/astra_teleop_web/src/astra_teleop_web
      python webserver.py
      (실행 명령어는 해당 스크립트의 구현 방식에 따라 다를 수 있습니다.)
    • 서버가 실행되면 특정 IP 주소와 포트에서 웹 요청을 기다립니다. (터미널 로그에서 주소 확인)
  3. 웹 브라우저 접속:

    • PC 또는 모바일 기기의 웹 브라우저를 열고, 웹 서버가 실행 중인 주소 (예: http://<서버_IP_주소>:<포트번호>, 예를 들어 http://192.168.1.100:8080)로 접속합니다.
    • index.html 페이지가 로드되고, 사용자는 화면의 컨트롤을 사용하여 로봇 조종을 시작할 수 있습니다.

주요 기술:

  • ESP32: Wi-Fi 통신, GPIO 제어, 센서 인터페이스.
  • 웹 기술: HTML, CSS, JavaScript (프론트엔드 UI).
  • 통신 프로토콜:
    • 웹소켓 (WebSockets): 웹 브라우저와 서버 간 실시간 양방향 통신에 주로 사용. 낮은 지연 시간.
    • HTTP (HyperText Transfer Protocol): 요청-응답 기반 통신. 간단한 명령 전송에 사용될 수 있음.
    • TCP/IP 또는 UDP: 서버(Python 스크립트)와 ESP32 간의 직접적인 네트워크 통신에 사용될 수 있음.
  • Python (서버 사이드):
    • 웹 프레임워크: aiohttp (비동기, teleop_web_node.py에서 사용 가능성 높음), Flask, FastAPI 등.
    • 웹소켓 라이브러리: websockets, aiohttp-websockets 등.
    • ROS2 클라이언트 라이브러리: rclpy.
    • (필요시) 시리얼 통신 라이브러리: pyserial.

이 다이어그램과 설명은 ESP32와 웹을 사용한 원격 조종 시스템의 일반적인 아키텍처를 보여줍니다. 실제 구현은 제공된 코드의 세부 사항에 따라 다소 차이가 있을 수 있으므로, teleop_web_node.pyastra_teleop_web 디렉토리 내의 Python 스크립트 및 HTML/JavaScript 파일들의 내용을 함께 살펴보시는 것이 중요합니다.

⚠️ **GitHub.com Fallback** ⚠️