ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于PuppyPi与串口舵机实现六轴机械臂视觉抓取与运动控制

基于PuppyPi与串口舵机实现六轴机械臂视觉抓取与运动控制 1. 项目缘起当“玩具”开发板遇上机械臂最近在折腾PuppyPi这块开发板它本质上是一块基于全志H618芯片的卡片电脑性能对于嵌入式项目来说相当够用。之前用它跑过一些图像识别、小车控制的项目总觉得它还能干点更“酷”的事。看着桌角吃灰的一个六轴机械臂模型一个想法冒了出来能不能把这俩结合一下这个念头并非空穴来风。市面上很多机械臂教学套件要么是封闭的“黑箱”二次开发空间有限要么就需要搭配一个笨重的工控机或树莓派成本和便携性都不理想。PuppyPi的尺寸和接口丰富的GPIO、USB、CSI等正好提供了一个绝佳的“大脑”载体。更重要的是我想实现的不是简单的“点动控制”而是解锁一些更智能、更自动化的“酷功能”比如视觉引导抓取、手势遥控甚至是简单的轨迹规划。所以这个项目的核心目标很明确以PuppyPi为控制核心集成一个开源的桌面级机械臂并基于此搭建一套软件框架实现超越基础示教的、可编程的自动化应用。这不仅仅是硬件连接更是一次关于嵌入式系统集成、实时控制与上层应用开发的实践。2. 硬件选型与连接为什么是“六轴”与“串口伺服”硬件是项目的基石选型决定了后续开发的难度和功能上限。经过一番调研和对比我选择了下面这套组合并详细说说背后的理由。2.1 机械臂本体的选择六轴与舵机的权衡我最终选用了一款常见的六轴桌面机械臂其关节由6个数字舵机Servo驱动。这里有几个关键考量点轴数6轴 vs 4轴四轴机械臂SCARA结构居多成本低控制简单但在空间灵活性上受限末端执行器夹爪的姿态调整能力弱。六轴机械臂仿人臂结构拥有更大的工作空间和更灵活的末端姿态能够完成更复杂的任务例如从不同角度抓取物体、进行装配等。为了实现“酷功能”六轴带来的灵活性是必要的。驱动方式舵机 vs 步进/伺服电机高精度工业机械臂使用伺服电机搭配精密减速器但成本高昂驱动电路复杂。对于桌面级、教育及爱好者项目数字舵机是更现实的选择。它们集成驱动、控制与反馈电位器于一体通过PWM信号即可控制位置接口简单成本可控。虽然绝对精度和扭矩不如伺服电机但对于抓取小物件、进行演示完全足够。通信协议PWM vs 串口传统舵机使用PWM信号控制每个舵机需要独占一个GPIO引脚。控制6个舵机就需要6个GPIO并且PWM信号容易受到线长干扰精度也有限。因此我选择了支持串口总线通信的舵机如一些基于RS485或TTL串口的智能舵机型号。这类舵机通过一根数据线通常为TTL电平以菊花链方式串联只需一个UART串口就能控制所有关节接线简洁抗干扰能力强还能回读位置、温度、负载等状态信息为后续的力反馈、异常检测提供了可能。注意如果你手头是PWM舵机也可以使用PCA9685这类PWM扩展板通过I2C来控制以节省GPIO。但串口总线方案在扩展性和功能上更具优势。2.2 PuppyPi作为控制核心的接口方案PuppyPi的40Pin GPIO排针提供了多种连接可能。对于串口总线舵机最直接的连接方式是使用其上的UART引脚。硬件连接电源分离这是最重要的一步机械臂舵机工作电流大单个堵转时可达2A以上绝对不能从PuppyPi的GPIO取电。必须使用独立的外接电源如12V/5A的开关电源为舵机供电。PuppyPi和舵机的GND地线必须连接在一起确保共地。信号连接将舵机主控板或第一个舵机的RX/TX线连接到PuppyPi的一个UART的TX/RX引脚上例如UART3。可能需要一个USB转TTL模块进行电平转换和调试但最终可以直接连接。电路保护建议在信号线上串联一个220Ω的电阻以限流并在PuppyPi端使用一个逻辑电平转换器如TXB0104如果舵机是5V TTL而PuppyPi GPIO是3.3V电平。为什么不用USB有些机械臂提供USB转串口适配器。直接使用USB固然方便但会占用一个USB口且在某些需要低延迟或严格定时的场景下USB的协议开销可能带来不确定的微小延迟。直接使用GPIO上的硬件UART可以获得更直接、更可控的通信链路。2.3 外围感官扩展让机械臂“看得见”仅有机械臂是“盲人摸象”。为了实现视觉引导我添加了一个CSI接口的摄像头模块到PuppyPi。选择CSI而非USB摄像头是因为CSI带宽更高、延迟更低直接由SoC的ISP处理CPU占用率小更适合做实时的图像处理。此外我在机械臂的末端End-Effector安装了一个小型激光笔通过一个微型舵机控制开关用于进行“手眼标定”——即确定摄像头看到的像素位置如何对应到机械臂末端的真实空间位置。这是所有视觉抓取项目的基石步骤。硬件清单总结如下表组件型号/规格关键作用连接至PuppyPi主控PuppyPi (Allwinner H618)提供算力、运行控制系统、视觉算法-机械臂六轴串口总线舵机版执行机构完成物理动作UART3 (TX/RX), 独立电源供电摄像头OV5640 CSI 摄像头模块机器视觉识别与定位目标CSI接口激光笔5V微型激光模组手眼标定辅助工具通过一个GPIO控制通断电源12V 5A开关电源为舵机提供动力独立供电GND共地3. 软件架构搭建从底层驱动到应用层硬件连接妥当后软件才是赋予其灵魂的关键。我的软件架构自底向上分为四层确保控制实时性、功能模块化和开发便捷性。3.1 底层舵机通信与驱动库首先需要与串口舵机通信。我使用了Python的pyserial库。但直接发送原始协议字节太繁琐因此我封装了一个简单的驱动类SerialServoDriver。import serial import time class SerialServoDriver: def __init__(self, port/dev/ttyS3, baudrate115200): self.ser serial.Serial(port, baudrate, timeout0.1) # 假设舵机协议指令格式为头(0xFF 0xFF) ID 指令长度 指令 校验和 self.header bytes([0xFF, 0xFF]) def send_cmd(self, servo_id, cmd_data): # 构造完整指令包 length len(cmd_data) 2 # ID 指令长度 数据 校验 packet self.header bytes([servo_id, length]) cmd_data checksum self._calculate_checksum(packet[2:]) # 从ID开始计算 packet bytes([checksum]) self.ser.write(packet) time.sleep(0.01) # 微小延时避免总线拥堵 def set_position(self, servo_id, position, time_ms500): # 位置通常为0-1000或0-4095根据舵机手册转换 pos_low position 0xFF pos_high (position 8) 0xFF time_low time_ms 0xFF time_high (time_ms 8) 0xFF cmd bytes([0x03]) bytes([pos_low, pos_high, time_low, time_high]) # 假设03为位置控制指令 self.send_cmd(servo_id, cmd) def _calculate_checksum(self, data): # 简单的求和取反校验 return (~sum(data)) 0xFF这个驱动类处理了协议帧的组装、校验和发送。不同的舵机品牌如LewanSoul, Dynamixel协议不同需要根据手册调整。关键点在于发送指令后要给予舵机足够的执行时间time_ms参数而不是连续发送否则会导致舵机抖动或响应异常。3.2 核心层运动学与轨迹规划直接给每个关节设定角度是“关节空间”控制。但要实现“把夹爪移动到(x,y,z)坐标处并保持水平”这种任务就需要逆运动学IK计算。正运动学FK已知各关节角度计算末端执行器的位置和姿态。这相对简单通过DH参数建立连杆坐标系连续变换即可。逆运动学IK给定末端位置和姿态反解出各关节角度。这是六轴机械臂的核心算法通常有解析解对于特定构型和数值解迭代法两种。我使用了ikpy这个Python库它支持基于URDF模型进行逆运动学计算。首先需要为我的机械臂创建一个URDF描述文件定义每个连杆的长度、关节类型和旋转轴。from ikpy.chain import Chain from ikpy.link import URDFLink import numpy as np # 定义机械臂的连杆链简化示例参数需实测 my_chain Chain(namepuppypi_arm, links[ URDFLink(namebase, translation_vector[0, 0, 0.1], orientation[0, 0, 0], rotation[0, 0, 1]), URDFLink(nameshoulder, translation_vector[0, 0, 0.05], orientation[0, 0, 0], rotation[0, 1, 0]), URDFLink(nameelbow, translation_vector[0.1, 0, 0], orientation[0, 0, 0], rotation[0, 1, 0]), # ... 更多连杆 ]) # 给定目标末端位置[x, y, z]和姿态[roll, pitch, yaw]欧拉角 target_position [0.15, 0.05, 0.2] target_orientation [0, np.pi/2, 0] # 末端朝下 # 计算逆运动学解关节角度弧度制 ik_solution my_chain.inverse_kinematics(target_position, target_orientation) print(关节角度弧度:, ik_solution) # 将弧度转换为舵机对应的脉宽或角度值有了IK我们就能指挥机械臂去往空间中的任意点。但直接从一个点跳到另一个点会让机械臂以最快速度运动产生剧烈抖动。因此需要轨迹规划在起点和终点之间插值出一系列平滑的中间点路径点并计算每个点的时间戳形成平滑的运动轨迹。我采用了简单的五次多项式插值保证起点和终点的速度、加速度均为零运动平滑。3.3 感知层手眼标定与视觉识别这是解锁“视觉抓取”功能的关键一步。手眼标定目的是找到摄像头坐标系到机械臂基座坐标系的变换矩阵。我采用了经典的“四点标定法”在机械臂末端安装激光笔控制其移动到工作空间内的四个已知位置在机械臂基座坐标系下已知。在每个位置点亮激光笔在摄像头画面中会形成一个光斑。记录下这四个光斑的像素坐标。现在我们有了四组对应的三维点机械臂坐标和二维点像素坐标。利用PnPPerspective-n-Point算法OpenCV中的solvePnP函数可以解出摄像头的外参矩阵旋转矩阵R和平移向量t。这个矩阵就是“手眼关系”。以后只要摄像头识别出一个物体的像素坐标通过这个矩阵和相机内参就能估算出它在机械臂基座坐标系下的粗略三维位置。import cv2 import numpy as np # 已知机械臂末端在基座坐标系下的4个3D点 (单位米) object_points np.array([[0.1, 0.0, 0.05], [0.1, 0.1, 0.05], [0.0, 0.1, 0.05], [0.0, 0.0, 0.05]], dtypenp.float32) # 已知对应在图像中的4个2D像素点 image_points np.array([[320, 240], [480, 240], [480, 360], [320, 360]], dtypenp.float32) # 相机内参矩阵和畸变系数需要通过相机标定预先获取 camera_matrix np.array([[600, 0, 320], [0, 600, 240], [0, 0, 1]], dtypenp.float32) dist_coeffs np.zeros((4, 1)) # 使用solvePnP求解外参 success, rvec, tvec cv2.solvePnP(object_points, image_points, camera_matrix, dist_coeffs) # rvec, tvec 即为从机械臂基座坐标系到相机坐标系的变换视觉识别使用OpenCV进行颜色识别或轮廓检测。例如识别一个红色的方块将图像从BGR转换到HSV色彩空间更容易分离颜色。设定红色的HSV阈值范围进行二值化。查找轮廓找到面积最大的轮廓计算其最小外接矩形或中心点。这个中心点的像素坐标结合手眼标定的结果就可以转换出目标物体相对于机械臂的大致位置。3.4 应用层具体“酷功能”的实现基于以上基础可以像搭积木一样实现各种功能。功能一视觉引导随机抓取流程摄像头持续捕捉画面 - 识别预设颜色/形状的物体 - 计算物体中心像素坐标 - 通过手眼标定矩阵转换为机械臂坐标系下的目标点Z轴高度可预设或通过双目视觉/结构光估算- 逆运动学求解关节角度 - 轨迹规划生成平滑路径 - 驱动舵机序列执行。避坑点视觉识别存在误差尤其是深度Z值。我的策略是让机械臂先运动到目标点正上方一个安全高度然后垂直下降。夹爪闭合后再垂直提升。这样可以避免因平面位置误差导致的碰撞。功能二手势遥控与示教使用MediaPipe库识别摄像头前的手部关键点21个点。将食指指尖的移动映射到机械臂末端在XY平面上的移动。握拳手势映射为夹爪闭合张开手掌映射为夹爪松开。这实际上实现了一种“示教”功能你可以用手“引导”机械臂记录一系列路径点然后让机械臂自动复现这个轨迹。我将轨迹点序列保存为JSON文件后续可以随时调用、编辑或循环执行。功能三自动化流水线演示在桌面上定义两个区域“原料区”和“成品区”。机械臂从“原料区”抓取一个积木块移动到摄像头前一个固定位置进行“拍照”实际上可以在这里集成更复杂的检测如二维码识别然后根据“检测结果”将积木块放置到“成品区”的不同位置。这个功能整合了移动、视觉、决策和放置模拟了一个微型的自动化分拣单元。4. 系统集成与调试让一切稳定运行将各个模块集成到一起并确保稳定运行是项目从“Demo”到“可用”的关键。4.1 多进程与通信架构视觉处理、运动规划、舵机控制、用户界面如果有最好运行在独立的进程或线程中通过进程间通信IPC交换数据。我使用了Python的multiprocessing模块和Queue。视觉进程持续抓取图像进行识别将识别到的目标坐标放入一个target_queue。主控进程从target_queue获取坐标进行运动学解算和轨迹规划生成一系列关节角度序列放入一个trajectory_queue。控制进程以固定的频率如50Hz从trajectory_queue读取当前时刻的目标角度并通过串口发送给舵机。这个进程优先级最高需要保证定时循环的稳定性。这种架构解耦了各模块视觉处理的延迟不会直接影响控制周期的稳定性只要控制进程能按时拿到最新的轨迹点即可。4.2 关键参数调试与性能优化串口通信稳定性波特率并非越高越好。过高的波特率在长线缆下误码率会增加。115200是一个在稳定性和速度之间很好的平衡点。务必在舵机协议允许范围内选择。超时设置pyserial的timeout参数设置很重要。设置过短可能读不到完整的返回包设置过长在无返回时程序会阻塞。建议根据舵机响应时间设置一个合理值如100ms。错误重试在发送指令后增加校验返回值如果舵机支持的逻辑。如果校验失败或超时无响应进行有限次数的重试。这能有效应对偶尔的通信干扰。运动平滑性调优轨迹规划的时间参数五次多项式轨迹中的总时间T直接影响运动速度。T太小运动急促抖动T太大效率低下。需要根据移动距离和舵机性能反复测试。我的经验是对于桌面小臂10cm距离移动T设置在1-2秒比较合适。舵机速度与加速度限制有些高级串口舵机支持设置速度、加速度参数。即使轨迹规划是平滑的如果舵机自身的加速度设置过大依然会产生冲击。应在舵机配置软件或通过指令将其加速度设置为一个适中的值。视觉处理延迟图像分辨率640x480通常足够过高分辨率会大幅增加处理时间。识别算法简化在保证识别率的前提下尽量使用计算量小的算法。例如颜色识别比深度学习目标检测快几个数量级。使用硬件加速PuppyPi的H618芯片带有GPU和NPU。可以考虑使用支持ARM NPU的推理框架如TFLite, ONNX Runtime来部署轻量级模型实现更快的物体识别。4.3 安全与异常处理机制机械臂是物理设备安全第一。软件限位在逆运动学计算后加入关节角度限位检查。如果解算出的角度超出舵机物理范围如0-180度则丢弃该解或调整目标位置。紧急停止设置一个全局的“急停”标志位。可以在主循环中监听一个特定的键盘按键如空格或GPIO按钮。一旦触发立即停止轨迹规划并向所有舵机发送“扭矩禁用”指令如果支持让机械臂在重力作用下自然下垂确保下方无障碍物。超时与卡死检测在控制循环中记录最后一次收到有效视觉目标或完成运动指令的时间。如果超时如10秒则让机械臂缓慢回归到安全的“归零”姿态。电源监控可以通过ADC读取外接电源的电压。如果电压过低可能带不动所有舵机同时运动此时应暂停任务并报警。5. 项目总结与延伸思考经过几周的折腾这套基于PuppyPi的机械臂系统终于能稳定地执行视觉抓取和手势遥控了。回顾整个过程最大的收获不是实现了某个具体功能而是打通了从感知、决策到执行的完整链路并对嵌入式AI应用有了更深的体会。几个印象深刻的教训第一电源一定要独立且足额我曾因电源功率不足导致舵机在复杂动作时集体“抽风”PuppyPi也意外重启。第二通信协议的稳定性是生命线串口通信的代码里必须加入足够的异常处理和重试机制不能假设每次发送都成功。第三手眼标定需要极大的耐心标定板的摆放、角点检测的准确性直接影响后续抓取的精度这个过程无法自动化必须手工精细调整。这个项目的扩展性很强。PuppyPi的性能还有余量完全可以跑一个轻量级的YOLO模型来实现更通用的物体识别。也可以集成麦克风和语音识别模块实现语音控制。更进一步可以尝试在机械臂末端换上毛笔或激光雕刻头结合轨迹规划让它写字或画画。硬件上增加力传感器如薄膜压力传感器贴在夹爪上可以实现力控抓取防止捏碎鸡蛋或抓不稳光滑物体。说到底PuppyPi加上机械臂构建了一个高度灵活、完全开源的智能执行平台。它就像一张白纸限制你的是想象力和对底层技术的理解深度而不是厂商提供的封闭接口。这种“从零搭建”的乐趣和挑战正是DIY和开源硬件的魅力所在。
返回列表