Compare commits

...

24 Commits

Author SHA1 Message Date
lyf
96aaeb0460 · 2026-03-28 05:03:21 +08:00
lyf
99226caf2f 1 2026-03-28 04:57:45 +08:00
lyf
24340d93f3 1 2026-03-28 04:56:40 +08:00
lyf
e8d6bbac42 1 2026-03-28 04:54:33 +08:00
lyf
f40c37c136 1 2026-03-28 04:50:38 +08:00
lyf
28827fe008 修改 2026-03-28 04:40:02 +08:00
lyf
b8dbb5aa47 xiugai 2026-03-28 04:32:08 +08:00
lyf
14c87856e7 跟新发包 2026-03-28 04:31:23 +08:00
lyf
d195deaa48 迁移入tty持续连接 2026-03-28 04:08:07 +08:00
Huang Haoyu
4a640cf7db Merge pull request 'PID 调整' (#1) from S-3-18 into master
Reviewed-on: #1
2026-03-18 22:25:26 +08:00
12a02f382a 3.18晚上8.51,小电脑上的代码上传。 2026-03-18 20:51:57 +08:00
3eb2d39942 fallback CMake + Armor PID 2026-03-18 17:06:04 +08:00
c0ee469118 参数修改,确认 PID 2026-03-17 18:10:15 +08:00
a133dea09a 去除 XMake 2026-03-16 14:47:30 +08:00
15be04d1f7 OpenCV4 + XMake 2026-03-15 01:42:59 +08:00
xinyang
7770503779 Update README.md 2021-03-16 20:47:23 +08:00
xinyang
5cf1a30ea0 Update README.md 2020-01-02 17:39:08 +08:00
xinyang
7f6e2f4e6e 添加百度网盘链接:部分比赛时相机录制的视频 2019-12-30 12:05:49 +08:00
xinyang
0b1f5ff47f fix bug 2019-10-24 21:48:53 +08:00
xinyang
e1eb8b2590 Update README.md 2019-09-18 18:17:16 +08:00
xinyang
b24542e97c update README.md 2019-09-09 23:29:48 +08:00
xinyang
de94ff2242 Create LICENSE 2019-09-04 13:22:14 +08:00
xinyang
d5df7ce7da Update README.md 2019-08-29 15:21:03 +08:00
xinyang
ef257b7df0 Add files via upload 2019-08-29 15:18:34 +08:00
35 changed files with 1384 additions and 481 deletions

3
.gitignore vendored
View File

@@ -1,6 +1,9 @@
cmake-build-debug
build
.idea
.xmake
.vscode/.cache
.vscode/compile_commands.json
Mark
armor_box_photo
tools/TrainCNN/.idea

8
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"clangd.arguments": [
"--compile-commands-dir=${workspaceFolder}/build",
"--completion-style=detailed",
"--query-driver=/usr/bin/clang",
"--header-insertion=never"
],
}

View File

@@ -1,13 +1,15 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
PROJECT(SJTU-RM-CV)
SET(CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_STANDARD 14)
SET(CMAKE_BUILD_TYPE RELEASE)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPATH=\"\\\"${PROJECT_SOURCE_DIR}\\\"\"")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${CMAKE_SYSTEM_NAME}")
SET(BIN_NAME "run")
SET(CMAKE_EXPORT_COMPILE_COMMANDS ON)
FIND_FILE(CONFIG_FOUND "config.h" "others/include/config")
if (CONFIG_FOUND)
@@ -24,7 +26,7 @@ IF(CCACHE_FOUND)
ENDIF()
FIND_PACKAGE(Eigen3 REQUIRED)
FIND_PACKAGE(OpenCV 3 REQUIRED)
FIND_PACKAGE(OpenCV 4 REQUIRED)
FIND_PACKAGE(Threads)
LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/others)

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 xinyang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -13,6 +13,12 @@
**如有BUG或者想交流的朋友欢迎积极联系我们**
**分享部分比赛时摄像头录制的视频:**
链接: https://pan.baidu.com/s/1LwxEpeYYblX3cSzb59MTVg 提取码: 84ju 复制这段内容后打开百度网盘手机App操作更方便哦
---
运行效果自瞄帧率120摄像头最大帧率,识别距离根据环境不同大约8米左右(5mm焦距镜头)。
![front](https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/picture0.png)
@@ -24,6 +30,8 @@
| ---------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------- |
| IntelNUC<br />MindVision工业相机×<br />USB转TTL× | Ubuntu18.04<br />Ubuntu16.04<br />indows10 | OpenCV3.4.5<br />OpenCV_contrib3.4.5<br />Eigen3<br />MindVision相机驱动 | Ubuntu18/16 : cmake3+gcc7+g++7 <br />Win10 : cmake3+VS2019 |
MindVision相机型号MV-UBS31GC
**关于Windows环境下的运行支持仅保证程序可以编译运行。对与部分辅助功能如生成自启动脚本则不支持。**
**实际装载在步兵和哨兵上的运行环境为Ubuntu18.04。**
@@ -60,6 +68,8 @@ sudo ./run
```./run --help```可以查看所有命令行参数及其作用。所有调试选项都集成到了命令行参数中。
**不使用任何参数直接运行将没有任何图像显示。**
需要调参的部分主要需要根据车辆情况而调参的参数存放在others/include/config/setconfig.h中
### 3.工作条件
@@ -118,7 +128,7 @@ sudo ./run
### 1.自瞄装甲板识别方式
首先对图像进行通道拆分以及二值化操作再进行开闭运算通过边缘提取和条件限制得出可能为灯条的部分。再对所有可能的灯条进行两两匹配根据形状大小特性进行筛选得出可能为装甲板的候选区。然后把所有候选区交给分类器判断得出真实的装甲板及其数字id。最后根据优先级选取最终击打目标以及后续处理。
![autoaim](https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/自瞄流程图.png)
### 2.能量机关识别方式
首先对图像进行二值化操作然后进行一定腐蚀和膨胀通过边缘提取和条件限制得出待击打叶片锤子形。在待击打叶片范围内进一步用类似方法寻找目标装甲板和流动条在二者连线上寻找中心的“R”。根据目标装甲板坐标和中心坐标计算极坐标系下的目标角度进而预测待击打点的坐标小符为装甲板本身大符需要旋转。最后将待击打点坐标和图像中心的差值转换为yaw和pitch轴角度增加一环PID后发送给云台主控板。

View File

@@ -0,0 +1,82 @@
#ifndef CSERIALPORT_SERIALPORT_H
#define CSERIALPORT_SERIALPORT_H
#include <cstdint>
#include <cstring>
namespace itas109 {
enum class Parity {
ParityNone = 0,
ParityOdd = 1,
ParityEven = 2,
};
enum class DataBits {
DataBits5 = 5,
DataBits6 = 6,
DataBits7 = 7,
DataBits8 = 8,
};
enum class StopBits {
StopOne = 1,
StopOneAndHalf = 3,
StopTwo = 2,
};
enum class FlowControl {
FlowNone = 0,
FlowHardware = 1,
FlowSoftware = 2,
};
class CSerialPort {
public:
CSerialPort();
~CSerialPort();
// 初始化串口参数(不打开)
void init(const char* portName,
int baudRate = 115200,
Parity parity = Parity::ParityNone,
DataBits dataBits = DataBits::DataBits8,
StopBits stopBits = StopBits::StopOne,
FlowControl flowControl = FlowControl::FlowNone,
int readTimeoutMs = 1000);
// 打开串口,成功返回 true
bool open();
// 关闭串口
void close();
// 写数据,返回实际写入字节数(失败返回 -1
int writeData(const uint8_t* data, size_t length);
// 读数据,返回实际读取字节数(失败返回 -1
int readData(uint8_t* buffer, size_t maxLength);
// 返回最近一次错误码
int getLastError() const { return m_lastError; }
private:
char m_portName[256];
int m_baudRate;
Parity m_parity;
DataBits m_dataBits;
StopBits m_stopBits;
FlowControl m_flowControl;
int m_readTimeoutMs;
int m_fd; // 文件描述符
int m_lastError;
bool m_isOpen;
// termios 波特率映射
static int toBaudRate(int baud);
};
} // namespace itas109
#endif // CSERIALPORT_SERIALPORT_H

View File

@@ -0,0 +1,27 @@
#ifndef CSERIALPORT_SERIALPORTINFO_H
#define CSERIALPORT_SERIALPORTINFO_H
#include <vector>
#include <cstring>
namespace itas109 {
struct SerialPortInfo {
char portName[256];
char description[256];
SerialPortInfo() {
std::memset(portName, 0, sizeof(portName));
std::memset(description, 0, sizeof(description));
}
};
class CSerialPortInfo {
public:
// 枚举系统上所有可用串口
static std::vector<SerialPortInfo> availablePortInfos();
};
} // namespace itas109
#endif // CSERIALPORT_SERIALPORTINFO_H

View File

@@ -9,7 +9,7 @@
#include <systime.h>
#include <constants.h>
#include <opencv2/core.hpp>
#include <opencv2/tracking.hpp>
#include <opencv2/tracking/tracking.hpp>
#include <serial.h>
#include <armor_finder/classifier/classifier.h>
#include <additions.h>
@@ -20,8 +20,8 @@
#define BOX_RED ENEMY_RED
#define BOX_BLUE ENEMY_BLUE
#define IMAGE_CENTER_X (320)
#define IMAGE_CENTER_Y (240-20)
#define IMAGE_CENTER_X (640)
#define IMAGE_CENTER_Y (512-20)
#define DISTANCE_HEIGHT_5MM (10700.0) // 单位: cm*pixel
#define DISTANCE_HEIGHT DISTANCE_HEIGHT_5MM
@@ -120,6 +120,10 @@ private:
vector<systime> time_seq; // 一个周期内的时间采样点
vector<float> angle_seq; // 一个周期内的角度采样点
float yaw_rotation, pitch_rotation;//云台yaw轴和pitch轴应该转到的角度
float last_yaw, last_pitch;//PID中微分项
float sum_yaw, sum_pitch;//yaw和pitch的累计误差即PID中积分项
bool findLightBlobs(const cv::Mat &src, LightBlobs &light_blobs);
bool findArmorBox(const cv::Mat &src, ArmorBox &box);
bool matchArmorBoxes(const cv::Mat &src, const LightBlobs &light_blobs, ArmorBoxes &armor_boxes);

View File

@@ -0,0 +1,58 @@
#ifndef SERIAL_COMM_H
#define SERIAL_COMM_H
#include <vector>
#include "CSerialPort/SerialPort.h"
#include "CSerialPort/SerialPortInfo.h"
#include <cstdio>
using namespace itas109;
// Serial port configuration
namespace SerialConfig {
constexpr int BAUD_RATE = 115200;
constexpr Parity PARITY_TYPE = Parity::ParityNone;
constexpr DataBits DATA_BITS = DataBits::DataBits8;
constexpr StopBits STOP_BITS = StopBits::StopOne;
constexpr FlowControl FLOW_CTRL = FlowControl::FlowNone;
constexpr int READ_TIMEOUT_MS = 1000;
}
class SerialComm {
private:
CSerialPort m_serialPort;
char m_portName[256];
bool m_isOpen;
public:
SerialComm();
~SerialComm();
// Find first ttyUSB device
bool findFirstTtyUSB();
// Open serial port
bool openPort();
// Close serial port
void closePort();
// Send data
bool sendData(const char* data, size_t length);
bool sendData(const uint8_t* data, size_t length);
// Receive data
int receiveData(uint8_t* buffer, size_t maxLength);
int receiveData(char* buffer, size_t maxLength);
// Check if port is open
bool isOpen() const { return m_isOpen; }
// Get current port name
const char* getPortName() const { return m_portName; }
// List all available ports
static void listAllPorts();
};
#endif // SERIAL_COMM_H

View File

@@ -0,0 +1,126 @@
#ifndef SERIAL_MANAGER_HPP
#define SERIAL_MANAGER_HPP
#include "serialComm.hpp"
#include <thread>
#include <atomic>
#include <mutex>
#include <cstdio>
#include <chrono>
class SerialManager {
private:
SerialComm m_serial;
std::thread m_connectionThread;
std::atomic<bool> m_isConnected;
std::atomic<bool> m_shouldStop;
std::mutex m_serialMutex;
int m_retryIntervalMs;
// 后台重连线程函数
void connectionThreadFunc() {
printf("[I][SerialMgr]: RETRY\n");
while (!m_shouldStop.load()) {
if (!m_isConnected.load()) {
std::lock_guard<std::mutex> lock(m_serialMutex);
if (m_serial.findFirstTtyUSB() && m_serial.openPort()) {
m_isConnected.store(true);
} else {
printf("[W][SerialMgr]: Failed, retry in %dms\n", m_retryIntervalMs);
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(m_retryIntervalMs));
}
printf("[I][SerialMgr]: EXIT\n");
}
public:
// 构造函数retryIntervalMs - 重试间隔(毫秒)
SerialManager(int retryIntervalMs = 500)
: m_isConnected(false)
, m_shouldStop(false)
, m_retryIntervalMs(retryIntervalMs) {
}
~SerialManager() {
stop();
}
// 启动管理器(开始后台重连)
void start() {
if (!m_connectionThread.joinable()) {
m_shouldStop.store(false);
m_connectionThread = std::thread(&SerialManager::connectionThreadFunc, this);
printf("[I][SerialMgr]: START\n");
}
}
// 停止管理器
void stop() {
if (m_connectionThread.joinable()) {
m_shouldStop.store(true);
m_connectionThread.join();
std::lock_guard<std::mutex> lock(m_serialMutex);
m_serial.closePort();
m_isConnected.store(false);
printf("[I][SerialMgr]: STOP\n");
}
}
// 检查是否已连接
bool isConnected() const {
return m_isConnected.load();
}
// 发送数据(线程安全)
bool sendData(const char* data, size_t length) {
if (!m_isConnected.load()) {
return false;
}
std::lock_guard<std::mutex> lock(m_serialMutex);
if (!m_serial.sendData(data, length)) {
// 发送失败,标记为断开
m_isConnected.store(false);
printf("[W][SerialMgr]: Failed, mark DISCONNECT\n");
return false;
}
return true;
}
// 发送数据(字节数组)
bool sendData(const uint8_t* data, size_t length) {
return sendData(reinterpret_cast<const char*>(data), length);
}
// 接收数据(线程安全)
int receiveData(uint8_t* buffer, size_t maxLength) {
if (!m_isConnected.load()) {
return -1;
}
std::lock_guard<std::mutex> lock(m_serialMutex);
return m_serial.receiveData(buffer, maxLength);
}
// 接收数据(字符数组)
int receiveData(char* buffer, size_t maxLength) {
return receiveData(reinterpret_cast<uint8_t*>(buffer), maxLength);
}
// 获取端口名
const char* getPortName() const {
return m_serial.getPortName();
}
};
#endif // SERIAL_MANAGER_HPP

View File

@@ -0,0 +1,40 @@
#ifndef UNIFIED_MANAGER_HPP
#define UNIFIED_MANAGER_HPP
#include "serialComm.hpp"
#include <memory>
#include <thread>
#include <atomic>
#include <mutex>
#include <chrono>
// Unified device manager (Serial only)
class UnifiedDeviceManager {
private:
std::unique_ptr<SerialComm> m_serial;
std::thread m_serialReconnectThread;
std::atomic<bool> m_serialConnected;
std::atomic<bool> m_shouldStop;
std::mutex m_serialMutex;
int m_retryIntervalMs;
void serialReconnectThreadFunc();
public:
UnifiedDeviceManager(int retryIntervalMs = 500);
~UnifiedDeviceManager();
void start();
void stop();
bool isSerialConnected() const { return m_serialConnected.load(); }
// Serial operations (thread-safe)
bool sendData(const char* data, size_t length);
int receiveData(uint8_t* buffer, size_t maxLength);
};
#endif // UNIFIED_MANAGER_HPP

View File

@@ -0,0 +1,207 @@
#include "CSerialPort/SerialPort.h"
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/select.h>
#include <cerrno>
#include <cstring>
namespace itas109 {
CSerialPort::CSerialPort()
: m_baudRate(115200)
, m_parity(Parity::ParityNone)
, m_dataBits(DataBits::DataBits8)
, m_stopBits(StopBits::StopOne)
, m_flowControl(FlowControl::FlowNone)
, m_readTimeoutMs(1000)
, m_fd(-1)
, m_lastError(0)
, m_isOpen(false)
{
std::memset(m_portName, 0, sizeof(m_portName));
}
CSerialPort::~CSerialPort() {
close();
}
void CSerialPort::init(const char* portName,
int baudRate,
Parity parity,
DataBits dataBits,
StopBits stopBits,
FlowControl flowControl,
int readTimeoutMs)
{
std::strncpy(m_portName, portName, sizeof(m_portName) - 1);
m_portName[sizeof(m_portName) - 1] = '\0';
m_baudRate = baudRate;
m_parity = parity;
m_dataBits = dataBits;
m_stopBits = stopBits;
m_flowControl = flowControl;
m_readTimeoutMs = readTimeoutMs;
}
bool CSerialPort::open() {
if (m_isOpen) close();
m_fd = ::open(m_portName, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (m_fd < 0) {
m_lastError = errno;
return false;
}
struct termios tty;
std::memset(&tty, 0, sizeof(tty));
if (tcgetattr(m_fd, &tty) != 0) {
m_lastError = errno;
::close(m_fd);
m_fd = -1;
return false;
}
// 波特率
speed_t speed = static_cast<speed_t>(toBaudRate(m_baudRate));
cfsetispeed(&tty, speed);
cfsetospeed(&tty, speed);
// 数据位
tty.c_cflag &= ~CSIZE;
switch (m_dataBits) {
case DataBits::DataBits5: tty.c_cflag |= CS5; break;
case DataBits::DataBits6: tty.c_cflag |= CS6; break;
case DataBits::DataBits7: tty.c_cflag |= CS7; break;
case DataBits::DataBits8:
default: tty.c_cflag |= CS8; break;
}
// 停止位
if (m_stopBits == StopBits::StopTwo)
tty.c_cflag |= CSTOPB;
else
tty.c_cflag &= ~CSTOPB;
// 校验位
switch (m_parity) {
case Parity::ParityOdd:
tty.c_cflag |= PARENB;
tty.c_cflag |= PARODD;
tty.c_iflag |= (INPCK | ISTRIP);
break;
case Parity::ParityEven:
tty.c_cflag |= PARENB;
tty.c_cflag &= ~PARODD;
tty.c_iflag |= (INPCK | ISTRIP);
break;
case Parity::ParityNone:
default:
tty.c_cflag &= ~PARENB;
tty.c_iflag &= ~(INPCK | ISTRIP);
break;
}
// 流控
if (m_flowControl == FlowControl::FlowHardware)
tty.c_cflag |= CRTSCTS;
else
tty.c_cflag &= ~CRTSCTS;
if (m_flowControl == FlowControl::FlowSoftware)
tty.c_iflag |= (IXON | IXOFF | IXANY);
else
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
// 本地模式 & 输入模式raw
tty.c_cflag |= (CREAD | CLOCAL);
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
tty.c_oflag &= ~OPOST;
// 非阻塞读,超时由 select 控制
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
if (tcsetattr(m_fd, TCSANOW, &tty) != 0) {
m_lastError = errno;
::close(m_fd);
m_fd = -1;
return false;
}
tcflush(m_fd, TCIOFLUSH);
m_isOpen = true;
m_lastError = 0;
return true;
}
void CSerialPort::close() {
if (m_fd >= 0) {
::close(m_fd);
m_fd = -1;
}
m_isOpen = false;
}
int CSerialPort::writeData(const uint8_t* data, size_t length) {
if (!m_isOpen || m_fd < 0) {
m_lastError = EBADF;
return -1;
}
ssize_t written = ::write(m_fd, data, length);
if (written < 0) {
m_lastError = errno;
return -1;
}
tcdrain(m_fd); // 等待发送完成
return static_cast<int>(written);
}
int CSerialPort::readData(uint8_t* buffer, size_t maxLength) {
if (!m_isOpen || m_fd < 0) {
m_lastError = EBADF;
return -1;
}
// 用 select 实现超时
struct timeval tv;
tv.tv_sec = m_readTimeoutMs / 1000;
tv.tv_usec = (m_readTimeoutMs % 1000) * 1000;
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_fd, &fds);
int ret = select(m_fd + 1, &fds, nullptr, nullptr, &tv);
if (ret <= 0) {
// 超时或错误
m_lastError = (ret == 0) ? ETIME : errno;
return 0;
}
ssize_t bytesRead = ::read(m_fd, buffer, maxLength);
if (bytesRead < 0) {
m_lastError = errno;
return -1;
}
m_lastError = 0;
return static_cast<int>(bytesRead);
}
int CSerialPort::toBaudRate(int baud) {
switch (baud) {
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
case 230400: return B230400;
case 460800: return B460800;
case 921600: return B921600;
default: return B115200;
}
}
} // namespace itas109

View File

@@ -0,0 +1,35 @@
#include "CSerialPort/SerialPortInfo.h"
#include <dirent.h>
#include <cstring>
#include <string>
namespace itas109 {
std::vector<SerialPortInfo> CSerialPortInfo::availablePortInfos() {
std::vector<SerialPortInfo> result;
// 扫描 /dev 目录,找 ttyUSB / ttyCH341USB / ttyACM / ttyS 等设备
DIR* dir = opendir("/dev");
if (!dir) return result;
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
const char* name = entry->d_name;
if (std::strstr(name, "ttyUSB") != nullptr ||
std::strstr(name, "ttyCH341") != nullptr ||
std::strstr(name, "ttyACM") != nullptr)
{
SerialPortInfo info;
std::string fullPath = std::string("/dev/") + name;
std::strncpy(info.portName, fullPath.c_str(), sizeof(info.portName) - 1);
std::strncpy(info.description, name, sizeof(info.description) - 1);
result.push_back(info);
}
}
closedir(dir);
return result;
}
} // namespace itas109

View File

@@ -38,6 +38,12 @@ void ArmorFinder::antiTop() {
// 通过两次装甲角度为零的时间差计算陀螺旋转周期
// 根据旋转周期计算下一次装甲出现在角度为零的时间点
if (getPointLength(last_box.getCenter() - target_box.getCenter()) > last_box.rect.height * 1.5) {
sum_yaw = sum_pitch = 0;
double dx = target_box.rect.x + target_box.rect.width / 2.0 - IMAGE_CENTER_X;
double dy = target_box.rect.y + target_box.rect.height / 2.0 - IMAGE_CENTER_Y;
last_yaw = dx;
last_pitch = dy;
auto front_time = getFrontTime(time_seq, angle_seq);
auto once_periodms = getTimeIntervalms(front_time, last_front_time);
// if (abs(once_periodms - top_periodms[-1]) > 50) {

View File

@@ -54,7 +54,9 @@ ArmorFinder::ArmorFinder(uint8_t &color, Serial &u, const string &paras_folder,
anti_switch_cnt(0),
classifier(paras_folder),
contour_area(0),
tracking_cnt(0) {
tracking_cnt(0),
last_yaw(0), last_pitch(0),
sum_yaw(0), sum_pitch(0) {
}
void ArmorFinder::run(cv::Mat &src) {
@@ -67,7 +69,7 @@ void ArmorFinder::run(cv::Mat &src) {
if ((target_box.rect & cv::Rect2d(0, 0, 640, 480)) == target_box.rect) { // 判断装甲板区域是否脱离图像区域
if (!classifier) { /* 如果分类器不可用 */
cv::Mat roi = src(target_box.rect).clone(), roi_gray; /* 就使用装甲区域亮点数判断是否跟丢 */
cv::cvtColor(roi, roi_gray, CV_RGB2GRAY);
cv::cvtColor(roi, roi_gray, cv::COLOR_RGB2GRAY);
cv::threshold(roi_gray, roi_gray, 180, 255, cv::THRESH_BINARY);
contour_area = cv::countNonZero(roi_gray);
}
@@ -75,6 +77,9 @@ void ArmorFinder::run(cv::Mat &src) {
tracker->init(src, target_box.rect);
state = TRACKING_STATE;
tracking_cnt = 0;
last_yaw = target_box.rect.x + target_box.rect.width / 2.0 - IMAGE_CENTER_X;
last_pitch = target_box.rect.y + target_box.rect.height / 2.0 - IMAGE_CENTER_Y;
sum_yaw = sum_pitch = 0;
LOGM(STR_CTR(WORD_LIGHT_CYAN, "into track"));
}
}
@@ -82,6 +87,8 @@ void ArmorFinder::run(cv::Mat &src) {
case TRACKING_STATE:
if (!stateTrackingTarget(src) || ++tracking_cnt > 100) { // 最多追踪100帧图像
state = SEARCHING_STATE;
last_yaw = last_pitch = 0;
sum_yaw = sum_pitch = 0;
LOGM(STR_CTR(WORD_LIGHT_YELLOW, "into search!"));
}
break;
@@ -93,6 +100,11 @@ end:
if(is_anti_top) { // 判断当前是否为反陀螺模式
antiTop();
}else if(target_box.rect != cv::Rect2d()) {
if (last_box.rect == cv::Rect2d()) {
sum_yaw = sum_pitch = 0;
last_yaw = target_box.rect.x + target_box.rect.width / 2.0 - IMAGE_CENTER_X;
last_pitch = target_box.rect.y + target_box.rect.height / 2.0 - IMAGE_CENTER_Y;
}
anti_top_cnt = 0;
time_seq.clear();
angle_seq.clear();

View File

@@ -156,6 +156,7 @@ bool ArmorFinder::findArmorBox(const cv::Mat &src, ArmorBox &box) {
for (auto &one_box : armor_boxes) {
if (one_box.id != 0) {
box = one_box;
break;
}
}
if (save_labelled_boxes) {

View File

@@ -83,11 +83,11 @@ bool ArmorFinder::findLightBlobs(const cv::Mat &src, LightBlobs &light_blobs) {
}else{
light_threshold = 200;
}
cv::threshold(color_channel, src_bin_light, light_threshold, 255, CV_THRESH_BINARY); // 二值化对应通道
cv::threshold(color_channel, src_bin_light, light_threshold, 255, cv::THRESH_BINARY); // 二值化对应通道
if (src_bin_light.empty()) return false;
imagePreProcess(src_bin_light); // 开闭运算
cv::threshold(color_channel, src_bin_dim, 140, 255, CV_THRESH_BINARY); // 二值化对应通道
cv::threshold(color_channel, src_bin_dim, 140, 255, cv::THRESH_BINARY); // 二值化对应通道
if (src_bin_dim.empty()) return false;
imagePreProcess(src_bin_dim); // 开闭运算
@@ -100,8 +100,8 @@ bool ArmorFinder::findLightBlobs(const cv::Mat &src, LightBlobs &light_blobs) {
std::vector<std::vector<cv::Point>> light_contours_light, light_contours_dim;
LightBlobs light_blobs_light, light_blobs_dim;
std::vector<cv::Vec4i> hierarchy_light, hierarchy_dim;
cv::findContours(src_bin_light, light_contours_light, hierarchy_light, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
cv::findContours(src_bin_dim, light_contours_dim, hierarchy_dim, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
cv::findContours(src_bin_light, light_contours_light, hierarchy_light, cv::RETR_CCOMP, cv::CHAIN_APPROX_NONE);
cv::findContours(src_bin_dim, light_contours_dim, hierarchy_dim, cv::RETR_CCOMP, cv::CHAIN_APPROX_NONE);
for (int i = 0; i < light_contours_light.size(); i++) {
if (hierarchy_light[i][2] == -1) {
cv::RotatedRect rect = cv::minAreaRect(light_contours_light[i]);

View File

@@ -5,40 +5,58 @@
#include <armor_finder/armor_finder.h>
#include <config/setconfig.h>
#include <log.h>
#include <string>
#include <cstdio>
static bool sendTarget(Serial &serial, double x, double y, double z, uint16_t shoot_delay) {
static short x_tmp, y_tmp, z_tmp;
uint8_t buff[10];
// ANGLE_SCALE: 角度单位换算系数。发出的整数 = yaw_deg / ANGLE_SCALE
// 例: 1.0f → 单位为度; 0.01f → 单位为厘度(精度更高)
static constexpr float ANGLE_SCALE = 1.0f;
static bool sendTarget(Serial &serial, double yaw_deg, double pitch_deg, double dist,
uint16_t shoot_delay, char type = 'r') {
// static short x_tmp, y_tmp, z_tmp;
// uint8_t buff[10];
#ifdef WITH_COUNT_FPS
static time_t last_time = time(nullptr);
static int fps;
time_t t = time(nullptr);
if (last_time != t) {
last_time = t;
cout << "Armor: fps:" << fps << ", (" << x << "," << y << "," << z << ")" << endl;
cout << "Armor: fps:" << fps << ", yaw=" << yaw_deg
<< " pitch=" << pitch_deg << " dist=" << dist << endl;
fps = 0;
}
fps += 1;
#endif
x_tmp = static_cast<short>(x * (32768 - 1) / 100);
y_tmp = static_cast<short>(y * (32768 - 1) / 100);
z_tmp = static_cast<short>(z * (32768 - 1) / 1000);
// ---- 新格式: 字符串帧 ----
// 格式: "s <yaw_int> <pitch_int> <dist> <shoot_delay> e\n"
int16_t yaw_tmp = static_cast<int16_t>(yaw_deg / ANGLE_SCALE);
int16_t pitch_tmp = static_cast<int16_t>(pitch_deg / ANGLE_SCALE);
char buf[64];
std::snprintf(buf, sizeof(buf), "s %d %d %.1f %u e\n",
(int)yaw_tmp, (int)pitch_tmp, dist, (unsigned)shoot_delay);
std::string frame(buf);
return serial.WriteData(reinterpret_cast<const uint8_t*>(frame.data()), frame.size());
buff[0] = 's';
buff[1] = static_cast<char>((x_tmp >> 8) & 0xFF);
buff[2] = static_cast<char>((x_tmp >> 0) & 0xFF);
buff[3] = static_cast<char>((y_tmp >> 8) & 0xFF);
buff[4] = static_cast<char>((y_tmp >> 0) & 0xFF);
buff[5] = static_cast<char>((z_tmp >> 8) & 0xFF);
buff[6] = static_cast<char>((z_tmp >> 0) & 0xFF);
buff[7] = static_cast<char>((shoot_delay >> 8) & 0xFF);
buff[8] = static_cast<char>((shoot_delay >> 0) & 0xFF);
buff[9] = 'e';
// if(buff[7]<<8 | buff[8])
// cout << (buff[7]<<8 | buff[8]) << endl;
return serial.WriteData(buff, sizeof(buff));
// ---- 原始二进制打包(已注释)----
// static short x_tmp, y_tmp, z_tmp;
// uint8_t buff[10];
// x_tmp = static_cast<short>(yaw_deg * (32768 - 1) / 100);
// y_tmp = static_cast<short>(pitch_deg * (32768 - 1) / 100);
// z_tmp = static_cast<short>(dist * (32768 - 1) / 1000);
// buff[0] = 's';
// buff[1] = static_cast<char>((x_tmp >> 8) & 0xFF);
// buff[2] = static_cast<char>((x_tmp >> 0) & 0xFF);
// buff[3] = static_cast<char>((y_tmp >> 8) & 0xFF);
// buff[4] = static_cast<char>((y_tmp >> 0) & 0xFF);
// buff[5] = static_cast<char>((z_tmp >> 8) & 0xFF);
// buff[6] = static_cast<char>((z_tmp >> 0) & 0xFF);
// buff[7] = static_cast<char>((shoot_delay >> 8) & 0xFF);
// buff[8] = static_cast<char>((shoot_delay >> 0) & 0xFF);
// buff[9] = 'e';
// return serial.WriteData(buff, sizeof(buff));
}
bool ArmorFinder::sendBoxPosition(uint16_t shoot_delay) {
@@ -49,6 +67,28 @@ bool ArmorFinder::sendBoxPosition(uint16_t shoot_delay) {
auto rect = target_box.rect;
double dx = rect.x + rect.width / 2 - IMAGE_CENTER_X;
double dy = rect.y + rect.height / 2 - IMAGE_CENTER_Y;
// PID
#define MAX_I 100 // 这里的数值可以根据实际调试情况进一步微调
sum_yaw += dx;
sum_pitch += dy;
sum_yaw = (sum_yaw > MAX_I) ? MAX_I : (sum_yaw < -MAX_I ? -MAX_I : sum_yaw);
sum_pitch = (sum_pitch > MAX_I) ? MAX_I : (sum_pitch < -MAX_I ? -MAX_I : sum_pitch);
float yaw_I_component = YAW_AIM_KI * sum_yaw;
float pitch_I_component = PITCH_AIM_KI * sum_pitch;
double tmp_yaw = dx;
double tmp_pitch = dy;
dx = YAW_AIM_KP * dx + YAW_AIM_KI * sum_yaw +
YAW_AIM_KD * (dx - last_yaw);
dy = PITCH_AIM_KP * dy + PITCH_AIM_KI * sum_pitch +
PITCH_AIM_KD * (dy - last_pitch);
last_yaw = tmp_yaw;
last_pitch = tmp_pitch;
//
double yaw = atan(dx / FOCUS_PIXAL) * 180 / PI;
double pitch = atan(dy / FOCUS_PIXAL) * 180 / PI;
double dist = DISTANCE_HEIGHT / rect.height;

View File

@@ -8,13 +8,13 @@
#include <show_images/show_images.h>
bool ArmorFinder::stateTrackingTarget(cv::Mat &src) {
auto pos = target_box.rect;
cv::Rect pos(target_box.rect);
if(!tracker->update(src, pos)){ // 使用KCFTracker进行追踪
target_box = ArmorBox();
LOGW("Track fail!");
return false;
}
if((pos & cv::Rect2d(0, 0, 640, 480)) != pos){
if((cv::Rect2d(pos) & cv::Rect2d(0, 0, 640, 480)) != cv::Rect2d(pos)){
target_box = ArmorBox();
LOGW("Track out range!");
return false;
@@ -52,7 +52,7 @@ bool ArmorFinder::stateTrackingTarget(cv::Mat &src) {
}
}else{ // 分类器不可用,使用常规方法判断
cv::Mat roi_gray;
cv::cvtColor(roi, roi_gray, CV_RGB2GRAY);
cv::cvtColor(roi, roi_gray, cv::COLOR_RGB2GRAY);
cv::threshold(roi_gray, roi_gray, 180, 255, cv::THRESH_BINARY);
contour_area = cv::countNonZero(roi_gray);
if(abs(cv::countNonZero(roi_gray) - contour_area) > contour_area * 0.3){

127
armor/src/serialComm.cpp Normal file
View File

@@ -0,0 +1,127 @@
#include "serialComm.hpp"
#include <cstring>
#include <cstdio>
SerialComm::SerialComm() : m_isOpen(false) {
std::memset(m_portName, 0, sizeof(m_portName));
m_serialPort.init(
"",
SerialConfig::BAUD_RATE,
SerialConfig::PARITY_TYPE,
SerialConfig::DATA_BITS,
SerialConfig::STOP_BITS,
SerialConfig::FLOW_CTRL,
SerialConfig::READ_TIMEOUT_MS
);
}
SerialComm::~SerialComm() {
closePort();
}
bool SerialComm::findFirstTtyUSB() {
std::vector<SerialPortInfo> portInfos = CSerialPortInfo::availablePortInfos();
for (const auto& info : portInfos) {
const char* portStr = info.portName;
if (std::strstr(portStr, "ttyCH341USB") != nullptr) {
std::strncpy(m_portName, portStr, sizeof(m_portName) - 1);
m_portName[sizeof(m_portName) - 1] = '\0';
printf("[I][SERIAL]: ttyUSB device found: %s\n", m_portName);
return true;
}
}
return false;
}
bool SerialComm::openPort() {
if (std::strlen(m_portName) == 0) {
printf("[E][Serial]: Port name is empty, call findFirstTtyUSB() first\n");
return false;
}
m_serialPort.init(
m_portName,
SerialConfig::BAUD_RATE,
SerialConfig::PARITY_TYPE,
SerialConfig::DATA_BITS,
SerialConfig::STOP_BITS,
SerialConfig::FLOW_CTRL,
SerialConfig::READ_TIMEOUT_MS
);
if (!m_serialPort.open()) {
printf("[E][Serial]: Failed to open port %s\n", m_portName);
printf("[E][Serial]: Error code: %d\n", m_serialPort.getLastError());
m_isOpen = false;
return false;
}
m_isOpen = true;
printf("[I][Serial]: Port opened successfully: %s\n", m_portName);
printf("[I][Serial]: Baud rate: %d\n", SerialConfig::BAUD_RATE);
printf("[I][Serial]: Data bits: 8\n");
printf("[I][Serial]: Stop bits: 1\n");
printf("[I][Serial]: Parity: None\n");
return true;
}
void SerialComm::closePort() {
if (m_isOpen) {
m_serialPort.close();
m_isOpen = false;
printf("[I][Serial]: Port closed: %s\n", m_portName);
}
}
bool SerialComm::sendData(const char* data, size_t length) {
return sendData(reinterpret_cast<const uint8_t*>(data), length);
}
bool SerialComm::sendData(const uint8_t* data, size_t length) {
if (!m_isOpen) {
printf("[E][Serial]: Port not open\n");
return false;
}
int bytesWritten = m_serialPort.writeData(data, length);
if (bytesWritten > 0) {
printf("[I][Serial]: Sent %d bytes\n", bytesWritten);
return true;
} else {
printf("[E][Serial]: Failed to send data\n");
return false;
}
}
int SerialComm::receiveData(char* buffer, size_t maxLength) {
return receiveData(reinterpret_cast<uint8_t*>(buffer), maxLength);
}
int SerialComm::receiveData(uint8_t* buffer, size_t maxLength) {
if (!m_isOpen) {
printf("[E][Serial]: Port not open\n");
return -1;
}
int bytesRead = m_serialPort.readData(buffer, maxLength);
if (bytesRead > 0) {
printf("[I][Serial]: Received %d bytes\n", bytesRead);
}
return bytesRead;
}
void SerialComm::listAllPorts() {
std::vector<SerialPortInfo> portInfos = CSerialPortInfo::availablePortInfos();
printf("[I][Serial]: Available ports:\n");
for (const auto& info : portInfos) {
printf("[I][Serial]: - %s\n", info.portName);
}
}

View File

@@ -0,0 +1,92 @@
#include "unifiedManager.hpp"
#include <cstdio>
UnifiedDeviceManager::UnifiedDeviceManager(int retryIntervalMs)
: m_serial(std::make_unique<SerialComm>())
, m_serialConnected(false)
, m_shouldStop(false)
, m_retryIntervalMs(retryIntervalMs)
{
printf("[I][Managr]: Unified device manager created\n");
}
UnifiedDeviceManager::~UnifiedDeviceManager() {
stop();
}
void UnifiedDeviceManager::serialReconnectThreadFunc() {
printf("[I][Managr]: Serial reconnect thread started\n");
while (!m_shouldStop.load()) {
if (!m_serialConnected.load()) {
printf("[I][Managr]: Attempting to connect serial port...\n");
std::lock_guard<std::mutex> lock(m_serialMutex);
if (m_serial->findFirstTtyUSB() && m_serial->openPort()) {
m_serialConnected.store(true);
printf("[I][Managr]: Serial port connected successfully\n");
} else {
printf("[W][Managr]: Serial connection failed, retry in %dms\n",
m_retryIntervalMs);
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(m_retryIntervalMs));
}
printf("[I][Managr]: Serial reconnect thread stopped\n");
}
void UnifiedDeviceManager::start() {
printf("[I][Managr]: Starting unified device manager\n");
m_shouldStop.store(false);
if (!m_serialReconnectThread.joinable()) {
m_serialReconnectThread = std::thread(&UnifiedDeviceManager::serialReconnectThreadFunc, this);
}
printf("[I][Managr]: Unified device manager started\n");
}
void UnifiedDeviceManager::stop() {
printf("[I][Managr]: Stopping unified device manager\n");
m_shouldStop.store(true);
if (m_serialReconnectThread.joinable()) {
m_serialReconnectThread.join();
std::lock_guard<std::mutex> lock(m_serialMutex);
m_serial->closePort();
m_serialConnected.store(false);
}
printf("[I][Managr]: Unified device manager stopped\n");
}
bool UnifiedDeviceManager::sendData(const char* data, size_t length) {
if (!m_serialConnected.load()) {
return false;
}
std::lock_guard<std::mutex> lock(m_serialMutex);
if (!m_serial->sendData(data, length)) {
m_serialConnected.store(false);
printf("[W][Managr]: Serial send failed, marked as disconnected\n");
return false;
}
return true;
}
int UnifiedDeviceManager::receiveData(uint8_t* buffer, size_t maxLength) {
if (!m_serialConnected.load()) {
return -1;
}
std::lock_guard<std::mutex> lock(m_serialMutex);
return m_serial->receiveData(buffer, maxLength);
}

View File

@@ -21,12 +21,12 @@ int Energy::findFans(const cv::Mat &src) {
static Mat src_bin;
src_bin = src.clone();
if (src.type() == CV_8UC3) {
cvtColor(src_bin, src_bin, CV_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
cvtColor(src_bin, src_bin, cv::COLOR_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
}
std::vector<vector<Point> > fan_contours;
FanStruct(src_bin);//图像膨胀,防止图像断开并更方便寻找
if (show_process)imshow("fan struct", src_bin);
findContours(src_bin, fan_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
findContours(src_bin, fan_contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
for (auto &fan_contour : fan_contours) {
if (!isValidFanContour(src_bin, fan_contour)) {
@@ -54,16 +54,16 @@ int Energy::findArmors(const cv::Mat &src) {
static Mat src_bin;
src_bin = src.clone();
if (src.type() == CV_8UC3) {
cvtColor(src_bin, src_bin, CV_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
cvtColor(src_bin, src_bin, cv::COLOR_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
}
std::vector<vector<Point> > armor_contours;
std::vector<vector<Point> > armor_contours_external;//用总轮廓减去外轮廓,只保留内轮廓,除去流动条的影响。
ArmorStruct(src_bin);//图像膨胀,防止图像断开并更方便寻找
findContours(src_bin, armor_contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
findContours(src_bin, armor_contours, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
if (show_process)imshow("armor struct", src_bin);
findContours(src_bin, armor_contours_external, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
findContours(src_bin, armor_contours_external, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
for (int i = 0; i < armor_contours_external.size(); i++)//去除外轮廓
{
unsigned long external_contour_size = armor_contours_external[i].size();
@@ -103,12 +103,12 @@ bool Energy::findCenterR(const cv::Mat &src) {
static Mat src_bin;
src_bin = src.clone();
if (src.type() == CV_8UC3) {
cvtColor(src_bin, src_bin, CV_BGR2GRAY);
cvtColor(src_bin, src_bin, cv::COLOR_BGR2GRAY);
}
std::vector<vector<Point> > center_R_contours;
CenterRStruct(src_bin);
if (show_process)imshow("R struct", src_bin);
findContours(src_bin, center_R_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
findContours(src_bin, center_R_contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
for (auto &center_R_contour : center_R_contours) {
if (!isValidCenterRContour(center_R_contour)) {
continue;
@@ -139,13 +139,13 @@ bool Energy::findFlowStripFan(const cv::Mat &src) {
src_bin = src.clone();
src_copy = src.clone();
if (src.type() == CV_8UC3) {
cvtColor(src_bin, src_bin, CV_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
cvtColor(src_bin, src_bin, cv::COLOR_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
}
std::vector<vector<Point> > flow_strip_fan_contours;
FlowStripFanStruct(src_bin);//图像膨胀,防止图像断开并更方便寻找
if (show_process)imshow("flow strip fan struct", src_bin);
findContours(src_bin, flow_strip_fan_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
findContours(src_bin, flow_strip_fan_contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
std::vector<cv::RotatedRect> candidate_flow_strip_fans;
for (auto &flow_strip_fan_contour : flow_strip_fan_contours) {
@@ -175,7 +175,7 @@ bool Energy::findFlowStrip(const cv::Mat &src) {
if (src_bin.type() == CV_8UC1) // 黑白图像
{
cvtColor(src_bin, src_bin, COLOR_GRAY2RGB);
cvtColor(src_bin, src_bin, cv::COLOR_GRAY2RGB);
}
std::vector<cv::RotatedRect> candidate_target_armors = target_armors;
@@ -189,13 +189,13 @@ bool Energy::findFlowStrip(const cv::Mat &src) {
}
}
cvtColor(src_bin, src_bin, CV_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
cvtColor(src_bin, src_bin, cv::COLOR_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
FlowStripStruct(src_bin);//图像膨胀,防止图像断开并更方便寻找
if (show_process)imshow("flow strip struct", src_bin);
std::vector<vector<Point> > flow_strip_contours;
findContours(src_bin, flow_strip_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
findContours(src_bin, flow_strip_contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
for (auto candidate_flow_strip_fan: flow_strip_fans) {
for (auto &flow_strip_contour : flow_strip_contours) {
@@ -261,7 +261,7 @@ bool Energy::findFlowStripWeak(const cv::Mat &src) {
if (src_bin.type() == CV_8UC1) // 黑白图像
{
cvtColor(src_bin, src_bin, COLOR_GRAY2RGB);
cvtColor(src_bin, src_bin, cv::COLOR_GRAY2RGB);
}
std::vector<cv::RotatedRect> candidate_armors = armors;
@@ -275,13 +275,13 @@ bool Energy::findFlowStripWeak(const cv::Mat &src) {
}
}
cvtColor(src_bin, src_bin, CV_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
cvtColor(src_bin, src_bin, cv::COLOR_BGR2GRAY);//若读取三通道视频文件,需转换为单通道
FlowStripStruct(src_bin);//图像膨胀,防止图像断开并更方便寻找
if (show_process)imshow("weak struct", src_bin);
std::vector<vector<Point> > flow_strip_contours;
findContours(src_bin, flow_strip_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
findContours(src_bin, flow_strip_contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
for (auto &flow_strip_contour : flow_strip_contours) {
if (!isValidFlowStripContour(flow_strip_contour)) {
@@ -336,5 +336,3 @@ bool Energy::findCenterROI(const cv::Mat &src) {
return true;
}

View File

@@ -20,6 +20,7 @@
#include <options.h>
#include <additions.h>
#include <config/setconfig.h>
#include "serialManager.hpp"
#define DO_NOT_CNT_TIME
@@ -41,7 +42,8 @@ McuData mcu_data = { // 单片机端回传结构体
WrapperHead *video = nullptr; // 云台摄像头视频源
Serial serial(115200); // 串口对象
Serial serial(115200); // 串口对象(旧版,供 armor_finder/energy 使用)
SerialManager serialManager; // TTY 串口自动恢复管理器
uint8_t last_state = ARMOR_STATE; // 上次状态,用于初始化
// 自瞄主程序对象
ArmorFinder armor_finder(mcu_data.enemy_color, serial, PROJECT_DIR"/tools/para/", mcu_data.anti_top);
@@ -50,6 +52,7 @@ Energy energy(serial, mcu_data.enemy_color);
int main(int argc, char *argv[]) {
processOptions(argc, argv); // 处理命令行参数
serialManager.start(); // 启动 TTY 串口自动重连线程
thread receive(uartReceive, &serial); // 开启串口接收线程
int from_camera = 1; // 根据条件选择视频源

View File

@@ -5,17 +5,17 @@ resolution :
image_size :
{
iIndex = 1;
acDescription = "640X480 ROI";
acDescription = "1280X1024 ROI";
uBinSumMode = 0;
uBinAverageMode = 0;
uSkipMode = 0;
uResampleMask = 0;
iHOffsetFOV = 56;
iVOffsetFOV = 0;
iWidthFOV = 640;
iHeightFOV = 480;
iWidth = 640;
iHeight = 480;
iWidthFOV = 1280;
iHeightFOV = 1024;
iWidth = 1280;
iHeight = 1024;
iWidthZoomHd = 0;
iHeightZoomHd = 0;
iWidthZoomSw = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@
#define _CAMERA_WRAPPER_H_
#include <additions.h>
#include <opencv2/core/core.hpp>
#include <opencv2/core.hpp>
#include <camera/wrapper_head.h>
#ifdef Windows
@@ -19,9 +19,9 @@ class CameraWrapper: public WrapperHead {
friend void cameraCallback(CameraHandle hCamera, BYTE *pFrameBuffer, tSdkFrameHead* pFrameHead,PVOID pContext);
private:
const std::string name;
int mode;
//int mode;
bool init_done;
//bool init_done;
unsigned char* rgb_buffer;
int camera_cnts;
@@ -33,7 +33,7 @@ private:
tSdkCameraCapbility tCapability;
tSdkFrameHead frame_info;
BYTE *pby_buffer;
IplImage* iplImage;
cv::Mat image_header;
int channel;
RoundQueue<cv::Mat, 2> src_queue;
@@ -41,6 +41,9 @@ public:
int gain;
int exposure;
int mode;
bool init_done;
CameraWrapper(int exposure, int gain, int camera_mode=1, const std::string &n="NULL");
~CameraWrapper() final;

View File

@@ -4,6 +4,7 @@
#ifndef _SETCONFIG_H_
#define _SETCONFIG_H_
#define WITH_CONFIG
#ifdef WITH_CONFIG
#include <config/config.h>
@@ -40,7 +41,7 @@
#endif
#ifndef YAW_AIM_KD
#define YAW_AIM_KD (0.4)
#define YAW_AIM_KD (0.1)
#endif
#ifndef YAW_AIM_KP
#define YAW_AIM_KP (0.75)
@@ -49,13 +50,13 @@
#define YAW_AIM_KI (0.01)
#endif
#ifndef PITCH_AIM_KD
#define PITCH_AIM_KD (0.4)
#define PITCH_AIM_KD (0)
#endif
#ifndef PITCH_AIM_KP
#define PITCH_AIM_KP (0.75)
#define PITCH_AIM_KP (0)
#endif
#ifndef PITCH_AIM_KI
#define PITCH_AIM_KI (0.01)
#define PITCH_AIM_KI (0)
#endif
#ifndef RED_COMPENSATE_YAW

View File

@@ -19,6 +19,6 @@
#define FOCUS_PIXAL_8MM (1488)
#define FOCUS_PIXAL_5MM (917)
#define FOCUS_PIXAL FOCUS_PIXAL_5MM
#define FOCUS_PIXAL FOCUS_PIXAL_8MM
#endif /* _CONSTANTS_H */

View File

@@ -32,6 +32,7 @@
#include <stdio.h>
#include <systime.h>
#include <iostream>
/************** Define the control code *************/
#define START_CTR "\033[0"

View File

@@ -36,9 +36,9 @@ void uartReceive(Serial *pSerial) {
pSerial->ReadData((uint8_t *) buffer, sizeof(mcu_data)+1);
if (buffer[sizeof(mcu_data)] == '\n') {
memcpy(&mcu_data, buffer, sizeof(mcu_data));
// LOGM("Get, state:%c, mark:%d!", mcu_data.state, (int) mcu_data.mark);
// LOGM("Get yaw: %f, pitch: %f!", mcu_data.curr_yaw, mcu_data.curr_pitch);
// LOGM("Get delta x: %d, delta y: %d!", mcu_data.delta_x, mcu_data.delta_y);
LOGM("Get, state:%c, mark:%d!", mcu_data.state, (int) mcu_data.mark);
LOGM("Get yaw: %f, pitch: %f!", mcu_data.curr_yaw, mcu_data.curr_pitch);
LOGM("Get delta x: %d, delta y: %d!", mcu_data.delta_x, mcu_data.delta_y);
// static int t = time(nullptr);
// static int cnt = 0;
// if(time(nullptr) > t){
@@ -77,8 +77,12 @@ bool checkReconnect(bool is_camera_connect) {
if (!is_camera_connect) {
int curr_gain = ((CameraWrapper* )video)->gain;
int curr_exposure = ((CameraWrapper* )video)->exposure;
int curr_mode = ((CameraWrapper* )video)->mode; // 获取原始模式
delete video;
video = new CameraWrapper(curr_exposure, curr_gain, 0/*, "armor"*/);
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 等待硬件释放
video = new CameraWrapper(curr_exposure, curr_gain, curr_mode/*, "armor"*/);
//video = new CameraWrapper(curr_exposure, curr_gain, 0/*, "armor"*/);
is_camera_connect = video->init();
}
return is_camera_connect;
@@ -103,14 +107,14 @@ void extract(cv::Mat &src) {//图像预处理将视频切成640×480的大小
if (src.empty()) return;
float length = static_cast<float>(src.cols);
float width = static_cast<float>(src.rows);
if (length / width > 640.0 / 480.0) {
length *= 480.0 / width;
resize(src, src, cv::Size(length, 480));
src = src(Rect((length - 640) / 2, 0, 640, 480));
if (length / width > 1280.0 / 1024.0) {
length *= 1024 / width;
resize(src, src, cv::Size(length, 1024));
src = src(Rect((length - 1280) / 2, 0, 1280, 1024));
} else {
width *= 640.0 / length;
resize(src, src, cv::Size(640, width));
src = src(Rect(0, (width - 480) / 2, 640, 480));
width *= 1280.0 / length;
resize(src, src, cv::Size(1280, width));
src = src(Rect(0, (width - 1024) / 2, 1280, 1024));
}
}

View File

@@ -17,7 +17,6 @@ CameraWrapper::CameraWrapper(int exposure, int gain, int camera_mode, const std:
mode(camera_mode),
camera_cnts(2),
camera_status(-1),
iplImage(nullptr),
rgb_buffer(nullptr),
channel(3),
gain(gain),
@@ -27,9 +26,11 @@ CameraWrapper::CameraWrapper(int exposure, int gain, int camera_mode, const std:
void cameraCallback(CameraHandle hCamera, BYTE *pFrameBuffer, tSdkFrameHead* pFrameHead,PVOID pContext){
CameraWrapper *c = (CameraWrapper*)pContext;
CameraImageProcess(hCamera, pFrameBuffer, c->rgb_buffer, pFrameHead);
auto iplImage = cvCreateImageHeader(cvSize(pFrameHead->iWidth, pFrameHead->iHeight), IPL_DEPTH_8U, c->channel);
cvSetData(iplImage, c->rgb_buffer, pFrameHead->iWidth * c->channel); //此处只是设置指针,无图像块数据拷贝,不需担心转换效率
c->src_queue.push(cv::cvarrToMat(iplImage).clone());
// 使用 cv::Mat 替代 IplImage
cv::Mat img(pFrameHead->iHeight, pFrameHead->iWidth,
c->channel == 3 ? CV_8UC3 : CV_8UC1,
c->rgb_buffer, pFrameHead->iWidth * c->channel);
c->src_queue.push(img.clone());
}
bool CameraWrapper::init() {
@@ -37,6 +38,7 @@ bool CameraWrapper::init() {
int camera_enumerate_device_status = CameraEnumerateDevice(camera_enum_list, &camera_cnts);
if (camera_enumerate_device_status != CAMERA_STATUS_SUCCESS) {
LOGE("CameraEnumerateDevice fail with %d!", camera_enumerate_device_status);
return false;
}
if (camera_cnts == 0) {
LOGE("No camera device detected!");
@@ -134,15 +136,9 @@ bool CameraWrapper::read(cv::Mat &src) {
bool CameraWrapper::readRaw(cv::Mat &src) {
if (CameraGetImageBuffer(h_camera, &frame_info, &pby_buffer, 500) == CAMERA_STATUS_SUCCESS) {
if (iplImage) {
cvReleaseImageHeader(&iplImage);
}
iplImage = cvCreateImageHeader(cvSize(frame_info.iWidth, frame_info.iHeight), IPL_DEPTH_8U, 1);
cvSetData(iplImage, pby_buffer, frame_info.iWidth); //此处只是设置指针,无图像块数据拷贝,不需担心转换效率
src = cv::cvarrToMat(iplImage).clone();
// 使用 cv::Mat 替代 IplImage
cv::Mat img(frame_info.iHeight, frame_info.iWidth, CV_8UC1, pby_buffer, frame_info.iWidth);
src = img.clone();
//在成功调用CameraGetImageBuffer后必须调用CameraReleaseImageBuffer来释放获得的buffer。
//否则再次调用CameraGetImageBuffer时程序将被挂起一直阻塞直到其他线程中调用CameraReleaseImageBuffer来释放了buffer
@@ -156,16 +152,13 @@ bool CameraWrapper::readRaw(cv::Mat &src) {
}
bool CameraWrapper::readProcessed(cv::Mat &src) {
// cerr << "Get-1" << endl;
if (CameraGetImageBuffer(h_camera, &frame_info, &pby_buffer, 500) == CAMERA_STATUS_SUCCESS) {
CameraImageProcess(h_camera, pby_buffer, rgb_buffer,
&frame_info); // this function is super slow, better not to use it.
if (iplImage) {
cvReleaseImageHeader(&iplImage);
}
iplImage = cvCreateImageHeader(cvSize(frame_info.iWidth, frame_info.iHeight), IPL_DEPTH_8U, channel);
cvSetData(iplImage, rgb_buffer, frame_info.iWidth * channel); //此处只是设置指针,无图像块数据拷贝,不需担心转换效率
src = cv::cvarrToMat(iplImage).clone();
// 使用 cv::Mat 替代 IplImage
int mat_type = (channel == 3) ? CV_8UC3 : CV_8UC1;
cv::Mat img(frame_info.iHeight, frame_info.iWidth, mat_type, rgb_buffer, frame_info.iWidth * channel);
src = img.clone();
//在成功调用CameraGetImageBuffer后必须调用CameraReleaseImageBuffer来释放获得的buffer。
//否则再次调用CameraGetImageBuffer时程序将被挂起一直阻塞直到其他线程中调用CameraReleaseImageBuffer来释放了buffer
CameraReleaseImageBuffer(h_camera, pby_buffer);

View File

@@ -6,19 +6,20 @@
#include <log.h>
#include <cstring>
#include <map>
#include <string>
bool show_armor_box = false;
bool show_armor_box = true;
bool show_armor_boxes = false;
bool show_light_blobs = false;
bool show_origin = false;
bool run_with_camera = false;
bool run_with_camera = true;
bool save_video = false;
bool wait_uart = false;
bool save_labelled_boxes = false;
bool show_process = false;
bool show_energy = false;
bool save_mark = false;
bool show_info = false;
bool show_info = true;
bool run_by_frame = false;
// 使用map保存所有选项及其描述和操作加快查找速度。

View File

@@ -177,11 +177,13 @@ bool Serial::ReadData(unsigned char *buffer, unsigned int length) {
#elif defined(Linux) || defined(Darwin)
#include <string.h>
#include <thread>
#include <chrono>
using namespace std;
string get_uart_dev_name() {
FILE *ls = popen("ls /dev/ttyUSB* --color=never", "r");
FILE *ls = popen("ls /dev/ttyCH341USB* --color=never", "r");
char name[20] = {0};
fscanf(ls, "%s", name);
return name;
@@ -189,17 +191,10 @@ string get_uart_dev_name() {
Serial::Serial(int nSpeed, char nEvent, int nBits, int nStop) :
nSpeed(nSpeed), nEvent(nEvent), nBits(nBits), nStop(nStop) {
if (wait_uart) {
LOGM("Wait for serial be ready!");
while (!InitPort(nSpeed, nEvent, nBits, nStop));
LOGM("Waiting for serial port...");
while (!InitPort(nSpeed, nEvent, nBits, nStop))
std::this_thread::sleep_for(std::chrono::milliseconds(500));
LOGM("Port set successfully!");
} else {
if (InitPort(nSpeed, nEvent, nBits, nStop)) {
LOGM("Port set successfully!");
} else {
LOGE("Port set fail!");
}
}
}
Serial::~Serial() {
@@ -228,18 +223,18 @@ bool Serial::InitPort(int nSpeed, char nEvent, int nBits, int nStop) {
bool Serial::WriteData(const unsigned char *pData, unsigned int length) {
int cnt = 0, curr = 0;
if (fd <= 0) {
if(wait_uart){
InitPort(nSpeed, nEvent, nBits, nStop);
}
LOGE("Serial offline! Reconnecting...");
while (!InitPort(nSpeed, nEvent, nBits, nStop))
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return false;
}
while ((curr = write(fd, pData + cnt, length - cnt)) > 0 && (cnt += curr) < length);
if (curr < 0) {
LOGE("Serial offline!");
LOGE("Serial write error! Reconnecting...");
close(fd);
if (wait_uart) {
InitPort(nSpeed, nEvent, nBits, nStop);
}
fd = -1;
while (!InitPort(nSpeed, nEvent, nBits, nStop))
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return false;
}
return true;
@@ -249,11 +244,11 @@ bool Serial::ReadData(unsigned char *buffer, unsigned int length) {
int cnt = 0, curr = 0;
while ((curr = read(fd, buffer + cnt, length - cnt)) > 0 && (cnt += curr) < length);
if (curr < 0) {
LOGE("Serial offline!");
LOGE("Serial read error! Reconnecting...");
close(fd);
if (wait_uart) {
InitPort(nSpeed, nEvent, nBits, nStop);
}
fd = -1;
while (!InitPort(nSpeed, nEvent, nBits, nStop))
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return false;
}
return true;

3
tools/tty-permission.sh Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
sudo touch /etc/udev/rules.d/70-ttyusb.rules

BIN
自瞄流程图.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB