GaitGeneration by Graph Search
読み取り中…
検索中…
一致する文字列を見つけられません
result_file_exporter.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 <algorithm>
11#include <filesystem>
12#include <format>
13#include <magic_enum.hpp>
14#include <map>
15
16#include "average_calculator.h"
17#include "cmdio_util.h"
18#include "map_file_exporter.h"
19#include "math_util.h"
20#include "stopwatch.h"
21
22namespace designlab {
23
24namespace sf =
25 ::std::filesystem; // 長すぎるので,filesystemの名前空間を短縮する.
26
27const std::string ResultFileConst::kDirectoryPath =
28 sf::current_path().string() + "\\" + "result";
29
30const std::string ResultFileConst::kLegDirectoryName = "leg_pos";
31
32const std::string ResultFileConst::kNodeListName = "node_list";
33
34const std::string ResultFileConst::kMapStateName = "map_state";
35
36const std::string ResultFileConst::kDetailFileName = "simulation_result_detail";
37
38const std::string ResultFileConst::kSuccessfulCount =
39 "simulation_successful_count";
40
41const std::string ResultFileConst::kLegAngleName = "leg_angle";
42
44 const std::shared_ptr<const IHexapodJointCalculator>& calculator_ptr,
45 const std::shared_ptr<const IHexapodCoordinateConverter>& converter_ptr)
46 : calculator_ptr_(calculator_ptr),
47 interpolated_node_creator_ptr_(
48 std::make_unique<InterpolatedNodeCreator>(converter_ptr)) {}
49
51 using enum OutputDetail;
52
53 // 結果出力先フォルダがなければ作成する.
54 if (!sf::exists(ResultFileConst::kDirectoryPath)) {
55 cmdio::OutputF(kInfo, "結果出力先フォルダ {} が存在しないので作成します.",
57
58 sf::create_directory(ResultFileConst::kDirectoryPath);
59 }
60}
61
63 const SimulationResultRecord& result) {
64 // 結果をセットする.
65 result_list_.push_back(result);
66}
67
69 using enum OutputDetail;
70
71 // 結果出力先フォルダがなければ終了する.
72 if (!sf::exists(ResultFileConst::kDirectoryPath)) {
73 cmdio::Output("出力先フォルダがないので終了します.", kError);
74 return;
75 }
76
77 // 出力先フォルダを作成する.
78 std::string output_folder_path = MakeOutputDirectory();
79
80 // NodeListを出力する.
81 ExportEachNodeList(output_folder_path);
82
83 // MapStateを出力する.
84 ExportEachMapState(output_folder_path);
85
86 // シミュレーション詳細を出力する.
87 ExportEachSimulationDetail(output_folder_path);
88
89 // シミュレーション全体の結果を出力する.
90 ExportSuccessfulCount(output_folder_path);
91
92 // 脚の位置を出力する.
93 ExportEachLegPos(output_folder_path);
94
95 // 脚の位置を出力する.
96 ExportAllLegPos(output_folder_path);
97
98 // 成功したシミュレーションのみを出力する.
99 ExportEachLegPosAllSuccessfulSimulation(output_folder_path);
100
101 // 成功したシミュレーションのみを出力する.
102 ExportAllLegPosAllSuccessfulSimulation(output_folder_path);
103
104 ExportAllLegAngle(output_folder_path);
105
106 cmdio::Output("結果の出力が完了しました.", kInfo);
107}
108
109std::string ResultFileExporter::MakeOutputDirectory() const {
110 using enum OutputDetail;
111
112 cmdio::Output("フォルダ名を入力してください.", kInfo);
113 const auto input_result = cmdio::InputDirName();
114
115 Stopwatch stopwatch;
116 const std::string folder_name =
117 std::format("{}\\{}_{}", ResultFileConst::kDirectoryPath, input_result,
118 stopwatch.GetNowTimeString());
119
120 // 指定されたフォルダを作成する.
121 if (!sf::exists(folder_name)) {
122 sf::create_directory(folder_name);
123 cmdio::Output(std::format("フォルダ {} を作成しました.", folder_name),
124 kInfo);
125 }
126
127 return folder_name;
128}
129
130void ResultFileExporter::ExportEachNodeList(const std::string& path) const {
131 using enum OutputDetail;
132
133 cmdio::Output("NodeListを出力します.", kInfo);
134
135 for (size_t i = 0; i < result_list_.size(); ++i) {
136 // 出力先ファイルを作成する.
137 std::string output_file_name = std::format(
138 "{}\\{}{}.csv", path, ResultFileConst::kNodeListName, i + 1);
139
140 std::ofstream ofs(output_file_name);
141
142 // ファイルが作成できなかった場合は,なにも出力しない.
143 if (!ofs) {
145 std::format("ファイル {} を作成できませんでした.", output_file_name),
146 kError);
147 return;
148 }
149
150 for (const auto& j : result_list_[i].graph_search_result_recorder) {
151 ofs << j.result_node << "\n"; // ノードを出力する.
152 }
153
154 ofs.close(); // ファイルを閉じる.
155
156 cmdio::Output("出力ファイル : " + output_file_name, kInfo);
157 }
158
159 cmdio::Output("NodeListの出力が完了しました.", kInfo);
160}
161
162void ResultFileExporter::ExportEachMapState(const std::string& path) const {
163 using enum designlab::OutputDetail;
164
165 cmdio::Output("MapStateを出力します.", kInfo);
166
167 for (size_t i = 0; i < result_list_.size(); ++i) {
168 // 出力先ファイルを作成する.
169 std::string output_file_name = std::format(
170 "{}\\{}{}.csv", path, ResultFileConst::kMapStateName, i + 1);
171
172 MapFileExporter map_file_exporter;
173
174 if (map_file_exporter.ExportMap(output_file_name,
175 result_list_[i].map_state)) {
176 cmdio::Output("出力ファイル : " + output_file_name, kInfo);
177 } else {
178 cmdio::Output("出力ファイル : " + output_file_name, kInfo);
179 }
180 }
181
182 cmdio::Output("MapStateの出力が完了しました.", kInfo);
183}
184
185void ResultFileExporter::ExportEachSimulationDetail(
186 const std::string& path) const {
187 using enum designlab::OutputDetail;
188
189 cmdio::Output("シミュレーション詳細を出力します.", kInfo);
190
191 for (size_t i = 0; i < result_list_.size(); ++i) {
192 // 出力先ファイルを作成する.
193 std::string output_file_name = std::format(
194 "{}\\{}{}.csv", path, ResultFileConst::kDetailFileName, i + 1);
195
196 std::ofstream ofs(output_file_name);
197
198 // ファイルが作成できなかった場合は,なにも出力しない.
199 if (!ofs) {
201 std::format("ファイル {} を作成できませんでした.", output_file_name),
202 kError);
203 return;
204 }
205
206 const SimulationResultRecord recorder = result_list_[i];
207
208 // 結果を出力する.
209 ofs << recorder.ToCsvString() << std::endl;
210
211 // 時間の統計を出力する.
212
213 double max_time = recorder.graph_search_result_recorder[1].computation_time;
214
215 double min_time = max_time;
216
217 AverageCalculator<double> average_calculator;
218
219 // 最初のノードは除く(計算時間0で固定のため)
220 if (recorder.graph_search_result_recorder.size() > 1) {
221 for (size_t j = 1; j < recorder.graph_search_result_recorder.size();
222 ++j) {
223 const double time =
224 recorder.graph_search_result_recorder[j].computation_time;
225
226 if (time > max_time) {
227 max_time = time;
228 }
229
230 if (time < min_time) {
231 min_time = time;
232 }
233
234 average_calculator.AddData(time);
235 }
236 }
237
238 ofs << "最大探索時間," << math_util::FloatingPointNumToString(max_time)
239 << ",[milli_sec]" << std::endl;
240
241 ofs << "最小探索時間," << math_util::FloatingPointNumToString(min_time)
242 << ",[milli_sec]" << std::endl;
243
244 ofs << "総合探索時間,"
246 average_calculator.GetSum().value_or(-1.f))
247 << ",[milli_sec]" << std::endl;
248
249 ofs << "平均探索時間,"
251 average_calculator.GetAverage().value_or(-1.f))
252 << ",[milli_sec]" << std::endl;
253
254 ofs << "分散,"
256 average_calculator.GetVariance().value_or(-1.f))
257 << ",[milli_sec^2]" << std::endl;
258
259 ofs << "標準偏差,"
261 average_calculator.GetStandardDeviation().value_or(-1.f))
262 << ",[milli_sec]" << std::endl;
263
264 const double time_1sigma_plus =
265 average_calculator.GetAverage().value_or(-1.f) +
266 average_calculator.GetStandardDeviation().value_or(-1.f);
267
268 const double time_1sigma_minus =
269 (std::max)(average_calculator.GetAverage().value_or(-1.f) -
270 average_calculator.GetStandardDeviation().value_or(-1.f),
271 0.0);
272
273 ofs << "全データの約68%は"
274 << math_util::FloatingPointNumToString(time_1sigma_plus)
275 << " [milli_sec]以下で"
276 << math_util::FloatingPointNumToString(time_1sigma_minus)
277 << " [milli_sec]以上です." << std::endl
278 << std::endl;
279
280 // 移動距離の統計を出力する.
281 if (recorder.graph_search_result_recorder.size() > 1) {
282 float x_move_sum = 0.0f;
283 float y_move_sum = 0.0f;
284 float z_move_sum = 0.0f;
285
286 for (size_t j = 0; j != recorder.graph_search_result_recorder.size() - 1;
287 ++j) {
288 RobotStateNode current_node =
289 recorder.graph_search_result_recorder[j].result_node;
290 RobotStateNode next_node =
291 recorder.graph_search_result_recorder[j + 1].result_node;
292 Vector3 com_dif = next_node.center_of_mass_global_coord -
293 current_node.center_of_mass_global_coord;
294
295 x_move_sum += com_dif.x;
296 y_move_sum += com_dif.y;
297 z_move_sum += com_dif.z;
298 }
299
300 const double x_move_average =
301 x_move_sum /
302 static_cast<double>(recorder.graph_search_result_recorder.size() - 1);
303
304 const double y_move_average =
305 y_move_sum /
306 static_cast<double>(recorder.graph_search_result_recorder.size() - 1);
307
308 const double z_move_average =
309 z_move_sum /
310 static_cast<double>(recorder.graph_search_result_recorder.size() - 1);
311
312 ofs << "X方向総移動距離,"
313 << math_util::FloatingPointNumToString(x_move_sum) << ",[mm]"
314 << std::endl;
315
316 ofs << "Y方向総移動距離,"
317 << math_util::FloatingPointNumToString(y_move_sum) << ",[mm]"
318 << std::endl;
319
320 ofs << "Z方向総移動距離,"
321 << math_util::FloatingPointNumToString(z_move_sum) << ",[mm]"
322 << std::endl;
323
324 ofs << "X方向平均移動距離,"
325 << math_util::FloatingPointNumToString(x_move_average) << ",[mm/動作]"
326 << std::endl;
327
328 ofs << "Y方向平均移動距離,"
329 << math_util::FloatingPointNumToString(y_move_average) << ",[mm/動作]"
330 << std::endl;
331
332 ofs << "Z方向平均移動距離,"
333 << math_util::FloatingPointNumToString(z_move_average) << ",[mm/動作]"
334 << std::endl;
335 }
336
337 ofs.close();
338
339 cmdio::Output("出力ファイル : " + output_file_name, kInfo);
340 }
341
342 cmdio::Output("シミュレーション詳細の出力が完了しました.", kInfo);
343}
344
345void ResultFileExporter::ExportSuccessfulCount(const std::string& path) const {
346 using enum designlab::OutputDetail;
348
350 std::format(
351 "シミュレーション全体の結果を出力します.シミュレーション数 : {}",
352 result_list_.size()),
353 kInfo);
354
355 // 出力先ファイルを作成する.
356 std::string output_file_name =
357 std::format("{}\\{}.csv", path, ResultFileConst::kSuccessfulCount);
358
359 std::ofstream ofs(output_file_name);
360
361 // ファイルが作成できなかった場合は,なにも出力しない.
362 if (!ofs) {
363 cmdio::Output(std::format("ファイルを作成できませんでした."), kError);
364 return;
365 }
366
367 ofs << "シミュレーション数, " << result_list_.size() << "\n";
368
369 // 成功したシミュレーション数を数える.
370 std::map<enums::SimulationResult, int> result_count;
371
372 for (const auto& i : result_list_) {
373 result_count[i.simulation_result]++;
374 }
375
376 // シミュレーション結果を出力する.
377 ofs << std::format("成功したシミュレーション数, {} \n",
378 result_count[kSuccess]);
379
380 ofs << std::format("失敗したシミュレーション数, {} \n",
381 result_count[kFailureByGraphSearch] +
382 result_count[kFailureByLoopMotion] +
383 result_count[kFailureByNodeLimitExceeded]);
384
385 ofs << std::format("グラフ探索に失敗, {} \n",
386 result_count[kFailureByGraphSearch]);
387
388 ofs << std::format("デッドロックに陥った, {} \n",
389 result_count[kFailureByLoopMotion]);
390
391 ofs << std::format("ノード数制限を超えた, {} \n",
392 result_count[kFailureByNodeLimitExceeded]);
393}
394
395void ResultFileExporter::ExportEachLegPos(const std::string& path) const {
396 // ディレクトリを作成する.
397 std::string leg_pos_dir_path =
399
400 if (!sf::exists(leg_pos_dir_path)) {
401 sf::create_directory(leg_pos_dir_path);
402 }
403
404 // ファイルを作成する.
405 for (size_t i = 0; i < result_list_.size(); ++i) {
406 for (int j = 0; j < HexapodConst::kLegNum; ++j) {
407 std::string output_file_name = std::format(
408 "{}\\simulation{}_leg{}.csv", leg_pos_dir_path, i + 1, j + 1);
409
410 std::ofstream ofs(output_file_name);
411
412 // ファイルが作成できなかった場合は,なにも出力しない.
413 if (!ofs) {
414 cmdio::Output(std::format("ファイル {} を作成できませんでした.",
415 output_file_name),
417 return;
418 }
419
420 ofs << GetHeader() << "\n";
421
422 std::optional<Vector3> past_pos;
423
424 for (const auto& recorder :
425 result_list_[i].graph_search_result_recorder) {
426 const Vector3 current_pos = recorder.result_node.leg_pos[j];
427
428 // 変化がない場合はスキップする.
429 if (past_pos.has_value() && current_pos == past_pos) {
430 continue;
431 }
432
433 // 高さが変化している and 平行に移動している場合は中継点も出力する.
434 if (past_pos.has_value() && current_pos.z != past_pos.value().z &&
435 current_pos.ProjectedXY() != past_pos.value().ProjectedXY()) {
436 if (current_pos.z < past_pos.value().z) {
437 const Vector3 relay_point =
438 Vector3(current_pos.x, current_pos.y, past_pos.value().z);
439 const auto joint_state =
440 calculator_ptr_->CalculateJointState(j, relay_point);
441
442 // 接地時.
443 ofs << current_pos.GetLength() << "," << past_pos.value().z
444 << ",true,g," << std::boolalpha
445 << calculator_ptr_->IsValidJointState(j, relay_point,
446 joint_state)
447 << "\n";
448 } else {
449 const Vector3 relay_point =
450 Vector3(past_pos.value().x, past_pos.value().y, current_pos.z);
451 const auto joint_state =
452 calculator_ptr_->CalculateJointState(j, relay_point);
453
454 // 遊脚時.
455 ofs << past_pos.value().GetLength() << "," << current_pos.z
456 << ",true,l," << std::boolalpha
457 << calculator_ptr_->IsValidJointState(j, relay_point,
458 joint_state)
459 << "\n";
460 }
461 }
462
463 const auto joint_state =
464 calculator_ptr_->CalculateJointState(j, current_pos);
465
466 ofs << current_pos.ProjectedXY().GetLength() << "," << current_pos.z
467 << ",false," << GetLegChangeStatus(past_pos, current_pos) << ","
468 << std::boolalpha
469 << calculator_ptr_->IsValidJointState(j, current_pos, joint_state)
470 << "\n";
471
472 past_pos = current_pos;
473 }
474 }
475 }
476}
477
478void ResultFileExporter::ExportAllLegPos(const std::string& path) const {
479 // ディレクトリを作成する.
480 std::string leg_pos_dir_path =
482
483 if (!sf::exists(leg_pos_dir_path)) {
484 sf::create_directory(leg_pos_dir_path);
485 }
486
487 for (size_t i = 0; i < result_list_.size(); ++i) {
488 std::string output_file_name =
489 std::format("{}\\simulation{}_all_leg.csv", leg_pos_dir_path, i + 1);
490
491 std::ofstream ofs(output_file_name);
492
493 // ファイルが作成できなかった場合は,なにも出力しない.
494 if (!ofs) {
496 std::format("ファイル {} を作成できませんでした.", output_file_name),
498 return;
499 }
500
501 ofs << GetHeader() << "\n";
502
503 for (int j = 0; j < HexapodConst::kLegNum; j++) {
504 std::optional<Vector3> past_pos;
505
506 for (const auto& recorder :
507 result_list_[i].graph_search_result_recorder) {
508 const Vector3 current_pos = recorder.result_node.leg_pos[j];
509
510 // 変化がない場合はスキップする.
511 if (past_pos.has_value() && current_pos == past_pos) {
512 continue;
513 }
514
515 // 高さが変化している and 平行に移動している場合は中継点も出力する.
516 if (past_pos.has_value() && current_pos.z != past_pos.value().z &&
517 current_pos.ProjectedXY() != past_pos.value().ProjectedXY()) {
518 if (current_pos.z < past_pos.value().z) {
519 const Vector3 relay_point =
520 Vector3(current_pos.x, current_pos.y, past_pos.value().z);
521 const auto joint_state =
522 calculator_ptr_->CalculateJointState(j, relay_point);
523
524 // 接地時
525 ofs << current_pos.GetLength() << "," << past_pos.value().z
526 << ",true,g," << std::boolalpha
527 << calculator_ptr_->IsValidJointState(j, relay_point,
528 joint_state)
529 << "," << j << "\n";
530 } else {
531 const Vector3 relay_point =
532 Vector3(past_pos.value().x, past_pos.value().y, current_pos.z);
533 const auto joint_state =
534 calculator_ptr_->CalculateJointState(j, relay_point);
535
536 // 遊脚時
537 ofs << past_pos.value().GetLength() << "," << current_pos.z
538 << ",true,l," << std::boolalpha
539 << calculator_ptr_->IsValidJointState(j, relay_point,
540 joint_state)
541 << "," << j << "\n";
542 }
543 }
544
545 const auto joint_state =
546 calculator_ptr_->CalculateJointState(j, current_pos);
547
548 ofs << current_pos.ProjectedXY().GetLength() << "," << current_pos.z
549 << ",false," << GetLegChangeStatus(past_pos, current_pos) << ","
550 << std::boolalpha
551 << calculator_ptr_->IsValidJointState(j, current_pos, joint_state)
552 << "," << j << "\n";
553
554 past_pos = current_pos;
555 }
556 }
557 }
558}
559
560void ResultFileExporter::ExportEachLegPosAllSuccessfulSimulation(
561 const std::string& path) const {
562 // ディレクトリを作成する.
563 std::string leg_pos_dir_path =
565
566 if (!sf::exists(leg_pos_dir_path)) {
567 sf::create_directory(leg_pos_dir_path);
568 }
569
570 // ファイルを作成する.
571
572 for (int j = 0; j < HexapodConst::kLegNum; ++j) {
573 std::string output_file_name =
574 std::format("{}\\all_simulation_leg{}.csv", leg_pos_dir_path, j + 1);
575
576 std::ofstream ofs(output_file_name);
577
578 // ファイルが作成できなかった場合は,なにも出力しない.
579 if (!ofs) {
581 std::format("ファイル {} を作成できませんでした.", output_file_name),
583 return;
584 }
585
586 ofs << GetHeader() << "\n";
587
588 for (int k = 0; k < result_list_.size(); ++k) {
589 if (result_list_[k].simulation_result !=
591 continue;
592 }
593
594 std::optional<Vector3> past_pos;
595
596 for (const auto& recorder :
597 result_list_[k].graph_search_result_recorder) {
598 const Vector3 current_pos = recorder.result_node.leg_pos[j];
599
600 // 変化がない場合はスキップする.
601 if (past_pos.has_value() && current_pos == past_pos) {
602 continue;
603 }
604
605 // 高さが変化している and 平行に移動している場合は中継点も出力する.
606 if (past_pos.has_value() && current_pos.z != past_pos.value().z &&
607 current_pos.ProjectedXY() != past_pos.value().ProjectedXY()) {
608 if (current_pos.z < past_pos.value().z) {
609 const Vector3 relay_point = {current_pos.x, current_pos.y,
610 past_pos.value().z};
611
612 const auto joint_state =
613 calculator_ptr_->CalculateJointState(j, relay_point);
614
615 // 接地時
616 ofs << current_pos.GetLength() << "," << past_pos.value().z
617 << ",true,g," << std::boolalpha
618 << calculator_ptr_->IsValidJointState(j, relay_point,
619 joint_state)
620 << "\n";
621 } else {
622 const Vector3 relay_point =
623 Vector3(past_pos.value().x, past_pos.value().y, current_pos.z);
624 const auto joint_state =
625 calculator_ptr_->CalculateJointState(j, relay_point);
626
627 // 遊脚時
628 ofs << past_pos.value().GetLength() << "," << current_pos.z
629 << ",true,l," << std::boolalpha
630 << calculator_ptr_->IsValidJointState(j, relay_point,
631 joint_state)
632 << "\n";
633 }
634 }
635
636 const auto joint_state =
637 calculator_ptr_->CalculateJointState(j, current_pos);
638
639 ofs << current_pos.ProjectedXY().GetLength() << "," << current_pos.z
640 << ",false," << GetLegChangeStatus(past_pos, current_pos) << ","
641 << std::boolalpha
642 << calculator_ptr_->IsValidJointState(j, current_pos, joint_state)
643 << "\n";
644
645 past_pos = current_pos;
646 }
647 }
648
649 ofs.close();
650 }
651}
652
653void ResultFileExporter::ExportAllLegPosAllSuccessfulSimulation(
654 const std::string& path) const {
655 using enum designlab::OutputDetail;
657
658 // ディレクトリを作成する.
659 std::string leg_pos_dir_path =
661
662 if (!sf::exists(leg_pos_dir_path)) {
663 sf::create_directory(leg_pos_dir_path);
664 }
665
666 std::string output_file_name =
667 std::format("{}\\all_simulation_all_leg.csv", leg_pos_dir_path);
668
669 std::ofstream ofs(output_file_name);
670
671 // ファイルが作成できなかった場合は,なにも出力しない.
672 if (!ofs) {
673 cmdio::OutputF(kError, "ファイル {} を作成できませんでした.",
674 output_file_name);
675 return;
676 }
677
678 ofs << GetHeader() << "\n";
679
680 for (int j = 0; j < HexapodConst::kLegNum; ++j) {
681 for (int k = 0; k < result_list_.size(); ++k) {
682 if (result_list_[k].simulation_result != kSuccess) {
683 continue;
684 }
685
686 for (int l = 0; l < HexapodConst::kLegNum; ++l) {
687 std::optional<Vector3> past_pos;
688
689 for (const auto& recorder :
690 result_list_[k].graph_search_result_recorder) {
691 const Vector3 current_pos = recorder.result_node.leg_pos[l];
692
693 // 変化がない場合はスキップする.
694 if (past_pos.has_value() && current_pos == past_pos) {
695 continue;
696 }
697
698 // 高さが変化している and 平行に移動している場合は中継点も出力する.
699 if (past_pos.has_value() && current_pos.z != past_pos.value().z &&
700 current_pos.ProjectedXY() != past_pos.value().ProjectedXY()) {
701 if (current_pos.z < past_pos.value().z) {
702 const Vector3 relay_point =
703 Vector3(current_pos.x, current_pos.y, past_pos.value().z);
704 const auto joint_state =
705 calculator_ptr_->CalculateJointState(l, relay_point);
706
707 // 接地時
708 ofs << current_pos.GetLength() << "," << past_pos.value().z
709 << ",true,g," << std::boolalpha
710 << calculator_ptr_->IsValidJointState(l, relay_point,
711 joint_state)
712 << "," << l << "\n";
713 } else {
714 const Vector3 relay_point = Vector3(
715 past_pos.value().x, past_pos.value().y, current_pos.z);
716 const auto joint_state =
717 calculator_ptr_->CalculateJointState(l, relay_point);
718
719 // 遊脚時
720 ofs << past_pos.value().GetLength() << "," << current_pos.z
721 << ",true,l," << std::boolalpha
722 << calculator_ptr_->IsValidJointState(l, relay_point,
723 joint_state)
724 << "," << l << "\n";
725 }
726 }
727
728 const auto joint_state =
729 calculator_ptr_->CalculateJointState(l, current_pos);
730
731 ofs << current_pos.ProjectedXY().GetLength() << "," << current_pos.z
732 << ",false," << GetLegChangeStatus(past_pos, current_pos) << ","
733 << std::boolalpha
734 << calculator_ptr_->IsValidJointState(l, current_pos, joint_state)
735 << "," << l << "\n";
736
737 past_pos = current_pos;
738 }
739 }
740 }
741
742 ofs.close();
743 }
744}
745
746void ResultFileExporter::ExportAllLegAngle(const std::string& path) const {
747 cmdio::InfoOutput("Outputs leg angles.");
748
749 for (int i = 0; i < result_list_.size(); ++i) {
750 std::ofstream ofs(std::format("{}\\{}{}.csv", path,
752
753 if (!ofs) {
754 cmdio::ErrorOutput("Failed to create file.");
755 return;
756 }
757
758 for (int j = 0; j < result_list_[i].graph_search_result_recorder.size() - 1;
759 ++j) {
760 const auto& current =
761 result_list_[i].graph_search_result_recorder[j].result_node;
762 const auto& next =
763 result_list_[i].graph_search_result_recorder[j + 1].result_node;
764
765 // 補間ノードを作成する.
766 const auto interpolated_nodes =
767 interpolated_node_creator_ptr_->CreateInterpolatedNode(current, next);
768
769 for (const auto& node : interpolated_nodes) {
770 // 逆運動学で関節角度を計算する.
771 const auto joint_states = calculator_ptr_->CalculateAllJointState(node);
772
773 for (int k = 0; k < HexapodConst::kLegNum; ++k) {
774 for (int l = 0; l < joint_states[k].joint_angle.size(); ++l) {
775 ofs << math_util::ConvertRadToDeg(joint_states[k].joint_angle[l])
776 << ",";
777 }
778 }
779
780 ofs << "\n";
781 }
782 }
783 }
784}
785
786std::string ResultFileExporter::GetHeader() const {
787 return "x,z,relay,string,error,index";
788}
789
790std::string ResultFileExporter::GetLegChangeStatus(
791 const std::optional<Vector3>& past, const Vector3& current) const {
792 if (!past.has_value()) {
793 return "f";
794 }
795
796 if (math_util::IsEqual(past.value().z, current.z)) {
797 return "b";
798 }
799
800 if (past.value().z > current.z) {
801 return "g";
802 }
803
804 if (past.value().z < current.z) {
805 return "l";
806 }
807
808 return "o";
809}
810
811} // namespace designlab
static constexpr int kLegNum
矩形軌道を生成し,ノード間を補間するクラス.
static const std::string kLegDirectoryName
出力先ディレクトリ(フォルダ)名.
static const std::string kNodeListName
ノードリストのファイル名 (プログラムの読み込み用)
static const std::string kDetailFileName
ファイル名 ( 人間が見る用 )
static const std::string kSuccessfulCount
シミュレーションの成功回数をまとめたファイルの名前.
static const std::string kDirectoryPath
出力先ディレクトリ(フォルダ)名.
static const std::string kMapStateName
マップ状態のファイル名 (プログラムの読み込み用)
static const std::string kLegAngleName
角度データのファイル名 (人間が見る用)
ResultFileExporter(const std::shared_ptr< const IHexapodJointCalculator > &calculator_ptr, const std::shared_ptr< const IHexapodCoordinateConverter > &converter_ptr)
void PushSimulationResult(const SimulationResultRecord &simulation_result)
シミュレーション結果を追加する.
void Export() const
結果をファイルに出力する.
void CreateRootDirectory()
resultフォルダがなければ作成する.また,フォルダ名を指定する.
時間計測用のクラス.
Definition stopwatch.h:29
std::string GetNowTimeString() const
現在の日時をYYYY/MM/DD HH:MM形式の文字列で取得する.
Definition stopwatch.cpp:76
void OutputF(OutputDetail detail, const std::format_string< Args... > str, Args &&... args)
コマンドラインに文字を出力する関数, format した文字列を出力する. SetOutputLimit() で設定した出力の許可範囲内であれば出力される.
Definition cmdio_util.h:91
void ErrorOutput(const std::string &str)
コマンドラインに文字を出力する関数. Error 用の出力.
Definition cmdio_util.h:69
std::string InputDirName(const std::string &str="Enter a directory name. (Japanese is not recommended).")
ディレクトリ名を入力させる関数. 出力される文字列は,必ず OutputDetail::kSystem で出力される. ディレクトリ名には次の文字は使えない....
void InfoOutput(const std::string &str)
コマンドラインに文字を出力する関数. Info 用の出力.
Definition cmdio_util.h:57
void Output(const std::string &str, OutputDetail detail)
コマンドラインに文字を出力する関数. SetOutputLimit 関数で設定した出力の許可範囲内であれば出力される.
SimulationResult
シミュレーション全体の結果を表す列挙型.
@ kSuccess
目標座標,姿勢を満たし,シミュレーションに成功した.
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
constexpr bool IsEqual(const T num1, const T num2) noexcept
C++において,小数同士の計算は誤差が出てしまう. 誤差込みで値が等しいか調べる.
Definition math_util.h:42
OutputDetail
コマンドラインに文字を出力する際に,その詳細を指定するための列挙体.
@ kInfo
優先度低めの情報.
@ kError
エラーメッセージ.
Definition com_type.h:21
シミュレーションの結果を格納する構造体.