Skip to content

Commit 977c5a7

Browse files
committed
add lingxi_x1 demo
1 parent bc82a7f commit 977c5a7

9 files changed

Lines changed: 260 additions & 93 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ English | [中文](README.zh_CN.md)
1616
| [my_ros2_channel](./my_ros2_channel) | A simple example demonstrating publisher-subscriber communication using Channel API with ROS2 messages | :star: |
1717
| [my_pb_rpc](./my_pb_rpc) | A simple example demonstrating request-response communication using Rpc API with Protobuf messages | :star: |
1818
| [my_ros2_rpc](./my_ros2_rpc) | A simple example demonstrating request-response communication using Rpc API with ROS2 messages | :star: |
19-
| [lingxi_x1](./lingxi_x1) | Demonstrates how to control Lingxi X1 in MuJoCo simulation | :star: :star: |
19+
| [lingxi_x1_sim](./lingxi_x1_sim) | Demonstrates how to control Lingxi X1 in MuJoCo simulation | :star: :star: |

README.zh_CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
| [my_ros2_channel](./my_ros2_channel) | 演示使用 Channel API 进行发布者-订阅者通信的简单示例,使用 ROS2 消息 | :star: |
1616
| [my_pb_rpc](./my_pb_rpc) | 演示使用 Rpc API 进行发布者-订阅者通信的简单示例,使用 Protobuf 消息 | :star: |
1717
| [my_ros2_rpc](./my_ros2_rpc) | 演示使用 Rpc API 进行发布者-订阅者通信的简单示例,使用 ROS2 消息 | :star: |
18-
| [lingxi_x1](./lingxi_x1) | 演示如何在 mujoco 仿真中控制灵犀X1 | :star: :star: |
18+
| [lingxi_x1_sim](./lingxi_x1_sim) | 演示如何在 mujoco 仿真中控制灵犀X1 | :star: :star: |

lingxi_x1_sim/cmake/GetAimrtMujocoSim.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ include(FetchContent)
33
message(STATUS "get aimrt mujoco sim ...")
44

55
set(aimrt_mujoco_sim_DOWNLOAD_URL
6-
"https://github.com/AimRT/aimrt_mujoco_sim/archive/refs/tags/v1.0.1.tar.gz"
6+
"https://github.com/AimRT/aimrt_mujoco_sim/archive/refs/tags/v1.0.2.tar.gz"
77
CACHE STRING "")
88

99

-3.14 MB
Binary file not shown.
Binary file not shown.

lingxi_x1_sim/src/module/keyboard_module/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ target_link_directories(
3030

3131
# Set link libraries of target
3232
find_package(sensor_msgs)
33-
find_package(SDL2)
33+
find_package(SDL2 REQUIRED)
3434

3535
target_link_libraries(
3636
${CUR_TARGET_NAME}

lingxi_x1_sim/src/module/keyboard_module/keyboard/keyboard.cc

Lines changed: 176 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,204 @@
11
#include "keyboard_module/keyboard/keyboard.h"
2-
#include <iostream>
2+
#include <cmath>
3+
#include <map>
4+
#include <stdexcept>
35

46
namespace my_robot::keyboard_module {
57

68
Keyboard::Keyboard() {
7-
printUsage();
8-
input_thread_ = std::thread(&Keyboard::inputLoop, this);
9+
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
10+
throw std::runtime_error("SDL init failed");
11+
}
12+
13+
if (TTF_Init() < 0) {
14+
SDL_Quit();
15+
throw std::runtime_error("TTF init failed");
16+
}
17+
18+
window_ =
19+
SDL_CreateWindow("Robot Control Interface", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
20+
UIConfig::WINDOW_WIDTH, UIConfig::WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
21+
if (!window_) {
22+
TTF_Quit();
23+
SDL_Quit();
24+
throw std::runtime_error("Window creation failed");
25+
}
26+
27+
renderer_ = SDL_CreateRenderer(window_, -1, SDL_RENDERER_ACCELERATED);
28+
if (!renderer_) {
29+
SDL_DestroyWindow(window_);
30+
TTF_Quit();
31+
SDL_Quit();
32+
throw std::runtime_error("Renderer creation failed");
33+
}
34+
35+
const char* fonts[] = {
36+
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
37+
"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
38+
};
39+
for (const auto* path : fonts) {
40+
font_ = TTF_OpenFont(path, UIConfig::FONT_SIZE);
41+
if (font_) break;
42+
}
43+
44+
current_event_ = KeyboardEvent::None();
45+
last_axis_state_.fill(0.0);
46+
47+
for (const auto& btn : UIConfig::MODE_BTNS) {
48+
last_button_state_[btn.state_id] = false;
49+
}
50+
51+
render();
52+
53+
event_thread_ = std::thread(&Keyboard::eventThread, this);
954
}
1055

1156
Keyboard::~Keyboard() {
1257
running_ = false;
13-
// 由于 std::cin 是阻塞的,在某些系统上可能需要按下回车才能完全退出
14-
if (input_thread_.joinable()) {
15-
input_thread_.detach();
58+
if (event_thread_.joinable()) {
59+
event_thread_.join();
1660
}
61+
if (font_) TTF_CloseFont(font_);
62+
if (renderer_) SDL_DestroyRenderer(renderer_);
63+
if (window_) SDL_DestroyWindow(window_);
64+
TTF_Quit();
65+
SDL_Quit();
1766
}
1867

1968
KeyboardEvent Keyboard::GetKeyboardEvent() {
2069
std::lock_guard<std::mutex> lock(mutex_);
2170
auto event = current_event_;
22-
current_event_ = KeyboardEvent::None(); // 消费掉事件
71+
current_event_ = KeyboardEvent::None();
2372
return event;
2473
}
2574

26-
void Keyboard::printUsage() {
27-
std::cout << "\n========== Robot CLI Control ==========\n"
28-
<< "[W/S] Forward/Backward [A/D] Left/Right\n"
29-
<< "[Q/E] Turn Left/Right [Space] Stop\n"
30-
<< "---------------------------------------\n"
31-
<< "[1] Stand [2] Zero [3] Walk [0] Idle\n"
32-
<< "=======================================\n"
33-
<< "Enter command: " << std::flush;
75+
void Keyboard::handleMouseButton(int x, int y, bool is_down) {
76+
std::lock_guard<std::mutex> lock(mutex_);
77+
78+
// 处理鼠标按下
79+
if (is_down) {
80+
for (const auto& btn : UIConfig::MODE_BTNS) {
81+
if (x >= btn.x && x <= btn.x + btn.w && y >= btn.y && y <= btn.y + btn.h) {
82+
if (!last_button_state_[btn.state_id]) {
83+
current_event_ = KeyboardEvent::StateChange(btn.state_id, btn.text);
84+
last_button_state_[btn.state_id] = true;
85+
}
86+
return;
87+
}
88+
}
89+
90+
for (const auto& key : UIConfig::DIR_KEYS) {
91+
if (x >= key.x && x <= key.x + key.w && y >= key.y && y <= key.y + key.h) {
92+
current_event_ =
93+
KeyboardEvent::VelocityChange(key.linear_x, key.linear_y, 0.0, 0.0, 0.0, key.angular_z);
94+
return;
95+
}
96+
}
97+
} else {
98+
for (const auto& btn : UIConfig::MODE_BTNS) {
99+
if (x >= btn.x && x <= btn.x + btn.w && y >= btn.y && y <= btn.y + btn.h) {
100+
last_button_state_[btn.state_id] = false;
101+
return;
102+
}
103+
}
104+
}
105+
}
106+
107+
void Keyboard::render() {
108+
if (!renderer_) return;
109+
110+
SDL_SetRenderDrawColor(renderer_, 245, 245, 250, 255);
111+
SDL_RenderClear(renderer_);
112+
113+
// 绘制方向按键
114+
for (const auto& k : UIConfig::DIR_KEYS) {
115+
SDL_Rect rect = {k.x, k.y, k.w, k.h};
116+
117+
SDL_SetRenderDrawColor(renderer_, 220, 220, 220, 255);
118+
SDL_RenderFillRect(renderer_, &rect);
119+
120+
SDL_SetRenderDrawColor(renderer_, 100, 100, 100, 255);
121+
SDL_RenderDrawRect(renderer_, &rect);
122+
123+
if (font_) {
124+
SDL_Color color = {60, 60, 60, 255};
125+
126+
// 按键字母
127+
SDL_Surface* s = TTF_RenderText_Blended(font_, k.key, color);
128+
if (s) {
129+
SDL_Texture* t = SDL_CreateTextureFromSurface(renderer_, s);
130+
SDL_Rect r = {k.x + (k.w - s->w) / 2, k.y + 15, s->w, s->h};
131+
SDL_RenderCopy(renderer_, t, nullptr, &r);
132+
SDL_DestroyTexture(t);
133+
SDL_FreeSurface(s);
134+
}
135+
136+
// 功能标签
137+
TTF_SetFontSize(font_, UIConfig::LABEL_FONT_SIZE);
138+
s = TTF_RenderText_Blended(font_, k.label, color);
139+
if (s) {
140+
SDL_Texture* t = SDL_CreateTextureFromSurface(renderer_, s);
141+
SDL_Rect r = {k.x + (k.w - s->w) / 2, k.y + 45, s->w, s->h};
142+
SDL_RenderCopy(renderer_, t, nullptr, &r);
143+
SDL_DestroyTexture(t);
144+
SDL_FreeSurface(s);
145+
}
146+
TTF_SetFontSize(font_, UIConfig::FONT_SIZE);
147+
}
148+
}
149+
150+
// 绘制模式按钮
151+
for (const auto& b : UIConfig::MODE_BTNS) {
152+
SDL_Rect rect = {b.x, b.y, b.w, b.h};
153+
154+
SDL_SetRenderDrawColor(renderer_, b.color.r, b.color.g, b.color.b, 255);
155+
SDL_RenderFillRect(renderer_, &rect);
156+
157+
SDL_SetRenderDrawColor(renderer_, 50, 50, 50, 255);
158+
SDL_RenderDrawRect(renderer_, &rect);
159+
160+
if (font_) {
161+
SDL_Color white = {255, 255, 255, 255};
162+
SDL_Surface* s = TTF_RenderText_Blended(font_, b.text, white);
163+
if (s) {
164+
SDL_Texture* t = SDL_CreateTextureFromSurface(renderer_, s);
165+
SDL_Rect r = {b.x + (b.w - s->w) / 2, b.y + (b.h - s->h) / 2, s->w, s->h};
166+
SDL_RenderCopy(renderer_, t, nullptr, &r);
167+
SDL_DestroyTexture(t);
168+
SDL_FreeSurface(s);
169+
}
170+
}
171+
}
172+
173+
SDL_RenderPresent(renderer_);
34174
}
35175

36-
void Keyboard::inputLoop() {
37-
char cmd;
176+
void Keyboard::eventThread() {
38177
while (running_) {
39-
if (!(std::cin >> cmd)) break;
40-
41-
std::lock_guard<std::mutex> lock(mutex_);
42-
switch (cmd) {
43-
// 速度控制
44-
case 'w':
45-
case 'W':
46-
current_event_ = KeyboardEvent::VelocityChange(0.5, 0.0, 0.0);
47-
break;
48-
case 's':
49-
case 'S':
50-
current_event_ = KeyboardEvent::VelocityChange(-0.5, 0.0, 0.0);
51-
break;
52-
case 'a':
53-
case 'A':
54-
current_event_ = KeyboardEvent::VelocityChange(0.0, 0.3, 0.0);
55-
break;
56-
case 'd':
57-
case 'D':
58-
current_event_ = KeyboardEvent::VelocityChange(0.0, -0.3, 0.0);
59-
break;
60-
case 'q':
61-
case 'Q':
62-
current_event_ = KeyboardEvent::VelocityChange(0.0, 0.0, 0.5);
63-
break;
64-
case 'e':
65-
case 'E':
66-
current_event_ = KeyboardEvent::VelocityChange(0.0, 0.0, -0.5);
67-
break;
68-
case ' ':
69-
current_event_ = KeyboardEvent::VelocityChange(0.0, 0.0, 0.0);
70-
break;
178+
SDL_Event e;
71179

72-
// 状态控制
73-
case '1':
74-
current_event_ = KeyboardEvent::StateChange(1, "Stand");
75-
break;
76-
case '2':
77-
current_event_ = KeyboardEvent::StateChange(2, "Zero");
78-
break;
79-
case '3':
80-
current_event_ = KeyboardEvent::StateChange(3, "Walk");
81-
break;
82-
case '0':
83-
current_event_ = KeyboardEvent::StateChange(0, "Idle");
180+
while (SDL_PollEvent(&e)) {
181+
if (e.type == SDL_QUIT) {
182+
running_ = false;
84183
break;
184+
}
85185

86-
default:
87-
std::cout << "Unknown command: " << cmd << "\n";
88-
printUsage();
89-
continue;
186+
switch (e.type) {
187+
case SDL_MOUSEBUTTONDOWN:
188+
handleMouseButton(e.button.x, e.button.y, true);
189+
break;
190+
case SDL_MOUSEBUTTONUP:
191+
handleMouseButton(e.button.x, e.button.y, false);
192+
break;
193+
}
90194
}
91-
std::cout << "Command Sent: " << cmd << "\nEnter command: " << std::flush;
195+
196+
{
197+
std::lock_guard<std::mutex> lock(mutex_);
198+
render();
199+
}
200+
201+
std::this_thread::sleep_for(std::chrono::milliseconds(33));
92202
}
93203
}
94204

0 commit comments

Comments
 (0)