13#include <magic_enum.hpp>
28 sf::current_path().string() +
"\\" +
"result";
39 "simulation_successful_count";
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_(
65 result_list_.push_back(result);
78 std::string output_folder_path = MakeOutputDirectory();
81 ExportEachNodeList(output_folder_path);
84 ExportEachMapState(output_folder_path);
87 ExportEachSimulationDetail(output_folder_path);
90 ExportSuccessfulCount(output_folder_path);
93 ExportEachLegPos(output_folder_path);
96 ExportAllLegPos(output_folder_path);
99 ExportEachLegPosAllSuccessfulSimulation(output_folder_path);
102 ExportAllLegPosAllSuccessfulSimulation(output_folder_path);
104 ExportAllLegAngle(output_folder_path);
109std::string ResultFileExporter::MakeOutputDirectory()
const {
116 const std::string folder_name =
121 if (!sf::exists(folder_name)) {
122 sf::create_directory(folder_name);
130void ResultFileExporter::ExportEachNodeList(
const std::string& path)
const {
135 for (
size_t i = 0; i < result_list_.size(); ++i) {
137 std::string output_file_name = std::format(
140 std::ofstream ofs(output_file_name);
145 std::format(
"ファイル {} を作成できませんでした.", output_file_name),
150 for (
const auto& j : result_list_[i].graph_search_result_recorder) {
151 ofs << j.result_node <<
"\n";
162void ResultFileExporter::ExportEachMapState(
const std::string& path)
const {
167 for (
size_t i = 0; i < result_list_.size(); ++i) {
169 std::string output_file_name = std::format(
172 MapFileExporter map_file_exporter;
174 if (map_file_exporter.ExportMap(output_file_name,
175 result_list_[i].map_state)) {
185void ResultFileExporter::ExportEachSimulationDetail(
186 const std::string& path)
const {
191 for (
size_t i = 0; i < result_list_.size(); ++i) {
193 std::string output_file_name = std::format(
196 std::ofstream ofs(output_file_name);
201 std::format(
"ファイル {} を作成できませんでした.", output_file_name),
206 const SimulationResultRecord recorder = result_list_[i];
209 ofs << recorder.ToCsvString() << std::endl;
213 double max_time = recorder.graph_search_result_recorder[1].computation_time;
215 double min_time = max_time;
217 AverageCalculator<double> average_calculator;
220 if (recorder.graph_search_result_recorder.size() > 1) {
221 for (
size_t j = 1; j < recorder.graph_search_result_recorder.size();
224 recorder.graph_search_result_recorder[j].computation_time;
226 if (time > max_time) {
230 if (time < min_time) {
234 average_calculator.AddData(time);
239 <<
",[milli_sec]" << std::endl;
242 <<
",[milli_sec]" << std::endl;
246 average_calculator.GetSum().value_or(-1.f))
247 <<
",[milli_sec]" << std::endl;
251 average_calculator.GetAverage().value_or(-1.f))
252 <<
",[milli_sec]" << std::endl;
256 average_calculator.GetVariance().value_or(-1.f))
257 <<
",[milli_sec^2]" << std::endl;
261 average_calculator.GetStandardDeviation().value_or(-1.f))
262 <<
",[milli_sec]" << std::endl;
264 const double time_1sigma_plus =
265 average_calculator.GetAverage().value_or(-1.f) +
266 average_calculator.GetStandardDeviation().value_or(-1.f);
268 const double time_1sigma_minus =
269 (std::max)(average_calculator.GetAverage().value_or(-1.f) -
270 average_calculator.GetStandardDeviation().value_or(-1.f),
277 <<
" [milli_sec]以上です." << std::endl
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;
286 for (
size_t j = 0; j != recorder.graph_search_result_recorder.size() - 1;
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;
295 x_move_sum += com_dif.x;
296 y_move_sum += com_dif.y;
297 z_move_sum += com_dif.z;
300 const double x_move_average =
302 static_cast<double>(recorder.graph_search_result_recorder.size() - 1);
304 const double y_move_average =
306 static_cast<double>(recorder.graph_search_result_recorder.size() - 1);
308 const double z_move_average =
310 static_cast<double>(recorder.graph_search_result_recorder.size() - 1);
345void ResultFileExporter::ExportSuccessfulCount(
const std::string& path)
const {
351 "シミュレーション全体の結果を出力します.シミュレーション数 : {}",
352 result_list_.size()),
356 std::string output_file_name =
359 std::ofstream ofs(output_file_name);
367 ofs <<
"シミュレーション数, " << result_list_.size() <<
"\n";
370 std::map<enums::SimulationResult, int> result_count;
372 for (
const auto& i : result_list_) {
373 result_count[i.simulation_result]++;
377 ofs << std::format(
"成功したシミュレーション数, {} \n",
378 result_count[kSuccess]);
380 ofs << std::format(
"失敗したシミュレーション数, {} \n",
381 result_count[kFailureByGraphSearch] +
382 result_count[kFailureByLoopMotion] +
383 result_count[kFailureByNodeLimitExceeded]);
385 ofs << std::format(
"グラフ探索に失敗, {} \n",
386 result_count[kFailureByGraphSearch]);
388 ofs << std::format(
"デッドロックに陥った, {} \n",
389 result_count[kFailureByLoopMotion]);
391 ofs << std::format(
"ノード数制限を超えた, {} \n",
392 result_count[kFailureByNodeLimitExceeded]);
395void ResultFileExporter::ExportEachLegPos(
const std::string& path)
const {
397 std::string leg_pos_dir_path =
400 if (!sf::exists(leg_pos_dir_path)) {
401 sf::create_directory(leg_pos_dir_path);
405 for (
size_t i = 0; i < result_list_.size(); ++i) {
407 std::string output_file_name = std::format(
408 "{}\\simulation{}_leg{}.csv", leg_pos_dir_path, i + 1, j + 1);
410 std::ofstream ofs(output_file_name);
420 ofs << GetHeader() <<
"\n";
422 std::optional<Vector3> past_pos;
424 for (
const auto& recorder :
425 result_list_[i].graph_search_result_recorder) {
426 const Vector3 current_pos = recorder.result_node.leg_pos[j];
429 if (past_pos.has_value() && current_pos == past_pos) {
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);
443 ofs << current_pos.GetLength() <<
"," << past_pos.value().z
444 <<
",true,g," << std::boolalpha
445 << calculator_ptr_->IsValidJointState(j, relay_point,
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);
455 ofs << past_pos.value().GetLength() <<
"," << current_pos.z
456 <<
",true,l," << std::boolalpha
457 << calculator_ptr_->IsValidJointState(j, relay_point,
463 const auto joint_state =
464 calculator_ptr_->CalculateJointState(j, current_pos);
466 ofs << current_pos.ProjectedXY().GetLength() <<
"," << current_pos.z
467 <<
",false," << GetLegChangeStatus(past_pos, current_pos) <<
","
469 << calculator_ptr_->IsValidJointState(j, current_pos, joint_state)
472 past_pos = current_pos;
478void ResultFileExporter::ExportAllLegPos(
const std::string& path)
const {
480 std::string leg_pos_dir_path =
483 if (!sf::exists(leg_pos_dir_path)) {
484 sf::create_directory(leg_pos_dir_path);
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);
491 std::ofstream ofs(output_file_name);
496 std::format(
"ファイル {} を作成できませんでした.", output_file_name),
501 ofs << GetHeader() <<
"\n";
504 std::optional<Vector3> past_pos;
506 for (
const auto& recorder :
507 result_list_[i].graph_search_result_recorder) {
508 const Vector3 current_pos = recorder.result_node.leg_pos[j];
511 if (past_pos.has_value() && current_pos == past_pos) {
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);
525 ofs << current_pos.GetLength() <<
"," << past_pos.value().z
526 <<
",true,g," << std::boolalpha
527 << calculator_ptr_->IsValidJointState(j, relay_point,
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);
537 ofs << past_pos.value().GetLength() <<
"," << current_pos.z
538 <<
",true,l," << std::boolalpha
539 << calculator_ptr_->IsValidJointState(j, relay_point,
545 const auto joint_state =
546 calculator_ptr_->CalculateJointState(j, current_pos);
548 ofs << current_pos.ProjectedXY().GetLength() <<
"," << current_pos.z
549 <<
",false," << GetLegChangeStatus(past_pos, current_pos) <<
","
551 << calculator_ptr_->IsValidJointState(j, current_pos, joint_state)
554 past_pos = current_pos;
560void ResultFileExporter::ExportEachLegPosAllSuccessfulSimulation(
561 const std::string& path)
const {
563 std::string leg_pos_dir_path =
566 if (!sf::exists(leg_pos_dir_path)) {
567 sf::create_directory(leg_pos_dir_path);
573 std::string output_file_name =
574 std::format(
"{}\\all_simulation_leg{}.csv", leg_pos_dir_path, j + 1);
576 std::ofstream ofs(output_file_name);
581 std::format(
"ファイル {} を作成できませんでした.", output_file_name),
586 ofs << GetHeader() <<
"\n";
588 for (
int k = 0; k < result_list_.size(); ++k) {
589 if (result_list_[k].simulation_result !=
594 std::optional<Vector3> past_pos;
596 for (
const auto& recorder :
597 result_list_[k].graph_search_result_recorder) {
598 const Vector3 current_pos = recorder.result_node.leg_pos[j];
601 if (past_pos.has_value() && current_pos == past_pos) {
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,
612 const auto joint_state =
613 calculator_ptr_->CalculateJointState(j, relay_point);
616 ofs << current_pos.GetLength() <<
"," << past_pos.value().z
617 <<
",true,g," << std::boolalpha
618 << calculator_ptr_->IsValidJointState(j, relay_point,
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);
628 ofs << past_pos.value().GetLength() <<
"," << current_pos.z
629 <<
",true,l," << std::boolalpha
630 << calculator_ptr_->IsValidJointState(j, relay_point,
636 const auto joint_state =
637 calculator_ptr_->CalculateJointState(j, current_pos);
639 ofs << current_pos.ProjectedXY().GetLength() <<
"," << current_pos.z
640 <<
",false," << GetLegChangeStatus(past_pos, current_pos) <<
","
642 << calculator_ptr_->IsValidJointState(j, current_pos, joint_state)
645 past_pos = current_pos;
653void ResultFileExporter::ExportAllLegPosAllSuccessfulSimulation(
654 const std::string& path)
const {
659 std::string leg_pos_dir_path =
662 if (!sf::exists(leg_pos_dir_path)) {
663 sf::create_directory(leg_pos_dir_path);
666 std::string output_file_name =
667 std::format(
"{}\\all_simulation_all_leg.csv", leg_pos_dir_path);
669 std::ofstream ofs(output_file_name);
678 ofs << GetHeader() <<
"\n";
681 for (
int k = 0; k < result_list_.size(); ++k) {
682 if (result_list_[k].simulation_result != kSuccess) {
687 std::optional<Vector3> past_pos;
689 for (
const auto& recorder :
690 result_list_[k].graph_search_result_recorder) {
691 const Vector3 current_pos = recorder.result_node.leg_pos[l];
694 if (past_pos.has_value() && current_pos == past_pos) {
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);
708 ofs << current_pos.GetLength() <<
"," << past_pos.value().z
709 <<
",true,g," << std::boolalpha
710 << calculator_ptr_->IsValidJointState(l, relay_point,
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);
720 ofs << past_pos.value().GetLength() <<
"," << current_pos.z
721 <<
",true,l," << std::boolalpha
722 << calculator_ptr_->IsValidJointState(l, relay_point,
728 const auto joint_state =
729 calculator_ptr_->CalculateJointState(l, current_pos);
731 ofs << current_pos.ProjectedXY().GetLength() <<
"," << current_pos.z
732 <<
",false," << GetLegChangeStatus(past_pos, current_pos) <<
","
734 << calculator_ptr_->IsValidJointState(l, current_pos, joint_state)
737 past_pos = current_pos;
746void ResultFileExporter::ExportAllLegAngle(
const std::string& path)
const {
749 for (
int i = 0; i < result_list_.size(); ++i) {
750 std::ofstream ofs(std::format(
"{}\\{}{}.csv", path,
758 for (
int j = 0; j < result_list_[i].graph_search_result_recorder.size() - 1;
760 const auto& current =
761 result_list_[i].graph_search_result_recorder[j].result_node;
763 result_list_[i].graph_search_result_recorder[j + 1].result_node;
766 const auto interpolated_nodes =
767 interpolated_node_creator_ptr_->CreateInterpolatedNode(current, next);
769 for (
const auto& node : interpolated_nodes) {
771 const auto joint_states = calculator_ptr_->CalculateAllJointState(node);
774 for (
int l = 0; l < joint_states[k].joint_angle.size(); ++l) {
786std::string ResultFileExporter::GetHeader()
const {
787 return "x,z,relay,string,error,index";
790std::string ResultFileExporter::GetLegChangeStatus(
791 const std::optional<Vector3>& past,
const Vector3& current)
const {
792 if (!past.has_value()) {
800 if (past.value().z > current.z) {
804 if (past.value().z < current.z) {
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フォルダがなければ作成する.また,フォルダ名を指定する.
std::string GetNowTimeString() const
現在の日時をYYYY/MM/DD HH:MM形式の文字列で取得する.
void OutputF(OutputDetail detail, const std::format_string< Args... > str, Args &&... args)
コマンドラインに文字を出力する関数, format した文字列を出力する. SetOutputLimit() で設定した出力の許可範囲内であれば出力される.
void ErrorOutput(const std::string &str)
コマンドラインに文字を出力する関数. Error 用の出力.
std::string InputDirName(const std::string &str="Enter a directory name. (Japanese is not recommended).")
ディレクトリ名を入力させる関数. 出力される文字列は,必ず OutputDetail::kSystem で出力される. ディレクトリ名には次の文字は使えない....
void InfoOutput(const std::string &str)
コマンドラインに文字を出力する関数. Info 用の出力.
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 とかで小数を文字列に変換できないため自作する.
constexpr T ConvertRadToDeg(const T rad) noexcept
角度を [rad]から [deg] に変換する関数.
constexpr bool IsEqual(const T num1, const T num2) noexcept
C++において,小数同士の計算は誤差が出てしまう. 誤差込みで値が等しいか調べる.
OutputDetail
コマンドラインに文字を出力する際に,その詳細を指定するための列挙体.