ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Unitree Z1 六轴机械臂 SDK 二次开发实战:从接口调用到自动化任务实现

2026/10/7 11:17:41 拓冰建站 浏览量
Unitree Z1 六轴机械臂 SDK 二次开发实战:从接口调用到自动化任务实现 1. 从一台桌面级六轴机械臂说起第一次把 Unitree Z1 从箱子里拎出来的时候我脑子里冒出来的第一个念头是这东西比想象中小太多了。整机不到 5 公斤六个关节臂展大概 70 厘米出头拿在手里像个精密的金属玩具。但通电之后它关节里那种干净利落的伺服响应会让你立刻意识到这不是玩具——这是一台真正可以拿来做研究、做原型验证、甚至做轻量级自动化任务的六轴协作机械臂。Z1 是宇树科技推出的一款轻量化六轴机械臂官方提供了完整的 SDK 来做二次开发。很多人拿到它之后的第一反应是我该怎么让它动起来第二反应是我该怎么让它自动地、有逻辑地动起来。这两个问题之间隔着的就是从接口调用到自动化任务实现的整条路。这篇文章想聊的就是这条路——不是官方文档的复述而是我在实际折腾过程中踩过的坑、想明白的道理、以及最后跑通的那套方案。如果你手上正好有一台 Z1或者正在评估要不要用它做项目又或者你用的是别的六轴机械臂但同样面临 SDK 二次开发的问题那这篇内容应该能给你省下不少时间。我会从 SDK 的基本接口讲起一路讲到怎么把零散的接口调用组织成一个能自动跑起来的任务流程中间涉及通信方式的选择、坐标系的理解、轨迹规划的思路、以及状态机设计这些实际开发中绕不开的东西。需要说明的是Z1 的 SDK 本身在持续迭代不同固件版本之间接口可能有细微差异。我下面讲的内容基于我实际使用的版本如果你发现对不上优先以你手头版本的官方文档为准。但底层的思路和方法论是通用的这部分不会因为版本变化而失效。2. 先搞清楚 SDK 到底给了你什么2.1 通信层UDP 还是串口这是个问题Z1 的 SDK 底层通信方式主要有两种一种是基于 UDP 的网络通信一种是串口通信。官方示例里两种都有但实际用下来选择哪种取决于你的部署场景。UDP 的好处是灵活你的控制程序可以跑在另一台电脑上通过网线或者局域网跟机械臂通信不用物理上贴着机械臂。这对于需要把控制逻辑放在性能更强的工控机或者带 GPU 的机器上的场景很友好。但 UDP 本身是不可靠传输虽然 Z1 的协议层做了一些处理但在网络状况不好的时候你可能会遇到指令丢失或者延迟抖动的问题。串口的好处是确定性更强物理连接直接延迟稳定。缺点是控制程序必须跑在跟机械臂物理连接的设备上而且串口的带宽有限高频控制指令的发送会受限。我自己的选择是开发调试阶段用 UDP因为方便最终部署如果对实时性要求高切到串口。这个切换成本不高因为 SDK 把通信层抽象得比较好上层调用的接口基本一致。注意不管你用哪种通信方式第一次连接之前一定要确认机械臂的固件版本和 SDK 版本匹配。我遇到过因为版本不匹配导致关节角度指令被解析成错误格式的情况机械臂会做出完全意料之外的动作非常危险。2.2 核心接口分类别被一堆函数名吓到Z1 SDK 暴露的接口看起来很多但归类之后其实就几大类状态获取类获取当前关节角度、关节速度、末端位姿、IMU 数据等。这类接口是只读的调用频率可以高一些用来做状态监控和反馈控制。运动控制类设置关节角度、设置关节速度、设置末端位姿等。这类接口是写的调用频率和参数范围需要谨慎控制。模式切换类在位置模式、速度模式、力矩模式之间切换。不同模式下同一类控制接口的行为不同。配置类设置关节限位、设置运动学参数、标定等。这类接口一般在初始化阶段调用运行中很少动。理解这个分类很重要因为它决定了你在写代码时的组织方式。我的习惯是把状态获取封装成一个独立的监控线程把运动控制封装成另一个线程两者通过共享内存或者消息队列通信。这样即使控制逻辑写得有问题监控线程也不会被阻塞你至少还能看到机械臂当前的真实状态。2.3 坐标系最容易出错的地方六轴机械臂的坐标系问题是个老生常谈的话题但每次都能坑到人。Z1 涉及到的坐标系至少有这几个关节空间六个关节角度组成的六维向量这是最底层的表示。基坐标系固定在机械臂底座上的坐标系。末端坐标系固定在末端法兰盘上的坐标系。工具坐标系如果你装了夹爪或者其他末端执行器还需要定义工具坐标系。SDK 里的正运动学和逆运动学接口默认是在基坐标系和末端坐标系之间转换。但如果你装了夹爪末端法兰盘的位置和夹爪实际抓取点的位置之间有一个固定的偏移这个偏移必须通过工具坐标系来补偿否则你的抓取位置永远会差那么几厘米。我踩过的坑是一开始没定义工具坐标系直接用末端法兰盘的位姿去做抓取结果夹爪总是差一点够不到目标。后来量了夹爪的尺寸在 SDK 里设置了工具坐标系偏移问题立刻解决。这个偏移量建议用游标卡尺实际测量不要靠目测或者猜。3. 从单次调用到连续控制实时性是怎么保证的3.1 控制频率的选择与权衡机械臂控制有一个绕不开的参数控制频率。你每秒给机械臂发多少次指令直接影响到运动的平滑度和响应速度。Z1 官方建议的控制频率范围大概在 100Hz 到 500Hz 之间。频率太低运动会有明显的顿挫感频率太高通信带宽和计算资源都会吃紧而且如果指令生成的速度跟不上发送速度反而会出现指令堆积或者丢帧。我的经验是对于大多数桌面级的抓取、放置、简单轨迹跟踪任务200Hz 是一个比较舒服的平衡点。这个频率下运动足够平滑同时对控制程序的实时性要求不至于太苛刻。如果你要做力控或者高速轨迹跟踪可能需要提到 500Hz 甚至更高但那就需要更认真的实时系统设计了。这里有个细节控制频率不等于你主循环的频率。你可以在主循环里以 50Hz 的频率做决策和规划但用一个独立的定时器以 200Hz 的频率发送控制指令。决策和执行的频率解耦是保证系统稳定的关键。3.2 指令插值让运动看起来像人做的如果你直接以 200Hz 的频率发送一系列离散的目标点机械臂的运动可能会显得很生硬尤其是在方向变化的地方。这时候需要做指令插值。最简单的插值是线性插值在两个目标点之间按照时间比例生成中间点。但线性插值在方向变化处会有速度不连续的问题机械臂会顿一下。更好的选择是样条插值或者 S 形速度规划让速度曲线连续加速度也连续。Z1 SDK 本身提供了一些轨迹规划的基础接口但功能比较基础。如果你需要更复杂的轨迹比如圆弧、样条曲线可能需要自己在上层实现然后把规划好的密集点序列通过 SDK 发送下去。我自己的做法是对于简单的点到点运动用 SDK 自带的规划接口就够了对于需要经过多个中间点的复杂轨迹我在上层用 Python 的 scipy 或者自己写的五次多项式插值生成密集点序列然后以 200Hz 的频率逐点发送。这样既保证了轨迹的平滑性又不需要在 SDK 层面做太多改动。3.3 状态反馈的读取与使用控制不是单向的。你发指令下去机械臂执行但执行的结果如何需要通过状态反馈来确认。Z1 SDK 的状态反馈接口可以读取当前关节角度、关节速度、关节电流等信息。这些信息可以用来做几件事闭环控制如果你在做力控或者阻抗控制状态反馈是必须的。异常检测如果某个关节的实际角度跟指令角度偏差过大可能意味着碰到了障碍物或者发生了堵转。状态估计通过关节电流可以粗略估计末端受到的力虽然精度不如力传感器但在没有力传感器的情况下是个有用的补充。我习惯在控制循环里加一个简单的异常检测如果连续 N 个周期内某个关节的实际角度与指令角度的偏差超过阈值就触发保护逻辑停止运动并报警。这个逻辑救过我几次尤其是在调试抓取动作的时候夹爪还没装好就发了抓取指令机械臂直接撞到了桌面幸好异常检测及时触发没有造成损坏。4. 自动化任务实现从脚本到状态机4.1 为什么需要状态机当你只是想让机械臂做一个简单的动作比如移动到某个位置写几行脚本调用接口就够了。但当你需要实现一个完整的自动化任务比如识别目标、规划路径、抓取、放置、回到初始位置事情就复杂了。复杂的地方不在于单个动作的实现而在于动作之间的衔接、异常情况的处理、以及任务状态的跟踪。这时候一个清晰的状态机设计就非常必要。状态机的基本思路是把整个任务分解成若干个状态每个状态对应一个明确的动作或者等待条件状态之间通过事件触发转移。比如IDLE等待任务开始信号。APPROACH移动到目标上方的预备位置。DESCEND下降到抓取位置。GRASP闭合夹爪。LIFT抬起。MOVE_TO_PLACE移动到放置位置上方。PLACE下降并松开夹爪。RETURN回到初始位置。每个状态里你只需要关心当前状态要做什么以及什么条件下转移到下一个状态。这样代码结构清晰调试也方便——你可以单独测试每个状态也可以手动触发状态转移来验证逻辑。4.2 状态机的实现方式状态机可以用很多种方式实现从最简单的 switch-case 到复杂的状态机框架。对于机械臂任务这种规模我推荐用 Python 的字典加函数指针的方式简单直接不需要引入额外的框架。核心结构大概是这样class ArmStateMachine: def __init__(self): self.state IDLE self.transitions { IDLE: {start: APPROACH}, APPROACH: {reached: DESCEND, error: ERROR}, DESCEND: {reached: GRASP, error: ERROR}, GRASP: {grasped: LIFT, error: ERROR}, LIFT: {reached: MOVE_TO_PLACE, error: ERROR}, MOVE_TO_PLACE: {reached: PLACE, error: ERROR}, PLACE: {released: RETURN, error: ERROR}, RETURN: {reached: IDLE, error: ERROR}, ERROR: {reset: IDLE} } self.handlers { IDLE: self.handle_idle, APPROACH: self.handle_approach, # ... 其他状态的处理函数 } def step(self): handler self.handlers.get(self.state) if handler: event handler() if event and event in self.transitions.get(self.state, {}): self.state self.transitions[self.state][event]这个结构的好处是状态转移逻辑集中在一处一目了然。每个状态的处理函数只负责执行动作和返回事件不关心下一步是什么。这样当你需要调整任务流程时只需要改转移表不需要动各个状态的处理逻辑。4.3 异常处理与恢复策略自动化任务最怕的就是异常。机械臂在无人值守的情况下跑一旦出错轻则任务失败重则设备损坏。所以异常处理必须做在前面。我的策略是分三层第一层是预防。在发送指令之前检查目标位置是否在工作空间内检查关节角度是否在限位范围内检查速度是否超过安全阈值。这些检查在软件层面做成本很低但能避免大部分低级错误。第二层是检测。在运动过程中持续监控关节角度偏差、电流、温度等指标。一旦发现异常立即停止运动进入错误状态。第三层是恢复。错误状态不是终点而是一个等待人工干预或者自动恢复的中间状态。根据错误的类型可以设计不同的恢复策略如果是通信超时可以尝试重连如果是关节偏差过大可以尝试回到安全位置如果是硬件故障那就只能报警等待处理。这里有个经验错误状态一定要设计一个明确的退出条件否则机械臂会一直卡在错误状态里既不能继续任务也不能接受新指令。我一般会设计一个复位事件触发后机械臂先回到一个安全的初始位置然后回到 IDLE 状态。5. 实操过程中的那些坑5.1 初始化顺序不能乱Z1 的初始化有一套固定的流程先上电等待自检完成然后建立通信连接然后使能关节然后设置工作模式最后才能发送运动指令。这个顺序不能乱乱了就会出问题。我遇到过的情况是通信连接建立之后没等自检完成就发了使能指令结果机械臂没反应但也不报错。后来查了半天才发现是自检还没结束。所以初始化的时候一定要在每一步之间加足够的等待时间或者通过状态查询接口确认上一步确实完成了再进入下一步。5.2 关节限位不是摆设Z1 的每个关节都有机械限位和软件限位。机械限位是物理上的硬限位撞上去会损坏机械结构软件限位是 SDK 里设置的可以在到达机械限位之前就阻止运动。我的建议是软件限位一定要设置而且要比机械限位留出足够的安全余量。我一般会留 5 到 10 度的余量。这样即使控制程序出了 bug机械臂也不会撞到硬限位。另外不同关节的限位范围不一样设置的时候要逐个确认。Z1 的关节 1 和关节 6 的限位范围比较大关节 2 和关节 3 的限位范围相对小一些具体数值查官方文档。5.3 通信超时与重连UDP 通信在网络状况不好的时候会出现超时。如果控制程序没有处理超时可能会一直等待导致整个任务卡住。我的做法是在通信层加一个超时计数器每次发送指令后等待反馈如果超过一定时间没有收到反馈就认为超时。连续超时达到阈值就触发重连逻辑。重连成功后先查询机械臂当前状态确认安全后再继续任务。这里有个细节重连之后不要直接继续之前的任务因为机械臂的实际位置可能已经跟预期不一样了。正确的做法是回到一个已知的安全状态然后重新开始任务。5.4 常见问题速查表问题现象可能原因排查方法解决方案机械臂不动无报错未使能关节或未设置模式查询关节使能状态和当前模式按初始化流程重新使能并设置模式运动方向与预期相反坐标系定义错误检查工具坐标系和基坐标系设置重新标定或修正坐标系参数运动过程中突然停止触发软件限位或异常检测查看错误码和关节角度调整限位范围或检查异常检测阈值通信频繁超时网络不稳定或带宽不足检查网络连接和通信频率降低控制频率或改用串口通信末端位置有固定偏差工具坐标系未设置测量实际末端位置与指令位置偏差设置工具坐标系偏移量关节发热严重控制频率过高或负载过大检查电流和温度降低控制频率或减轻负载6. 把零散接口组织成可复用的控制框架6.1 分层设计让代码好维护当你把上面这些东西都跑通之后会发现代码里有很多重复的逻辑连接、初始化、发送指令、读取状态、异常处理。如果每个任务都重新写一遍不仅效率低而且容易出错。我的做法是把控制代码分成三层通信层封装 SDK 的底层接口处理连接、发送、接收、超时重连。这一层不涉及任何业务逻辑只负责把数据从 A 传到 B。控制层封装运动控制的基本操作比如移动到某个关节角度、移动到某个末端位姿、设置速度、读取状态。这一层处理单位转换、限位检查、插值等。任务层实现具体的自动化任务用状态机组织控制层的调用。这一层只关心任务逻辑不关心底层怎么实现。这样分层之后换一个机械臂或者换一个通信方式只需要改通信层调整控制参数只需要改控制层修改任务流程只需要改任务层。各层之间通过清晰的接口通信互不干扰。6.2 参数配置化别把数值写死在代码里控制参数、限位范围、工具坐标系偏移、状态机转移条件这些东西都不应该写死在代码里。我的做法是全部放到一个 YAML 或者 JSON 配置文件里代码启动时读取。这样做的好处是调整参数不需要改代码不需要重新编译甚至不需要重启程序如果做了热加载。对于需要反复调试的场景这个效率提升非常明显。配置文件的结构大概是这样robot: connection: type: udp ip: 192.168.1.100 port: 8080 control: frequency: 200 max_velocity: 0.5 max_acceleration: 1.0 limits: joint_1: [-150, 150] joint_2: [-90, 90] # ... tool: offset: [0.0, 0.0, 0.12] orientation: [0.0, 0.0, 0.0] task: approach_height: 0.15 grasp_height: 0.02 place_height: 0.106.3 日志与可视化调试的好帮手机械臂调试过程中最有用的是两样东西日志和可视化。日志要记录每一次状态转移、每一次指令发送、每一次异常触发。日志的格式要结构化方便后续分析。我一般用 Python 的 logging 模块输出到文件和控制台级别可以配置。可视化方面如果条件允许用一个简单的 3D 可视化工具实时显示机械臂的位姿和轨迹会大大加快调试速度。ROS 的 RViz 是一个选择但如果你不想引入 ROS 的复杂性也可以用 matplotlib 的 3D 绘图做一个简易的可视化。虽然简陋但足够看清机械臂在干什么。我自己的调试流程是先在可视化界面里确认轨迹规划的结果是对的然后再实际发送指令给机械臂。这样可以把软件 bug 和硬件问题分开排查起来快很多。7. 一些实际任务中的经验7.1 抓取任务视觉与运动的配合如果你的自动化任务涉及抓取那大概率需要视觉系统的配合。视觉负责识别目标的位置和姿态机械臂负责移动到目标位置并执行抓取。这里的关键是坐标转换视觉系统给出的目标位置通常是在相机坐标系下的需要转换到机械臂的基坐标系。这个转换涉及相机标定和手眼标定是另一个大话题这里不展开。但我要强调的是标定的精度直接决定了抓取的成败。标定没做好后面怎么调都是白搭。我的经验是标定完成后一定要做验证。拿一个已知位置的目标让机械臂去抓看实际抓取位置和预期位置的偏差。如果偏差在可接受范围内标定就算通过了。如果偏差大先检查标定流程再检查坐标系转换的代码。7.2 轨迹跟踪平滑比精确更重要有些任务需要机械臂沿着一条轨迹运动比如画圆、画直线、或者跟踪一个移动的目标。这时候轨迹的平滑性往往比精确性更重要。原因很简单机械臂的动力学特性决定了它无法瞬间改变速度方向。如果你给的轨迹有尖锐的拐角机械臂要么跟不上要么会产生很大的振动。所以轨迹规划的时候一定要做平滑处理让速度和加速度连续。我一般用五次多项式或者 B 样条来做轨迹平滑。五次多项式可以保证位置、速度、加速度都连续适合点到点的轨迹。B 样条更灵活适合需要经过多个中间点的复杂轨迹。7.3 多任务调度别让机械臂闲着如果你的自动化系统需要处理多个任务比如从传送带上抓取不同位置的物体那任务调度就很重要。机械臂的运动时间往往是瓶颈所以调度策略的目标是让机械臂尽可能少地等待。我的做法是把任务分解成必须串行和可以并行的部分。比如视觉识别可以和机械臂运动并行因为相机拍照不需要机械臂停下来。抓取和放置必须串行因为涉及机械臂的物理动作。在代码层面我用一个任务队列来管理待执行的任务用一个调度器来决定下一个执行哪个任务。调度器的策略可以根据实际场景调整比如优先执行距离最近的任务或者优先执行截止时间最近的任务。8. 关于 SDK 二次开发的一些思考Z1 的 SDK 二次开发表面上是学一堆接口怎么调用实际上是在学怎么把一个物理设备的行为抽象成软件可以控制的模型。这个抽象过程涉及到通信、控制、状态管理、异常处理等多个方面每一方面都有它的门道。我自己的体会是不要一上来就想着做复杂的任务。先把最简单的让机械臂移动到指定位置跑通然后加上状态反馈然后加上异常处理然后加上状态机一步一步来。每一步都确认稳定了再进入下一步。这样虽然看起来慢但实际上是最快的路径因为每一步的问题都局限在很小的范围内排查起来容易。另外官方文档和示例代码一定要仔细看。Z1 的 SDK 文档虽然不算特别详细但关键信息都有。很多问题其实文档里已经写了只是没注意到。我遇到过好几次折腾半天的问题回头翻文档发现第一页就写了注意事项。最后安全永远是第一位的。机械臂是物理设备它的运动会对周围的人和物产生实际影响。在调试阶段一定要确保工作空间内没有障碍物机械臂的限位设置正确异常检测逻辑有效。不要因为赶进度就跳过安全检查一次事故的代价远大于省下来的时间。如果你也在做 Z1 或者其他机械臂的二次开发欢迎交流。这个领域的东西很杂一个人踩坑不如大家一起踩踩完了分享出来后来的人就能少走弯路。