Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: data send example
  • Loading branch information
juni5184 committed Jul 11, 2025
commit 682490ac1c71e94a5057cf4d8592fbada39d4dcf
10 changes: 8 additions & 2 deletions example/go2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ find_package(OpenCV REQUIRED)

add_executable(go2_video_client_custom go2_video_client_custom.cpp)
target_include_directories(go2_video_client_custom PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(go2_video_client_custom unitree_sdk2 ${OpenCV_LIBS})
# target_link_libraries(go2_video_client_custom unitree_sdk2 ${OpenCV_LIBS})
target_link_libraries(go2_video_client_custom
unitree_sdk2
${OpenCV_LIBS}
curl
)


# add_executable(go2_trajectory_follow go2_trajectory_follow.cpp)
# target_link_libraries(go2_trajectory_follow unitree_sdk2)
Expand All @@ -24,4 +30,4 @@ target_link_libraries(go2_video_client_custom unitree_sdk2 ${OpenCV_LIBS})
# target_link_libraries(go2_video_client unitree_sdk2)

# add_executable(go2_vui_client go2_vui_client.cpp)
# target_link_libraries(go2_vui_client unitree_sdk2)
# target_link_libraries(go2_vui_client unitree_sdk2)
57 changes: 57 additions & 0 deletions example/go2/go2_video_client_custom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <chrono>

#include <opencv2/opencv.hpp> // OpenCV for image processing
#include <curl/curl.h> // HTTP request

#include <unitree/robot/go2/video/video_client.hpp>
#include <unitree/robot/channel/channel_subscriber.hpp>
Expand All @@ -20,6 +21,57 @@ void state_callback(const void* message)
g_state = *(unitree_go::msg::dds_::SportModeState_*)message;
}

void send_to_vllm_server(const std::string& image_path,
const std::array<float, 3>& pos,
const std::array<float, 3>& rpy)
{
CURL *curl;
CURLcode res;
curl_mime *form = nullptr;
curl_mimepart *field = nullptr;

curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl) {
form = curl_mime_init(curl);

// 이미지 전송
field = curl_mime_addpart(form);
curl_mime_name(field, "image");
curl_mime_filedata(field, image_path.c_str());

// 위치 (position)
std::string pos_json = "{\"x\":" + std::to_string(pos[0]) +
",\"y\":" + std::to_string(pos[1]) +
",\"z\":" + std::to_string(pos[2]) + "}";
field = curl_mime_addpart(form);
curl_mime_name(field, "position");
curl_mime_data(field, pos_json.c_str(), CURL_ZERO_TERMINATED);

// RPY (roll/pitch/yaw)
std::string rpy_json = "{\"roll\":" + std::to_string(rpy[0]) +
",\"pitch\":" + std::to_string(rpy[1]) +
",\"yaw\":" + std::to_string(rpy[2]) + "}";
field = curl_mime_addpart(form);
curl_mime_name(field, "rpy");
curl_mime_data(field, rpy_json.c_str(), CURL_ZERO_TERMINATED);

// 전송 URL 설정 (VLLM 서버 주소)
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:5000/analyze"); // IP:PORT 수정
curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);

res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "[curl error] " << curl_easy_strerror(res) << std::endl;
}

curl_mime_free(form);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}


int main()
{
// Init SDK
Expand Down Expand Up @@ -93,6 +145,11 @@ int main()
auto end_time = std::chrono::steady_clock::now();
auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
std::cout << "Elapsed time: " << elapsed_time << " ms" << std::endl;

// 전송
std::array<float, 3> pos_arr = {pos[0], pos[1], pos[2]};
std::array<float, 3> rpy_arr = {rpy[0], rpy[1], rpy[2]};
send_to_vllm_server(processed_name, pos_arr, rpy_arr);
}

usleep(50000); // 0.05 seconds
Expand Down
70 changes: 70 additions & 0 deletions server_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from flask import Flask, request, send_file, jsonify, render_template_string
import json
import os

app = Flask(__name__)

UPLOAD_PATH = "received.jpg"
POSE_PATH = "pose.json" # 위치 + 자세 값 저장용

@app.route("/analyze", methods=["POST"])
def analyze():
image = request.files["image"]
pos = json.loads(request.form["position"])
rpy = json.loads(request.form["rpy"])

print("Received image:", image.filename)
print("Position:", pos)
print("RPY:", rpy)

# 이미지 저장
image.save(UPLOAD_PATH)

# 값 저장
with open(POSE_PATH, "w") as f:
json.dump({"position": pos, "rpy": rpy}, f)

return {"result": "ok"}

@app.route("/view", methods=["GET"])
def view():
if not os.path.exists(UPLOAD_PATH) or not os.path.exists(POSE_PATH):
return "No image or pose data found."

with open(POSE_PATH, "r") as f:
data = json.load(f)

html = """
<html>
<head>
<title>Latest Image and Pose</title>
<style>
body { font-family: Arial; text-align: center; padding: 30px; }
img { max-width: 400px; border: 1px solid #ccc; margin-bottom: 20px; }
.info { font-size: 18px; }
</style>
</head>
<body>
<h1>Latest Image</h1>
<img src="/latest_image" alt="Latest Image"/>
<div class="info">
<h2>Position</h2>
<p>x = {{ pos['x'] }}, y = {{ pos['y'] }}, z = {{ pos['z'] }}</p>
<h2>RPY</h2>
<p>roll = {{ rpy['roll'] }}, pitch = {{ rpy['pitch'] }}, yaw = {{ rpy['yaw'] }}</p>
</div>
</body>
</html>
"""

return render_template_string(html, pos=data["position"], rpy=data["rpy"])

@app.route("/latest_image", methods=["GET"])
def latest_image():
if os.path.exists(UPLOAD_PATH):
return send_file(UPLOAD_PATH, mimetype="image/jpeg")
else:
return jsonify({"error": "No image found"}), 404

if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)