跳转到内容

机器人软件系统概览

机器人软件不是若干算法的简单集合。它首先是一套持续运行的信息系统:传感器不断产生数据,程序据此判断机器人当前的状态,控制模块计算下一步应当施加的动作,执行器改变机器人的运动,新产生的运动又会被传感器测量。理解这条闭环,比一开始记忆具体框架和函数更重要。

本章以四足机器人为主要例子,介绍机器人软件中长期不变的基本概念。不同项目会采用不同硬件、通信协议和软件框架,但都需要处理状态、坐标系、时间、控制指令和模块间通信。后续的强化学习部署、定位导航、机械臂控制和整机联调,都建立在这些概念之上。

一台正在运动的四足机器人,其软件数据流可以先简化为:

上层任务 / 遥控指令
运动控制模块
关节目标 / 期望力矩
底层关节控制
电机驱动器 → 机器人
┌────────────┴────────────┐
编码器 IMU 其他传感器
└────────────┬────────────┘
状态估计
运动控制模块

图中的箭头并不一定对应某一根线、某一个进程或某一种通信方式,而是表示信息的主要流向。程序运行起来以后,这条路径会反复执行:

  1. 读取关节编码器、惯性测量单元(Inertial Measurement Unit,IMU)等传感器数据;
  2. 整理和融合数据,得到控制所需的机器人状态;
  3. 根据当前状态与任务目标计算控制量;
  4. 将关节位置、速度或力矩等指令下发给底层控制;
  5. 机器人产生运动,传感器再次返回新的测量结果。

因此,机器人软件一直在做的事情可以概括为:读取状态、处理信息、计算控制量、下发指令,再读取新的状态。 实际系统可能同时存在多个频率、多个计算设备和多条反馈路径,但基本闭环没有改变。

为了让各部分可以独立开发和替换,机器人软件通常按照职责拆分。模块之间的边界不必在所有项目中完全一致,但常见职责大致如下。

传感器模块负责取得机器人与环境的测量数据。关节编码器可以提供关节位置,驱动器通常还能反馈速度、电流、温度和故障状态;IMU 直接测量角速度和比力,姿态、速度等量需要在一定假设下通过算法估计;相机、激光雷达和力传感器则用于获取环境或接触信息。

传感器输出并不等于可以直接使用的完整状态。原始数据可能包含噪声、偏置、延迟或异常值,也可能来自不同坐标系和不同采样时刻。数据采集模块需要记录测量来源、单位和时间戳,并完成必要的校验与预处理。

控制器关心的量往往不能由某一个传感器直接给出。例如,编码器只能测到各关节相对机身的运动,IMU 也不会直接给出机器人在世界中的准确位置。状态估计模块需要结合传感器测量、机器人模型和已有状态,得到机身姿态、速度、位置、足端接触等控制所需的信息。

本章只需要先建立一个认识:测量值是传感器直接或间接提供的数据,状态是系统对机器人当前情况的表示,估计值则是根据测量与模型计算出来的状态。 具体估计算法会在后续相关章节学习。

运动控制模块根据机器人当前状态和目标,计算下一时刻的动作。目标可能来自遥控器、导航系统或比赛任务,例如期望前进速度、目标位置、指定姿态或某个动作指令。控制模块的输出也因架构而异,可以是期望关节位置和速度,也可以是期望关节力矩或其他中间量。

强化学习策略在这里通常承担运动控制的一部分,但它并不等于完整机器人软件。策略运行之前需要准备状态,运行之后需要解释输出并完成限幅、安全检查和底层控制;任务调度、通信、日志与故障处理也仍由其他模块负责。

执行器将电信号转换为机械运动。四足机器人常见的关节执行器由电机、减速机构、编码器、驱动器以及结构件组成。底层控制程序按照较高频率接收关节目标,读取反馈并计算驱动输出,同时监控电流、温度、通信和故障状态。

软件中的一个“关节指令”最终需要经过通信链路和驱动器才能作用到电机。中间任何一层的单位、方向、编号或时间不一致,都可能造成明显错误,所以接口定义和安全限制与算法本身同样重要。

上层任务决定机器人要完成什么,而运动控制决定机器人怎样运动。遥控指令可以给出期望速度;导航模块可以给出目标位置和局部路径;比赛任务程序则可能根据识别结果、裁判信号和当前阶段选择下一项动作。上层通常不直接生成每个电机的高频指令,而是通过稳定的接口调用下层能力。

这种分层使任务逻辑不必了解每个关节的控制细节,也让运动控制模块可以在仿真、遥控和自主任务之间复用。

机器人程序当然可以从一个 main.cpp 开始,但完整项目通常不会把采集、估计、控制、通信和任务逻辑全部堆在同一个循环里。原因并不只是代码长度,而是各部分的运行条件不同:电机通信可能要求稳定的高频循环,策略推理计算量较大,定位算法依赖雷达或相机,上层任务又更关注事件和状态切换。它们需要分别测试、记录和处理故障。

这里所说的“模块”是一种职责划分,不等同于进程。多个模块可以作为函数或类运行在同一个进程中,也可以分别运行在不同进程、不同计算机甚至不同控制器上。线程是进程内部可以并发执行的路径,常用于把通信、计算和日志等工作分开。具体怎样划分,要根据实时性、故障隔离、开发成本和硬件条件决定。

无论实现方式如何,模块之间都需要明确接口。例如状态估计模块输出的 RobotState 应当说明包含哪些量、采用什么单位、对应哪个坐标系以及数据产生于什么时刻;控制器输出的 MotorCommand 也需要说明控制模式、关节顺序和允许范围。接口明确以后,模块才能独立替换,日志也才有解释价值。

第一次进入一个机器人代码仓库时,不必立即追踪每个函数。可以先围绕数据流回答几组问题:

  • 系统从哪里获得关节、IMU 和其他传感器数据?
  • 哪些量是原始测量,哪些量经过估计或滤波?
  • 控制器的输入和输出分别是什么,采用什么频率?
  • 控制指令经过哪些步骤才到达电机?
  • 各模块在同一进程、不同进程还是不同设备上运行?
  • 系统怎样记录时间、故障、日志和实验数据?

能回答这些问题,就已经获得了理解项目的主线。至于某个控制算法怎样推导、某种协议每一帧怎样编码,可以在需要进入对应模块时继续学习。

后续三篇将依次展开这条主线:先认识机器人状态、关节和坐标系,再把循环频率、模块职责、数据流和通信放进同一套运行时结构中,最后用 ROS 2 的工具观察并调试一个已有系统。