【工创赛2025-智能物流搬运塔吊方案开源(2分15秒)】西安理工大学工程训练中心
一、前言
时光荏苒,岁月如梭。三年的本科竞赛生涯随着工训赛的结束告一段落。竞赛路途中,受到了诸多大佬的帮助和鼓励。为了将这份开源精神传递下去,本团队全体成员一致决定无偿开源本项目机械设计图纸、PCB设计、电控代码、视觉代码及镜像文件、参赛文档以及其他有关设计资料。
请注意,本项目开源文件完全免费,内容遵循CC 4.0 BY-NC-SA版权协议,转载请给出适当的署名,不可用作商业用途,严禁倒卖,若广大网友发现以上行为,请第一时间与我取得联系。
在此,由衷感谢西安理工大学工程训练中心的各位老师对我们竞赛项目的悉心指导与鼎力支持。

这里放一张二代小车同堂的照片作为纪念

二、关于开源项目
运行视频:[开源]2025工训赛智能物流搬运,初赛第八,2分26秒_哔哩哔哩_bilibili
本项目参与了2025年中国大学生工程实践与创新能力大赛全国总决赛,初赛成绩仅1个二环,其余均为一环,总时间2分26秒。决赛由于准备不足以及现场不可预料的因素,成绩不算理想,最后总成绩为全国特等奖。
整车机械、电控、视觉及硬件均自主开发,核心 PCB(主控 / 电源 / 拓展 / 塔吊转接)自主绘制打样,保障硬件系统的高集成与可靠性。机械结构创新性采用 “塔吊”设计,灵活性大大提高。
视觉算法通过抗干扰优化,在强光干扰或目标部分遮挡的情况下仍然可以准确识别目标;底盘定位结合麦克纳姆轮逆运动学解算,配合视觉校正算法,实现 ±2mm 级精度以及全流程陀螺仪无偏差,可精确按照预定轨迹运动,并无误差返回出发点。

开源内容分为三个部分,团队队员将会自行整理各自负责的工作内容,编写开源文档呈现给大家,本文作为导航。
电路硬件设计及电控代码开源:本文
机械模型开源:【工创赛2025-塔吊方案机械开源(2分15秒)】西安理工大学工程训练中心-CSDN博客
视觉算法开源:【工创赛2025-塔吊方案视觉开源(2分15秒)】西安理工大学工程训练中心-CSDN博客
三、电路硬件设计及电控
1.硬件设计与PCB工程文件
1.主控板采用STM32F407VET6单片机,自主绘制核心板,核心板具有以下元件及功能:

| 元器件 | 功能 |
| CH340 | 与工控机通讯 |
| MCP2551 | 塔吊电机CAN通讯 |
| MAX3232 | OPS(RS232转串口) |
| 0.96寸OLED | 显示系统状态 |
| 2.42寸OLED | 显示任务码 |


| 正面元件(红色) | 对象 | 背面元件(绿色) | 对象 |
| 1 | 塔吊总接口 | 1 | 蜂鸣器 |
| 2 | 7针OLED接口(0.96寸) | 2 | 电源板接口 |
| 3 | 塔吊35步进接口 | 3 | OPS接口 |
| 4 | 工控机USB接口 | 4 | 扩展板接口 |
| 5 | 一键启动接口 | ||
| 6 | 三色LED指示灯 | ||
| 7 | 遥控器接口(调试用,比赛拆除) | ||
| 8 | 7针OLED接口(2.42寸,模拟SPI) | ||
| 9 | 烧录接口 | ||
| 10 | 复位按钮 | ||
| 11 | BOOT0按钮 |
2.电源板:整车采用6S供电,电池24V直接接入电机,并有自制开关电源模块(也在PCB工程当中)输出5V作为逻辑供电,通过隔离电源输出12V作为工控机供电,保证工控机供电稳定。支持电池热替换,换电池无需关闭工控机。

| 序号 | 对象 |
| 1 | 电源总开关(带灯,控制MOS管) |
| 2 | DM4310电机CAN信号 |
| 3 | DM4310电机供电 |
| 4、13 | 电机接口(串口总线不分顺序) |
| 5 | 5V稳压模块 |
| 6 | 5VUSB供电 |
| 7 | 主控板接口 |
| 8 | 工控机12V供电 |
| 9 | 隔离电源 |
| 10 | 隔离电源开关(严禁带电操作) |
| 11 | 电池热替换接口(二极管防倒灌) |
| 12 | 主供电接口 |
使用的隔离电源
3.主控板扩展板带有急停按钮,一键切断动力电(程序也可以控制动力电,急停按钮是强制下拉),此外预备了一个ESP01插槽和一些通信接口与供电接口。

| 正面元件(红色) | 对象 | 背面元件(绿色) | 对象 |
| 1 | 扩展排针 | 1 | 主控板连接器 |
| 2 | HDMI屏幕供电(5V) | 2 | ESP01跳线排针 |
| 3 | 急停按钮指示灯 | 3 | ESP01插槽 |
| 4 | 急停按钮 | ||
| 5 | 扫码摄像头固定孔 | ||
| 6 | 供电排针 |
4.塔吊综合板的作用类似一个分线器,把塔吊总接口的线束按照功能转换为接口,在早期的版本中,这里设计了一个STM32H750用于刷OPENMV固件进行扫码,后经过多方权衡,将OPENMV置于主控板拓展板,省赛之后又换成USB摄像头扫码。

| 序号 | 对象 |
| 1 | 备用舵机接口 |
| 2 | 夹爪舵机接口 |
| 3 | 28步进电机(伸缩控制)接口 |
| 4 | 摄像头补光灯控制接口 |
| 5 | 主控板接口 |
| 6 | 电源指示灯 |
| 7 | 降压模块(输出8V给舵机) |
5.电池PCB:苦于电池价格高昂、维修不便、形状不方便隐藏的问题,我们自主设计了一块6S电池盒,采用6节18650(3500mah)串联,经测试可以给车供电1小时以上。

| 序号 | 对象 |
| 1 | 平衡头接口(注意线序) |
| 2 | 立焊XT60输出接口 |
电池成品
其他的PCB为转接板或功能较为简单的模块,在此不做过多介绍。
6.连接线的制作
PCB模块之间的连接线多采用FC灰排线,包括塔吊的连接线,但是FC灰排线质地较软,不耐磨,省赛前一天晚上塔吊绝缘皮被磨破,整车大短路,JestonOrinNano烧毁,连夜移植了哪吒开发板勉强使用。省赛结束后为了解决这个安全隐患,经过一个周的选型和测试,最终使用TRVV工业拖链电缆自行压接牛角座。经测试该线强度高,抗拉扯,柔软适中,十分合适。
另外,市面上没有找到质地柔软且纤细的USB摄像头线,我们经过多次选型,使用超细特氟龙屏蔽线自制USB线缆,线缆直径仅1.5mm,抗屏蔽性能良好。因制作较为复杂,后续可能会考虑出售成品。
通过该图片可以看到塔吊的拖链电缆和超细USB线
2.电控逻辑实现及主要算法
1.主体框架
整车采用FreeRTOS操作系统,任务如下:
| 任务 | 功能 |
| MAIN_TASK | 主任务,实现工作流程 |
| UART_RX_TASK | 串口空闲中断之后的解包任务 |
| CHASSIS_TASK | 底盘定位任务 |
| TOWER_TASK | 塔吊动作任务 |
| OUTPU_TASK | 信息的输入或输出任务 |
下面以以上任务为模块进行介绍
2.底盘
底盘采用OPS平面定位系统配合麦克纳姆轮运动学逆解算,可以实现小陀螺位置环,即一边旋转一边向目标位置移动。诚然,OPS成本较高,我们的OPS是学校多年前购买的,可以考虑使用光流计进行替代,亦或是放弃小陀螺,单纯使用步进电机脉冲进行开环位置控制,但无法克服打滑的问题。
/**
* @brief 麦克纳姆轮底盘移动控制函数
* @param x 目标X坐标
* @param y 目标Y坐标
* @param z 目标角度(绕Z轴)
* @note 实现底盘向目标位置(x,y)和目标角度z的移动控制,包含PID速度调节和运动学解算
*/
void chassis_move(int x, int y, int z)
{
// 运动参数限制值定义
XYVmax = 1600; // X/Y方向最大速度限制
ZVmax = 750; // 旋转方向最大速度限制
XYVmin = 5; // X/Y方向最小速度限制
ZVmin = 5; // 旋转方向最小速度限制
int Speed[4] = {0}; // 四个轮子的目标速度数组(FL, FR, BL, BR)
int devx, devy, devz; // 位置和角度的偏差值
// 计算X方向位置偏差(当前位置到目标位置的差值)
devx = pos_x - x;
// 计算Y方向位置偏差
devy = pos_y - y;
// 计算角度偏差(考虑最短路径,避免超过180度的旋转)
if (z * zangle < 0 && abs(z) + fabs(zangle) > 180)
devz = -(360 - abs(z) - fabs(zangle)); // 反向旋转更优
else
devz = z - zangle; // 直接计算偏差
// 速度分量变量定义
float vx1 = 0, vy1 = 0; // 用于合成速度的中间变量
float vx2 = 0, vy2 = 0;
float vz = 0; // 旋转速度分量
// 根据Y方向偏差计算速度分量(含角度补偿)
vy1 = cos(zangle * 3.141f / 180) * mKpy * devy; // 角度余弦分量
vx2 = sin(zangle * 3.141f / 180) * mKpy * devy; // 角度正弦分量
// 限制速度在安全范围内(最大、最小、死区)
numerical_limit(&vy1, XYVmax, XYVmin, 5);
numerical_limit(&vx2, XYVmax, XYVmin, 5);
// 根据X方向偏差计算速度分量(含角度补偿)
vy2 = sin(zangle * 3.141f / 180) * mKpx * devx; // 角度正弦分量
vx1 = cos(zangle * 3.141f / 180) * mKpx * devx; // 角度余弦分量
// 限制速度在安全范围内
numerical_limit(&vy2, XYVmax, XYVmin, 5);
numerical_limit(&vx1, XYVmax, XYVmin, 5);
// 计算旋转速度分量(比例控制)
vz = mKpz * devz;
numerical_limit(&vz, ZVmax, 0, 5); // 限制旋转速度
// 麦克纳姆轮运动学逆解:计算四个轮子的目标速度
// 注:公式根据轮子安装方式和辊子角度推导,符号对应电机转向
Speed[0] = - (vy1 - vy2 + vx1 + vx2 + vz); // 前左轮(FL)
Speed[1] = (vy1 - vy2 - vx1 - vx2 - vz); // 前右轮(FR)
Speed[2] = - (vy1 - vy2 - vx1 - vx2 + vz); // 后左轮(BL)
Speed[3] = (vy1 - vy2 + vx1 + vx2 - vz); // 后右轮(BR)
// 速度平滑处理与执行(限制加速度,避免冲击)
for (uint8_t i = 0; i < 4; i++)
{
// 加速限制:正向加速不超过20单位/周期
if (Speed[i] > 0 && Speed[i] > last_Speed[i])
Speed[i] = last_Speed[i] + 20;
// 减速限制:负向加速不超过20单位/周期
if (Speed[i] < 0 && Speed[i] < last_Speed[i])
Speed[i] = last_Speed[i] - 20;
// 速度转换:将计算速度转换为电机目标值(系数0.238为硬件相关参数)
SpeedTarget[i] = (int)(Speed[i] * 0.238);
// 保存当前速度用于下一次加速度限制
last_Speed[i] = Speed[i];
}
// 判断是否接近目标位置(用于状态反馈)
if ((devx < 100 && devx > -100) &&
(devy < 100 && devy > -100) &&
(devz < 30 && devz > -30))
near_pos = 1; // 标记为接近目标
// 判断是否到达目标位置(带延迟确认,防止抖动)
if ((devx < 60 && devx > -60) &&
(devy < 60 && devy > -60) &&
(devz < 15 && devz > -15))
delay_pos++; // 累计满足条件的周期数
else
delay_pos = 0; // 不满足则重置计数器
// 连续10个周期满足条件则判定为到达目标
if (delay_pos > 10)
in_pos = 1; // 标记为已到达目标
}
许多队伍遇到陀螺仪或OPS有累积误差的问题,我们是通过视觉校准的方式解决的,每次机器人看到三色环,通过左环与右环相减校准Z轴,中间环坐标与左环右环中点坐标加权校准OPS位置,整个比赛过程中校准2次,达到了比较好的精度效果。
identify_posture(1500, 1700); //识别姿态
Chassis_Go_Pos(1900, 1020, 0, 0, 100); //底盘目标更新
CRing_Dual_Calibration(); //色环双重校准
Update_OPS(1940, 1050, 0); //更新OPS位置
底盘主任务分为自动模式与遥控模式,自动模式又分为场地位置闭环和视觉校准闭环,得益于RTOS的多任务调度,底盘可以和其他模块并行运行,互不干扰。
//-------------------------------------------------------------------------------------------------------------------
// @brief 底盘主任务
// @return /
// @param /
// @Sample usage: /
//-------------------------------------------------------------------------------------------------------------------
void Chassis_task(void) //底盘任务
{
Motor_Init();
osDelay(100);
while(!ready) { osDelay(10);}
while(1)
{
if(control_mode==0)
{
switch(chassis_mode)
{
case 1:
chassis_move(X_target + d_X, Y_target + d_Y, Z_target); //底盘OPS定位
break;
case 2:
if (v_in_pos == 1) //底盘视觉校准
SpeedTarget_stop(); //校准完成电机停转
osDelay(5);
break;
}
}
else if(control_mode==1)
{
chassis_RC(RC_RX.LX,RC_RX.LY,RC_RX.RX); //底盘遥控模式
}
SetMotorVoltageAndDirection(SpeedTarget[0], SpeedTarget[1], SpeedTarget[2], SpeedTarget[3]); //速度提交
osDelay(5);
}
}
3.塔吊
塔吊与底盘类似,一直处于位置更新状态,在其他任务中设置目标值(编写动作组)以执行动作。因为偷懒没有写到位检测,而只是用延时进行了替代,这样而来修改动作组较为耗时,且有可能导致塔吊撞击车身使得结构件损坏(决赛夹爪断了呜呜呜)。
//-------------------------------------------------------------------------------------------------------------------
// @brief 塔吊主任务函数
// @return 无返回值
// @param 无参数
// @Sample usage: 作为RTOS任务创建,负责塔吊系统的主控制逻辑
//-------------------------------------------------------------------------------------------------------------------
void Tower_task(void)
{
// 初始化塔吊系统
OS_Tower_Init();
// 等待系统就绪
// 循环等待ready标志位为真,未就绪时每10ms延时一次
while(!ready)
{
osDelay(10); // RTOS延时函数,释放CPU资源
}
// 更新塔吊初始目标位置
// 参数含义:(X坐标, Y坐标, Z坐标),其中h_comp为高度补偿值
Tower_target_updata(2300, 1850 + h_comp, -250);
// 主控制循环,持续运行塔吊控制逻辑
while(1)
{
// 控制模式0:自动控制模式
if(control_mode == 0)
{
// 控制28电机到目标位置(带半径补偿)
// 参数:目标位置 = 基础目标位置 + 半径补偿值d_radius,最大速度2000
Motor28_AbsPosition(Motor28_target + d_radius, 2000);
osDelay(1); // 短暂延时,确保指令执行
// 控制35电机到目标位置
// 参数:目标位置Motor35_target,最大速度2800
Motor35_AbsPosition(Motor35_target, 2800);
osDelay(1); // 短暂延时,确保指令执行
// 控制G6220电机到目标位置(带角度补偿)
// 参数:目标位置 = 基础目标位置 + 角度补偿值d_theta,最大速度300
G6220_Position(MotorDM_target + d_theta, 300);
}
// 控制模式1:遥控器手动控制模式
else if(control_mode == 1)
{
// 通过遥控器左旋钮控制28电机
Motor28_AbsPosition((RC_RX.KNOB_L + 100) * 15, 1500);
osDelay(5); // 延时等待电机响应
// 通过遥控器右旋钮控制35电机
Motor35_AbsPosition((RC_RX.KNOB_R + 100) * 10, 3000);
osDelay(5); // 延时等待电机响应
}
// 主循环延时,控制任务执行频率(约100Hz)
osDelay(10);
}
}
以下是一个车上放置物料的动作组示例
//-------------------------------------------------------------------------------------------------------------------
// @brief 控制机械臂在车内放置物料
// @return 无返回值
// @param order 放置序号(1/2/3),对应车内不同的放置位置
// @Sample usage: put_materials_car(2); // 在车内第二个位置放置物料
//-------------------------------------------------------------------------------------------------------------------
void put_materials_car(uint8_t order)
{
short theta = 0; // 机械臂旋转角度目标值(含补偿)
short delay = 1000; // 动作间基础延时时间(ms)
// 根据放置序号选择对应的角度参数和延时
switch (order)
{
case 1: // 第一个放置位置
theta = 265 + 20; // 角度值(基础角度+补偿值)
delay = 1000; // 该位置对应的移动延时
break;
case 2: // 第二个放置位置
theta = 0 + 18; // 角度值(基础角度+补偿值)
delay = 800; // 该位置对应的移动延时
break;
case 3: // 第三个放置位置
theta = -265 + 40; // 角度值(基础角度+补偿值)
delay = 600; // 该位置对应的移动延时
break;
}
// 放置物料的动作流程(按时间顺序执行)
// 1. 移动机械臂到放置点上方准备位置(含高度补偿)
Tower_target_updata(1840, 1900 + h_comp, theta);
osDelay(delay); // 等待移动完成
// 2. 机械臂下降到放置高度
Tower_target_updata(1840, 1700, theta);
osDelay(150 + t_comp); // 等待下降完成(含时间补偿t_comp)
// 3. 打开夹爪释放物料
LFD_01M_OP_C; // 夹爪打开控制指令
osDelay(100); // 等待夹爪完全打开
// 4. 机械臂上升离开放置点(回到准备高度)
Tower_target_updata(1840, 1900 + h_comp, theta);
osDelay(100 + t_comp); // 等待上升完成(含时间补偿t_comp)
// 5. 机械臂返回初始待命位置
Tower_target_updata(1500, 1900 + h_comp, -270);
osDelay(100 * (3 - order)); // 等待返回完成(序号越小,等待时间越长)
}
4.串口解包
所有串口使用空闲中断+DMA传输实现了不定长度数据包接收,当空闲中断触发,DMA搬运完成,把缓冲区指针通过队列传输给解包任务,进行相应的数据解包。
参考了这位大佬的文章,大家可以去支持一下:STM32HAL库CubeMx FreeRTOS 消息队列 DMA串口空闲中断接收消息_stm32 freertos 串口接收数据-CSDN博客
/* USER CODE END Header_UART_RX_Task */
/**
* @brief UART接收任务函数
* @param argument: 任务参数(未使用)
* @note 负责处理多个UART接口的数据接收与解包,包括UART1~UART6
* 采用队列机制接收中断传来的数据,实现异步处理
*/
void UART_RX_Task(void *argument)
{
/* USER CODE BEGIN UART_RX_Task */
// 串口功能定义
// UART1 -- 连接NANO设备
// UART2 -- 连接OPS设备
// UART4 -- 连接底盘系统
// UART5 -- 连接遥控器(RC)
// UART6 -- 连接ESP01无线模块
// 定义各串口接收数据结构体指针
UART2_RX_TypeDef *pRecvUart2Data; /* 指向UART2接收数据的指针 */
UART5_RX_TypeDef *pRecvUart5Data; /* 指向UART5接收数据的指针 */
UART1_RX_TypeDef *pRecvUart1Data; /* 指向UART1接收数据的指针 */
// 打印任务初始化信息
printf("Uart Thread init. \r\n");
osDelay(500); // 初始化延时,等待外设就绪
// System_message("Uart RX init"); // 系统消息提示(注释掉的备选方案)
/* 无限循环处理接收数据 */
for(;;)
{
/* 检查UART2队列是否有数据 */
/* xQueueReceive参数说明:
- 队列句柄:uart2_queueHandle
- 接收缓冲区:存储指向接收到的数据结构体的指针
- 等待时间:0(非阻塞,立即返回)
*/
if (xQueueReceive(uart2_queueHandle, &pRecvUart2Data, 0) == pdTRUE)
{
/* 对接收到的UART2数据进行解包处理(OPS设备数据) */
POS_Data_Unpack(pRecvUart2Data->buffer);
}
/* 检查UART5队列是否有数据(遥控器数据) */
if (xQueueReceive(uart5_queueHandle, &pRecvUart5Data, 0) == pdTRUE)
{
/* 对接收到的UART5数据进行解包处理(遥控器数据) */
RC_Data_Unpack(pRecvUart5Data->buffer);
}
/* 检查UART1队列是否有数据(NANO设备数据) */
if (xQueueReceive(uart1_queueHandle, &pRecvUart1Data, 0) == pdTRUE)
{
/* 对接收到的UART1数据进行解包处理(NANO设备数据) */
NANO_Data_Unpack(pRecvUart1Data->buffer);
}
// 任务延时5ms,降低CPU占用率
osDelay(5);
}
/* USER CODE END UART_RX_Task */
}
5.主任务
主任务主要进行各个任务的调度和目标更新,尽量简洁的写明整个任务的流程。以下是初赛第一圈的示例。
// 底盘移动到出库位置(坐标:X=150, Y=165,角度=-45°,模式=0,速度=300)
Chassis_Go_Pos(150, 165, -45, 0, 300); // 出库
// 重置塔吊姿态(可能包括位置、角度校准,确保初始状态一致)
reset_posture();
// 移动到扫码位置(坐标:X=730, Y=130,角度=-90°,模式=0,速度=10)
Chassis_Go_Pos(730, 130, -90, 0, 10); // 扫码
// 获取任务代码(如物料颜色、加工指令等任务信息)
Get_Task_Code();
// 显示任务代码(可能通过显示屏或指示灯展示)
Show_Task_Code();
/* 第一轮任务执行 */
// 移动到物料台(坐标:X=1450, Y=110,角度=-90°,模式=0,速度=100)
Chassis_Go_Pos(1450, 110, -90, 0, 100); // 物料台
// 注释:等待启动信号(通过GPIO读取启动引脚,未启用)
// while(!(HAL_GPIO_ReadPin(Start_GPIO_Port,Start_Pin))) {osDelay(50);} // 物料盘抓取
// 从物料台抓取第1个颜色任务对应的物料(位置1)
grab_turntable_B(color_task[0],1);
// 抓取第2个颜色任务对应的物料(位置2)
grab_turntable_B(color_task[1],2);
// 抓取第3个颜色任务对应的物料(位置3)
grab_turntable_B(color_task[2],3);
// 移动到T1路口(坐标:X=1050, Y=165,角度=0°,模式=1,速度=300)
Chassis_Go_Pos(1050, 165, 0, 1, 300); // T1路口
// 移动到粗加工区1(坐标:X=1050, Y=1900,角度=0°,模式=1,速度=300)
Chassis_Go_Pos(1050, 1900, 0, 1, 300); // 粗加工区1
// 旋转到加工角度(坐标不变,角度调整为90°,模式=1,速度=50)
Chassis_Go_Pos(1050, 1900, 90, 1, 50); // 组加工区2
// 调整识别姿态(参数为识别相关坐标)
identify_posture(1500, 1700); // 识别姿态
// 移动到加工操作位置(坐标:X=1000, Y=1950,角度=90°,模式=0,速度=300)
Chassis_Go_Pos(1000, 1950, 90, 0, 300);
// 执行色环双重校准(提高物料颜色识别精度)
CRing_Dual_Calibration(); // 色环双重校准
// 从车上抓取第1个物料,放置到地面(对应第1个颜色任务)
grab_materials_car(1, 0);
put_materials_ground(color_task[0],0);
// 抓取第2个物料,放置到地面(对应第2个颜色任务)
grab_materials_car(2, 0);
put_materials_ground(color_task[1],0);
// 抓取第3个物料,放置到地面(对应第3个颜色任务)
grab_materials_car(3, 0);
put_materials_ground(color_task[2],0);
// 重置塔吊姿态
reset_posture();
// 从地面抓取第1个颜色任务的物料,放置到车上位置1
grab_materials_ground(color_task[0],1);
put_materials_car(1);
// 抓取第2个颜色任务的物料,放置到车上位置2
grab_materials_ground(color_task[1],0);
put_materials_car(2);
// 抓取第3个颜色任务的物料,放置到车上位置3
grab_materials_ground(color_task[2],0);
put_materials_car(3);
// 移动到下一个位置(坐标:X=1000, Y=1880,角度=90°,模式=1,速度=300)
Chassis_Go_Pos(1000, 1880, 90, 1, 300);
// 移动到下一区域(坐标:X=1860, Y=1880,角度=90°,模式=1,速度=300)
Chassis_Go_Pos(1860, 1880, 90, 1, 300);
// 旋转调整角度(坐标不变,角度调整为0°,模式=1,速度=300)
Chassis_Go_Pos(1860, 1880, 0, 1, 300);
// 再次调整识别姿态
identify_posture(1500, 1700); // 识别姿态
// 移动到目标位置(坐标:X=1900, Y=1020,角度=0°,模式=0,速度=100)
Chassis_Go_Pos(1900, 1020, 0, 0, 100);
// 色环双重校准(再次校准,确保后续操作精度)
CRing_Dual_Calibration(); // 色环双重校准
// 更新OPS坐标(可能是视觉定位系统的坐标更新)
Update_OPS(1940, 1050, 0);
// 注释:等待启动信号(未启用)
// while(!(HAL_GPIO_ReadPin(Start_GPIO_Port,Start_Pin))) {osDelay(50);}
// 从车上抓取物料并放置到地面(重复之前的物料转移逻辑)
grab_materials_car(1, 0);
put_materials_ground(color_task[0],0);
grab_materials_car(2, 0);
put_materials_ground(color_task[1],0);
grab_materials_car(3, 0);
put_materials_ground(color_task[2],0);
reset_posture();
// 移动到目标位置(坐标:X=1900, Y=165,角度=-90°,模式=1,速度=300)
Chassis_Go_Pos(1900, 165, -90, 1, 300);
其他功能的具体实现在工程文件中均有体现,在工程开始初期就考虑到了开源的可能性,所有文件名、变量、任务名均由英文组成,如果遇到疑惑,可以删除下划线丢进百度翻译。代码包含详细注释,函数的功能、参数、返回值均有说明。
四、开源文件链接
百度网盘:https://pan.baidu.com/s/52RIeV8DY5et8Gdv43Estjw(停止更新)
gitee链接:工创赛2025-智能物流搬运塔吊方案: 工创赛2025-智能物流搬运塔吊方案开源
进群下载文件:940304442

五、后记
第一次开源如此规模的项目,恐有不周,感谢谅解。
有任何问题可以在下方留言,如果大家有需求,我会建立一个QQ群供大家交流讨论。
QQ群:940304442(开源文件同步更新)
全体作者:
- 空城少年_Space的个人空间-空城少年_Space个人主页-哔哩哔哩视频
- KRFranz的个人空间-KRFranz个人主页-哔哩哔哩视频
- 雾霭烟_的个人空间-雾霭烟_个人主页-哔哩哔哩视频
- Morii_an的个人空间-Morii_an个人主页-哔哩哔哩视频
西安理工大学 2025年8月

六、更新日志
| 时间 | 更新内容 |
| 2025年8月11日 | 初稿发布 |
| 2025年8月11日 | 勘误 |
| 2025年8月12日 | 更新下载链接 |
| 2025年8月13日 | 添加视觉算法开源链接 |
更多推荐


所有评论(0)