拓冰建站拓冰建站
首页 / 资讯中心 / 正文

慧鱼四自由度机械手控制:RS232两字节协议与VB串口编程

简介这份《慧鱼四自由度机械手说明书》面向机械设计制造及其自动化等专业的学生与实验指导教师对应综合课程设计任务用于指导慧鱼智能创意教具的机构搭建、VB 编程与远程控制调试。内容围绕五组运动展开前后运动、左右转动、上下运动、手爪顺逆时针转动与手爪开闭合逐项说明电动机、减速箱、链条、蜗杆、蜗轮、齿轮及限位开关的传动路径与行程控制方式并配有机架侧视图、正视图、俯视图等原理图便于对照实物理解动力传递关系。远程控制部分则讲解主控端与被控端的网络交互、慧鱼接口板的主动与被动两种处理模式以及计算机与接口板之间的串行通讯协议涉及 mscomm、winsock、timer 控件的使用和 TCP 数据交换。压缩包内为 1 个 PDF 文件约 1.51MB将机构原理、通讯机制与程序设计思路整合在一处。目前已有 231 人学习适合需要课程设计参考、机械结构与远程控制入门练手的读者。1. 从一份 2013 年的课程设计说明书说起慧鱼四自由度机械手到底在解决什么翻这份说明书最扎眼的地方不是机械手的装配照片而是它把「机械」和「通信」硬塞进同一份文档一边是蜗杆、导杆、限位开关的爆炸图一边是 VB 里 MSComm、Winsock、Timer 三个控件的属性罗列。慧鱼四自由度机械手就是这么个东西——用慧鱼拼装件搭出前后、左右、上下、手爪旋转、手爪开合五个动作再用一台 PC 通过 RS232 把指令灌进接口板必要时借助 TCP 把控制端挪到网络上。它适合两类人正在做机电或自动化课程设计、需要一份能真跑通的参考实现的学生以及手上有一块慧鱼智能接口板、想搞清那套两字节串口协议到底怎么收发的人。真正的坑不在机械结构而在时序——接口板要求电机控制数据 300ms 内必须刷新一次断一次电机立刻停转。2. 五个动作拆开看慧鱼机械手的机构原理与限位开关布局慧鱼件的价值在于把工业机械手的传动链等比缩小了减速箱、蜗轮蜗杆、链条、导杆、限位开关一样不少。理解这套模型的顺序应该是「先看动力从哪来再看运动被谁叫停」而不是一上来就照着装配图拧螺丝。下面按动力链把五组运动拆开。2.1 前三个自由度的动力链减速箱、链条、蜗杆与蜗轮前后运动的链条最完整电机正转经减速箱降速增扭通过齿轮链条把动力送到蜗杆蜗杆旋转驱动底盘沿导轨前后移动电机反转即回程。中间那级蜗杆不只是传动它同时承担自锁——断电时底盘不会因为重力或推力自己滑回去这是很多学生第一次搭模型时没意识到的好处。左右转动走的是蜗轮加齿轮的路子电机的正反转直接对应顺逆时针说明书里没有给这一轴的限位开关转动角度靠程序里的计时或人眼反馈来卡。上下运动则把蜗杆的旋转转成手爪沿导杆的直线升降同样靠限位开关兜底。三条链的共同点是电机只负责「转」方向由正反转决定行程由限位开关或计时决定没有任何闭环位置反馈。这里有个资料与实现的差异要提前说清楚说明书正文里每一节都写成「电动机 1」但接口板只有 4 路电机输出VB 代码里 M1 到 M4 分别对应前后、左右、上下、手爪开合。手爪的「旋转」要么与开合共用一路通道要么在机构上是纯随动说明书那种编号方式属于写作时的笔误照抄会把自己带沟里。运动轴动力链驱动位十六进制停止条件前后电机→减速箱→链条→蜗杆→底盘0x01 / 0x02E1 前限位 / E2 后限位左右电机→减速箱→蜗轮→齿轮0x04 / 0x08计时或人工判断角度上下电机→减速箱→蜗杆→导杆0x10 / 0x20E4 上限位 / E5 下限位手爪开合电机→减速箱→连杆→蜗杆0x40 / 0x80E3 张开限位 / 夹持堵转手爪旋转齿轮副多为随动或共用通道视接线而定计时2.2 手爪开闭合的连杆机构与堵转停机手爪开合是全部动作里唯一不能只靠限位开关收场的。张开方向有触板会压到限位开关开关一闭合程序把驱动位撤掉动作结束逻辑干净。闭合方向没有对应的限位——工件尺寸不固定机械上没法预设终点位置所以只能靠「抓住工件后电机堵转」来间接判断结束。原程序用的办法比较朴素闭合动作启动后累加 Timer 计数数到 45 就强制停机本质是拿时间当传感器。这种做法在课程设计里够用但有两个边界要记住一是电池电压下降后同样的脉冲数走不完相同的行程计数阈值需要重标二是堵转时间过长会让驱动电路发热连续作业时应把计数阈值调小而不是调大。2.3 限位开关编号、触点逻辑与行程对照限位开关的编号必须和代码里的位定义严格对齐否则会出现「压下限位但电机不停」这种最磨人的故障。按 VB 的 OnComm 事件里的掩码判断输入字节的位 0 到 位 4 分别对应 E1 到 E5含义如下表。注意原说明书正文里写的「限位开关 5、6」是图上的零件编号跟接口板的输入端子编号 E1、E2 不是一回事排查时以端子号为准。输入位掩码端子物理位置触发后应停止的动作bit00x01E1底盘最前端向前0x01bit10x02E2底盘最后端向后0x02bit20x04E3手爪张开到位张开0x40bit30x08E4手爪升至最高上升0x10bit40x10E5手爪降至最低下降0x20把这套「驱动位 停止位」的配对关系写成数据结构程序里就不用散落一堆 if。下面这段是回零流程的骨架思路是朝限位方向给驱动位每轮刷新读一次输入撞到就撤驱动位。# 每个轴就是一个 (驱动位, 停止输入位) 的配对回零时循环处理 AXES { X: {fwd: 0x01, rev: 0x02, home: 0x01, stop_bit: 0x01}, # 向前回零撞 E1 Z: {fwd: 0x10, rev: 0x20, home: 0x20, stop_bit: 0x10}, # 向下回零撞 E5 } def homing(robo, axis): cfg AXES[axis] robo.state | cfg[home] # 只置一个方向位另一方向保持为 0 for _ in range(600): # 最多 60 秒防止限位失效时电机空转 data robo.tick() # 下行两字节取回输入字节 if data and (data cfg[stop_bit]): robo.state ~cfg[home] # 撞到限位立刻撤掉驱动位 robo.tick() # 再刷一帧把「停」写进接口板 return True time.sleep(0.1) robo.state ~cfg[home] return False # 超时未撞限位八成是开关或接线的问题stop_bit用位掩码而不是等值比较是因为输入字节里其他位可能同时为 1比如手爪张开限位和底盘前限位同时压着拿data 0x01判断会漏掉这种情况。homing里的最大循环次数是安全兜底限位开关本身失效时没有这个上限电机就会一直顶到底。3. 接口板被动模式与 RS232 两字节协议命令到底怎么发到电机上机械部分搭好之后剩下的事情全部发生在串口上。慧鱼智能接口板自带微处理器工作模式、输入端子编号、命令字节格式都是固定的不搞清楚这三件事写多少行 VB 都是试错。3.1 主动模式与被动模式的选择依据接口板有两种处理模式。主动模式下程序下载到板上的 RAM 里可以拔掉串口线独立运行适合行走机器人这类移动设备代价是板载微处理器的算力远小于 PC捕获快速计数脉冲这类活儿干不了。被动模式下计算全在 PC 上每个程序周期接口板采集数字量和模拟量输入传上来PC 再把要输出的电机状态传下去串口线一刻不能断。机械手、绘图机这类固定式设备一律选被动模式理由很实际需要实时读取限位开关状态来停机需要频繁改参数需要把状态显示在屏幕上。原设计选的也是被动模式。这里有个容易被忽视的操作细节——从主动模式切回被动模式必须切断接口板电源让 EPROM 里的操作软件重新启动靠软件复位是切不过去的同理在 PC 上改完主动模式程序要重新下载也得先断电。3.2 串口参数与命令字节 0xC1 / 0xC5 / 0xC9物理层是 9 针 D 型插头经 RS232 电缆接到 COM1 或 COM2端口参数固定为 9600 波特、8 位数据、无校验、1 位停止位。下行数据固定两个字节第一字节是接口命令告诉接口板这次要回送什么第二字节是电机状态。命令字节只有三种。命令字节十进制回送内容应答长度0xC1193只回送 E1—E8 数字量输入1 字节0xC5197回送 E1—E8 加模拟量 EX3 字节1 字节数字量 2 字节模拟量0xC9201回送 E1—E8 加模拟量 EY3 字节1 字节数字量 2 字节模拟量本设计只用了 0xC1。选它的理由是机械手的所有停机判断都来自限位开关全是数字量模拟量通道留着接光敏、热敏之类传感器课程设计阶段用不上发 0xC1 每次只收 1 字节解析最简单也不容易和下一帧的数据粘包。3.3 电机状态字节的位映射与 300ms 刷新约束状态字节的 8 个位正好对应 4 个电机的两个方向1 表示动作0 表示停止。这个映射和实际接线方向必须一一对应接反了会出现「按前进键手爪往上升」这种现场翻车。位掩码含义bit00x01电机 1 正转底盘向前bit10x02电机 1 反转底盘向后bit20x04电机 2 正转向左转bit30x08电机 2 反转向右转bit40x10电机 3 正转上升bit50x20电机 3 反转下降bit60x40电机 4 正转手爪张开bit70x80电机 4 反转手爪闭合两个约束必须记牢。第一个是刷新周期接口板发出的电机控制数据必须在 300ms 内刷新否则电机停转。所以被动模式下哪怕什么都不做也要周期性地下发「当前状态」间隔建议取 100ms留出三倍余量应对系统调度抖动。第二个是互斥如果对同一个电机同时置了正转位和反转位比如状态字节写成 0x03电机会拒绝运行——不是报错是干脆不动这个现象很容易被误判成电机坏了。多轴同时动是被允许的4 个电机可以并行这也是后面做节拍优化的基础。3.4 用 Python 复现两字节收发的完整链路用 VB 之外的任何语言验证这套协议都不难pyserial 十几行就够。下面这份实现把位定义、命令字节和刷新逻辑封装在一起可以拿来对照检查硬件是否正常。import serial import time class FischerROBO: 慧鱼接口板被动模式驱动下行 2 字节上行 1 到 3 字节 CMD_DIGITAL 0xC1 # 只回送 E1-E8应答 1 字节 CMD_ANALOG_X 0xC5 # 回送 E1-E8 模拟量 EX应答 3 字节 CMD_ANALOG_Y 0xC9 # 回送 E1-E8 模拟量 EY应答 3 字节 MOTOR { M1_FWD: 0x01, M1_REV: 0x02, # 前后 M2_LEFT: 0x04, M2_RIGHT: 0x08, # 左右 M3_UP: 0x10, M3_DOWN: 0x20, # 上下 M4_OPEN: 0x40, M4_CLOSE: 0x80, # 开合 } def __init__(self, portCOM3, baudrate9600, timeout0.05): self.ser serial.Serial( portport, baudratebaudrate, bytesizeserial.EIGHTBITS, parityserial.PARITY_NONE, stopbitsserial.STOPBITS_ONE, timeouttimeout, ) self.cmd self.CMD_DIGITAL self.state 0x00 # 当前电机状态字节常驻内存 def set_motor(self, name, onTrue): bit self.MOTOR[name] if on: # 同一电机的正反转位互斥先清掉相反方向再置位 opposite bit 1 if bit 0x55 else bit 1 self.state ~opposite self.state | bit else: self.state ~bit def tick(self): 下发一帧并取回输入字节必须保证 300ms 内至少调用一次 self.ser.reset_output_buffer() # 丢掉上一帧没发完的残留 self.ser.write(bytes([self.cmd, self.state])) resp self.ser.read(1) return resp[0] if resp else None staticmethod def inputs(byte): return { E1: bool(byte 0x01), # 底盘前限位 E2: bool(byte 0x02), # 底盘后限位 E3: bool(byte 0x04), # 手爪张开到位 E4: bool(byte 0x08), # 上升到位 E5: bool(byte 0x10), # 下降到位 }几个参数的取舍值得说一下。reset_output_buffer()不能省被动模式下 PC 卡顿会让输出缓冲堆积好几帧旧命令不清掉的话你按下「停」接口板执行的还是两帧之前的「动」。timeout给 50ms比 100ms 的刷新周期短避免读不到应答时把整个循环拖死。set_motor里做互斥处理是因为手写状态字节太容易写出 0x03、0x0C 这类非法组合让代码替你挡住比事后查电机为什么不转省事得多。主循环按 100ms 节拍跑撞限位就撤位if __name__ __main__: robo FischerROBO(COM3) robo.set_motor(M1_FWD, True) # 底盘向前 while True: t0 time.time() data robo.tick() if data is not None and robo.inputs(data)[E1]: robo.set_motor(M1_FWD, False) # 撞到前限位立刻撤掉驱动位 robo.tick() # 补一帧确保接口板收到「停」 break time.sleep(max(0, 0.10 - (time.time() - t0)))4. VB 端三件套MSComm 收字节、Timer 刷电机、Winsock 做远程链路课程设计的原始实现跑在 VB6 上用的是三个老控件。这三个东西的分工很清楚MSComm 管串口物理层Timer 管节拍Winsock 管网络。搞混了职责程序就会变成一堆互相触发的事件泥潭。4.1 窗体控件清单与初始化顺序原程序的界面上方有一个文本框记录手爪闭合时的行程开关状态十进制中部是复选框和按钮组控制四路电机底下塞了一个 Winsock、一个 MSComm 和 12 个 Timer。12 个定时器的用意是每个电机方向占一个靠Enabled的开关来启停对应动作逻辑直观但后期扩展性差。控件名称关键属性职责WinsockWinsock1LocalPort、Protocol TCP侦听主控端连接收远程指令MSCommMSComm1CommPort 1、Settings 9600,n,8,1串口收发MSCommMSComm1RThreshold 1、InputLen 0每收 1 字节触发事件、读空缓冲区TimerTimer1—Timer12Interval延时、状态刷新、动作停机初始化顺序有讲究先关掉全部 Timer再让 Winsock 进入侦听最后打开串口。反过来的话串口一开就可能收到数据而处理数据的代码还在依赖尚未初始化的控件状态。4.2 MSComm 参数设定与 OnComm 事件里的位掩码判断Form_Load里那几行看着平淡每一行都有必须存在的理由。InBufferCount 0清掉上电前的残留字节RThreshold 1让每收到一个字符就产生一次 OnComm 事件这是被动模式下「来一帧解一帧」的前提InputLen 0表示一次读走缓冲区全部内容配合固定长度的应答使用Settings 9600,n,8,1必须和接口板一致波特率差一档收到的就是乱码。Private Sub Form_Load() Timer1.Enabled False 先停所有定时器避免未初始化就触发 ... Timer2 到 Timer12 同样处理 ... Winsock1.Listen 进入侦听等待主控端连接 MSComm1.InBufferCount 0 清除接收缓冲区残留 MSComm1.CommPort 1 选用 COM1 MSComm1.RThreshold 1 收到 1 个字符即触发 OnComm MSComm1.InputLen 0 一次读空接收缓冲区 MSComm1.Settings 9600,n,8,1 MSComm1.PortOpen True End Sub Private Sub MSComm1_OnComm() Dim inbyte() As Byte Select Case MSComm1.CommEvent Case comEvReceive inbyte MSComm1.Input 取回 1 字节输入状态 End Select If inbyte(0) And 1 Then Timer1.Enabled False E1向前停止 If inbyte(0) And 2 Then Timer2.Enabled False E2向后停止 If inbyte(0) And 4 Then Timer7.Enabled False E3手爪张开停止 If inbyte(0) And 8 Then Timer5.Enabled False E4上升停止 If inbyte(0) And 16 Then Timer6.Enabled False E5下降停止 End Sub用And做位与而不是做等值判断原因在第 2 章讲过多个限位可能同时压着等值判断会漏。另外inbyte是动态数组只发了 0xC1 命令时它只有 1 个元素如果误用了 0xC5 命令索引 1、2 上是模拟量的两个字节程序不会报错但数据含义全变——这是排查「限位偶尔不灵」时值得回头看一眼的地方。4.3 Timer 轮询的刷新周期与停机联动每个动作对应一个 Timer触发时组装两字节下发。以底盘向前为例Private Sub Timer1_Timer() Dim outbyte(1) As Byte outbyte(0) HC1 命令字节只回送 E1-E8 outbyte(1) H1 状态字节电机 1 正转向前 MSComm1.OutBufferCount 0 丢弃积压的旧帧只发最新状态 MSComm1.Output outbyte End SubOutBufferCount 0这一行是整个 VB 实现里最关键的一行也是很多人抄代码时会漏掉的一行。被动模式要求 300ms 内刷新但如果 PC 忙于界面重绘导致发送缓冲堆积接口板执行的是延迟的命令序列表现出来就是「松开按键后机械手还在动」。清空输出缓冲保证接口板永远只看到最新状态是最便宜的修法。Timer 的 Interval 建议设成 100ms。设成 300ms 一帧不差地卡在临界值上遇到 Windows 消息队列繁忙就会丢帧停机设成 10ms 又会把串口和 CPU 都占满9600 波特下传两个字节本身就要 2ms 左右。4.4 Winsock 的主控端/被控端与指令下行远程控制部分用 Winsock 走 TCP。被控端连着机械手的那台 PC执行Listen等待连接主控端主动连接后把「哪个电机、什么方向、按了还是松了」编码成短指令发过去被控端解出指令后去操作对应的 Timer 的Enabled属性。这样一来串口那一侧的逻辑完全不用改网络只是替换了按键的触发源。要注意的是指令下行和串口刷新是两条不同频率的时间线。网络侧受延迟和丢包影响可能几百毫秒才来一条指令串口侧必须稳定在 100ms 一帧。正确做法是网络线程只改内存里的目标状态串口帧由本地 Timer 按固定节拍从内存取状态发送绝不能让「收到网络包」直接去写串口——否则网络一卡机械手就停。5. 从限位抖动到节拍优化慧鱼机械手调参排错的几个实操技巧机械手跑起来之后问题才真正开始。下面这几类故障在慧鱼模型上出现频率最高处理顺序也有讲究。5.1 三类高频故障的定位顺序「电机不转」先看状态字节有没有写出正反转同时置位的非法组合0x03、0x0C、0x30、0xC0再量接口板输出端有没有电压最后才怀疑电机。慧鱼电机烧的概率远低于写错位。「撞限位不停」先确认发的命令字节是不是 0xC1——发成 0xC5 或 0xC9 时回送的是 3 字节VB 里inbyte(0)虽然仍是数字量但接收长度和后续处理都对不上确认命令没错再看限位开关的触板是不是被机械结构顶偏了压不到触点。「动作有抖动」多半是 300ms 边界问题把刷新周期从 300ms 压到 100ms 基本能解决。还有一类执行时序问题撤掉驱动位之后接口板不会立刻响应因为被动模式下接口板是「收一帧、执行一帧」中间隔着通讯周期。稳妥的做法是撤位后再补发一帧也就是前面代码里那个额外的tick()。5.2 没有编码器时怎么「标定」工业机械手谈旋转中心标定靠的是编码器和标定板慧鱼模型没有任何位置反馈做不了真正意义上的标定只能做「归零 计时」的近似。可用的办法是把每次开机都强制走一遍回零流程各轴朝限位方向运动撞到开关即认为当前位置是机械原点然后把从原点到任意目标位的行程换算成执行时间存进配置表。这样换来的是可重复性不是绝对精度风阻、电压、装配误差都会让同一段时间走出不同距离得多跑几轮取平均。原程序里手爪闭合靠计数 45 拍停机用的就是这个思路。5.3 多轴协同与节拍优化节拍优化的核心是别把动作串成队列。接口板允许 4 个电机同时动那么「手爪张开」和「底盘后退」完全可以并行。做法是把两个方向的位按位或进同一个状态字节# 把两个不冲突的动作合成一帧省掉一趟完整行程的时间 mask robo.MOTOR[M3_UP] | robo.MOTOR[M4_OPEN] # 0x10 | 0x40 0x50 robo.state | mask robo.tick() # 冲突组合会被拒绝执行必须靠代码挡住例如 BAD robo.MOTOR[M1_FWD] | robo.MOTOR[M1_REV] # 0x03电机 1 拒动 assert not (robo.state BAD), 同一电机正反转位同时置位接口板不会动作要注意哪些动作能并行升降和开合互不干涉可以叠前后和左右共用底盘受力同时动会让底盘偏移量变大限位开关可能提前或延迟触发叠之前最好实测。真正影响节拍的往往不是电机转速而是等待限位的收敛时间把刷新周期压到 100ms、撞限位后立刻补一帧往往比调机械结构更立竿见影。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门