基于STM32的智能仓储盘点机器人
项目开发背景
随着现代物流行业的快速发展,仓储管理作为供应链中的核心环节,其效率与准确性直接影响企业的运营成本与客户满意度。传统仓储盘点主要依赖人工操作,不仅耗时费力,还容易因人为因素导致数据误差,尤其在货物种类繁多、库存动态变化频繁的场景下,人工盘点已难以满足高效、精准的实时管理需求。
在此背景下,自动化与智能化技术成为提升仓储管理水平的关键突破口。通过引入嵌入式系统与机器视觉技术,可实现仓储盘点的无人化与智能化。基于STM32的智能仓储盘点机器人项目,正是针对这一需求而设计。机器人能够沿预设轨道自主移动,利用视觉模块快速识别货物信息,并通过无线通信将数据实时同步至后台系统,有效解决了传统盘点中效率低、误差高、数据更新滞后等问题。
该项目的开发不仅顺应了工业4.0与智能物流的发展趋势,也为中小型仓储企业提供了一套低成本、高可靠性的自动化解决方案。通过集成STM32的实时控制能力、OpenMV的视觉识别技术以及Wi-Fi数据传输功能,实现了从货物识别、数据采集到远程管理的全流程自动化,为仓储管理的数字化升级提供了切实可行的技术路径。
设计实现的功能
(1) 机器人能够沿预设货架轨道自主巡线移动。
(2) 通过OpenMV视觉模块扫描货物条形码或二维码。
(3) 系统自动记录扫描到的货物信息,并生成库存报表。
(4) 通过Wi-Fi模块将库存数据无线传输到后台管理系统。
项目硬件模块组成
(1) 主控芯片:STM32F103系列单片机。
(2) 视觉模块:OpenMV摄像头模组。
(3) 通信模块:ESP8266或ESP32-S3 Wi-Fi模块。
(4) 驱动模块:L298N电机驱动板、直流减速电机与车轮。
(5) 电源模块:12V锂电池组及5V/3.3V降压模块。
设计意义
该智能仓储盘点机器人的设计意义在于显著提升仓储管理的自动化水平,通过自主巡线移动功能,机器人能够替代人工在货架间进行重复性巡检,减少人力成本并降低操作误差,从而优化整体仓储作业流程。
通过集成OpenMV视觉模块进行条形码或二维码扫描,系统实现了对货物信息的快速准确识别,避免了传统手动盘点中常见的漏检或误读问题,确保库存数据的真实性和可靠性,为决策提供精准依据。
自动记录货物信息并生成库存报表的功能,简化了数据整理环节,使管理人员能够及时掌握库存动态,支持高效的库存控制和补货计划,同时通过Wi-Fi模块无线传输数据,实现了与后台管理系统的无缝对接,促进仓储信息的实时共享和远程监控。
设计思路
该系统以STM32F103系列单片机作为核心控制器,负责协调各硬件模块的工作流程。通过集成OpenMV视觉模块、Wi-Fi通信模块、电机驱动模块和电源模块,实现机器人的自主移动、货物识别、数据记录与无线传输功能。
在自主巡线移动方面,OpenMV摄像头实时采集货架轨道的图像数据,通过内置的图像处理算法识别预设路径的标识线。STM32主控芯片根据识别结果输出控制信号,驱动L298N电机驱动板调节直流减速电机的转速和转向,使机器人能够沿轨道平稳行进,并保持稳定的移动轨迹。
当机器人行进至货架位置时,OpenMV模块会主动扫描货物上的条形码或二维码。扫描到的图像经过解码处理后,通过串口通信将货物信息传输至STM32主控芯片。STM32对接收到的数据进行解析和校验,确保信息的准确性和完整性。
系统会自动记录解析后的货物信息,包括货物编号、类型和数量等关键数据。这些数据被存储在主控芯片的存储器或外部存储设备中,并通过内置算法实时汇总生成库存报表。报表内容可涵盖库存状态、变化趋势等实用信息,为仓储管理提供数据支持。
通过ESP8266或ESP32-S3 Wi-Fi模块,STM32将生成的库存报表数据以无线方式发送至后台管理系统。通信过程采用标准的网络协议,确保数据传输的可靠性和实时性,实现库存信息的远程监控与更新。
电源模块由12V锂电池组及降压模块组成,为整个系统提供稳定供电。降压模块将电压转换为5V和3.3V,分别满足电机驱动板和主控芯片等模块的功耗需求,同时通过电源管理策略优化能耗,保障机器人的持续运行能力。
框架图
智能仓储盘点机器人系统框架图
+---------------------------------------------+
| 电源模块 |
| - 12V锂电池组 |
| - 5V/3.3V降压模块 |
+---------------------------------------------+
|
v 供电
+---------------------------------------------+
| 主控芯片 |
| STM32F103系列 |
+---------------------------------------------+
| | |
| 控制/数据 | 控制/数据 | 控制/数据
v v v
+-----------+ +-----------+ +-----------+
| 视觉模块 | | 通信模块 | | 驱动模块 |
| OpenMV | | Wi-Fi | | L298N |
| 摄像头 | | ESP8266/ | | 电机驱动 |
| | | ESP32-S3 | | 电机+车轮 |
+-----------+ +-----------+ +-----------+
| |
| 扫描条形码 | 传输数据
v v
+---------------------------------------------+
| 后台管理系统 |
| (库存数据接收与处理) |
+---------------------------------------------+
系统总体设计
该系统以STM32F103系列单片机作为核心控制器,负责协调各硬件模块的运行,实现智能仓储盘点功能。机器人通过巡线传感器检测预设轨道,结合L298N电机驱动板控制直流减速电机与车轮的运动,确保沿货架轨道自主移动,完成巡检任务。
视觉识别部分由OpenMV摄像头模组承担,该模块实时采集货物图像,通过内置算法识别条形码或二维码信息,并将解码后的数据发送至STM32主控芯片进行处理。
系统在STM32主控中集成数据管理功能,自动记录扫描到的货物信息,包括货物编号和数量,并基于这些数据生成结构化库存报表,便于后续分析和使用。
通信模块采用ESP8266或ESP32-S3 Wi-Fi模块,将生成的库存报表通过无线网络传输至后台管理系统,实现数据的实时同步和远程监控,提升仓储管理效率。
电源模块由12V锂电池组供电,通过降压模块转换为5V和3.3V电压,分别为电机驱动、主控芯片及其他低功耗模块提供稳定电力,确保系统长时间可靠运行。
系统功能总结
| 功能编号 | 功能描述 |
|---|---|
| 1 | 机器人能够沿预设货架轨道自主巡线移动。 |
| 2 | 通过OpenMV视觉模块扫描货物条形码或二维码。 |
| 3 | 系统自动记录扫描到的货物信息,并生成库存报表。 |
| 4 | 通过Wi-Fi模块将库存数据无线传输到后台管理系统。 |
设计的各个功能模块描述
STM32F103系列单片机作为系统的主控制器,负责整体协调与控制。它处理来自传感器的输入数据,执行自主巡线算法,控制驱动模块实现移动,同时管理视觉模块的扫描过程和通信模块的数据传输,确保系统按预设功能运行。
OpenMV视觉模块用于货物识别,通过摄像头捕获条形码或二维码图像,并利用内置算法进行解码。解码后的货物信息被发送至主控芯片,用于自动记录和生成库存报表,实现高效的仓储盘点。
通信模块采用ESP8266或ESP32-S3 Wi-Fi芯片,负责无线数据传输。它将主控芯片处理后的库存信息通过Wi-Fi网络发送到后台管理系统,实现远程数据同步和实时库存监控。
驱动模块包括L298N电机驱动板、直流减速电机和车轮,实现机器人的移动功能。主控芯片输出控制信号,通过驱动板调节电机转速和方向,使机器人能够沿预设货架轨道自主巡线移动,确保稳定运行。
电源模块由12V锂电池组及降压模块组成,为系统提供电力支持。锂电池输出12V电压,经降压模块转换为5V和3.3V,分别供给主控芯片、视觉模块、通信模块和驱动模块,保证各组件稳定工作。
上位机代码设计
// main.cpp - 智能仓储盘点机器人上位机系统
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <thread>
#include <chrono>
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
// 库存数据结构
struct InventoryItem {
std::string barcode;
std::string name;
int quantity;
std::string timestamp;
std::string location;
};
class WarehouseManager {
private:
std::vector<InventoryItem> inventory;
SOCKET serverSocket;
bool isRunning;
std::string dataFile = "inventory_data.txt";
public:
WarehouseManager() : serverSocket(INVALID_SOCKET), isRunning(false) {}
~WarehouseManager() {
stopServer();
}
// 初始化Winsock
bool initNetwork() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed!" << std::endl;
return false;
}
return true;
}
// 创建服务器Socket
bool createServer(int port = 8080) {
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET) {
std::cerr << "Socket creation failed!" << std::endl;
return false;
}
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(port);
if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "Bind failed!" << std::endl;
closesocket(serverSocket);
return false;
}
if (listen(serverSocket, 5) == SOCKET_ERROR) {
std::cerr << "Listen failed!" << std::endl;
closesocket(serverSocket);
return false;
}
std::cout << "Server started on port " << port << std::endl;
return true;
}
// 处理客户端连接
void handleClient(SOCKET clientSocket) {
char buffer[1024];
int bytesReceived;
while ((bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0)) > 0) {
buffer[bytesReceived] = '\0';
std::string data(buffer);
// 解析机器人发送的数据
if (parseInventoryData(data)) {
std::cout << "Inventory data received and processed: " << data << std::endl;
// 发送确认回执
std::string ack = "ACK:Data received successfully";
send(clientSocket, ack.c_str(), ack.length(), 0);
}
}
closesocket(clientSocket);
}
// 解析库存数据
bool parseInventoryData(const std::string& data) {
// 数据格式: BARCODE|NAME|QUANTITY|TIMESTAMP|LOCATION
std::vector<std::string> tokens;
size_t start = 0, end = 0;
while ((end = data.find('|', start)) != std::string::npos) {
tokens.push_back(data.substr(start, end - start));
start = end + 1;
}
tokens.push_back(data.substr(start));
if (tokens.size() >= 5) {
InventoryItem item;
item.barcode = tokens[0];
item.name = tokens[1];
item.quantity = std::stoi(tokens[2]);
item.timestamp = tokens[3];
item.location = tokens[4];
// 更新库存
updateInventory(item);
saveToFile();
return true;
}
return false;
}
// 更新库存
void updateInventory(const InventoryItem& newItem) {
// 检查是否已存在相同条码的商品
for (auto& item : inventory) {
if (item.barcode == newItem.barcode && item.location == newItem.location) {
item.quantity = newItem.quantity;
item.timestamp = newItem.timestamp;
return;
}
}
// 新商品,添加到库存
inventory.push_back(newItem);
}
// 保存数据到文件
void saveToFile() {
std::ofstream file(dataFile);
if (!file.is_open()) {
std::cerr << "Cannot open data file!" << std::endl;
return;
}
file << "BARCODE|NAME|QUANTITY|TIMESTAMP|LOCATION\n";
for (const auto& item : inventory) {
file << item.barcode << "|" << item.name << "|"
<< item.quantity << "|" << item.timestamp << "|"
<< item.location << "\n";
}
file.close();
}
// 从文件加载数据
void loadFromFile() {
std::ifstream file(dataFile);
if (!file.is_open()) {
std::cout << "No existing data file found. Starting fresh." << std::endl;
return;
}
std::string line;
std::getline(file, line); // 跳过标题行
while (std::getline(file, line)) {
std::vector<std::string> tokens;
size_t start = 0, end = 0;
while ((end = line.find('|', start)) != std::string::npos) {
tokens.push_back(line.substr(start, end - start));
start = end + 1;
}
tokens.push_back(line.substr(start));
if (tokens.size() >= 5) {
InventoryItem item;
item.barcode = tokens[0];
item.name = tokens[1];
item.quantity = std::stoi(tokens[2]);
item.timestamp = tokens[3];
item.location = tokens[4];
inventory.push_back(item);
}
}
file.close();
}
// 生成库存报表
void generateReport() {
std::cout << "\n=== 智能仓储库存报表 ===" << std::endl;
std::cout << "更新时间: " << getCurrentTime() << std::endl;
std::cout << "总商品种类: " << inventory.size() << std::endl;
std::cout << "----------------------------------------" << std::endl;
int totalItems = 0;
for (const auto& item : inventory) {
std::cout << "条码: " << item.barcode
<< " | 名称: " << item.name
<< " | 数量: " << item.quantity
<< " | 位置: " << item.location
<< " | 时间: " << item.timestamp << std::endl;
totalItems += item.quantity;
}
std::cout << "----------------------------------------" << std::endl;
std::cout << "总库存数量: " << totalItems << std::endl;
// 保存报表到文件
std::ofstream report("inventory_report.txt");
report << "智能仓储库存报表\n";
report << "更新时间: " << getCurrentTime() << "\n";
report << "总商品种类: " << inventory.size() << "\n";
report << "总库存数量: " << totalItems << "\n\n";
for (const auto& item : inventory) {
report << item.barcode << " | " << item.name << " | "
<< item.quantity << " | " << item.location << " | "
<< item.timestamp << "\n";
}
report.close();
}
// 获取当前时间
std::string getCurrentTime() {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
char buffer[80];
ctime_s(buffer, sizeof(buffer), &time_t);
std::string timeStr(buffer);
timeStr.pop_back(); // 移除换行符
return timeStr;
}
// 启动服务器
void startServer() {
if (!initNetwork() || !createServer()) {
return;
}
isRunning = true;
loadFromFile();
std::cout << "Warehouse Management System Started!" << std::endl;
std::cout << "Waiting for robot connections..." << std::endl;
while (isRunning) {
sockaddr_in clientAddr;
int clientAddrLen = sizeof(clientAddr);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket == INVALID_SOCKET) {
if (isRunning) {
std::cerr << "Accept failed!" << std::endl;
}
continue;
}
std::cout << "Robot connected from " << inet_ntoa(clientAddr.sin_addr)
<< ":" << ntohs(clientAddr.sin_port) << std::endl;
// 在新线程中处理客户端
std::thread clientThread(&WarehouseManager::handleClient, this, clientSocket);
clientThread.detach();
}
}
// 停止服务器
void stopServer() {
isRunning = false;
if (serverSocket != INVALID_SOCKET) {
closesocket(serverSocket);
serverSocket = INVALID_SOCKET;
}
WSACleanup();
}
// 显示菜单
void showMenu() {
std::cout << "\n=== 智能仓储管理系统菜单 ===" << std::endl;
std::cout << "1. 查看当前库存" << std::endl;
std::cout << "2. 生成库存报表" << std::endl;
std::cout << "3. 手动添加商品" << std::endl;
std::cout << "4. 退出系统" << std::endl;
std::cout << "请输入选择 (1-4): ";
}
// 手动添加商品
void manualAddItem() {
InventoryItem item;
std::cout << "请输入商品条码: ";
std::cin >> item.barcode;
std::cout << "请输入商品名称: ";
std::cin >> item.name;
std::cout << "请输入商品数量: ";
std::cin >> item.quantity;
std::cout << "请输入商品位置: ";
std::cin >> item.location;
item.timestamp = getCurrentTime();
updateInventory(item);
saveToFile();
std::cout << "商品添加成功!" << std::endl;
}
// 运行用户界面
void runUI() {
int choice;
while (true) {
showMenu();
std::cin >> choice;
switch (choice) {
case 1:
generateReport();
break;
case 2:
generateReport();
std::cout << "报表已保存到 inventory_report.txt" << std::endl;
break;
case 3:
manualAddItem();
break;
case 4:
std::cout << "系统关闭中..." << std::endl;
return;
default:
std::cout << "无效选择,请重新输入!" << std::endl;
}
}
}
};
int main() {
WarehouseManager manager;
// 在后台线程中启动服务器
std::thread serverThread(&WarehouseManager::startServer, &manager);
// 在主线程中运行用户界面
manager.runUI();
// 用户选择退出,停止服务器
manager.stopServer();
if (serverThread.joinable()) {
serverThread.join();
}
std::cout << "系统已安全关闭" << std::endl;
return 0;
}
模块代码设计
#include "stm32f10x.h"
// 电机控制引脚定义
#define MOTOR1_IN1_PIN GPIO_Pin_0 // PA0
#define MOTOR1_IN2_PIN GPIO_Pin_1 // PA1
#define MOTOR2_IN1_PIN GPIO_Pin_2 // PA2
#define MOTOR2_IN2_PIN GPIO_Pin_3 // PA3
#define MOTOR_PWM1_PIN GPIO_Pin_6 // PA6 (TIM3_CH1)
#define MOTOR_PWM2_PIN GPIO_Pin_7 // PA7 (TIM3_CH2)
// OpenMV串口配置
#define OPENMV_USART USART1
#define OPENMV_BAUDRATE 115200
// WiFi模块串口配置
#define WIFI_USART USART2
#define WIFI_BAUDRATE 115200
// 巡线传感器引脚定义
#define LINE_SENSOR1_PIN GPIO_Pin_4 // PB4
#define LINE_SENSOR2_PIN GPIO_Pin_5 // PB5
#define LINE_SENSOR3_PIN GPIO_Pin_6 // PB6
#define LINE_SENSOR4_PIN GPIO_Pin_7 // PB7
// 电机PWM配置
void Motor_PWM_Init(void)
{
// 开启GPIOA和TIM3时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
// 配置PA6,PA7为复用推挽输出
GPIOA->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_MODE6 |
GPIO_CRL_CNF7 | GPIO_CRL_MODE7);
GPIOA->CRL |= (GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6_1 |
GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7_1);
// 配置TIM3
TIM3->ARR = 999; // 自动重装载值,PWM频率=72MHz/(999+1)=72kHz
TIM3->PSC = 0; // 预分频器
// 配置通道1和2为PWM模式1
TIM3->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | // PWM模式1
TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2;
TIM3->CCMR1 |= TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE; // 预装载使能
// 使能通道1和2输出
TIM3->CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E;
// 使能TIM3
TIM3->CR1 |= TIM_CR1_ARPE | TIM_CR1_CEN;
}
// 电机方向控制初始化
void Motor_GPIO_Init(void)
{
// 开启GPIOA时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// 配置电机方向控制引脚为推挽输出
GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0 |
GPIO_CRL_CNF1 | GPIO_CRL_MODE1 |
GPIO_CRL_CNF2 | GPIO_CRL_MODE2 |
GPIO_CRL_CNF3 | GPIO_CRL_MODE3);
GPIOA->CRL |= (GPIO_CRL_MODE0_0 | GPIO_CRL_MODE0_1 |
GPIO_CRL_MODE1_0 | GPIO_CRL_MODE1_1 |
GPIO_CRL_MODE2_0 | GPIO_CRL_MODE2_1 |
GPIO_CRL_MODE3_0 | GPIO_CRL_MODE3_1);
}
// 电机控制函数
void Motor_Control(uint8_t motor, uint8_t direction, uint16_t speed)
{
if(motor == 1) {
if(direction == 1) { // 正转
GPIOA->BSRR = MOTOR1_IN1_PIN;
GPIOA->BRR = MOTOR1_IN2_PIN;
} else { // 反转
GPIOA->BRR = MOTOR1_IN1_PIN;
GPIOA->BSRR = MOTOR1_IN2_PIN;
}
TIM3->CCR1 = speed; // 设置PWM占空比
} else if(motor == 2) {
if(direction == 1) { // 正转
GPIOA->BSRR = MOTOR2_IN1_PIN;
GPIOA->BRR = MOTOR2_IN2_PIN;
} else { // 反转
GPIOA->BRR = MOTOR2_IN1_PIN;
GPIOA->BSRR = MOTOR2_IN2_PIN;
}
TIM3->CCR2 = speed; // 设置PWM占空比
}
}
// OpenMV串口初始化
void OpenMV_USART_Init(void)
{
// 开启USART1和GPIOA时钟
RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPAEN;
// 配置PA9为复用推挽输出(TX),PA10为浮空输入(RX)
GPIOA->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9 |
GPIO_CRH_CNF10 | GPIO_CRH_MODE10);
GPIOA->CRH |= (GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9_0 | GPIO_CRH_MODE9_1 |
GPIO_CRH_CNF10_0);
// 配置USART1
USART1->BRR = 72000000 / OPENMV_BAUDRATE; // 设置波特率
USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // 使能USART, TX, RX
USART1->CR1 |= USART_CR1_RXNEIE; // 使能接收中断
}
// WiFi模块串口初始化
void WiFi_USART_Init(void)
{
// 开启USART2和GPIOA时钟
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// 配置PA2为复用推挽输出(TX),PA3为浮空输入(RX)
GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_MODE2 |
GPIO_CRL_CNF3 | GPIO_CRL_MODE3);
GPIOA->CRL |= (GPIO_CRL_CNF2_1 | GPIO_CRL_MODE2_0 | GPIO_CRL_MODE2_1 |
GPIO_CRL_CNF3_0);
// 配置USART2
USART2->BRR = 36000000 / WIFI_BAUDRATE; // 设置波特率
USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // 使能USART, TX, RX
USART2->CR1 |= USART_CR1_RXNEIE; // 使能接收中断
}
// 巡线传感器初始化
void Line_Sensor_Init(void)
{
// 开启GPIOB时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
// 配置巡线传感器引脚为浮空输入
GPIOB->CRL &= ~(GPIO_CRL_CNF4 | GPIO_CRL_MODE4 |
GPIO_CRL_CNF5 | GPIO_CRL_MODE5 |
GPIO_CRL_CNF6 | GPIO_CRL_MODE6 |
GPIO_CRL_CNF7 | GPIO_CRL_MODE7);
GPIOB->CRL |= (GPIO_CRL_CNF4_0 | GPIO_CRL_CNF5_0 |
GPIO_CRL_CNF6_0 | GPIO_CRL_CNF7_0);
}
// 读取巡线传感器状态
uint8_t Read_Line_Sensors(void)
{
uint8_t sensor_state = 0;
if(GPIOB->IDR & LINE_SENSOR1_PIN) sensor_state |= 0x01;
if(GPIOB->IDR & LINE_SENSOR2_PIN) sensor_state |= 0x02;
if(GPIOB->IDR & LINE_SENSOR3_PIN) sensor_state |= 0x04;
if(GPIOB->IDR & LINE_SENSOR4_PIN) sensor_state |= 0x08;
return sensor_state;
}
// 串口发送函数
void USART_Send(USART_TypeDef* USARTx, uint8_t data)
{
while(!(USARTx->SR & USART_SR_TXE)); // 等待发送缓冲区空
USARTx->DR = data; // 发送数据
}
void USART_SendString(USART_TypeDef* USARTx, char* str)
{
while(*str) {
USART_Send(USARTx, *str++);
}
}
// 串口接收缓冲区
volatile uint8_t openmv_rx_buffer[256];
volatile uint8_t openmv_rx_index = 0;
volatile uint8_t wifi_rx_buffer[256];
volatile uint8_t wifi_rx_index = 0;
// USART1中断服务函数(OpenMV)
void USART1_IRQHandler(void)
{
if(USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR;
openmv_rx_buffer[openmv_rx_index++] = data;
// 处理接收到的数据(条形码/二维码信息)
if(data == '\n') { // 以换行符作为结束标志
Process_Barcode_Data((char*)openmv_rx_buffer);
openmv_rx_index = 0;
}
}
}
// USART2中断服务函数(WiFi)
void USART2_IRQHandler(void)
{
if(USART2->SR & USART_SR_RXNE) {
uint8_t data = USART2->DR;
wifi_rx_buffer[wifi_rx_index++] = data;
// 处理WiFi模块响应
if(data == '\n') {
Process_WiFi_Response((char*)wifi_rx_buffer);
wifi_rx_index = 0;
}
}
}
// 处理条形码数据
void Process_Barcode_Data(char* data)
{
// 解析OpenMV发送的条形码数据
// 数据格式: "BARCODE:123456789\n"
// 记录库存信息
Add_To_Inventory(data);
// 通过WiFi发送到服务器
WiFi_Send_Data(data);
}
// 添加库存记录
void Add_To_Inventory(char* barcode_data)
{
// 实现库存记录逻辑
// 这里可以添加EEPROM存储或直接发送到服务器
}
// WiFi发送数据
void WiFi_Send_Data(char* data)
{
// 发送数据到WiFi模块
USART_SendString(USART2, "AT+CIPSEND=");
USART_SendString(USART2, itoa(strlen(data), 10));
USART_SendString(USART2, "\r\n");
Delay_ms(100);
USART_SendString(USART2, data);
}
// 巡线控制算法
void Line_Following_Control(void)
{
uint8_t sensor_state = Read_Line_Sensors();
switch(sensor_state) {
case 0x06: // 0110 - 直线前进
Motor_Control(1, 1, 600); // 左电机正转,速度600
Motor_Control(2, 1, 600); // 右电机正转,速度600
break;
case 0x04: // 0100 - 轻微左偏
Motor_Control(1, 1, 400); // 左电机减速
Motor_Control(2, 1, 700); // 右电机加速
break;
case 0x02: // 0010 - 轻微右偏
Motor_Control(1, 1, 700); // 左电机加速
Motor_Control(2, 1, 400); // 右电机减速
break;
case 0x00: // 0000 - 丢失路线,停止
Motor_Control(1, 1, 0);
Motor_Control(2, 1, 0);
break;
default:
// 保持当前状态
break;
}
}
// 系统初始化
void System_Init(void)
{
// 配置系统时钟
RCC->CR |= RCC_CR_HSEON;
while(!(RCC->CR & RCC_CR_HSERDY));
RCC->CFGR = RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC;
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
RCC->CFGR |= RCC_CFGR_SW_PLL;
while(!(RCC->CFGR & RCC_CFGR_SWS_PLL));
// 初始化各模块
Motor_GPIO_Init();
Motor_PWM_Init();
OpenMV_USART_Init();
WiFi_USART_Init();
Line_Sensor_Init();
// 配置NVIC
NVIC->ISER[0] |= (1 << USART1_IRQn) | (1 << USART2_IRQn);
}
// 主函数
int main(void)
{
System_Init();
// WiFi模块初始化
WiFi_Init();
while(1) {
// 巡线移动
Line_Following_Control();
// 处理其他任务
Delay_ms(10);
}
}
// WiFi模块初始化
void WiFi_Init(void)
{
Delay_ms(1000);
USART_SendString(USART2, "AT+RST\r\n");
Delay_ms(2000);
USART_SendString(USART2, "AT+CWMODE=1\r\n");
Delay_ms(1000);
USART_SendString(USART2, "AT+CWJAP=\"SSID\",\"PASSWORD\"\r\n");
Delay_ms(5000);
USART_SendString(USART2, "AT+CIPSTART=\"TCP\",\"192.168.1.100\",8080\r\n");
Delay_ms(2000);
}
// 简单延时函数
void Delay_ms(uint32_t ms)
{
for(uint32_t i = 0; i < ms * 1000; i++);
}
// 整数转字符串函数
char* itoa(int value, char* result)
{
char* ptr = result;
char* ptr1 = result;
char tmp_char;
int tmp_value;
do {
tmp_value = value;
value /= 10;
*ptr++ = "0123456789"[tmp_value - value * 10];
} while(value);
*ptr-- = '\0';
while(ptr1 < ptr) {
tmp_char = *ptr;
*ptr-- = *ptr1;
*ptr1++ = tmp_char;
}
return result;
}
项目核心代码
#include "stm32f10x.h"
// 硬件模块初始化函数声明
void System_Init(void);
void GPIO_Init(void);
void USART_Init(void);
void TIM_Init(void);
void NVIC_Init(void);
// 模块驱动函数声明
void Motor_Control(int left_speed, int right_speed);
void OpenMV_Process(void);
void WiFi_SendData(const char* data);
void Barcode_Scan(void);
// 全局变量
volatile uint32_t system_tick = 0;
char inventory_data[256] = {0};
int main(void)
{
// 系统初始化
System_Init();
GPIO_Init();
USART_Init();
TIM_Init();
NVIC_Init();
// 启动系统滴答定时器
SysTick_Config(SystemCoreClock / 1000);
// 主循环
while(1)
{
// 1. 自主巡线移动
Line_Following();
// 2. 扫描货物条码
Barcode_Scan();
// 3. 处理视觉数据
OpenMV_Process();
// 4. 定期发送库存数据
if(system_tick % 5000 == 0) // 每5秒发送一次
{
WiFi_SendData(inventory_data);
}
// 延时处理
Delay_ms(10);
}
}
// 系统时钟初始化
void System_Init(void)
{
// 开启时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |
RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN |
RCC_APB2ENR_USART1EN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_USART3EN |
RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN;
}
// GPIO初始化
void GPIO_Init(void)
{
// 电机控制引脚 PB6, PB7, PB8, PB9
GPIOB->CRL &= ~(0xFF << 24); // 清除PB6,PB7配置
GPIOB->CRL |= (0x0B << 24) | (0x0B << 28); // PB6,PB7复用推挽输出
GPIOB->CRH &= ~(0xFF << 0); // 清除PB8,PB9配置
GPIOB->CRH |= (0x0B << 0) | (0x0B << 4); // PB8,PB9复用推挽输出
// 串口引脚 PA9(TX), PA10(RX) - USART1
GPIOA->CRH &= ~(0xFF << 4); // 清除PA9,PA10配置
GPIOA->CRH |= (0x0B << 4) | (0x04 << 8); // PA9复用推挽, PA10浮空输入
// WiFi模块引脚 PA2(TX), PA3(RX) - USART2
GPIOA->CRL &= ~(0xFF << 8); // 清除PA2,PA3配置
GPIOA->CRL |= (0x0B << 8) | (0x04 << 12); // PA2复用推挽, PA3浮空输入
}
// 串口初始化
void USART_Init(void)
{
// USART1 - 调试和OpenMV通信
USART1->BRR = 0x1D4C; // 115200 @72MHz
USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
USART1->CR1 |= USART_CR1_RXNEIE;
// USART2 - WiFi模块通信
USART2->BRR = 0x1D4C; // 115200 @72MHz
USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
}
// 定时器初始化 - 用于PWM电机控制
void TIM_Init(void)
{
// TIM3 - 左电机 PWM CH1(PB6), CH2(PB7)
TIM3->PSC = 71; // 72MHz/72 = 1MHz
TIM3->ARR = 999; // 1MHz/1000 = 1kHz PWM频率
TIM3->CCMR1 = (0x6 << 4) | (0x6 << 12); // PWM模式1
TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E; // 开启输出
TIM3->CR1 = TIM_CR1_CEN; // 启动定时器
// TIM4 - 右电机 PWM CH1(PB8), CH2(PB9)
TIM4->PSC = 71;
TIM4->ARR = 999;
TIM4->CCMR2 = (0x6 << 4) | (0x6 << 12);
TIM4->CCER = TIM_CCER_CC3E | TIM_CCER_CC4E;
TIM4->CR1 = TIM_CR1_CEN;
}
// 中断配置
void NVIC_Init(void)
{
NVIC_EnableIRQ(USART1_IRQn);
NVIC_SetPriority(USART1_IRQn, 0);
}
// 电机控制函数
void Motor_Control(int left_speed, int right_speed)
{
// 限制速度范围
if(left_speed > 1000) left_speed = 1000;
if(left_speed < 0) left_speed = 0;
if(right_speed > 1000) right_speed = 1000;
if(right_speed < 0) right_speed = 0;
// 设置PWM占空比
TIM3->CCR1 = left_speed; // 左电机前进
TIM3->CCR2 = 0; // 左电机后退
TIM4->CCR3 = right_speed; // 右电机前进
TIM4->CCR4 = 0; // 右电机后退
}
// 巡线控制
void Line_Following(void)
{
// 简单的巡线算法 - 根据传感器数据调整电机速度
// 这里需要根据实际的巡线传感器数据来调整
Motor_Control(800, 800); // 直行
}
// 条码扫描处理
void Barcode_Scan(void)
{
// 发送扫描指令给OpenMV
USART_SendString(USART1, "SCAN\r\n");
}
// WiFi发送数据
void WiFi_SendData(const char* data)
{
if(strlen(data) > 0)
{
USART_SendString(USART2, data);
}
}
// USART1中断服务函数 - 接收OpenMV数据
void USART1_IRQHandler(void)
{
if(USART1->SR & USART_SR_RXNE)
{
uint8_t data = USART1->DR;
// 处理接收到的条码数据
// 这里添加条码数据处理逻辑
}
}
// 简单的延时函数
void Delay_ms(uint32_t ms)
{
uint32_t start_tick = system_tick;
while((system_tick - start_tick) < ms);
}
// 系统滴答定时器中断
void SysTick_Handler(void)
{
system_tick++;
}
// 串口发送字符串函数
void USART_SendString(USART_TypeDef* USARTx, const char* str)
{
while(*str)
{
while(!(USARTx->SR & USART_SR_TXE));
USARTx->DR = (*str++ & 0xFF);
}
}
总结
该智能仓储盘点机器人基于STM32主控芯片设计,实现了仓储环境中的自动化盘点任务。机器人能够沿预设轨道自主巡线移动,通过OpenMV视觉模块精准识别货物条形码或二维码,并自动记录货物信息生成库存报表,有效提升仓储管理效率。
系统硬件集成多个关键模块,包括STM32F103系列单片机作为控制核心,OpenMV摄像头负责视觉采集,ESP8266或ESP32-S3 Wi-Fi模块实现无线通信,L298N驱动板与直流电机保障移动功能,以及12V锂电池与降压模块提供稳定电源支持。
通过Wi-Fi模块,机器人可将库存数据实时传输至后台管理系统,实现远程监控和数据同步。整体设计紧凑可靠,适用于现代智能仓储场景,显著降低了人工盘点成本,并提高了数据准确性和响应速度。
- 点赞
- 收藏
- 关注作者
评论(0)