16#include <magic_enum.hpp>
28namespace sf = ::std::filesystem;
32sf::current_path().string() +
"\\" +
"result";
46 const std::shared_ptr<const IHexapodJointCalculator>& calculator_ptr) :
47 calculator_ptr_(calculator_ptr)
60 "結果出力先フォルダ {} が存在しないので作成します.",
70 result_list_.push_back(result);
85 std::string output_folder_path = MakeOutputDirectory();
88 ExportEachNodeList(output_folder_path);
91 ExportEachMapState(output_folder_path);
94 ExportEachSimulationDetail(output_folder_path);
97 ExportSuccessfulCount(output_folder_path);
100 ExportEachLegPos(output_folder_path);
103 ExportAllLegPos(output_folder_path);
106 ExportEachLegPosAllSuccessfulSimulation(output_folder_path);
109 ExportAllLegPosAllSuccessfulSimulation(output_folder_path);
115std::string ResultFileExporter::MakeOutputDirectory()
const
123 const std::string folder_name = std::format(
"{}\\{}_{}",
128 if (!sf::exists(folder_name))
130 sf::create_directory(folder_name);
137void ResultFileExporter::ExportEachNodeList(
const std::string& path)
const
143 for (
size_t i = 0; i < result_list_.size(); ++i)
148 std::ofstream ofs(output_file_name);
157 for (
const auto& j : result_list_[i].graph_search_result_recorder)
159 ofs << j.result_node <<
"\n";
170void ResultFileExporter::ExportEachMapState(
const std::string& path)
const
176 for (
size_t i = 0; i < result_list_.size(); ++i)
181 MapFileExporter map_file_exporter;
183 if (map_file_exporter.ExportMap(output_file_name, result_list_[i].map_state))
196void ResultFileExporter::ExportEachSimulationDetail(
const std::string& path)
const
202 for (
size_t i = 0; i < result_list_.size(); ++i)
207 std::ofstream ofs(output_file_name);
216 const SimulationResultRecord recorder = result_list_[i];
219 ofs << recorder.ToCsvString() << std::endl;
224 double max_time = recorder.graph_search_result_recorder[1].computation_time;
226 double min_time = max_time;
228 AverageCalculator<double> average_calculator;
231 if (recorder.graph_search_result_recorder.size() > 1)
233 for (
size_t j = 1; j < recorder.graph_search_result_recorder.size(); ++j)
235 const double time = recorder.graph_search_result_recorder[j].computation_time;
237 if (time > max_time) { max_time = time; }
239 if (time < min_time) { min_time = time; }
241 average_calculator.AddData(time);
246 ",[milli_sec]" << std::endl;
249 ",[milli_sec]" << std::endl;
253 ",[milli_sec]" << std::endl;
257 ",[milli_sec]" << std::endl;
261 ",[milli_sec^2]" << std::endl;
264 average_calculator.GetStandardDeviation().value_or(-1.f)) <<
265 ",[milli_sec]" << std::endl;
268 const double time_1sigma_plus = average_calculator.GetAverage().value_or(-1.f) +
269 average_calculator.GetStandardDeviation().value_or(-1.f);
271 const double time_1sigma_minus = (std::max)(
272 average_calculator.GetAverage().value_or(-1.f) -
273 average_calculator.GetStandardDeviation().value_or(-1.f), 0.0);
275 ofs <<
"全データの約68%は" <<
278 std::endl << std::endl;
282 if (recorder.graph_search_result_recorder.size() > 1)
284 float x_move_sum = 0.0f;
285 float y_move_sum = 0.0f;
286 float z_move_sum = 0.0f;
288 for (
size_t j = 0; j != recorder.graph_search_result_recorder.size() - 1; ++j)
290 RobotStateNode current_node = recorder.graph_search_result_recorder[j].result_node;
291 RobotStateNode next_node = recorder.graph_search_result_recorder[j + 1].result_node;
292 Vector3 com_dif = next_node.center_of_mass_global_coord - current_node.center_of_mass_global_coord;
294 x_move_sum += com_dif.x;
295 y_move_sum += com_dif.y;
296 z_move_sum += com_dif.z;
299 const double x_move_average = x_move_sum /
300 static_cast<double>(recorder.graph_search_result_recorder.size() - 1);
302 const double y_move_average = y_move_sum /
303 static_cast<double>(recorder.graph_search_result_recorder.size() - 1);
305 const double z_move_average = z_move_sum /
306 static_cast<double>(recorder.graph_search_result_recorder.size() - 1);
309 ",[mm]" << std::endl;
312 ",[mm]" << std::endl;
315 ",[mm]" << std::endl;
318 ",[mm/動作]" << std::endl;
321 ",[mm/動作]" << std::endl;
324 ",[mm/動作]" << std::endl;
335void ResultFileExporter::ExportSuccessfulCount(
const std::string& path)
const
345 std::ofstream ofs(output_file_name);
354 ofs <<
"シミュレーション数, " << result_list_.size() <<
"\n";
357 std::map<enums::SimulationResult, int> result_count;
359 for (
const auto& i : result_list_)
361 result_count[i.simulation_result]++;
365 ofs << std::format(
"成功したシミュレーション数, {} \n", result_count[kSuccess]);
367 ofs << std::format(
"失敗したシミュレーション数, {} \n", result_count[kFailureByGraphSearch] + result_count[kFailureByLoopMotion] + result_count[kFailureByNodeLimitExceeded]);
369 ofs << std::format(
"グラフ探索に失敗, {} \n", result_count[kFailureByGraphSearch]);
371 ofs << std::format(
"デッドロックに陥った, {} \n", result_count[kFailureByLoopMotion]);
373 ofs << std::format(
"ノード数制限を超えた, {} \n", result_count[kFailureByNodeLimitExceeded]);
376void ResultFileExporter::ExportEachLegPos(
const std::string& path)
const
381 if (!sf::exists(leg_pos_dir_path))
383 sf::create_directory(leg_pos_dir_path);
387 for (
size_t i = 0; i < result_list_.size(); ++i)
391 std::string output_file_name = std::format(
"{}\\simulation{}_leg{}.csv", leg_pos_dir_path, i + 1, j + 1);
393 std::ofstream ofs(output_file_name);
402 ofs << GetHeader() <<
"\n";
404 std::optional<Vector3> past_pos;
406 for (
const auto& recorder : result_list_[i].graph_search_result_recorder)
408 const Vector3 current_pos = recorder.result_node.leg_pos[j];
411 if (past_pos.has_value() && current_pos == past_pos) {
continue; }
414 if (past_pos.has_value() &&
415 current_pos.z != past_pos.value().z &&
416 current_pos.ProjectedXY() != past_pos.value().ProjectedXY())
418 if (current_pos.z < past_pos.value().z)
420 const Vector3 relay_point = Vector3(current_pos.x, current_pos.y, past_pos.value().z);
421 const auto joint_state = calculator_ptr_->CalculateJointState(j, relay_point);
424 ofs << current_pos.GetLength() <<
"," << past_pos.value().z <<
",true,g," << std::boolalpha << calculator_ptr_->IsValidJointState(j, relay_point, joint_state) <<
"\n";
428 const Vector3 relay_point = Vector3(past_pos.value().x, past_pos.value().y, current_pos.z);
429 const auto joint_state = calculator_ptr_->CalculateJointState(j, relay_point);
432 ofs << past_pos.value().GetLength() <<
"," << current_pos.z <<
",true,l," << std::boolalpha << calculator_ptr_->IsValidJointState(j, relay_point, joint_state) <<
"\n";
436 const auto joint_state = calculator_ptr_->CalculateJointState(j, current_pos);
438 ofs << current_pos.ProjectedXY().GetLength() <<
"," <<
439 current_pos.z <<
",false," << GetLegChangeStatus(past_pos, current_pos) <<
"," <<
440 std::boolalpha << calculator_ptr_->IsValidJointState(j, current_pos, joint_state) <<
"\n";
442 past_pos = current_pos;
448void ResultFileExporter::ExportAllLegPos(
const std::string& path)
const
453 if (!sf::exists(leg_pos_dir_path))
455 sf::create_directory(leg_pos_dir_path);
458 for (
size_t i = 0; i < result_list_.size(); ++i)
460 std::string output_file_name = std::format(
"{}\\simulation{}_all_leg.csv", leg_pos_dir_path, i + 1);
462 std::ofstream ofs(output_file_name);
471 ofs << GetHeader() <<
"\n";
475 std::optional<Vector3> past_pos;
477 for (
const auto& recorder : result_list_[i].graph_search_result_recorder)
479 const Vector3 current_pos = recorder.result_node.leg_pos[j];
482 if (past_pos.has_value() && current_pos == past_pos) {
continue; }
485 if (past_pos.has_value() &&
486 current_pos.z != past_pos.value().z &&
487 current_pos.ProjectedXY() != past_pos.value().ProjectedXY())
489 if (current_pos.z < past_pos.value().z)
491 const Vector3 relay_point = Vector3(current_pos.x, current_pos.y, past_pos.value().z);
492 const auto joint_state = calculator_ptr_->CalculateJointState(j, relay_point);
495 ofs << current_pos.GetLength() <<
"," << past_pos.value().z <<
",true,g," <<
496 std::boolalpha << calculator_ptr_->IsValidJointState(j, relay_point, joint_state) <<
501 const Vector3 relay_point = Vector3(past_pos.value().x, past_pos.value().y, current_pos.z);
502 const auto joint_state = calculator_ptr_->CalculateJointState(j, relay_point);
505 ofs << past_pos.value().GetLength() <<
"," << current_pos.z <<
",true,l," <<
506 std::boolalpha << calculator_ptr_->IsValidJointState(j, relay_point, joint_state) <<
511 const auto joint_state = calculator_ptr_->CalculateJointState(j, current_pos);
513 ofs << current_pos.ProjectedXY().GetLength() <<
"," << current_pos.z <<
",false," <<
514 GetLegChangeStatus(past_pos, current_pos) <<
"," <<
515 std::boolalpha << calculator_ptr_->IsValidJointState(j, current_pos, joint_state) <<
518 past_pos = current_pos;
524void ResultFileExporter::ExportEachLegPosAllSuccessfulSimulation(
const std::string& path)
const
529 if (!sf::exists(leg_pos_dir_path))
531 sf::create_directory(leg_pos_dir_path);
538 std::string output_file_name = std::format(
"{}\\all_simulation_leg{}.csv", leg_pos_dir_path, j + 1);
540 std::ofstream ofs(output_file_name);
549 ofs << GetHeader() <<
"\n";
551 for (
int k = 0; k < result_list_.size(); ++k)
555 std::optional<Vector3> past_pos;
557 for (
const auto& recorder : result_list_[k].graph_search_result_recorder)
559 const Vector3 current_pos = recorder.result_node.leg_pos[j];
562 if (past_pos.has_value() && current_pos == past_pos) {
continue; }
565 if (past_pos.has_value() &&
566 current_pos.z != past_pos.value().z &&
567 current_pos.ProjectedXY() != past_pos.value().ProjectedXY())
569 if (current_pos.z < past_pos.value().z)
571 const Vector3 relay_point = { current_pos.x, current_pos.y, past_pos.value().z };
573 const auto joint_state = calculator_ptr_->CalculateJointState(j, relay_point);
576 ofs << current_pos.GetLength() <<
"," << past_pos.value().z <<
",true,g," << std::boolalpha << calculator_ptr_->IsValidJointState(j, relay_point, joint_state) <<
"\n";
580 const Vector3 relay_point = Vector3(past_pos.value().x, past_pos.value().y, current_pos.z);
581 const auto joint_state = calculator_ptr_->CalculateJointState(j, relay_point);
584 ofs << past_pos.value().GetLength() <<
"," << current_pos.z <<
",true,l," << std::boolalpha << calculator_ptr_->IsValidJointState(j, relay_point, joint_state) <<
"\n";
588 const auto joint_state = calculator_ptr_->CalculateJointState(j, current_pos);
590 ofs << current_pos.ProjectedXY().GetLength() <<
"," <<
591 current_pos.z <<
",false," << GetLegChangeStatus(past_pos, current_pos) <<
"," <<
592 std::boolalpha << calculator_ptr_->IsValidJointState(j, current_pos, joint_state) <<
"\n";
594 past_pos = current_pos;
602void ResultFileExporter::ExportAllLegPosAllSuccessfulSimulation(
603 const std::string& path)
const
611 if (!sf::exists(leg_pos_dir_path))
613 sf::create_directory(leg_pos_dir_path);
616 std::string output_file_name =
617 std::format(
"{}\\all_simulation_all_leg.csv", leg_pos_dir_path);
619 std::ofstream ofs(output_file_name);
625 "ファイル {} を作成できませんでした.", output_file_name);
629 ofs << GetHeader() <<
"\n";
633 for (
int k = 0; k < result_list_.size(); ++k)
635 if (result_list_[k].simulation_result != kSuccess) {
continue; }
639 std::optional<Vector3> past_pos;
641 for (
const auto& recorder : result_list_[k].graph_search_result_recorder)
643 const Vector3 current_pos = recorder.result_node.leg_pos[l];
646 if (past_pos.has_value() && current_pos == past_pos) {
continue; }
649 if (past_pos.has_value() &&
650 current_pos.z != past_pos.value().z &&
651 current_pos.ProjectedXY() != past_pos.value().ProjectedXY())
653 if (current_pos.z < past_pos.value().z)
655 const Vector3 relay_point = Vector3(current_pos.x, current_pos.y, past_pos.value().z);
656 const auto joint_state = calculator_ptr_->CalculateJointState(l, relay_point);
659 ofs << current_pos.GetLength() <<
"," << past_pos.value().z <<
",true,g," << std::boolalpha << calculator_ptr_->IsValidJointState(l, relay_point, joint_state) <<
"," << l <<
"\n";
663 const Vector3 relay_point = Vector3(past_pos.value().x, past_pos.value().y, current_pos.z);
664 const auto joint_state = calculator_ptr_->CalculateJointState(l, relay_point);
667 ofs << past_pos.value().GetLength() <<
"," << current_pos.z <<
",true,l," << std::boolalpha << calculator_ptr_->IsValidJointState(l, relay_point, joint_state) <<
"," << l <<
"\n";
671 const auto joint_state = calculator_ptr_->CalculateJointState(l, current_pos);
673 ofs << current_pos.ProjectedXY().GetLength() <<
"," << current_pos.z <<
",false," << GetLegChangeStatus(past_pos, current_pos) <<
"," << std::boolalpha << calculator_ptr_->IsValidJointState(l, current_pos, joint_state) <<
"," << l <<
"\n";
675 past_pos = current_pos;
684std::string ResultFileExporter::GetHeader()
const
686 return "x,z,relay,string,error,index";
689std::string ResultFileExporter::GetLegChangeStatus(
const std::optional<Vector3>& past,
const Vector3& current)
const
691 if (!past.has_value()) {
return "f"; }
695 if (past.value().z > current.z) {
return "g"; }
697 if (past.value().z < current.z) {
return "l"; }
static std::string InputDirName(const std::string &str="Enter a directory name. (Japanese is not recommended).")
ディレクトリ名を入力させる関数. 出力される文字列は,必ず OutputDetail::kSystem で出力される. ディレクトリ名には次の文字は使えない. \ / : * ?...
static void Output(const std::string &str, OutputDetail detail)
コマンドラインに文字を出力する関数. SetOutputLimit() で設定した出力の許可範囲内であれば出力される. 必ず SetOutputLimit() を呼び出してから使うこと.
static void FormatOutput(OutputDetail detail, const std::format_string< Args... > str, Args &&... args)
コマンドラインに文字を出力する関数. SetOutputLimit() で設定した出力の許可範囲内であれば出力される. 必ず SetOutputLimit() を呼び出してから使うこと.
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
マップ状態のファイル名 (プログラムの読み込み用)
void PushSimulationResult(const SimulationResultRecord &simulation_result)
シミュレーション結果を追加する.
ResultFileExporter(const std::shared_ptr< const IHexapodJointCalculator > &calculator_ptr)
void Export() const
結果をファイルに出力する.
void CreateRootDirectory()
resultフォルダがなければ作成する.また,フォルダ名を指定する.
std::string GetNowTimeString() const
現在の日時をYYYY/MM/DD HH:MM形式の文字列で取得する.
SimulationResult
シミュレーション全体の結果を表す列挙型.
@ kSuccess
目標座標,姿勢を満たし,シミュレーションに成功した.
std::string FloatingPointNumToString(const T num, const int digit=kDigit, const int width=kWidth)
小数を文字列に変換する関数. C++ では C のフォーマットのように %3.3f とかで小数を文字列に変換できないため自作する.
constexpr bool IsEqual(const T num1, const T num2) noexcept
C++において,小数同士の計算は誤差が出てしまう. 誤差込みで値が等しいか調べる.
OutputDetail
コマンドラインに文字を出力する際に,その詳細を指定するための列挙体.