Files
RCS-3000/examples/gui_demo.cpp
CaiXiang af65c2425d initial
2025-11-14 16:09:58 +08:00

244 lines
8.8 KiB
C++

#include "path_tracker.h"
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cmath>
/**
* @brief Simple console GUI display class
* Display control values in table format in terminal
*/
class ConsoleGUI {
public:
ConsoleGUI() = default;
/**
* @brief Clear screen
*/
void clear() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
/**
* @brief Show title
*/
void showTitle() {
std::cout << "\n";
std::cout << "========================================================================\n";
std::cout << "| Single Steering Wheel AGV Path Tracking Control System - GUI |\n";
std::cout << "========================================================================\n";
std::cout << "\n";
}
/**
* @brief Show AGV parameters
*/
void showAGVParams(const AGVModel& model) {
std::cout << "+---------------------- AGV Parameters ----------------------+\n";
std::cout << "| Wheelbase: " << std::setw(8) << std::fixed << std::setprecision(2)
<< model.getWheelbase() << " m |\n";
std::cout << "| Max Velocity: " << std::setw(8) << model.getMaxVelocity() << " m/s |\n";
std::cout << "| Max Steering Angle: " << std::setw(8) << (model.getMaxSteeringAngle() * 180.0 / M_PI)
<< " degrees |\n";
std::cout << "+------------------------------------------------------------+\n\n";
}
/**
* @brief Show control sequence table
*/
void showControlTable(const ControlSequence& sequence, int max_rows = 20) {
if (sequence.size() == 0) {
std::cout << "Control sequence is empty!\n";
return;
}
std::cout << "+---------------- Control Sequence ----------------+\n";
std::cout << "| " << std::setw(8) << "Step"
<< " | " << std::setw(8) << "Time(s)"
<< " | " << std::setw(10) << "Velocity(m/s)"
<< " | " << std::setw(12) << "Steering(deg)"
<< " |\n";
std::cout << "+----------+----------+------------+---------------+\n";
int display_rows = std::min(max_rows, static_cast<int>(sequence.size()));
int step = std::max(1, static_cast<int>(sequence.size()) / display_rows);
for (size_t i = 0; i < sequence.size(); i += step) {
if (i / step >= display_rows) break;
double time = sequence.timestamps[i];
double velocity = sequence.controls[i].v;
double steering_deg = sequence.controls[i].delta * 180.0 / M_PI;
std::cout << "| " << std::setw(8) << i
<< " | " << std::setw(8) << std::fixed << std::setprecision(2) << time
<< " | " << std::setw(12) << std::setprecision(4) << velocity
<< " | " << std::setw(13) << std::setprecision(4) << steering_deg
<< " |\n";
}
std::cout << "+----------+----------+------------+---------------+\n";
std::cout << "Total steps: " << sequence.size() << "\n\n";
}
/**
* @brief Show current state and control (dashboard style)
*/
void showDashboard(const AGVModel::State& state, const AGVModel::Control& control, int step) {
std::cout << "+------------ Current State (Step: " << std::setw(4) << step << ") ------------+\n";
// Position
std::cout << "| |\n";
std::cout << "| Position: X = " << std::setw(8) << std::fixed << std::setprecision(3)
<< state.x << " m Y = " << std::setw(8) << state.y << " m |\n";
// Heading
double theta_deg = state.theta * 180.0 / M_PI;
std::cout << "| Heading: theta = " << std::setw(8) << std::setprecision(2)
<< theta_deg << " degrees |\n";
std::cout << "| |\n";
std::cout << "+------------------------------------------------------------+\n";
std::cout << "| Control Values |\n";
std::cout << "+------------------------------------------------------------+\n";
// Velocity bar
std::cout << "| Velocity: " << std::setw(6) << std::setprecision(3) << control.v << " m/s ";
drawBar(control.v, 0, 2.0, 20);
std::cout << " |\n";
// Steering bar
double delta_deg = control.delta * 180.0 / M_PI;
std::cout << "| Steering: " << std::setw(6) << std::setprecision(2) << delta_deg << " deg ";
drawBar(delta_deg, -45, 45, 20);
std::cout << " |\n";
std::cout << "+------------------------------------------------------------+\n\n";
}
/**
* @brief Show statistics
*/
void showStatistics(const ControlSequence& sequence) {
if (sequence.size() == 0) return;
// Calculate statistics
double avg_velocity = 0.0;
double max_velocity = -1e9;
double min_velocity = 1e9;
double avg_steering = 0.0;
double max_steering = -1e9;
double min_steering = 1e9;
for (const auto& ctrl : sequence.controls) {
avg_velocity += ctrl.v;
max_velocity = std::max(max_velocity, ctrl.v);
min_velocity = std::min(min_velocity, ctrl.v);
double delta_deg = ctrl.delta * 180.0 / M_PI;
avg_steering += delta_deg;
max_steering = std::max(max_steering, delta_deg);
min_steering = std::min(min_steering, delta_deg);
}
avg_velocity /= sequence.size();
avg_steering /= sequence.size();
std::cout << "+---------------- Statistics ----------------+\n";
std::cout << "| Velocity Statistics: |\n";
std::cout << "| Average: " << std::setw(8) << std::fixed << std::setprecision(4)
<< avg_velocity << " m/s |\n";
std::cout << "| Maximum: " << std::setw(8) << max_velocity << " m/s |\n";
std::cout << "| Minimum: " << std::setw(8) << min_velocity << " m/s |\n";
std::cout << "| |\n";
std::cout << "| Steering Angle Statistics: |\n";
std::cout << "| Average: " << std::setw(8) << std::setprecision(4)
<< avg_steering << " degrees |\n";
std::cout << "| Maximum: " << std::setw(8) << max_steering << " degrees |\n";
std::cout << "| Minimum: " << std::setw(8) << min_steering << " degrees |\n";
std::cout << "+--------------------------------------------+\n";
}
private:
/**
* @brief Draw bar chart
*/
void drawBar(double value, double min_val, double max_val, int width) {
double normalized = (value - min_val) / (max_val - min_val);
normalized = std::max(0.0, std::min(1.0, normalized));
int filled = static_cast<int>(normalized * width);
std::cout << "[";
for (int i = 0; i < width; ++i) {
if (i < filled) {
std::cout << "=";
} else {
std::cout << " ";
}
}
std::cout << "]";
}
};
int main() {
ConsoleGUI gui;
// Create AGV model
AGVModel agv_model(1.0, 2.0, M_PI / 4);
// Create path tracker
PathTracker tracker(agv_model);
// Create circular arc path as example
PathCurve path;
path.generateCircleArc(5.0, 0.0, 5.0, 0.0, M_PI / 2, 100);
tracker.setReferencePath(path);
// Set initial state
AGVModel::State initial_state(0.0, 0.0, 0.0);
tracker.setInitialState(initial_state);
// Generate control sequence
tracker.generateControlSequence("pure_pursuit", 0.1, 10.0);
const ControlSequence& sequence = tracker.getControlSequence();
// Show interface
gui.clear();
gui.showTitle();
gui.showAGVParams(agv_model);
gui.showControlTable(sequence, 15);
gui.showStatistics(sequence);
// Real-time simulation display (optional)
std::cout << "\nPlay real-time simulation? (y/n): ";
char choice;
std::cin >> choice;
if (choice == 'y' || choice == 'Y') {
for (size_t i = 0; i < sequence.size(); i += 5) { // Display every 5 steps
gui.clear();
gui.showTitle();
gui.showDashboard(sequence.predicted_states[i], sequence.controls[i], i);
// Pause for viewing
#ifdef _WIN32
system("timeout /t 1 /nobreak > nul");
#else
system("sleep 0.5");
#endif
}
}
std::cout << "\nProgram ended. Press Enter to exit...";
std::cin.ignore();
std::cin.get();
return 0;
}