十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

四足机器人原型开发:从零搭建到稳定行走的工程路径

四足机器人原型开发:从零搭建到稳定行走的工程路径 王兴兴早期做宇树时拿到的那笔 200 万经常被创业媒体当成故事素材。最近又出现“给下一个宇树当董事长”的说法。投资人的选择和商业回报不是技术博客能展开的部分但这个故事背后有一个更值得工程师关注的问题一个拿到早期启动资金的团队怎么把一台足式机器人从“能站起来”做到“能走起来”再做到“能在真实场景里稳定作业”。下面不讨论估值、股权和公司运作只从工程实现角度拆解一条从零搭建四足机器人最小原型的路径并把开发过程中每一层该解决什么问题、该怎么验证、出了问题怎么排查一次说清楚。1. 先拆解四足机器人的四层技术栈再决定预算和人力足式机器人看起来是整机产品但工程上更像四个独立系统拼在一起。机械结构负责承受载荷和冲击运动控制负责让关节按步态运动状态估计负责让机器人知道自己身体的姿态和位置感知与决策负责判断前方是什么、下一步去哪。很多团队踩坑是因为在一开始就把“走起来”当成单一目标。实际开发中这四层会遇到完全不同的问题排错手段也完全不一样。1.1 机械与驱动层决定机器人最多能承受什么四足机器人的每条腿通常配置 3 个主动自由度分别是髋关节外摆、髋关节俯仰、膝关节俯仰。为了让大腿和小腿摆动得更灵活驱动电机尽量靠近身体一侧小腿部分用同步带或连杆传递动力。原型阶段可以用 6061 铝合金加工结构件配合 3D 打印支架进入生产阶段后才需要考虑碳纤维、拓扑优化和整机减重。驱动系统往往决定整机成本和迭代速度。常见方案如下表所示。驱动类型特点原型阶段适合度主要注意点普通位置舵机便宜、控制简单适合第一版验证思路响应慢负载大时容易抖动串行总线舵机带角度反馈可组网适合小尺寸四足原型需要单独处理供电和通信速率无框电机配减速器力矩控制基础好成本高建议有经验后再上涉及驱动器、编码器、电流环调试拿到早期资金后第一版没有必要直接上无框电机。先用带反馈的串行舵机把步态和感知跑通再评估力矩和响应是否够用是更稳妥的顺序。这个阶段如果盲目追求工业级硬件往往会把预算和人力大量消耗在机械结构上反而拖慢控制算法的迭代。1.2 运动控制层决定机器人走得是否像生物运动控制层的输入是脚的目标位置或身体目标速度输出是各关节的角度或力矩指令。它内部至少包含逆运动学、步态规划、姿态稳定、力分配几个模块。不同模块对控制频率有不同的要求。位置控制模式下100 Hz 是一个底线。低于这个频率机器人动作会明显发涩步态很难稳定。加入姿态反馈后推荐把控制频率做到 500 Hz 以上。需要说明的是控制频率不是靠主控算力堆出来的更多取决于电机驱动器的通信速率和代码执行路径。控制循环里如果塞了大量打印日志或耗时计算频率会迅速掉下来。1.3 感知与状态估计层机器人判断“我在哪、我是什么姿态”的依据状态估计主要解决两个问题身体姿态和空间位置。姿态通常由 IMU 提供加速度和角速度再与关节编码器推算出的运动学结果融合。位置则需要视觉里程计、激光雷达或 UWB 等传感器辅助。很多刚接触四足机器人的人以为装上 IMU 就万事大吉。实际上 IMU 有零偏和漂移角度数据经过积分获得一旦融合策略不对几分钟后角度误差就会大到把机器人带偏。感知层则决定机器人能不能避开障碍物、能不能完成建图导航。最低版本可以只接一个遥控器由人控制前进后退做演示和产品化时才需要引入深度相机、激光雷达和视觉识别。1.4 决策与任务层面向实际场景的功能入口决策层负责根据任务目标输出速度指令比如前进、转弯、避障、停障。它不直接控制关节而是给运动控制层下达行为指令。在最低版本里决策层可以只是一个遥控器接口到了做具体应用时才需要引入导航栈、视觉识别和任务状态机。把四层区分清楚之后预算和精力分配才有意义。第一次做原型建议顺序是先做机械与驱动再做单腿运动控制然后排步态最后接感知。反过来如果先花大量精力调试视觉算法机器人却连稳定行走都做不到产出会很难落地。2. 开发环境准备先把依赖和目录结构固定下来2.1 学习环境的硬件方案第一版四足机器人不需要追求工业级性能。常见的学习配置如下表整体预算可以控制在几千元以内。原始材料没有给出指定型号时落地前需要先确认驱动、ROS 分发包和操作系统版本是否兼容。部件作用学习环境推荐主控板运行控制循环和感知算法Jetson Orin Nano 或树莓派 5关节驱动产生角度和力矩串行总线舵机单腿 3 个整机 12 个电源给主控和舵机供电3S 锂电池舵机供电单独走线IMU测量加速度和角速度常见 9 轴模块即可深度相机获取深度点云深度相机或激光雷达二选一通信遥控和调试Wi-Fi 或有线网口串口保留备用这里最容易被忽略的是供电。12 个舵机同时动作时的峰值电流远大于主控板供电能力因此主控和舵机必须分开供电。如果共用一个电源运动瞬间电压跌落会导致主控复位、舵机抽搐这类问题排查起来非常费时间。2.2 Ubuntu 和 ROS 2 Humble 环境以 Ubuntu 22.04 和 ROS 2 Humble 为例安装最小开发环境# 安装 ROS 2 Humble 基础包 sudo apt update sudo apt install -y ros-humble-ros-base python3-colcon-common-extensions # 安装常用的 Python 依赖 pip install numpy pyserial transforms3dROS 2 不是四足机器人必需的组件但它解决三个问题传感器模块之间解耦、时钟同步、可视化调试。用ros2 topic echo可以直接查看传感器数据用rviz2可以看到机器人模型和点云。这些能力在排错阶段非常关键。2.3 建议的项目目录如果整个项目只有一个主脚本后续联调会非常吃力。建议一开始就按模块划分目录legged_robot/ ├── config/ │ ├── urdf/ │ │ ├── leg.urdf │ │ └── robot.urdf │ └── control.yaml ├── src/ │ ├── driver/ │ │ └── serial_servo.py │ ├── kinematics/ │ │ └── leg_ik.py │ ├── gait/ │ │ └── trot_controller.py │ ├── state_estimation/ │ │ └── imu_filter.py │ └── perception/ │ └── depth_camera.py ├── launch/ │ └── bringup.launch.py ├── scripts/ │ └── read_sensors.py └── tests/ ├── test_ik.py └── test_gait.py驱动、算法、感知、测试分开之后替换硬件或算法时影响面会小很多。一个常见错误是把所有代码都铺在顶层目录靠文件名区分模块。等联调时发现某个环节有问题排查成本会非常高。3. 最小运动闭环先让一条腿动起来四足机器人调试中最容易犯的错误是一上来就四腿同时控制。如果四条腿同时乱动很难分清是机构干涉、通信错误还是算法错误。正确顺序是先控制一条腿确认坐标、方向、角度范围再扩展到四条腿。3.1 先驱动一个串行舵机串行舵机通常使用 TTL 或 RS485 总线通信每条指令带舵机 ID。示例指令格式如下import serial ser serial.Serial(/dev/ttyUSB0, 115200, timeout0.1) def set_servo(servo_id, pulse, move_time500): cmd f#{servo_id}P{pulse}T{move_time}\r\n.encode() ser.write(cmd)实际项目中不同舵机厂商的协议差别很大有些使用 JSON 帧有些使用自定义二进制帧。上面只是说明控制思路发送目标脉宽或角度并指定运动时间。拿到舵机后第一件事是确认中位脉宽、正转方向和限位值不要直接装到机器人上盲调。3.2 用几何逆运动学求关节角单腿可以简化为二连杆机构。已知目标脚位置 (x, y)可求髋关节角和膝关节角import math def leg_ik_2d(x, y, l10.10, l20.10): L math.hypot(x, y) if L l1 l2 or L abs(l1 - l2): raise ValueError(目标位置超出工作空间) cos_knee (l1 * l1 l2 * l2 - L * L) / (2.0 * l1 * l2) knee math.acos(max(-1.0, min(1.0, cos_knee))) alpha math.atan2(y, x) beta math.atan2(l2 * math.sin(knee), l1 l2 * math.cos(knee)) hip alpha - beta return hip, knee这段代码解决的是给定脚在机器人身体坐标系下的位置反推出两个关节的目标角度。两点需要注意。第一当目标点距离 L 大于大小腿长度之和时物理上不可达代码要抛错而不是继续计算。第二膝关节角计算结果要映射到舵机的实际运动范围通常需要做一次线性缩放。3.3 单腿控制主循环import math import time def to_pulse(angle_rad, min_pulse500, max_pulse2500): # 假设舵机角度范围是 -90 度到 90 度 ratio (angle_rad math.pi / 2) / math.pi return int(min_pulse ratio * (max_pulse - min_pulse)) while True: x, y 0.08, -0.12 hip, knee leg_ik_2d(x, y) set_servo(1, to_pulse(hip)) set_servo(2, to_pulse(knee)) time.sleep(0.01)运行这一段后脚应该稳定在一个固定位置。如果脚在抖动先检查电源电流是否足够再检查舵机运动时间是否设置得太短。可以先让move_time设为 1000 毫秒观察动作是否平滑。3.4 单腿调试的检查点单腿闭环在哪个阶段算合格可以按如下清单确认舵机中位对应关节 0 度。脚的实际位置与目标位置误差在 2 毫米以内。控制频率稳定在 100 Hz。连续运行 10 分钟没有过热、掉线或复位。只有一条腿满足这个标准后再去复制到其他腿才不会把硬件误差和算法误差混在一起。4. 步态编排从单腿到四条腿的关键一步4.1 摆动相和支撑相四足机器人行走时每条腿在“摆动”和“支撑”两个状态之间切换。摆动相表示脚离开地面向前迈出支撑相表示脚接触地面并推动身体前进。步态规划的本质就是安排好四条腿在时间和空间上的相对关系。Trot对角小跑是最适合原型的步态左前腿和右后腿作为一对右前腿和左后腿作为另一对。两条对角腿同时支撑或摆动比静态 Walk 更快又不像 Gallop 那样需要精确的腾空相控制。4.2 Trot 步态表步态周期可以用 0 到 1 表示。假设支撑相占 0.7摆动相占 0.3四条腿的默认相位关系如下表。腿相位偏移0~0.7 周期0.7~1.0 周期左前0.0支撑摆动右后0.0支撑摆动右前0.5摆动支撑左后0.5摆动支撑相位偏移 0.5表示右前腿和左后腿刚好错开半个周期。这个偏移直接决定了机器人会不会跌倒。如果四条腿相位偏移全部为 0机器人会四条腿同时抬离地面马上摔倒。4.3 步态控制器的代码骨架下面代码只负责回答“某个时间点某条腿应该在摆动还是支撑”class TrotController: def __init__(self, period0.4, swing_ratio0.3): self.period period self.swing_ratio swing_ratio # 依次是左前、右后、右前、左后 self.offsets [0.0, 0.0, 0.5, 0.5] def phase_at(self, t, offset): return ((t / self.period) offset) % 1.0 def is_swing(self, leg_index, t): phase self.phase_at(t, self.offsets[leg_index]) return phase self.swing_ratio def foot_target(self, t, leg_index, default_pos, step_length): if self.is_swing(leg_index, t): # 摆动相向前迈出 step_length return (default_pos[0] step_length, default_pos[1], default_pos[2]) else: # 支撑相回到默认位置 return default_pos这段代码把“哪条腿什么时候摆动”和“脚点该到哪里”分开方便以后替换更复杂的步态比如 Walk 或 Pace。实际项目中脚点轨迹应该使用五次多项式插值避免起步和落地瞬间加速度突变否则地面冲击会直接反映到关节负载上。4.4 加上身体姿态后步态才算闭环如果腿部控制只做位置指令机器人很容易在外力扰动下侧翻。生产级方案会加入姿态反馈根据 IMU 测得的横滚角和俯仰角计算身体需要的补偿量再把补偿量映射到四条腿的脚点位置。第一步可以先把姿态显示在调试界面观察身体是不是在左右晃动第二步再让姿态参与控制。5. 状态估计没有 IMU机器人走三步就会偏5.1 为什么不能只靠电机角度电机编码器只能告诉关节角度但机器人是一个运动基座可以移动的系统。每条腿的脚点位置可以合成机体位置变化然而足端打滑、编码器误差、结构形变都会让推算结果越来越偏。IMU 提供的是加速度和角速度短期相对准确长期积分会漂移。工程上必须把两类信息融合起来。5.2 IMU 数据滤波互补滤波实现第一版可以使用互补滤波估计横滚角和俯仰角。它把陀螺仪积分和加速度计结算结果做加权平均import math class ComplementaryFilter: def __init__(self, alpha0.98): self.alpha alpha self.roll 0.0 self.pitch 0.0 def update(self, acc_x, acc_y, acc_z, gyro_roll, gyro_pitch, dt): acc_roll
返回列表