Moth 연동 예제
정보
본 가이드에서는 Moth 서버가 구축되어 있는 상태를 전제로, 연동을 위한 예제 코드와 코드에 대한 설명을 제공합니다. Moth 서버 구축 방법은 시작하기-서버 구축를 참고하세요.
더 복잡한 커스터마이징을 원하시면 아래 예제를 참고하여 직접 Agent와 웹 UI를 개발할 수 있습니다. 전체 예제 코드는 Github에 업로드 된 Example Repository에서 확인할 수 있습니다. GitHub 예제에는 Lidar 연동 예제가 함께 제공됩니다.
로봇 연동 예제
ROS 토픽을 구독하여 영상 프레임을 수신하고, GStreamer로 h.264 코덱으로 인코딩하여 Moth로 전달하는 예제입니다.
초기화
생성자
이번 예제에서 사용될 Video객체의 생성자입니다. 연동을 위해 다음 변수 값 설정이 필요합니다.
host_: Moth 서버의 주소port_: Moth 서버의 포트 번호ROS TOPIC(/your/image_raw/topic): 로봇에서 영상 프레임을 전송하는 ROS 토픽
Video::Video() : Node("video")
{
host_ = "your_host";
port_ = "your_port";
text = "video/h264;width=640;height=480;framerate=30;codecs=avc1.42002A";
while (!initWebSocket()){
std::cout << "reconnecting..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
initVideo();
RCLCPP_INFO(this->get_logger(), "Node has been started.");
subscription_ = this->create_subscription<sensor_msgs::msg::Image>("/your/image_raw/topic", 10, std::bind(&Video::video_callback, this, std::placeholders::_1));
}
Websocket 초기화
Moth 서버로 연결하기 위한 웹소켓 초기화 코드입니다. 다음 값을 설정해 주어야 합니다.
"your_path": Moth에 웹소켓으로 연결하기 위한 도메인 이하 Path값을 설정합니다. 자세한 내용은 Moth 서버의 API 문서를 참고하세요.
bool Video::initWebSocket(){
try{
tcp::resolver resolver{ioc}; // HOST, PORT의 값을 ip로 변환하기 위한 resolver 생성
ws_ptr = std::make_shared<websocket::stream<tcp::socket>>(ioc); // 웹소켓 포인터 객체 생성
auto const results = resolver.resolve(host_, port_); // ip 변환
auto ep = net::connect(ws_ptr->next_layer(), results); // ip에 접근
std::string host = host_ + ':' + std::to_string(ep.port());
ws_ptr->set_option(websocket::stream_base::decorator( // HTTP 헤더 설정
[](websocket::request_type& req)
{
req.set(http::field::user_agent,
std::string(BOOST_BEAST_VERSION_STRING) +
" websocket-client-coro");
}));
ws_ptr->handshake(host, "your_path"); // 웹소켓 PATH 설정
ws_ptr->write(net::buffer(text)); // MIME 전송 (현재 String)
ws_ptr->binary(true); // 이후 데 이터 바이너리로 전송
std::cout << "connect" << std::endl;
return true;
} catch (...){
std::cout << "ERR initWebSocket()" << std::endl;
return false;
}
}
GStreamer 초기화
영상 인코딩을 위해 GStreamer를 초기화하는 코드입니다. appsrc파이프라인으로 프레임을 전달하면 GStreamer에서 인코딩합니다. 인코딩이 완료된 프레임은 appsink로 전달되고,
appsink_callback()함수에서 프레임을 서버로 전송합니다.
void Video::initVideo(){
// appsrc: 영상 데이터를 직접 넣어주는 source, videoconvert: 비디오 데이터를 원하는 형식으로 변환 (지금은 기본으로), openh264enc: H.264 코덱으로 이미지를 인코딩 하는 하드웨어 인코더, h264parse: NAL Unit 및 SPS/PPS 정보를 담기 위한 파서, appsink: 데이터를 직접 받을 수 있는 sink
const char* pipeline_cmd = "appsrc name=src do-timestamp=true is-live=true emit-signals=true format=time caps=video/x-raw,format=RGB,width=640,height=480 ! videoconvert ! openh264enc bitrate=1000000 ! video/x-h264, profile=high, alignment=au, stream-format=byte-stream ! h264parse config-interval=1 ! queue leaky=2 ! appsink name=sink sync=false drop=true emit-signals=true max-buffers=3";
pipeline = gst_parse_launch(pipeline_cmd, NULL); // 파이프라인을 pipeline_cmd기반으로 생성
appsrc = gst_bin_get_by_name(GST_BIN(pipeline), "src"); // src라는 이름의 오브젝트를 가져와 appsrc에 저장
appsink = gst_bin_get_by_name(GST_BIN(pipeline), "sink"); // sink라는 이름의 오브젝트를 가져와 appsink에 저장
g_signal_connect(appsink, "new-sample", G_CALLBACK(appsink_callback), this); // appsink의 출력을 on_new_sample_static이라는 callback함수에 전달
gst_element_set_state(pipeline, GST_STATE_PLAYING); // 파이프라인의 상태를 PLAYING으로 변환, 즉 파이프라인 시작
}