3. 运动控制中的神经网络:全连接网络与卷积网络在控制中的应用、循环网络与序列决策、输入输出规范

各位同学,咱们今天聊点实在的。神经网络在运动控制里到底怎么用?说白了,就是让机器人学会「看情况办事」。我做了这么多年嵌入式控制,见过太多人把网络结构搭得花里胡哨,结果一上硬件就崩。今天咱们就掰开揉碎,把几种常用网络在控制里的角色讲清楚。

3.1 全连接网络:最朴素的「万能逼近器」

全连接网络,也叫MLP。它干的事很简单:把输入向量一顿线性变换加激活,映射到输出。在运动控制里,我最常用它做两件事:状态到动作的直接映射,以及动力学模型的拟合

举个例子。你有一个机械臂,要控制它抓杯子。输入是关节角度、角速度,输出是关节力矩。一个三层全连接网络就能搞定。我当年做四足机器人步态控制时,就用了一个128-64-32的MLP,把躯干姿态映射到腿的摆动轨迹。效果还行,就是调参调得我头秃。

核心要点:全连接网络适合输入输出维度固定、且特征之间没有明显空间结构关系的场景。比如状态向量是 [位置, 速度, 加速度] 这种,用MLP最直接。

但要注意,全连接网络有个毛病:参数太多。你想想看,输入100维,第一层256个神经元,光这一层就25600个参数。在嵌入式芯片上,这玩意儿跑起来发热量感人。所以我在实际项目中,能用小网络绝不用大的。

3.2 卷积网络:给控制加上「眼睛」

卷积网络在控制里最大的价值,就是处理高维感知输入。比如摄像头图像、激光雷达点云投影图。我做过一个无人机避障项目,输入是前视摄像头拍的640x480图像,用三层卷积提取特征,然后接全连接输出偏航角指令。

为什么用卷积?因为图像有局部相关性。你想想,一个像素和它旁边的像素关系最紧密,离得远的像素基本没关系。卷积核正好捕捉这种局部模式。而且卷积的权值共享特性,让参数量比全连接少了好几个数量级。

# 一个典型的控制用卷积网络结构
class ConvController(nn.Module):
    def __init__(self):
        super().__init__()
        # 输入: 3通道图像, 64x64
        self.conv1 = nn.Conv2d(3, 16, kernel_size=5, stride=2)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=2)
        self.conv3 = nn.Conv2d(32, 64, kernel_size=3, stride=2)
        # 全连接输出动作
        self.fc = nn.Linear(64 * 7 * 7, 4)  # 输出4个关节力矩
        
    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = F.relu(self.conv3(x))
        x = x.view(x.size(0), -1)
        return self.fc(x)
我的经验:在嵌入式平台上跑卷积,一定要考虑推理速度。我建议用深度可分离卷积替代标准卷积,参数量能降到原来的1/8左右,精度损失不到2%。另外,输入图像分辨率别贪大,128x128在很多场景下已经够用了。

3.3 循环网络与序列决策:让控制「有记忆」

运动控制本质上是个序列决策问题。你当前的动作,不仅取决于当前状态,还取决于之前的状态和动作。比如你控制一个倒立摆,光看当前角度是不够的,还得知道角速度——这其实就是一种「短时记忆」。

循环网络,尤其是LSTM和GRU,就是专门干这个的。它们内部有个隐藏状态,能记住过去一段时间的信息。我在做机械臂力控时,用了一个双层LSTM,输入是过去10步的关节位置和力矩,输出是下一步的期望力矩。效果比纯MLP好很多,因为力控本身就有滞后性。

但要注意,RNN在控制里有个坑:训练不稳定。梯度爆炸和梯度消失是家常便饭。我建议用GRU替代LSTM,参数少、收敛快,效果差不太多。另外,序列长度别太长,我一般控制在20步以内。

避坑指南:我曾经在一个项目中,把RNN的隐藏层维度设到了512,结果训练时loss直接飞了。后来发现是梯度爆炸。解决方案:加梯度裁剪(gradient clipping),阈值设到1.0。另外,用LayerNorm也能缓解这个问题。

3.4 输入输出规范:状态空间与动作空间

这部分是控制里最容易出错的地方。我见过太多人,网络结构搭得完美,但输入输出没对齐,结果训练出来的模型根本没法用。

状态空间就是网络的输入。它应该包含所有对决策有影响的信息。比如机器人控制,状态通常包括:

  • 关节位置(角度或位移)
  • 关节速度(角速度或线速度)
  • 末端执行器位姿(如果有)
  • 传感器读数(力、力矩、IMU等)

动作空间就是网络的输出。它取决于你的控制方式:

  • 位置控制:输出期望关节角度
  • 速度控制:输出期望关节角速度
  • 力矩控制:输出期望关节力矩
  • 混合控制:比如有些关节用位置,有些用力矩
控制模式 动作空间维度 典型范围 注意事项
位置控制 关节数 [-π, π] 或 [-180°, 180°] 注意角度周期性,用sin/cos编码
速度控制 关节数 [-max_speed, max_speed] 输出要乘以最大速度限制
力矩控制 关节数 [-max_torque, max_torque] 输出后要经过力矩限幅
末端位姿 6 (位置+姿态) 位置: [-1,1]m, 姿态: 四元数 四元数要归一化
输入输出规范的核心原则:
  1. 归一化:所有输入输出都归一化到[-1, 1]或[0, 1]区间。我习惯用[-1, 1],因为tanh激活函数输出范围正好匹配。
  2. 尺度匹配:网络输出的原始值要乘以实际物理范围。比如力矩输出范围是[-10, 10]Nm,那网络输出要乘以10。
  3. 单位统一:角度用弧度还是角度?速度用rad/s还是deg/s?一定要统一。我吃过这个亏,训练时用弧度,部署时用了角度,结果机器人直接抽风。

嗯,这里还要提一个我踩过的坑:状态空间的冗余性。有一次我做四足机器人控制,把24个关节的状态全塞进去了,结果网络死活不收敛。后来发现很多关节是耦合的,冗余信息反而干扰了学习。所以,状态空间不是越大越好,要精选。

3.5 知识体系总览

下面这张图,是我自己总结的运动控制中神经网络选型逻辑。你照着这个思路走,基本不会跑偏。

运动控制神经网络选型逻辑 输入(状态空间) 输入类型是什么? 低维状态向量 图像/空间数据 时序/序列数据 ↓ 推荐 ↓ 推荐 ↓ 推荐 全连接网络 (MLP) 卷积网络 (CNN) 循环网络 (RNN/LSTM/GRU) 输出(动作空间) 输出规范:归一化 → 尺度匹配 → 限幅

这张图的核心逻辑很简单:根据输入数据的结构选网络。低维向量用MLP,图像用CNN,时序用RNN。别搞混了。我见过有人拿CNN处理一维时序数据,效果还不如一个简单的MLP,纯粹是杀鸡用牛刀。

我的建议:刚开始做运动控制项目时,先用MLP试水。如果效果不行,再考虑更复杂的网络。很多时候,问题不在网络结构,而在数据预处理和超参数调优。我80%的项目,一个三层MLP就搞定了。

好了,这一章的内容就到这儿。记住,网络结构只是工具,真正重要的是你对控制问题的理解。下一章咱们聊聊模型压缩的具体技术,到时候我会分享一些在STM32上跑神经网络的实战经验。

专注资料整理