简介围绕2023年全国大学生电子设计竞赛E题整理的参考工程包面向准备电赛控制类赛题的高校学生与指导教师核心示例为基于STM32F1的自动追光云台。资料以Keil工程为主线包含STM32F10x标准外设库的C源文件与头文件、启动和链接配置、编译中间文件及可烧录的HEX/AXF产物便于对照理解光敏传感器采样、电机驱动、PID调节与电源管理等环节的实现组织方式也适合作为嵌入式系统课程设计与赛前训练的参考底稿。压缩包共199个文件以h头文件、c源码、o/d/crf编译中间文件及少量s汇编和工程配置文件为主整体约6.74MB目录保留了较完整的开发过程痕迹。已有7810人学习读者可从中获得赛题方案框架、工程配置范例与调试排错思路用于搭建自己的追光云台原型并查漏补缺。1. 红点打靶与自动追踪2023 年电赛 E 题参考拆开看是四块活2023 年电赛 E 题那套运动目标控制与自动追踪表面上是激光笔打靶实际拆开是四件互不相同的活视觉把红点从白靶纸上抠出来、坐标从像素换算到毫米、云台把角度闭环到毫秒级、绿光云台再去追红光。这四个环节任何一环抖一下整套系统就在靶纸上画出一条歪歪扭扭的线。这份 2023 年电赛 E 题参考的价值不在于给一份能跑的工程而在于它把上面四块拼装顺序、标定方法和常见参数给固定下来了。适合两类人一是正在备赛、需要一套能当基线改的队伍二是把它当课程设计或选拔考核题的高校师生照着复现一遍就能明白每一环为什么要这么接。2. 视觉前端红点坐标怎么稳定吐出来视觉这一环最容易出问题也最容易被低估。很多人以为把摄像头往上一架、调个阈值就能得到红点坐标结果到了现场日光灯一开、靶纸一反光质心就开始乱跳。这一章把布局、取点、标定三件事拆开讲顺序不要颠倒因为布局决定了标定方式标定方式决定了取点的精度要求。2.1 固定俯视还是同轴随动两种布局各有各的坑E 题这类题目在视觉布局上只有两条主流路线选择之前先想清楚一件事你需要的是绝对位置还是相对误差。固定俯视是把摄像头架在靶纸正上方一到两米的位置视野覆盖整张靶纸检测到的红点像素坐标经过标定直接变成靶面毫米坐标。好处是天然绝对定位云台偏了多少、现在指到哪里一目了然做沿边框走一圈回到中心点这类基础题非常省事。代价是视野大导致单个红点只有十几个像素稍有噪声质心就飘一两个像素换算到靶面可能就是几毫米。同轴随动是把摄像头和激光笔一起装在云台上光轴尽量与激光笔指向平行。此时红点在图像里的偏移量本质上就是激光指向的偏差量直接拿来做闭环误差分辨率极高红点能占几十个像素。但它只能做伺服系统不知道自己在靶面的绝对位置一旦积分累加出现偏差走完一圈回到中心点时可能已经偏了两三厘米。实际项目里我一般会做混合主视觉用固定俯视拿绝对坐标做轨迹控制追踪环节用跟随绿光云台的同轴摄像头做高分辨率误差反馈。两套镜头各干各的活互不干扰。对比项固定俯视同轴随动输出量靶面绝对毫米坐标指向相对偏差单点分辨率低视野大高视野窄是否需要标定必须四点或多点只需量比例系数累积误差无有靠零点复位消除适合承担的题走边框、定点、画圆追踪对齐、末端精调主要风险支架晃动、透视畸变长时间漂移、机械回差支架这一项值得单独提一句。俯视摄像头如果用的是三脚架或者临时搭的铝型材稍微碰一下就整体偏移之前标定的单应矩阵全部作废。稳妥的做法是把摄像头和靶纸固定在同一块底板上或者至少用夹具把支架脚锁死标定完之后不要再动。2.2 用 LAB 阈值把红点从白靶纸上抠出来摄像头一上来就要关掉自动曝光、自动增益和自动白平衡。这三个自动在出厂设置里是为了让你拍风景好看在光斑检测里全是灾难激光笔一亮自动曝光立刻把整幅图压暗红点面积瞬间缩水激光笔一移开画面又亮回来阈值全废。锁定之后阈值空间建议用 LAB 而不是 RGB。RGB 三个通道高度相关光照一变三个值一起动你得同时改三个上限三个下限LAB 里 A 通道直接表示红绿方向红色光斑的 A 值明显偏高跟亮度解耦得比较干净环境光变化时主要影响的只是 L 通道。import sensor, image, time sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) # 320x240视野够、帧率稳追踪够用 sensor.skip_frames(time1000) # 等自动曝光跑完再锁 sensor.set_auto_gain(False) # 锁增益否则光斑一亮面积就缩 sensor.set_auto_exposure(False, exposure_us8000) # 曝光值按现场亮度手调 sensor.set_auto_whitebal(False) # LAB 阈值 (L_min, L_max, A_min, A_max, B_min, B_max) RED_TH (25, 100, 30, 127, -25, 65) # 红激光A 通道偏高 GREEN_TH (25, 100, -75, -15, -20, 70) # 绿激光A 通道偏低 def find_spot(img, th, min_pix12): blobs img.find_blobs([th], pixels_thresholdmin_pix, # 小于这个像素数当噪声丢掉 area_thresholdmin_pix, mergeTrue, # 光斑碎成几块时合并 margin10) # 合并的像素间距 if not blobs: return None b max(blobs, keylambda x: x.pixels()) # 取最大块滤掉反光点 return (b.cx(), b.cy(), b.pixels()) while True: img sensor.snapshot() red find_spot(img, RED_TH) if red: print(R, red)这段代码里有三个参数需要按现场改exposure_us、RED_TH的 A 下限、min_pix。曝光值调法是把激光笔关掉让白靶纸在图像里呈中灰而不是纯白此时红点打上去才不会过曝成一团。min_pix的取值跟摄像头高度直接相关一米五左右的高度、QVGA 分辨率红点通常在 20 到 60 像素之间取 12 能滤掉绝大部分噪点又不会误杀远处的小光斑。mergeTrue这个参数很多人不用但打白靶纸时红点边缘经常因为过曝被切成两半不合并的话质心会在两半之间来回跳云台就跟着抽。2.3 像素到毫米四点单应标定比量比例尺靠谱最省事的标定方式是量一下靶纸在图像里占多少像素然后除一下得到 mm/px 的比例。这个做法只在摄像头光轴严格垂直于靶纸、且镜头畸变可以忽略时成立。实际架设时摄像头总有点倾斜靶纸四角在图像里被拉伸的程度不一样用单一比例尺换算中心点误差可能只有 1mm到边角就能到 8mm 以上走边框的时候你会看到光斑在四个角上明显往外飘。正确的做法是用单应变换。在靶纸上选四个已知物理坐标的点比如四个角或者四条边的中点分别记录它们在图像里的像素坐标解一个 3×3 的矩阵之后任意像素点都能映射到毫米坐标。import numpy as np # 四组对应点像素坐标 (u,v) 与靶面物理坐标 (x,y)单位 mm px np.array([[42, 36], [612, 40], [608, 452], [38, 448]], dtypefloat) mm np.array([[0, 0], [600, 0], [600, 400], [0, 400]], dtypefloat) A, b [], [] for (u, v), (x, y) in zip(px, mm): A.append([u, v, 1, 0, 0, 0, -x * u, -x * v]); b.append(x) A.append([0, 0, 0, u, v, 1, -y * u, -y * v]); b.append(y) h np.linalg.lstsq(np.array(A), np.array(b), rcondNone)[0] H np.append(h, 1.0).reshape(3, 3) def to_mm(u, v, HH): p H np.array([u, v, 1.0]) return p[0] / p[2], p[1] / p[2] # 反过来验证把四个标定点再映射回去残差超过 2mm 就重新采点 for (u, v), (x, y) in zip(px, mm): mx, my to_mm(u, v) print(f({x},{y}) - ({mx:.1f},{my:.1f}) err{np.hypot(mx-x, my-y):.2f}mm)代码逻辑很直白把八元线性方程组堆出来最小二乘求解矩阵H就是像素到毫米的映射。最后那段反算是必须做的四个标定点的回代残差长期超过 2mm说明要么点没点准要么镜头畸变太大、四点模型撑不住这时候就得加点到六点或九点改用二次多项式拟合。提示标定用的四个点不要用激光笔去打激光笔本身就有安装误差。用一张打印好的网格纸铺在靶面位置手工量出四个角点误差来源会少一半。采点这一步如果靠人眼对图像找角点重复性很差同一个人两次采点能差三四个像素。常见做法是在靶面四个角贴黑色小方块用色块识别自动找中心把采点这件事也自动化标定一次只要几秒钟反复标定取平均能进一步压低噪声。3. 云台闭环从像素误差到两路 PWM视觉把坐标吐出来之后剩下的问题就是把它变成舵机角度。这一章的内容看起来是控制理论实际绝大多数翻车都发生在标定和限幅这两个最土的地方而不是 PID 参数本身。3.1 舵机 PWM 与角度标定先让零位和水平对上标准舵机走 50Hz、0.52.5ms 脉宽的协议对应大致 -90° 到 90°。用定时器产生这个信号的关键是让计数器频率刚好是 1MHz这样一个计数就是 1 微秒脉宽直接等于比较寄存器的值调试时肉眼可读。/* TIM3: 72MHz / (711) 1MHz 计数ARR19999 - 20ms 周期 比较值 500~2500 即 0.5~2.5ms 脉宽 */ void servo_pwm_init(void) { /* 省略 GPIO/TIM 时钟与引脚复用配置只保留关键计数值 */ TIM3-PSC 71; TIM3-ARR 19999; TIM3-CCR1 1500; /* 上电先给中位防止舵机猛甩 */ TIM3-CCR2 1500; } /* 角度 - 比较值deg 为期望指向角trim 为该轴的机械零位补偿 */ static uint16_t angle_to_ccr(float deg, float trim) { if (deg 85.0f) deg 85.0f; /* 软限位防止顶死烧舵机 */ if (deg -85.0f) deg -85.0f; float us 1500.0f (deg trim) * (1000.0f / 90.0f); return (uint16_t)us; }trim这个参数就是每个舵机的机械零位补偿。同一型号的两个舵机中位脉宽可能差 3080 微秒换算成角度就是三到七度在靶纸上就是好几厘米。标定方法是把deg固定为 0调trim让激光点正好落在靶面中心记下这个值写死到代码里之后不要再用软件的方式去补偿机械误差。软限位那两行看着不起眼但它是防止烧舵机的最后一道保险。二维云台在走边界轨迹时如果 PID 积分项冲起来输出会远远超过机械行程舵机顶在限位上持续堵转几秒钟电流就能把齿轮箱里的塑料齿扫平。3.2 位置式 PID 的起手参数与抗饱和写法这题的被控对象是舵机本质是个带滞后的位置伺服用位置式 PID 就够不需要上更复杂的东西。写法上有两个细节必须处理积分限幅和微分项的 dt 保护。typedef struct { float kp, ki, kd; float i_max; /* 积分项绝对值上限 */ float out_max; /* 输出限幅单位度 */ float integ; float last_err; } pid_t; float pid_calc(pid_t *p, float err, float dt) { if (dt 1e-4f) dt 1e-4f; /* 防止首帧或丢帧时微分爆掉 */ p-integ err * dt; if (p-integ p-i_max) p-integ p-i_max; /* 抗积分饱和 */ if (p-integ -p-i_max) p-integ -p-i_max; float d (err - p-last_err) / dt; p-last_err err; float out p-kp * err p-ki * p-integ p-kd * d; if (out p-out_max) out p-out_max; if (out -p-out_max) out -p-out_max; return out; }起手参数的顺序建议是先把ki和kd归零kp从 0.05 开始往上加加到系统开始等幅振荡时退回来取一半再把ki从 0.01 慢慢加消除到位后的静差kd最后加因为这题视觉噪声大微分项会放大噪声一般给 0.01 到 0.05 就够甚至干脆不给。i_max的取值跟out_max挂钩经验值是i_max out_max / ki * 0.3量级保证积分项最多贡献三成输出。不设这个限幅光斑被遮挡一两秒后重新出现时积分已经攒了一大坨云台会猛甩一下这个现象在调试现场非常常见。3.3 轨迹生成边框、中心往返、画圆三套参数化函数轨迹这部分的坑在于用相对量还是绝对量。如果轨迹是用角度增量一点点累加出来的跑完一圈回到起点累积误差可能已经偏了两三度用靶面绝对坐标做参数方程每一帧都重新算目标点误差不会累积。/* 靶面坐标系原点左下角x 向右y 向上单位 mm */ #define TARGET_W 600.0f #define TARGET_H 400.0f /* 沿边框走一圈t 从 0 到 1s 为走过的总周长比例 */ void traj_border(float s, float *x, float *y) { float w TARGET_W, h TARGET_H; float per 2.0f * (w h); float d s * per; if (d w) { *x d; *y h; } else if (d w h) { *x w; *y h - (d - w); } else if (d 2*w h) { *x w - (d - w - h); *y 0; } else { *x 0; *y d - 2*w - h; } } /* 画圆中心 (cx,cy)半径 R角速度 w(rad/s)t 为秒 */ void traj_circle(float cx, float cy, float R, float w, float t, float *x, float *y) { *x cx R * cosf(w * t); *y cy R * sinf(w * t); }traj_border用周长比例做参数好处是可以随时暂停、随时反向走不用担心走到拐角处速度突变。实际调试时把s递增的速率跟题目要求的完成时间对齐比如要求 30 秒走完一圈就让s每秒增加 1/30这样不用额外写定时逻辑。画圆的时候要注意舵机的角速度跟随能力。半径越小、角速度越高云台越跟不上轨迹在四个象限交界的地方会被切角。参数上600×400mm 的靶面画 R100mm 的圆角速度超过 1.5 rad/s 之后就明显跟不上一般做到 1.01.2 rad/s 比较稳。3.4 视觉板与主控的串口帧定长、帧头、校验、超时视觉板和主控之间的串口通信是最容易被忽视的一环。用printf(%d,%d\n)这种 ASCII 格式解析简单但帧长不定一旦丢字节整个包就错位云台会突然抽一下。稳妥的做法是定长二进制帧。字节内容说明00xAA帧头 110x55帧头 2双字节帧头抗误同步2len载荷长度固定 53type0红点1绿点2双点4-5x_lo, x_hi靶面 x 坐标单位 0.1mm6-7y_lo, y_hi靶面 y 坐标8sum前三到七字节累加和低 8 位/* 解析状态机逐字节喂入凑够一帧才更新目标点 */ void frame_feed(uint8_t byte) { static uint8_t buf[16], idx 0; static uint32_t last_ms 0; if (HAL_GetTick() - last_ms 100) { /* 超时 100ms 丢弃残帧 */ idx 0; } last_ms HAL_GetTick(); buf[idx] byte; if (idx 2) return; if (buf[0] ! 0xAA || buf[1] ! 0x55) { /* 滑动窗口重新对齐 */ memmove(buf, buf 1, idx - 1); idx--; return; } if (idx 9) return; uint8_t sum 0; for (int i 2; i 8; i) sum buf[i]; if (sum buf[8]) { g_target.x (int16_t)(buf[4] | (buf[5] 8)) * 0.1f; g_target.y (int16_t)(buf[6] | (buf[7] 8)) * 0.1f; g_target.valid 1; } idx 0; }超时那一行是兜底逻辑。视觉板死机或者线松了主控如果一直用最后一帧的目标点做 PID云台会朝着一个方向慢慢走偏顶到限位。超时后把valid清零、输出回中位是最省事的保护。4. 绿光追红光双色识别、滞后补偿和两个闭环发挥部分要求绿光自动追踪红光本质上是两个云台、两个闭环、一个共享视觉源的问题。很多队伍在这里卡住不是因为算法难而是两个闭环的节拍没理清楚互相踩。4.1 一张图里同时抓红绿面积、亮度、位置做判据一张图里同时找红点和绿点最直接的问题是阈值相互串。红激光过曝后在 LAB 空间里的 A 值会冲得很高但边缘像素在 B 通道上也可能落进绿光的区间反过来绿光打在白纸上中心过曝区域接近白色L 值高、A 和 B 都接近零反而两个阈值都不命中。实际处理方式是把两个色块的检测结果做一次交叉校验如果绿光色块的中心落在红点色块内部且面积明显小于红点那多半是过曝串色丢掉如果两个色块的面积比超过 5 倍也丢掉小的那个。def pick_pair(img): red find_spot(img, RED_TH, 12) grn find_spot(img, GREEN_TH, 8) if red and grn: # 绿点面积远小于红点且位置重合 - 判为串色只保留红点 if grn[2] red[2] * 0.2 and abs(grn[0]-red[0]) 15 and abs(grn[1]-red[1]) 15: grn None return red, grn面积判据之外还有一个更稳的判据是颜色饱和度。红激光和绿激光的纯度都很高反光杂点的饱和度低得多在 LAB 里体现为 A/B 通道的绝对值偏小。把阈值的下限往上提一点杂点会被天然滤掉代价是远处的小光斑可能丢需要配合曝光值一起调。4.2 追踪滞后从哪来前馈和死区怎么加追踪系统的滞后是三段叠加起来的摄像头曝光加读出大约 3060ms视觉算法一帧 1030ms串口传输和主控周期再吃 10ms最后舵机本身响应 100ms 上下。总滞后两百毫秒量级红光如果以 0.5 m/s 在靶面上移动两百毫秒就是 10cm 的偏差纯 PID 追不上只能看到绿点一路落后。最有效的补偿是前馈把目标点红点的速度估计出来直接叠加到绿光云台的角度指令上让 PID 只负责修正残差。/* 由视觉帧差分估计红点速度前馈叠加到角度指令 */ static float ff_gain 0.045f; /* 单位 deg/(mm/s)需实测标定 */ void tracking_update(float rx, float ry, float dt) { static float last_x 0, last_y 0, init 0; if (!init) { last_x rx; last_y ry; init 1; return; } float vx (rx - last_x) / dt; float vy (ry - last_y) / dt; last_x rx; last_y ry; /* 速度做一阶低通避免差分噪声直接进前馈 */ static float vx_f 0, vy_f 0; vx_f 0.7f * vx_f 0.3f * vx; vy_f 0.7f * vy_f 0.3f * vy; float ang_h pid_calc(pid_h, rx - g_green_x, dt) ff_gain * vx_f; float ang_v pid_calc(pid_v, ry - g_green_y, dt) ff_gain * vy_f; servo_set(ang_h, ang_v); }ff_gain这个系数没有理论值可抄标定方法是让红光以恒定速度横穿靶面手动调ff_gain直到绿点跟红点的水平距离最小。云台高度和靶面距离一变这个值就得重标所以现场调试时把它做成串口可调的全局变量比反复烧程序快得多。死区也值得加。追踪误差在 23mm 以内时视觉噪声本身就超过真实误差了继续让 PID 追只会让绿点在红点周围画小圈。加一个误差绝对值小于阈值时输出保持上一帧的死区判断画面立刻安静下来。4.3 两个云台的节拍分时调度而不是互相打断两个云台如果都放在主循环里同步跑会出现一个隐蔽的问题红点云台的控制周期被绿点云台的视觉处理拖长PID 的 dt 忽大忽小积分项算出来的值完全不可预期。我的做法是把两路控制拆成不同频率的任务。红点云台的轨迹控制是开环或准闭环跑 50Hz 就够了绿点追踪需要快跑到 100Hz视觉板独立跑按自己的帧率往串口发主控收到就更新目标缓存不阻塞主循环。/* 分频调度基准 1ms 中断 */ void systick_task(void) { static uint16_t cnt 0; cnt; if (cnt % 20 0) red_gimbal_task(); /* 50Hz红点轨迹控制 */ if (cnt % 10 0) green_gimbal_task(); /* 100Hz绿点追踪 */ if (cnt % 5 0) uart_poll_task(); /* 200Hz串口收帧 */ }任务里只做计算和写 PWM 寄存器不做任何阻塞操作。串口用中断加环形缓冲收主循环里取数据这样波特的传输时间不会侵占控制周期。这套调度结构看着土但现场的稳定性比一个大循环里什么都干要好一个量级。5. 避坑清单五条从调试现场捡回来的经验这一章的五条基本覆盖了从能点亮到能稳定跑之间会遇到的全部问题。每条按现象—原因—解决写遇到问题时按顺序排查就行。5.1 光斑质心乱跳云台却纹丝不动现象是串口打印出来的红点坐标每秒跳十几毫米但盯着云台看激光点稳稳地打在靶纸中心没动。原因是自动曝光没锁。摄像头在光斑出现和消失之间不断调整曝光红点的面积和形状每帧都在变质心自然跟着漂。另一个可能是merge没开过曝的红点被切成两块质心在两块之间来回横跳。解决办法是三道锁一起上set_auto_gain(False)、set_auto_exposure(False, ...)、set_auto_whitebal(False)把曝光值手动调到白纸呈中灰。然后把mergeTrue和margin8~12打开。做完这两步如果还跳就该检查是不是有第二个红色物体进了视野比如红色扎带、红色指示灯把它们移出视野或者用pixels_threshold卡掉。5.2 光斑走到靶纸边缘就贴住不动现象是沿边框走的时候光斑在四条边中段还好一接近拐角就慢下来甚至在边线附近来回摩擦走不过去。原因通常有两个。一是舵机分辨率不够普通模拟舵机的理论分辨率约 0.3°但实际有效分辨率受齿隙影响往往到 1° 以上对应靶面上就是几厘米的死区指令在小范围内变化时舵机根本不动。二是软限位设得太保守angle_to_ccr里限到 ±85°而实际机械行程可能只有 ±80°走到边界时指令被限幅光斑就卡在那里。解决方式是先把软限位放宽到实测机械行程的 95%再在靶面坐标上做一次映射补偿把期望的靶面范围映射到舵机能稳定覆盖的角度范围内让轨迹计算时就不去碰限位。齿隙的问题换数字舵机最直接如果换不了就在控制上加一点抖动偏置让舵机始终有微小的运动避免停在齿隙里。5.3 激光一开整幅图像泛白、阈值全废现象是激光笔点亮之后画面上出现一大片白色亮斑红点反而找不到了或者找到的质心整个偏到亮斑中心。这是典型的过曝。激光笔功率大、摄像头增益高打在白纸上形成的亮斑中心区域三个通道全部饱和到 255色相信息完全丢失此时检测到的不是红点而是白点。解决办法有三条按优先级先降曝光值把exposure_us调到白纸呈中灰再考虑在镜头前加一片中性密度滤光片或者一小片磨砂片把光斑打散一点让它不那么集中最后才是软件上的补救在阈值里把 L 上限压到 90 以下让过曝区落不进阈值。软件补救的问题在于过曝区的形状随功率变化阈值得反复调不如从光学上解决。5.4 云台到位后小幅摆动停不下来现象是光斑到达目标点后在目标位置周围做 13mm 的等幅或者衰减很慢的摆动几秒钟都停不下来。原因一般是三个叠加。微分项给得太大视觉噪声被放大成控制量积分项没有限幅到位后积分还在慢慢累积形成极限环机械回差导致舵机在两度范围内反复跨越齿隙。排查顺序是先去掉微分项看是否改善如果摆动周期变长但幅度还在就是积分在起作用把i_max压到out_max的四分之一以内如果两种都试过还在摆就是机械问题检查云台支架有没有松动、激光笔有没有夹紧、靶纸是不是被风扇吹得在动。最后这一条听着荒唐但现场确实遇到过。5.5 串口偶发丢帧云台突然抽一下现象是整体运行正常但每隔十几秒云台会突然朝某个方向猛甩一下然后又恢复。原因是帧同步丢了。ASCII 格式或者单字节帧头的协议一旦某个字节丢失或者被干扰接收端会把后续数据错位解析把坐标里的高位当成低位解出来一个巨大的数值PID 立刻给一个巨大的输出。解决办法是把协议改成双字节帧头加定长加校验就是第 3.4 节给的那套。另外在接收端加两重保护一是超时丢弃超过 100ms 没凑齐一帧就清空缓冲二是数值合法性检查解出来的坐标如果在靶面范围之外就直接丢弃不要送进 PID。第二重保护尤其重要它不解决协议问题但能保证即使协议出错云台也不会做出危险动作。6. 现场三小时复现一套固定的标定顺序与自检表所有东西都调通之后我给自己定了一套固定的上电顺序每次搬到新场地都按这个顺序走一遍能省掉大量临场抓瞎。顺序是机械固定 → 摄像头锁定参数 → 四点标定 → 舵机零位 → 单轴闭环 → 双轴闭环 → 轨迹 → 追踪。这个顺序的核心逻辑是先解决静态误差再解决动态误差任何一步跳过去后面都会以更难排查的形式还回来。现场自检表我一般贴在箱子内侧逐项打勾检查项合格判据不达标先查摄像头支架刚性手轻碰靶纸图像不晃支架脚是否锁死曝光锁定关激光时白纸呈中灰exposure_us是否合适四点标定残差四点回代残差均 2mm采点是否准确、镜头畸变舵机零位指令 0° 时光斑落靶心 ±5mmtrim补偿值单轴阶跃响应阶跃到位时间 0.30.6s无超调或仅一次kp与i_max边框轨迹四角偏离边线 10mm软限位与机械行程匹配圆轨迹圆度误差 8mm无明显切角轨迹角速度是否过快追踪稳态误差红光静止时绿点偏差 5mm死区阈值、PID 参数追踪动态误差红光 0.3m/s 匀速时偏差 15mmff_gain前馈系数串口健壮性拔插一次后 100ms 内自动恢复超时丢弃与合法性检查表格之外还有一个技巧值得单独说把trim、ff_gain、kp/ki/kd、i_max这几个参数全部挂到串口命令上支持在线读写并掉电保存。这样现场调整不用重新编译烧录一次调参从五分钟缩短到十秒钟。我在第二次现场调试时才加上这个功能之前反复烧程序浪费的时间足够把整套轨迹跑通三遍。最后一个习惯是记录。每次调整参数无论成功失败都在纸上记一行时间 改了什么 现象变化。追踪调不好的时候凭记忆回想上次那个参数是不是更好百分之百会记错翻记录两秒钟就能定位。说个自己的教训第一次跑完整流程时我把所有参数都调到了看起来最好的状态结果第二天换了个场地日光灯位置一变红点阈值全线失效两个小时全花在重调阈值上。从那以后我每次换场地都强制走一遍上面这张自检表从曝光开始重新锁一遍哪怕看起来一切正常也不跳过。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?