简介:面向嵌入式课程设计、期末大作业和毕业设计的一份完整工程方案,基于STM32F103C8T6与OpenMV实现六轴机械臂对多种颜色物块的识别与自动分放。控制部分为MDK工程,基于HAL库编写,源码分层清晰、便于快速移植;OpenMV视觉程序可独立修改颜色阈值,配合机械臂完成抓取放置。硬件端采用兼容常见六轴机械臂的飞控底板,外接5V电源即可驱动6路MG996R舵机,稍作参数调整即可适配市面多数机械臂结构,适合实物搭建与功能演示。
资源包共841个文件,压缩后约23.11MB,以516个C源文件和183个H头文件为主体,同时包含OpenMV的py视觉脚本、MDK工程配置、CubeMX初始化文件及说明文档。从底层驱动到应用逻辑均有覆盖,便于在现有代码上增加颜色类别、修改运动轨迹或移植到其他STM32平台。
目前已有115人学习,适合需要快速搭建机械臂视觉分拣系统的开发者。完整工程中可学习STM32定时器/PWM舵机控制、HAL库外设配置、OpenMV颜色识别及串口通信等关键环节,是一份能直接用于课程答辩与二次开发的实用资源。
1. 从颜色分选到机械臂动作:这套方案真正解决的是什么
真要做一套六轴机械臂分拣物块的系统,你首先遇见的不是机器人学,而是光线。同一个红色物块,在自然光和日光灯下给到的 LAB 取值能漂出几十个单位的差距,这会让你一度怀疑 OpenMV 的镜头挂了彩;而 STM32 这边要做的事反而简单到枯燥——收一个颜色 ID,查一张动作表,把六路舵机按规划角度送到位,再回到原点。标题里“基于 STM32 的六轴机械臂控制 + OpenMV 颜色识别,识别不同的物块分放”,本质上是一套视觉引导机械臂分拣的最小可行方案:OpenMV 负责看懂颜色,STM32 负责干体力活,两者之间用串口传递极短的信息。适合正在做毕业设计、准备电赛或机器人赛,以及想在嵌入式方向快速跑通“视觉 + 运动控制”闭环的开发者。它不需要你啃完整个机器人学,但能让你在三天内看到机械臂真的把红色方块和蓝色方块分别放进两个料槽。
2. 系统架构与硬件选型:为什么 OpenMV 只输出颜色 ID、STM32 只负责轨迹
2.1 硬件清单与职责边界:谁做什么、不做什么
这套系统的硬件构成并不复杂,常见配置如下表所示。每一部分的选型都有明确理由,而不是单纯堆性能。
| 组件 | 常见型号 | 职责 | 选型要点 |
|---|---|---|---|
| 主控 | STM32F103RCT6 或类似型号 | 串口收帧、动作表查找、PWM 输出 | F103 完全够用,不必上 F4/F7;片上资源紧张时再考虑换型 |
| 视觉模块 | OpenMV4 Cam / OpenMV M7 | 颜色识别、输出物块颜色 ID | 默认带 RGB565 感光芯片和镜头,IDE 自带阈值编辑器,调试效率高 |
| 机械臂 | 六轴舵机机械臂(角度舵机或总线舵机) | 抓取、搬运、分放 | 角度舵机成本低、驱动简单,适合课设级;总线舵机适合追求稳定性 |
| 夹爪/吸盘 | 舵机夹爪或微型真空泵 | 执行物块抓取 | 夹爪对物块形状要求高,吸盘对表面平整度要求高,二选一 |
| 电源 | 5V/6V 大电流电源,独立给舵机供电 | 提供动力 | 六路舵机同时动作时电流峰值很大,单独供电是硬要求 |
职责边界是整个架构里最重要的事。OpenMV 的算力只够做图像处理,不适合同时承担运动规划和状态管理;STM32 擅长实时控制和外设调度,但让它做颜色识别要么挂 OpenCV 库要么性能吃紧。所以我把分工定死:OpenMV 只输出“看到了什么颜色,在图像的哪个大致区域”,STM32 只负责“收到颜色 ID 之后,执行哪一段动作轨迹”。中间不要有任何跨界逻辑,否则后续联调时每加一个功能都会牵动两端。
2.2 信息流与数据格式:从物块颜色到机械臂动作
整条数据链路只有一步是视觉计算,其余全是搬运:OpenMV 采集一帧图像,找到目标物块的 blob,算出颜色类别和中心坐标。然后通过 UART 串口把结果发出去。STM32 收到串口帧,解析出颜色 ID,查表得到对应的放置槽位和抓取目标位置。最后通过定时器 PWM 输出六个通道的脉宽信号,驱动六个舵机按顺序运动。
串口帧格式必须固定下来,联调阶段九成的问题都出在收发双方对帧格式的理解不一致。我常用的帧格式是这样定义:
| 字节位置 | 内容 | 说明 |
|---|---|---|
| Byte 0 | 0xAA | 帧头 |
| Byte 1 | 0x55 | 帧头校验,防止噪声误触发 |
| Byte 2 | 颜色 ID(0x01 红,0x02 绿,0x03 蓝) | 有效数据 |
| Byte 3 | 校验和(前四字节累加取低八位) | 简单校验 |
| Byte 4 | 0x0D | 帧尾 |
为什么不用文本协议?很多入门教程喜欢让 OpenMV 直接 print("red") 然后 STM32 用串口助手看,这在纯调试阶段没问题,但一旦接入 STM32 中断收帧,文本解析要处理大小写、换行、半包截断等一系列边界条件。二进制帧配合状态机收帧,代码量少一半,出错概率也低得多。
2.3 分选动作状态机:三个动作状态与安全回中
机械臂分放物块的标准流程可以抽象成六个状态:空闲、抓取、抬起、移动、放下、回中。STM32 里的动作调度直接做成状态机,比顺序执行一堆延时函数要可靠得多。
状态机的好处在于它天然处理了“当前动作被新任务打断”的情况。比如机械臂正在把绿色物块送往绿色槽位,OpenMV 又发来一个红色物块 ID,主循环里的状态机不会立刻切换任务,而是等当前状态进入“放下”并且机械臂回到安全高度之后,才响应新的颜色 ID。这个安全回中逻辑必须写死,否则机械臂会在半路急转弯,轻则掉物块,重则撞到限位开关。
动作执行上,我一般把“抬起”这个动作单独提炼出来。任何分放任务,动作序列都是“抓取 → 抬起 → 移动 → 放下 → 回中”,其中抬起动作和回中动作完全复用,不需要为每种颜色单独写一套。颜色只决定“移动”阶段的目标槽位。这样动作表从六种全组合缩减为“三套目标槽位 + 一套通用抓取/回中”,调试时可以逐个动作验证,哪里翻车一目了然。
3. OpenMV 颜色识别:LAB 阈值标定与物块分放的颜色过滤策略
3.1 LAB 色彩空间与阈值标定:为什么 RGB 阈值稳定不住
OpenMV 官方库里的 find_blobs 函数接受的是 LAB 色彩空间阈值,而不是 RGB。这是因为 LAB 的三个通道里,L 表示亮度,A 表示从洋红到绿色的范围,B 表示从黄色到蓝色的范围。相比 RGB,LAB 把亮度信息单独拆了出来,所以当光照强度变化、物块表面出现反光时,A 和 B 通道的波动远小于 RGB 三个通道的联动漂移。
实际标定时,我发现很多初学者直接拿颜色色卡上的 RGB 值去换算 LAB 阈值,结果到现场全废。正确做法是用 OpenMV IDE 自带的阈值编辑器,对着实物在真实光照条件下采集。具体流程是先让物块充满画面,微调 L 通道上下限让物块区域与背景分离,再收缩 A 和 B 通道范围,直到只有目标颜色被选中。这个过程不是我写的,而是 OpenMV 官方 IDE 提供的标准工作流,但绝大多数翻车不是因为流程不熟,而是因为偷懒跳过了“在真实光照下标定”这一步。
3.2 用 IDE 调出稳定阈值的最小流程
打开 OpenMV IDE,连接开发板,运行一段初始化脚本,然后点击“工具 → 阈值编辑器”,选择帧缓冲区里的实时画面,接下来按四个步骤操作:
- 关闭自动白平衡和自动曝光,将曝光值固定在一个中间档位(比如 exposure_us = 20000),这一步的作用是让画面亮度不随环境自动跳变。
- 把物块放到传送带或料槽区域,让它占据画面 20%~40% 的面积。
- 在阈值编辑器里调整 L 通道下限,让背景变黑、物块保留;再调整 A、B 通道范围,把目标颜色封在白色选区里。
- 移动物块到画面四个角落,确认依然能选中;如果某个位置阈值失效,通常是反光造成的,微调 L 上限压制高光。
完成之后,把编辑器生成的阈值元组复制进主程序。下面是一段可以直接跑通的 OpenMV 颜色识别脚本骨架:
import sensor, image, time from pyb import UART # 初始化摄像头 sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) # 320x240,识别速度与精度取中间值 sensor.skip_frames(time=2000) sensor.set_auto_whitebal(False) # 关白平衡,颜色阈值才稳定 sensor.set_auto_exposure(False, exposure_us=20000) # 初始化串口,与STM32对接 uart = UART(3, 115200, timeout_char=1000) # LAB阈值:从阈值编辑器复制而来,顺序是(L_MIN, L_MAX, A_MIN, A_MAX, B_MIN, B_MAX) thresholds = { 'red': (30, 60, 30, 70, 10, 50), 'green': (45, 70, -50, -20, 20, 60), 'blue': (20, 45, -10, 20, -60, -10), } colors = {'red': 0x01, 'green': 0x02, 'blue': 0x03} def send_color(color_id): frame = bytes([0xAA, 0x55, color_id, (0xAA + 0x55 + color_id) & 0xFF, 0x0D]) uart.write(frame) while True: img = sensor.snapshot() detected = None max_area = 0 # 遍历三种颜色的阈值,分别找blob,取面积最大的为准 for name, threshold in thresholds.items(): blobs = img.find_blobs([threshold], pixels_threshold=300, area_threshold=300) for b in blobs: if b.area() > max_area: max_area = b.area() detected = name # 只有目标blob面积超过画面一定比例才认为有效 if detected and max_area > 600: send_color(colors[detected]) time.sleep_ms(50)逻辑说明:主循环每轮执行一次“快照 → 三色阈值匹配 → 面积排序 → 串口发送”。detected 变量是实时覆盖的,只有本轮面积最大的 blob 对应的颜色才会被保留。面积阈值 300 用于过滤噪点,pixels_threshold 和 area_threshold 这两个参数来自 find_blobs 的可选参数,作用是滤除掉那些只有几个像素的零散色块。串口发送使用的是固定五字节帧,帧头和帧尾自带噪声过滤能力。如果你发现识别结果频繁跳变,先把曝光固定参数调低,再看是否因为物块表面反光生成了一片高亮区,这两项的优先级最高。
3.3 颜色判定的置信度与误判策略:防串色先于防漏判
分放任务最怕的不是漏检,而是错分。漏检顶多是机械臂少抓一次,错分则是把红色物块丢进蓝色料槽,整批分放结果作废。所以我的策略是宁可不发也不发错。代码里没有直接返回“最大面积 blob 的颜色”,而是加了一层面积门槛:只有检测到的物块面积超过画面一定比例时才发送结果。这样在物块刚进入视野边缘、只有一小部分露出来的时候,系统保持静默,等物块完全进入视野后再识别。
多色阈值重叠是另一个高频问题。红色和橙色、绿色和黄色的 LAB 区间在一定光照下会重叠,find_blobs 有可能同时命中两个阈值。此时以上一轮检测结果为参考:如果上一轮已经稳定识别为红色,本轮即使绿色阈值命中了更大的噪声区域,也要等待连续三帧结果一致再切换。在代码里实现这个逻辑很简单,定义一个计数器和 last_detected 变量,连续三帧相同才发送新的颜色 ID。代价是约 150ms 的响应延迟,换来的是分放稳定性的显著提升。
4. STM32 机械臂控制:串口收帧、轨迹规划与关节驱动代码骨架
4.1 运动学与轨迹规划的落地取舍:逐点插值
六轴机械臂的完整控制需要正逆运动学解算,但分放物块这件事有很强的规律性:物块位置和槽位位置在机械臂工作空间内固定,抓取路径可以预先规划成一组关节角度序列。工程落地时不需要实时逆解,而是离线算好路径点位,运行时逐点执行。常见的做法是让机械臂从初始姿态开始,手动把夹爪移动到抓取点、抬起点、槽位点,记录下每个关键点的六路舵机角度,把角度数组写进 STM32 的常量表里。
关节空间里的运动用“逐点插值 + 定时触发”实现。每个动作点停留 200ms~500ms,让机械臂完成物理摆动后再发下一个角度指令。直接连续发送角度值很短的时间内会带动机械臂快速抖动,六轴臂的惯量叠加起来甚至会把舵机齿轮打滑。常见做法是把目标角度数组拆成多步,每步增加一个固定增量,类似软件梯形速度规划。增量大小决定运动速度,我一般用每 20ms 变化 3 到 5 度,这种速度在多数角度舵机臂上表现平顺。
4.2 串口中断收帧与分派调度:收颜色 ID 驱动动作表
STM32 侧的核心工作是两件事:用串口中断把 OpenMV 发来的帧完整收下来,以及根据颜色 ID 查找动作表并启动执行。收帧用状态机实现,和 OpenMV 发送端的帧格式一一对应:
// 串口接收状态机:等待帧头 -> 接收校验值 -> 校验 #define FRAME_SIZE 5 volatile uint8_t rx_frame[FRAME_SIZE]; volatile uint8_t rx_index = 0; volatile uint8_t rx_state = 0; // 0:等待帧头, 1:正在收数据 volatile uint8_t rx_ready = 0; // 收到完整帧置1 volatile uint8_t parse_color = 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { static uint8_t byte; if (huart->Instance == USART1) { byte = rx_frame[0]; // 从接收寄存器读出的数据存入临时变量 if (rx_state == 0) { if (byte == 0xAA) { rx_frame[0] = byte; rx_index = 1; rx_state = 1; } // 帧头不匹配就继续等,不做任何处理 } else if (rx_state == 1) { rx_frame[rx_index++] = byte; if (rx_index >= FRAME_SIZE) { // 校验:帧头0xAA + 0x55 + 颜色ID 的低8位 uint8_t sum = rx_frame[0] + rx_frame[1] + rx_frame[2]; if (rx_frame[3] == (sum & 0xFF) && rx_frame[4] == 0x0D) { parse_color = rx_frame[2]; rx_ready = 1; } else { parse_color = 0; } rx_index = 0; rx_state = 0; } } // 重新启动接收 HAL_UART_Receive_IT(huart, rx_frame, 1); } }逻辑说明:这段代码用的是 HAL 库中断接收方式,每次只收一个字节,收完立即重新使能接收中断。状态机里 rx_state 只有两态,等待帧头时不存任何数据,避免串口噪声凑出假帧;收到 0xAA 后才开始累积后续字节。收满五字节后执行帧尾和校验和双重验证,两个条件同时满足才把颜色 ID 写入 parse_color。这么做的好处是:哪怕串口线上有毛刺干扰,帧头不匹配或者校验失败,整个状态机都会回到初始态,不会出现半包数据卡死接收的问题。rx_ready 标志位给主循环轮询,主循环里检测到之后立刻查动作表,随后清零。
4.3 六路 PWM 关节输出:映射表与限位保护
STM32 控制角度舵机用定时器输出 PWM,周期固定 20ms,脉宽 500µs 到 2500µs 对应 0 到 180 度。六路输出可以挂在一个定时器的六个通道上,也可以拆成两个定时器。以 F103 为例,常见配置是 TIM1_CH1 到 CH4 接前四路舵机,TIM2_CH1 和 CH2 接后两路,两路定时器都是 72MHz 主频分频下来。
动作表在代码里的形态是六路目标脉宽的数组。每个动作点保存六个 uint16_t 值,代表六路通道的 CCR 寄存器值。执行动作点的方法是逐路写比较寄存器,然后加一段短延时,让舵机物理跟上。注意一点:不同舵机有个体差异,同样脉宽下实际角度可能偏差几度。所以动作表里的数值永远要以你手里那台机械臂实测为准,不能拿网上别人的数值直接套,这是所有舵机臂调参逃不开的玄学。
// 动作表定义:一个动作点包含六路PWM脉宽值 typedef struct { uint16_t ch1; uint16_t ch2; uint16_t ch3; uint16_t ch4; uint16_t ch5; uint16_t ch6; } ActionPoint; // 每个动作点按 [抓取点 -> 抬起点 -> 槽位点 -> 放下点 -> 回中点] 排列 const ActionPoint action_red[] = { {1500, 1200, 1800, 1500, 1000, 900}, // 抓取点 {1500, 900, 1500, 1300, 900, 900}, // 抬起点 {1900, 900, 1500, 1400, 1100, 1300}, // 红色槽位上方 {1900, 1200, 1500, 1500, 1100, 1300}, // 放下点 {1500, 1000, 1500, 1500, 1000, 1000}, // 回中点 }; void execute_action(const ActionPoint *action, uint8_t len) { uint8_t i; for (i = 0; i < len; i++) { __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, action[i].ch1); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, action[i].ch2); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, action[i].ch3); __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, action[i].ch4); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, action[i].ch5); __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, action[i].ch6); HAL_Delay(300); } } void dispatch_by_color(uint8_t color_id) { switch (color_id) { case 0x01: // 红色 execute_action(action_red, sizeof(action_red) / sizeof(ActionPoint)); break; case 0x02: // 绿色 execute_action(action_green, sizeof(action_green) / sizeof(ActionPoint)); break; case 0x03: // 蓝色 execute_action(action_blue, sizeof(action_blue) / sizeof(ActionPoint)); break; default: break; } }逻辑说明:execute_action 函数按顺序执行动作表里的每个动作点,每执行一个点后延时 300ms,等六路舵机到达目标角度后进入下一个点。dispatch_by_color 根据串口解析出的颜色 ID 选择对应的动作表执行。如果使用的是吸盘而不是夹爪,在“抓取点”和“放下点”前后还要各加一段 GPIO 控制逻辑,用来打开和关闭真空泵;夹爪则相对简单,只需在抓取点让舵机转到闭合角度即可。
限位保护是必须做的。机械臂在到达机械限位后仍收到大脉宽指令,舵机会堵转发热,几次之后齿轮就打滑。常见做法是在动作表里夹入一个“软限位校验函数”,在写 CCR 之前判断六个通道的目标脉宽是否在 800µs 到 2200µs 范围内,超出则拒绝执行并回中。这类保护和中断收帧逻辑一样,属于“写过一次以后所有项目都能复用”的代码资产。
5. OpenMV + STM32 联调避坑:五个最常翻车的地方
5.1 光线一变颜色就飘,识别率从 95% 掉到一半
现象:上午调试时红色物块识别率接近满分,下午移到窗边或者打开日光灯之后,红色阈值把橙色纸片也当成目标,甚至完全不识别红色物块。
原因:OpenMV 的自动曝光和自动白平衡是默认开启的,环境光一旦变化,传感器会自动调整增益和白平衡参数,LAB 阈值虽然比 RGB 稳定,但 L 通道的漂移依然会让原有阈值失效。
解决:在初始化代码里把 set_auto_whitebal(False) 和 set_auto_exposure(False) 写死,并固定 exposure_us 参数。每次改动工作台位置或灯光方向后,重新用阈值编辑器跑一遍四角验证。如果你不希望每次开机手动调,可以在程序里加一段“开机自动曝光采样”逻辑:上电后前 20 帧图像只用来统计亮度均值,再根据均值从预置的三组阈值里选一组。这个方法不完美,但能覆盖早晚光线差异。
5.2 串口偶发丢帧,红色物块被放进蓝色区域
现象:分放一百个物块,出现了两三次红色物块进了蓝色槽位。OpenMV 端打印日志显示发送的是 0x01,但 STM32 端解析出来的颜色 ID 变成了 0x03。
原因:串口接收状态机在校验失败后把 parse_color 清零,但主循环里没及时处理,下一帧合法数据还没来之前,某个残留变量参与了动作表查找。更隐蔽的原因是 OpenMV 发送端在 uart.write 之前没有等待上一帧发送完成,数据被新帧覆盖。
解决:STM32 端只在 rx_ready 位置 1 时读取 parse_color,读完立刻清零,绝不允许多次读取同一帧数据。OpenMV 端发送前加 uart.wait_TX 或在发送函数里加一个 5ms 延时,确保上一帧完全移出串口 FIFO。如果丢帧仍然存在,降低串口波特率从 115200 降到 38400,物块分放场景数据量极小,38400 完全足够,换来的是更高的抗干扰能力。
5.3 机械臂末端偏差逐关节累积,夹爪总是差一公分才碰到物块
现象:单个关节转动角度看起来都正确,但六个关节叠加起来,末端位置偏离目标位置一公分以上,夹爪合拢时总是擦着物块边缘滑过去。
原因:六轴机械臂的每个舵机都存在中立点误差,两个舵机臂杆公差叠加后,末端位置误差会被放大。这是角度舵机方案的物理硬伤,不是算法能解决的。
解决:动作表里的目标角度数值必须实测修正,不能依赖图纸参数。标定方法是:在机械臂末端绑一支记号笔,控制单关节分别转动到多个角度,在纸上标注实际末端轨迹,然后反算舵机脉宽与实际角度的映射偏移量。如果误差源主要来自某个舵机的线性度,可以用一个“脉宽-角度校准表”对那个关节单独补偿。多数课设级机械臂通过这种一次性标定能把误差压到 3mm 以内,对普通物块抓取已经够用。
5.4 电源功率不足,机械臂动作时 STM32 突然重启
现象:六路舵机同时动作的瞬间,STM32 复位,OpenMV 仍然亮着,串口通信中断。单独动作某一路时一切正常。
原因:舵机堵转或瞬态电流超过电源输出能力,电压跌落导致 STM32 的 3.3V 稳压器进入欠压复位。很多廉价舵机臂配的电源宣称 10A 峰值,实际持续输出能力只有一半。
解决:把舵机电源和 STM32 电源完全分开,舵机用 5V/6V 独立电源,STM32 用 USB 或单独 5V 适配器供电,两者共地但不在同一根电源线上。在舵机电源端并联一个 1000µF 以上电解电容和一个 0.1µF 陶瓷电容,吸收瞬态电流。如果你用的还是劣质电源,换一台输出电流 2A 以上的稳压电源做舵机供电,问题通常直接消失。
5.5 OpenMV 和 STM32 串口电平不匹配,通信时好时坏
现象:OpenMV 接 STM32 的串口,有时候能收数据,有时候收不到,用手同时摸两块板子的金属外壳时通信恢复。
原因:OpenMV 的 UART 引脚是 3.3V TTL 电平,STM32 也是 3.3V TTL 电平,两者本来可以直接连接。但部分 OpenMV 模块的 UART3 引脚被复用或者配备了一键下载电路,板上电平被拉到 5V,与 STM32 的 3.3V 引脚直连后造成串口状态不确定。
解决:确认两块板子的型号和引脚定义,用万用表量一下 OpenMV TX 引脚空载电压,如果超过 3.6V 就不要直连,在数据线上串一个 1kΩ 电阻做分压。另外 OpenMV 的串口 TX 和 RX 不要接反,这是所有串口联调里最容易被忽略的翻车点——连接线颜色一致不等于接口顺序一致,上电前先查原理图。
6. 分放准确率验证与轨迹参数的调参顺序
系统联调完成后,最直观的验证方法是连续分放测试:准备 30 个物块,颜色各 10 个,随机混放,统计“漏检、错分、抓取失败、放位偏差”四类指标。重点不是单次跑通,而是连续跑三轮数据是否一致。我常用的测试脚本是一边在 OpenMV IDE 里打开串口终端,一边看 STM32 的调试串口输出颜色 ID 和当前状态,两边日志对齐后比对分放结果,定位出问题发生在视觉段还是运动段。
调参顺序也有讲究,先调视觉再调运动。具体来说:第一,固定曝光和白平衡,标定三色阈值,确保在测试台上任意摆放位置都能稳定输出颜色 ID。第二,单独验证每个动作点的脉宽值,逐点确认机械臂到达位置与预期一致,不做连续动作,避免误差叠加后难以定位。第三,把动作点串成完整动作链,在无物块的情况下空跑十次,观察机械臂是否每次都能回到相同的回中点。第四,才放入真实物块做抓取测试,此时如果失败,原因基本集中在夹爪舵机的闭合角度或抓取点的 Z 向高度上,不会干扰前面已验证的部分。
一套值得长期留着的习惯是把每轮测试的阈值和动作表都备份一份,文件名带日期和光照条件。我吃过一次亏,把调试到完美的红色阈值随手改了,第二天现场识别率暴跌,想回退却找不到备份,只能重新标定。后来每次联调前都先在工程里留一个 config_xxx.h 的副本,标定完就锁版本。这套“基于 STM32 的六轴机械臂 + OpenMV 颜色识别”的方案,最终能走到多稳,拼的不只是代码,而是你是否愿意为每一个参数留记录。希望帮到你。
本文还有配套的精品资源,点击获取