GaitGeneration by Graph Search
読み取り中…
検索中…
一致する文字列を見つけられません
dxlib_gui_node_displayer.cpp
[詳解]
1
3
4// Copyright(c) 2023-2025 Design Engineering Laboratory, Saitama University
5// Released under the MIT license
6// https://opensource.org/licenses/mit-license.php
7
9
10#include <format>
11
12#include <magic_enum.hpp>
13
14#include <Dxlib.h>
15
16#include "math_util.h"
17#include "math_rot_converter.h"
18#include "string_util.h"
19#include "font_loader.h"
20#include "leg_state.h"
21#include "phantomx_mk2_const.h"
22
23
24namespace designlab {
25
27 const int pos_x,
28 const int pos_y,
29 const std::shared_ptr<const IHexapodCoordinateConverter>& converter_ptr,
30 const std::shared_ptr<const IHexapodJointCalculator>& calculator_ptr,
31 const std::shared_ptr<const IHexapodPostureValidator>& checker_ptr) :
32 AbstractDxlibGui{ 470, 680 },
33 converter_ptr_(converter_ptr),
34 calculator_ptr_(calculator_ptr),
35 checker_ptr_(checker_ptr),
36 display_type_(DisplayMode::kDefualt),
37 window_x_(pos_x),
38 window_y_(pos_y) {
39 // ボタンを作成する.
40 const int button_size_x = 90;
41 const int button_size_y = 30;
42
43 button_.push_back(
44 std::make_unique<SimpleButton>(
45 "基本情報",
46 10 + button_size_x / 2,
47 gui_top_pos_y_ + height_ - button_size_y / 2 - 10,
48 button_size_x,
49 button_size_y));
50
51 button_.back()->SetActivateFunction(
52 [this]() {display_type_ = DisplayMode::kDefualt; });
53
54 button_.push_back(
55 std::make_unique<SimpleButton>(
56 "joint",
57 (10 + button_size_x / 2) + (10 + button_size_x),
58 gui_top_pos_y_ + height_ - button_size_y / 2 - 10,
59 button_size_x,
60 button_size_y));
61
62 button_.back()->SetActivateFunction(
63 [this]() {display_type_ = DisplayMode::kJointState; });
64
65 button_.push_back(
66 std::make_unique<SimpleButton>(
67 "脚先座標",
68 (10 + button_size_x / 2) + (10 + button_size_x) * 2,
69 gui_top_pos_y_ + height_ - button_size_y / 2 - 10,
70 button_size_x,
71 button_size_y));
72
73 button_.back()->SetActivateFunction(
74 [this]() {display_type_ = DisplayMode::kGlobalPos; });
75
76 const int close_button_size = 28;
77 const int close_button_x = gui_left_pos_x_ + width_ - close_button_size / 2 - 2;
78 const int close_button_y = gui_top_pos_y_ + close_button_size / 2 + 2;
79
80 button_.push_back(std::make_unique<SimpleButton>(
81 "×", close_button_x, close_button_y,
82 close_button_size, close_button_size));
83
84 button_.back()->SetActivateFunction([this]() { SetVisible(false); });
85}
86
88 // ノードをセットする.
89 display_node_ = node;
90
91 if (!calculator_ptr_) {
92 return;
93 }
94
95 // 関節の角度をセットする.
96 joint_state_ = calculator_ptr_->CalculateAllJointState(display_node_);
97}
98
100 // ボタンの更新を行う.
101 for (auto& button : button_) {
102 button->Update();
103 }
104
105 if (!IsInWindow()) {
106 SetVisible(false);
107 }
108}
109
111 // 枠.
112 DrawBackground("NodeDisplay");
113
114 // テキスト.
115 if (display_type_ == DisplayMode::kDefualt) {
116 DrawNodeInfo();
117 }
118 else if (display_type_ == DisplayMode::kJointState) {
119 DrawJointInfo();
120 }
121 else if (display_type_ == DisplayMode::kGlobalPos) {
122 DrawGlobalPosInfo();
123 }
124
125 // ボタンを描画する.
126 for (auto& button : button_) {
127 button->Draw();
128 }
129}
130
131void DxlibGuiNodeDisplayer::DrawNodeInfo() const {
133
134 const unsigned int text_color = GetColor(10, 10, 10);
135 const unsigned int text_color_dark = GetColor(80, 80, 80);
136
137 const int text_pos_x = gui_left_pos_x_ + 10;
138 const int text_pos_y_min = gui_top_pos_y_ + kTitleBarHeight + 10;
139 const int text_interval_y = 20;
140
141 const std::array<std::string, HexapodConst::kLegNum> leg_name = {
142 "右前", "右中", "右後", "左後", "左中", "左前" };
143
144 int text_line = 0;
145
146 DrawFormatStringToHandle(
147 text_pos_x, text_pos_y_min + text_interval_y * (text_line++),
148 text_color, font_handle_, "階層");
149 DrawFormatStringToHandle(
150 text_pos_x, text_pos_y_min + text_interval_y * (text_line++),
151 text_color, font_handle_,
152 " bit : %s", display_node_.leg_state.to_string().c_str());
153
154 const auto com_pos_name =
155 EnumToStringRemoveTopK(
157 const auto com_pos_num = leg_func::GetDiscreteComPos(display_node_.leg_state);
158
159 DrawFormatStringToHandle(
160 text_pos_x, text_pos_y_min + text_interval_y * (text_line++),
161 text_color, font_handle_,
162 " 重心 : %s(%d)", com_pos_name.c_str(), com_pos_num);
163
164 std::string str_leg_pos_right = "";
165 std::string str_leg_pos_left = "";
166 std::string str_ground = "";
167
168 for (int i = 0; i < HexapodConst::kLegNum; i++) {
169 if (leg_func::IsGrounded(display_node_.leg_state, i)) {
170 str_ground += "接地,";
171 }
172 else {
173 str_ground += "遊脚,";
174 }
175
176 const DiscreteLegPos pos =
177 leg_func::GetDiscreteLegPos(display_node_.leg_state, i);
178
179 if (i < HexapodConst::kLegNum / 2) {
180 str_leg_pos_right += std::format(
181 "{}-{}({}), ",
182 leg_name[i],
184 static_cast<int>(pos));
185 }
186 else {
187 str_leg_pos_left += std::format(
188 "{}-{}({}), ",
189 leg_name[i],
191 static_cast<int>(pos));
192 }
193 }
194
195 DrawFormatStringToHandle(
196 text_pos_x,
197 text_pos_y_min + text_interval_y * (text_line++),
198 text_color, font_handle_,
199 " 脚位置 : ", str_leg_pos_right.c_str());
200
201 DrawFormatStringToHandle(
202 text_pos_x,
203 text_pos_y_min + text_interval_y * (text_line++),
204 text_color, font_handle_,
205 " %s", str_leg_pos_right.c_str());
206
207 DrawFormatStringToHandle(
208 text_pos_x, text_pos_y_min + text_interval_y * (text_line++),
209 text_color, font_handle_,
210 " %s", str_leg_pos_left.c_str());
211
212 DrawFormatStringToHandle(
213 text_pos_x, text_pos_y_min + text_interval_y * (text_line++),
214 text_color, font_handle_,
215 " 脚の状態:%s", str_ground.c_str());
216
217 // 重心を表示する
218 ++text_line;
219 DrawFormatStringToHandle(
220 text_pos_x,
221 text_pos_y_min + text_interval_y * (text_line++),
222 text_color, font_handle_,
223 "重心位置");
224
225 DrawFormatStringToHandle(
226 text_pos_x,
227 text_pos_y_min + text_interval_y * (text_line++),
228 text_color, font_handle_,
229 " %s", display_node_.center_of_mass_global_coord.ToString().c_str());
230
231 // 回転を表示する
232 DrawFormatStringToHandle(
233 text_pos_x,
234 text_pos_y_min + text_interval_y * (text_line++),
235 text_color, font_handle_,
236 "回転 (w:%5.3f,x:%5.3f,y:%5.3f,z:%5.3f)",
237 display_node_.posture.w,
238 display_node_.posture.v.x,
239 display_node_.posture.v.y,
240 display_node_.posture.v.z);
241
242 // オイラー角にして表示する.
243 const EulerXYZ euler = ToEulerXYZ(display_node_.posture);
244
245 DrawFormatStringToHandle(
246 text_pos_x,
247 text_pos_y_min + text_interval_y * (text_line++),
248 text_color, font_handle_,
249 " オイラー角(x:%5.3f[deg],y:%5.3f[deg],z:%5.3f[deg])",
250 math_util::ConvertRadToDeg(euler.x_angle),
251 math_util::ConvertRadToDeg(euler.y_angle),
252 math_util::ConvertRadToDeg(euler.z_angle));
253
254 // 脚の位置を表示する.
255 ++text_line;
256 DrawFormatStringToHandle(
257 text_pos_x,
258 text_pos_y_min + text_interval_y * (text_line++),
259 text_color, font_handle_,
260 "脚位置");
261
262 for (int i = 0; i < HexapodConst::kLegNum; i++) {
263 DrawFormatStringToHandle(
264 text_pos_x,
265 text_pos_y_min + text_interval_y * (text_line++),
266 text_color, font_handle_,
267 "%s %s",
268 leg_name[i].c_str(),
269 display_node_.leg_pos[i].ToString().c_str());
270 }
271
272 // 脚の基準座標を表示する.
273 for (int i = 0; i < HexapodConst::kLegNum; i++) {
274 if (display_node_.leg_pos[i] == display_node_.leg_reference_pos[i]) {
275 DrawFormatStringToHandle(
276 text_pos_x,
277 text_pos_y_min + text_interval_y * (text_line++),
278 text_color_dark, font_handle_,
279 " %s脚の基準座標は現在の脚位置と同じです.", leg_name[i].c_str());
280 }
281 else {
282 DrawFormatStringToHandle(
283 text_pos_x,
284 text_pos_y_min + text_interval_y * (text_line++),
285 text_color_dark, font_handle_,
286 " %s脚の基準座標(x:%5.3f,y:%5.3f,z:%5.3f)",
287 leg_name[i].c_str(),
288 display_node_.leg_reference_pos[i].x,
289 display_node_.leg_reference_pos[i].y,
290 display_node_.leg_reference_pos[i].z);
291 }
292 }
293
294 // 深さと次の動作を表示する.
295 ++text_line;
296
297 DrawFormatStringToHandle(
298 text_pos_x,
299 text_pos_y_min + text_interval_y * (text_line++),
300 text_color, font_handle_,
301 "深さ:%d, 次の動作 : %s",
302 display_node_.depth,
303 EnumToStringRemoveTopK(display_node_.next_move).c_str());
304
305 DrawFormatStringToHandle(
306 text_pos_x,
307 text_pos_y_min + text_interval_y * (text_line++),
308 text_color, font_handle_,
309 "指定がなければ単位は長さが[mm],角度が[rad]");
310}
311
312void DxlibGuiNodeDisplayer::DrawJointInfo() const {
315
316 const unsigned int text_color = GetColor(10, 10, 10);
317 const unsigned int error_text_color = GetColor(128, 10, 10);
318 const int text_pos_x = gui_left_pos_x_ + 10;
319 const int text_pos_y_min = gui_top_pos_y_ + kTitleBarHeight + 10;
320 const int text_interval_y = 20;
321
322 if (!calculator_ptr_) {
323 DrawFormatStringToHandle(
324 text_pos_x,
325 text_pos_y_min + text_interval_y * 0,
326 text_color,
328 "計算クラスが nullptr です");
329 return;
330 }
331
332 if (!checker_ptr_) {
333 DrawFormatStringToHandle(
334 text_pos_x,
335 text_pos_y_min + text_interval_y * 0,
336 text_color,
338 "チェッカークラスが nullptr です");
339 return;
340 }
341
342 for (int i = 0; i < HexapodConst::kLegNum; i++) {
343 if (joint_state_[i].joint_angle.size() != 3) {
344 DrawFormatStringToHandle(
345 text_pos_x,
346 text_pos_y_min + text_interval_y * 0,
347 text_color,
349 "間接の計算ができていない,またはされていません.");
350
351 return;
352 }
353
354 if (joint_state_[i].joint_pos_leg_coordinate.size() != 4) {
355 DrawFormatStringToHandle(
356 text_pos_x,
357 text_pos_y_min + text_interval_y * 0,
358 text_color,
360 "間接の計算ができていない,またはされていません.");
361
362 return;
363 }
364 }
365
366 int text_line = 0;
367
368
369 for (int i = 0; i < HexapodConst::kLegNum; i++) {
370 const float coxa_angle_deg =
371 ConvertRadToDeg(joint_state_[i].joint_angle[0]);
372 const float femur_angle_deg =
373 ConvertRadToDeg(joint_state_[i].joint_angle[1]);
374 const float tibia_angle_deg =
375 ConvertRadToDeg(joint_state_[i].joint_angle[2]);
376
377 DrawFormatStringToHandle(
378 text_pos_x,
379 text_pos_y_min + text_interval_y * (text_line++),
380 text_color,
382 "[%d] c %s[deg],f %s[deg],t %s[deg]",
383 i,
384 FloatingPointNumToString(coxa_angle_deg).c_str(),
385 FloatingPointNumToString(femur_angle_deg).c_str(),
386 FloatingPointNumToString(tibia_angle_deg).c_str());
387
388 const auto coxa_to_femur = joint_state_[i].joint_pos_leg_coordinate[0] -
389 joint_state_[i].joint_pos_leg_coordinate[1];
390 const auto femur_to_tibia = joint_state_[i].joint_pos_leg_coordinate[1] -
391 joint_state_[i].joint_pos_leg_coordinate[2];
392 const auto tibia_to_end = joint_state_[i].joint_pos_leg_coordinate[2] -
393 joint_state_[i].joint_pos_leg_coordinate[3];
394
395 DrawFormatStringToHandle(
396 text_pos_x,
397 text_pos_y_min + text_interval_y * (text_line++),
398 text_color,
400 " c %3.3f[mm],f %3.3f[mm],t %3.3f[mm]",
401 coxa_to_femur.GetLength(),
402 femur_to_tibia.GetLength(),
403 tibia_to_end.GetLength());
404
405
406 if (checker_ptr_->IsLegInRange(
407 i, joint_state_[i].joint_pos_leg_coordinate[3])) {
408 DrawFormatStringToHandle(
409 text_pos_x,
410 text_pos_y_min + text_interval_y * (text_line++),
411 text_color,
413 " 近似値された可動域内にあります.");
414 }
415 else {
416 DrawFormatStringToHandle(
417 text_pos_x,
418 text_pos_y_min + text_interval_y * (text_line++),
419 error_text_color,
421 " 近似値された可動域外です.");
422 }
423
424
425 std::string str = "";
426
427 const float coxa_min_angle =
430 const float coxa_max_angle =
433
434 if (joint_state_[i].joint_angle[0] < coxa_min_angle) {
435 str += "coxa_min ";
436 }
437
438 if (joint_state_[i].joint_angle[0] > coxa_max_angle) {
439 str += "coxa_max ";
440 }
441
442 if (joint_state_[i].joint_angle[1] < PhantomXMkIIConst::kFemurAngleMin) {
443 str += "femur_min ";
444 }
445
446 if (joint_state_[i].joint_angle[1] > PhantomXMkIIConst::kFemurAngleMax) {
447 str += "femur_max ";
448 }
449
450 if (joint_state_[i].joint_angle[2] < PhantomXMkIIConst::kTibiaAngleMin) {
451 str += "tibia_min ";
452 }
453
454 if (joint_state_[i].joint_angle[2] > PhantomXMkIIConst::kTibiaAngleMax) {
455 str += "tibia_max ";
456 }
457
458 if (!joint_state_[i].is_in_range) {
459 str += "脚先が届いていません ";
460 }
461
462 if (!str.empty()) {
463 const size_t max_str_size = 30;
464 if (str.size() > max_str_size) { str = str.substr(0, max_str_size); }
465
466 DrawFormatStringToHandle(
467 text_pos_x,
468 text_pos_y_min + text_interval_y * (text_line++),
469 error_text_color,
471 " 実際の可動域の外です. %s", str.c_str());
472 }
473 else {
474 DrawFormatStringToHandle(
475 text_pos_x,
476 text_pos_y_min + text_interval_y * (text_line++),
477 text_color,
479 " 実際の可動域の内です.");
480 }
481 }
482}
483
484void DxlibGuiNodeDisplayer::DrawGlobalPosInfo() const {
485 const unsigned int text_color = GetColor(10, 10, 10);
486 const int text_pos_x = gui_left_pos_x_ + 10;
487 const int text_pos_y_min = gui_top_pos_y_ + kTitleBarHeight + 10;
488 const int text_interval_y = 20;
489 const std::array<std::string, HexapodConst::kLegNum> leg_name = {
490 "右前", "右中", "右後", "左後", "左中", "左前" };
491
492 int text_line = 0;
493
494 DrawFormatStringToHandle(text_pos_x,
495 text_pos_y_min + text_interval_y * (text_line++),
496 text_color,
498 "脚先座標(ローカル座標)");
499
500 for (int i = 0; i < HexapodConst::kLegNum; i++) {
501 DrawFormatStringToHandle(
502 text_pos_x,
503 text_pos_y_min + text_interval_y * (text_line++),
504 text_color,
506 "%s %s",
507 leg_name[i].c_str(),
508 display_node_.leg_pos[i].ToString().c_str());
509 }
510
511
512 ++text_line;
513 DrawFormatStringToHandle(text_pos_x,
514 text_pos_y_min + text_interval_y * (text_line++),
515 text_color,
517 "脚先座標(ロボット座標)");
518
519 for (int i = 0; i < HexapodConst::kLegNum; i++) {
520 const std::string str =
521 converter_ptr_->ConvertLegToRobotCoordinate(
522 display_node_.leg_pos[i], i).ToString();
523
524 DrawFormatStringToHandle(
525 text_pos_x,
526 text_pos_y_min + text_interval_y * (text_line++),
527 text_color,
529 "%s %s", leg_name[i].c_str(), str.c_str());
530 }
531
532 ++text_line;
533 DrawFormatStringToHandle(text_pos_x,
534 text_pos_y_min + text_interval_y * (text_line++),
535 text_color,
537 "脚先座標(グローバル座標)");
538
539 DrawFormatStringToHandle(
540 text_pos_x,
541 text_pos_y_min + text_interval_y * (text_line++),
542 text_color,
544 "重心 %s",
545 display_node_.center_of_mass_global_coord.ToString().c_str());
546
547 ++text_line;
548
549 for (int i = 0; i < HexapodConst::kLegNum; i++) {
550 DrawFormatStringToHandle(
551 text_pos_x,
552 text_pos_y_min + text_interval_y * (text_line++),
553 text_color,
555 "%s %s",
556 leg_name[i].c_str(),
557 converter_ptr_->ConvertLegToGlobalCoordinate(
558 display_node_.leg_pos[i],
559 i,
560 display_node_.center_of_mass_global_coord,
561 display_node_.posture,
562 true).ToString().c_str());
563 }
564
565 ++text_line;
566
567 // 脚先の付け根からの距離を表示する.
568 for (int i = 0; i < HexapodConst::kLegNum; i++) {
569 DrawFormatStringToHandle(
570 text_pos_x + width_ / 2 * (i % 2),
571 text_pos_y_min + text_interval_y * text_line,
572 text_color,
574 "%s %f",
575 leg_name[i].c_str(),
576 display_node_.leg_pos[i].ProjectedXY().GetLength());
577
578 if (i % 2 == 1) { ++text_line; }
579 }
580}
581
582bool DxlibGuiNodeDisplayer::IsInWindow() const {
583 return gui_left_pos_x_ < window_x_ && gui_top_pos_y_ < window_y_ &&
585}
586
587} // namespace designlab
Dxlibを使ったGUIの抽象クラス.
void DrawBackground(const std::string &str) const
int gui_left_pos_x_
GUIの左端の位置.
static constexpr int kTitleBarHeight
タイトルバーの高さ.
const int width_
GUIの横幅.
int gui_top_pos_y_
GUIの上端の位置.
void SetVisible(bool visible) override
GUIの表示を行うかどうかを設定する.
int font_handle_
フォントハンドル.
std::vector< std::unique_ptr< SimpleButton > > button_
ボタンのリスト.
const int height_
GUIの縦幅.
void SetNode(const RobotStateNode &node) override
ノードをセットする.
void Draw() const override
GUIの描画.
DxlibGuiNodeDisplayer(int window_x, int window_y, const std::shared_ptr< const IHexapodCoordinateConverter > &converter_ptr, const std::shared_ptr< const IHexapodJointCalculator > &calculator_ptr, const std::shared_ptr< const IHexapodPostureValidator > &checker_ptr)
void Update() override
GUIの更新,毎フレーム実行すること.
static constexpr int kLegNum
static constexpr float kTibiaAngleMax
static constexpr float kFemurAngleMin
第2関節の可動範囲の最大値[rad].詳しくは referenceフォルダ参照.
static constexpr float kFemurAngleMax
第2関節の可動範囲の最小値[rad].詳しくは referenceフォルダ参照.
static constexpr float kCoxaAngleMin
第1関節の可動範囲の最大値[rad].詳しくは referenceフォルダ参照.
static constexpr float kTibiaAngleMin
第2関節の可動範囲の最大値[rad].詳しくは referenceフォルダ参照.
static constexpr std::array< float, kPhantomXLegNum > kCoxaDefaultAngle
第1関節の初期角度[rad]
static constexpr float kCoxaAngleMax
第2関節の可動範囲の最小値[rad].詳しくは referenceフォルダ参照.
bool IsGrounded(const LegStateBit &leg_state, const int leg_index)
脚番号 leg_index 0 ~ 5 に応じて,その脚が接地しているかを調べる. 脚は右前脚を0番として,時計回りに0,1,2,3,4,5となる.左前足が5番.
Definition leg_state.cpp:46
DiscreteLegPos GetDiscreteLegPos(const LegStateBit &leg_state, const int leg_index)
脚状態を取得する.
enums::DiscreteComPos GetDiscreteComPos(const LegStateBit &leg_state)
現在の脚状態から重心パターンを取得する.
std::string FloatingPointNumToString(const T num, const int digit=kDigit, const int width=kWidth)
小数を文字列に変換する関数. C++ では C のフォーマットのように %3.3f とかで小数を文字列に変換できないため自作する.
Definition math_util.h:152
constexpr T ConvertRadToDeg(const T rad) noexcept
角度を [rad]から [deg] に変換する関数.
Definition math_util.h:110
std::string EnumToStringRemoveTopK(const T &enum_value)
enumを文字列に変換する関数. Google C++ coding style だと enumの要素は 先頭にkをつけてキャメルケースで書くことが推奨されている....
Definition string_util.h:52
DiscreteLegPos
離散化された脚位置を表す列挙体. 先行研究では 1~7の int型の数値で表現されているが, 可読性を上げるために列挙体にした. 離散化された脚位置は 3bit (0 ~ 7)の範囲で表現される...
EulerXYZ ToEulerXYZ(const RotationMatrix3x3 &rot)
回転角行列からXYZオイラー角への変換.
Vector3 v
ベクトル成分.
float w
スカラー成分.
グラフ構造のためのノード(頂点).
std::array< Vector3, HexapodConst::kLegNum > leg_pos
[4 * 3 * 6 = 72 byte] 脚先の座標.(coxa(脚の付け根)を原点とする)
leg_func::LegStateBit leg_state
[4 byte] 脚状態,重心パターンを bitで表す.旧名 leg_con.
Vector3 center_of_mass_global_coord
[4 * 3 = 12byte] グローバル座標系における重心の位置.旧名 GCOM
Quaternion posture
[4 * 4 = 16byte] 姿勢を表すクォータニオン.
std::array< Vector3, HexapodConst::kLegNum > leg_reference_pos
int depth
[4 byte] 自身の深さ.一番上の親が深さ0となる.
float x
ロボットの正面方向に正.
float z
ロボットの上向きに正.
std::string ToString() const
このベクトルを文字列にして返す. (x, y, z) の形式,小数点以下3桁まで.
float y
ロボットの左向きに正.