Files
Catalyst-MDVS/src/MindVisionMain.cpp
2025-12-01 18:41:43 +08:00

260 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <iostream>
#include <string>
#include <vector>
#include <chrono>
#include <thread>
#include <memory>
#include <unistd.h>
#include <opencv2/opencv.hpp>
#include "config.h"
#include "MindVisionCamera.h" // 使用MindVision相机
#include "ImagePreprocessor.h"
#include "ArmorDetector.h"
#include "KalmanFilter.h"
#include "Visualizer.h"
#include "BallisticPredictor.h"
#include "TTLCommunicator.h"
// Function to output control data to TTL device (with enable control)
void output_control_data(const cv::Point2f* ballistic_point,
const std::string& target_color,
int frame_counter,
TTLCommunicator* ttl_communicator,
const cv::Point2f& img_center,
bool use_ttl) {
// Only send data when TTL is enabled, meets frame interval, and has valid target
if (use_ttl && frame_counter % 5 == 0 && ballistic_point != nullptr) {
std::ostringstream send_str;
// Calculate offset (based on actual image center)
int ballistic_offset_x = static_cast<int>(ballistic_point->x - img_center.x);
int ballistic_offset_y = static_cast<int>(img_center.y - ballistic_point->y);
// Color simplification mapping
std::string simplified_color = target_color;
if (target_color == "red") simplified_color = "r";
else if (target_color == "blue") simplified_color = "b";
// Construct send string
send_str << "s " << simplified_color << " " << std::to_string(ballistic_offset_x) << " " << std::to_string(ballistic_offset_y) << "\n";
// Send data
if (ttl_communicator != nullptr) {
ttl_communicator->send_data(send_str.str());
}else{
std::cerr << "Error: TTLCommunicator is a null pointer!" << std::endl;
}
}
}
void set_camera_resolution(MindVisionCamera& , int width, int height) {
// The resolution is set during camera initialization in MindVision
// We need to implement a method in MindVisionCamera to change resolution
// For now, we'll just log the intended change
std::cout << "Setting camera resolution to: " << width << "x" << height << std::endl;
}
int main(int /*argc*/, char* /*argv*/[]) {
std::string target_color = "red";
int cam_id = 0;
cv::Size default_resolution(640, 480);
bool use_ttl = true; // Set to false to disable TTL communication
// Define optional resolution list (adjust based on camera support)
std::vector<cv::Size> resolutions = {
cv::Size(320, 240), // Low resolution, high frame rate
cv::Size(640, 480), // Standard resolution
cv::Size(1280, 720), // HD resolution
cv::Size(1920, 1080) // Full HD resolution
};
// Find the index of default resolution
int res_index = 1; // Default to index 1 (640x480)
for (size_t i = 0; i < resolutions.size(); i++) {
if (resolutions[i] == default_resolution) {
res_index = static_cast<int>(i);
break;
}
}
// Initialize TTL communication (only when enabled)
TTLCommunicator* ttl = nullptr;
if (use_ttl) {
// On Linux, the port would typically be /dev/ttyUSB0, /dev/ttyACM0, etc.
ttl = new TTLCommunicator("/dev/ttyUSB0", TTL_BAUDRATE);
if (ttl != nullptr) {
if (!ttl->connect()) {
std::cout << "Warning: Cannot establish TTL connection, will continue running but not send data" << std::endl;
}
} else {
std::cout << "Error: Failed to create TTL communicator instance" << std::endl;
}
} else {
std::cout << "TTL communication disabled" << std::endl;
}
// Initialize visual processing modules with MindVision camera
MindVisionCamera camera(cam_id, target_color);
if (!camera.is_opened) {
std::cerr << "Cannot open MindVision camera!" << std::endl;
return -1;
}
// Initialize ballistic predictor (adjustable bullet speed, e.g., 16m/s)
BallisticPredictor ballistic_predictor(16.0f);
// Record target speed (obtained from Kalman filter)
cv::Point2f target_speed(0.0f, 0.0f);
// Set initial resolution
set_camera_resolution(camera, resolutions[res_index].width, resolutions[res_index].height);
std::cout << "Initial camera resolution: " << resolutions[res_index].width << "x" << resolutions[res_index].height << std::endl;
ImagePreprocessor preprocessor;
ArmorDetector detector;
KalmanFilter kalman_tracker;
Visualizer visualizer;
int frame_counter = 0; // Counter to control output frequency
int max_consecutive_predicts = 20; // Maximum consecutive prediction times
int consecutive_predicts = 0; // Current consecutive prediction count
cv::Mat frame;
try {
while (true) {
// 使用新的颜色过滤方法同时获取图像和原始掩码
cv::Mat raw_mask;
if (!camera.read_frame_with_color_filter(frame, raw_mask, target_color)) {
std::cout << "Cannot read from MindVision camera, exiting!HERERER" << std::endl;
break;
}
// Get actual image size and calculate center
cv::Point2f img_center(frame.cols / 2.0f, frame.rows / 2.0f);
// 使用OpenCV进行形态学处理
cv::Mat mask;
preprocessor.apply_morphology(raw_mask, mask);
// 生成彩色图像(仅显示目标颜色)
cv::Mat color_only_frame;
frame.copyTo(color_only_frame, raw_mask);
// Armor plate detection
std::vector<LightBar> valid_light_bars;
std::vector<ArmorPlate> armor_plates;
detector.detect(mask, target_color, valid_light_bars, armor_plates);
// Kalman filter tracking (modified part: get target speed)
cv::Point2f* current_center = nullptr;
if (!armor_plates.empty()) {
current_center = &(armor_plates[0].center);
}
// Use smart pointer for safer memory management
std::unique_ptr<cv::Point2f> predicted_center = nullptr;
if (current_center != nullptr) {
// Has detection result: update Kalman filter, reset consecutive prediction count
kalman_tracker.update(*current_center);
predicted_center = std::make_unique<cv::Point2f>(kalman_tracker.predict());
// Get velocity from the Kalman filter
target_speed = cv::Point2f(0.0f, 0.0f); // Kalman filter in OpenCV doesn't directly expose velocity
consecutive_predicts = 0;
} else {
// No detection result: only use Kalman prediction, limit consecutive predictions
consecutive_predicts++;
if (consecutive_predicts < max_consecutive_predicts) {
cv::Point2f temp_pred = kalman_tracker.predict();
if (temp_pred.x != 0 || temp_pred.y != 0) { // Check if prediction is valid
predicted_center = std::make_unique<cv::Point2f>(temp_pred);
}
} else {
predicted_center = nullptr;
target_speed = cv::Point2f(0.0f, 0.0f);
}
}
// Determine display center
cv::Point2f* display_center = current_center;
if (display_center == nullptr && predicted_center != nullptr) {
display_center = predicted_center.get();
}
bool is_predicted = (display_center != nullptr) && (current_center == nullptr);
// Calculate ballistic prediction point
cv::Point2f* ballistic_point = ballistic_predictor.predict_ballistic_point(
predicted_center.get(), img_center, target_speed
);
auto current_time = std::chrono::high_resolution_clock::now();
// Visualization
visualizer.draw_light_bars(frame, valid_light_bars, target_color);
if (!armor_plates.empty()) {
visualizer.draw_armor_plate(frame, armor_plates[0]);
}
visualizer.draw_offset_text(frame, display_center, target_color, is_predicted);
visualizer.draw_ballistic_point(frame, ballistic_point);
// Output control data to TTL (passing use_ttl to control whether to send)
output_control_data(display_center, target_color, frame_counter, ttl, img_center, use_ttl);
// Display windows
cv::imshow("Armor Detection", frame);
cv::imshow(target_color + " Mask", mask);
cv::imshow(target_color + " Only", color_only_frame);
// Exit on 'q' key press
if (cv::waitKey(1) == 'q') {
break;
}
frame_counter++;
// Control max frame rate (100 FPS)
auto end_time = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - current_time).count();
if (elapsed < 10) { // 100 FPS = 10ms per frame
std::this_thread::sleep_for(std::chrono::milliseconds(10 - elapsed));
}
// Smart pointers automatically handle memory cleanup
}
}
catch (const std::exception& e) {
std::cerr << "Error in main loop: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "Unknown error occurred in main loop" << std::endl;
}
// Cleanup
try {
camera.release();
} catch (...) {
std::cerr << "Error during camera release" << std::endl;
}
try {
cv::destroyAllWindows();
} catch (...) {
std::cerr << "Error during window destruction" << std::endl;
}
// Only close TTL connection when enabled and initialized
if (use_ttl && ttl != nullptr) {
try {
ttl->close();
} catch (...) {
std::cerr << "Error during TTL close" << std::endl;
}
delete ttl;
ttl = nullptr;
}
return 0;
}