3. 运动控制中的神经网络:全连接网络与卷积网络在控制中的应用、循环网络与序列决策、输入输出规范
各位同学,咱们今天聊点实在的。神经网络在运动控制里到底怎么用?说白了,就是让机器人学会「看情况办事」。我做了这么多年嵌入式控制,见过太多人把网络结构搭得花里胡哨,结果一上硬件就崩。今天咱们就掰开揉碎,把几种常用网络在控制里的角色讲清楚。
3.1 全连接网络:最朴素的「万能逼近器」
全连接网络,也叫MLP。它干的事很简单:把输入向量一顿线性变换加激活,映射到输出。在运动控制里,我最常用它做两件事:状态到动作的直接映射,以及动力学模型的拟合。
举个例子。你有一个机械臂,要控制它抓杯子。输入是关节角度、角速度,输出是关节力矩。一个三层全连接网络就能搞定。我当年做四足机器人步态控制时,就用了一个128-64-32的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)
3.3 循环网络与序列决策:让控制「有记忆」
运动控制本质上是个序列决策问题。你当前的动作,不仅取决于当前状态,还取决于之前的状态和动作。比如你控制一个倒立摆,光看当前角度是不够的,还得知道角速度——这其实就是一种「短时记忆」。
循环网络,尤其是LSTM和GRU,就是专门干这个的。它们内部有个隐藏状态,能记住过去一段时间的信息。我在做机械臂力控时,用了一个双层LSTM,输入是过去10步的关节位置和力矩,输出是下一步的期望力矩。效果比纯MLP好很多,因为力控本身就有滞后性。
但要注意,RNN在控制里有个坑:训练不稳定。梯度爆炸和梯度消失是家常便饭。我建议用GRU替代LSTM,参数少、收敛快,效果差不太多。另外,序列长度别太长,我一般控制在20步以内。
3.4 输入输出规范:状态空间与动作空间
这部分是控制里最容易出错的地方。我见过太多人,网络结构搭得完美,但输入输出没对齐,结果训练出来的模型根本没法用。
状态空间就是网络的输入。它应该包含所有对决策有影响的信息。比如机器人控制,状态通常包括:
- 关节位置(角度或位移)
- 关节速度(角速度或线速度)
- 末端执行器位姿(如果有)
- 传感器读数(力、力矩、IMU等)
动作空间就是网络的输出。它取决于你的控制方式:
- 位置控制:输出期望关节角度
- 速度控制:输出期望关节角速度
- 力矩控制:输出期望关节力矩
- 混合控制:比如有些关节用位置,有些用力矩
| 控制模式 | 动作空间维度 | 典型范围 | 注意事项 |
|---|---|---|---|
| 位置控制 | 关节数 | [-π, π] 或 [-180°, 180°] | 注意角度周期性,用sin/cos编码 |
| 速度控制 | 关节数 | [-max_speed, max_speed] | 输出要乘以最大速度限制 |
| 力矩控制 | 关节数 | [-max_torque, max_torque] | 输出后要经过力矩限幅 |
| 末端位姿 | 6 (位置+姿态) | 位置: [-1,1]m, 姿态: 四元数 | 四元数要归一化 |
- 归一化:所有输入输出都归一化到[-1, 1]或[0, 1]区间。我习惯用[-1, 1],因为tanh激活函数输出范围正好匹配。
- 尺度匹配:网络输出的原始值要乘以实际物理范围。比如力矩输出范围是[-10, 10]Nm,那网络输出要乘以10。
- 单位统一:角度用弧度还是角度?速度用rad/s还是deg/s?一定要统一。我吃过这个亏,训练时用弧度,部署时用了角度,结果机器人直接抽风。
嗯,这里还要提一个我踩过的坑:状态空间的冗余性。有一次我做四足机器人控制,把24个关节的状态全塞进去了,结果网络死活不收敛。后来发现很多关节是耦合的,冗余信息反而干扰了学习。所以,状态空间不是越大越好,要精选。
3.5 知识体系总览
下面这张图,是我自己总结的运动控制中神经网络选型逻辑。你照着这个思路走,基本不会跑偏。
这张图的核心逻辑很简单:根据输入数据的结构选网络。低维向量用MLP,图像用CNN,时序用RNN。别搞混了。我见过有人拿CNN处理一维时序数据,效果还不如一个简单的MLP,纯粹是杀鸡用牛刀。
好了,这一章的内容就到这儿。记住,网络结构只是工具,真正重要的是你对控制问题的理解。下一章咱们聊聊模型压缩的具体技术,到时候我会分享一些在STM32上跑神经网络的实战经验。