面部表情技术
面部表情是人类情感交流的主要方式,55% 的情感信息通过面部传递。人形机器人要实现自然的人机交互,必须具备面部表情能力。本文深入解析人形机器人面部表情技术的驱动方式、表情生成、情感识别与应用。
一、面部表情基础
1.1 人类面部解剖
面部肌肉系统:
人类面部肌肉(44 块)
├── 额部
│ ├── 额肌:抬眉、皱眉
│ └── 降眉肌:降眉
│
├── 眼部
│ ├── 眼轮匝肌:闭眼、眨眼
│ ├── 皱眉肌:皱眉
│ └── 提上睑肌:睁眼
│
├── 鼻部
│ ├── 鼻肌:皱鼻
│ └── 降鼻中隔肌:降鼻尖
│
├── 口部
│ ├── 口轮匝肌:闭嘴、噘嘴
│ ├── 提上唇肌:提上唇
│ ├── 降下唇肌:降下唇
│ ├── 颧大肌:微笑(拉嘴角)
│ ├── 颧小肌:辅助微笑
│ ├── 颊肌:鼓腮
│ └── 降口角肌:降嘴角(悲伤)
│
└── 颈部
└── 颈阔肌:拉下唇和嘴角
面部神经:
├── 面神经(CN VII):主要运动神经
└── 三叉神经(CN V):感觉 + 咀嚼
1.2 面部动作编码系统(FACS)
FACS 定义:
- 由 Ekman 和 Friesen 于 1978 年开发
- 基于面部肌肉的动作单元(AU)
- 用于客观描述面部表情
主要动作单元:
FACS 动作单元(部分)
├── 眉部动作
│ ├── AU1:内眉上扬(悲伤)
│ ├── AU2:外眉上扬(惊讶)
│ ├── AU4:皱眉(愤怒、专注)
│ └── AU1+2+4:复杂表情
│
├── 眼部动作
│ ├── AU5:上睑提升(惊讶)
│ ├── AU6:脸颊提升(真笑)
│ ├── AU7:眼睑收紧(愤怒)
│ └── AU43:闭眼
│
├── 鼻部动作
│ ├── AU9:皱鼻(厌恶)
│ └── AU10:上唇提升(厌恶)
│
├── 口部动作
│ ├── AU12:嘴角拉向耳(微笑)
│ ├── AU14:酒窝(抿嘴)
│ ├── AU15:嘴角下拉(悲伤)
│ ├── AU16:下唇下拉
│ ├── AU17:下巴提升(威胁)
│ ├── AU18:噘嘴
│ ├── AU20:水平拉嘴(恐惧)
│ ├── AU23:缩唇
│ └── AU24:压唇
│
└── 头部动作
├── AU51:头部左转
├── AU52:头部右转
├── AU53:头部上抬
└── AU54:头部下低
基本表情与 AU 组合:
├── 快乐:AU6 + AU12(杜兴微笑)
├── 悲伤:AU1 + AU4 + AU15
├── 愤怒:AU4 + AU5 + AU7 + AU23
├── 惊讶:AU1 + AU2 + AU5 + AU26
├── 恐惧:AU1 + AU2 + AU4 + AU20
├── 厌恶:AU9 + AU10
└── 轻蔑:AU14(单侧)
1.3 基本表情理论
Ekman 六种基本表情:
跨文化基本表情
├── 快乐(Happiness)
│ ├── 特征:嘴角上扬、眼角皱纹
│ ├── AU:6 + 12
│ └── 识别率:95%+
│
├── 悲伤(Sadness)
│ ├── 特征:嘴角下拉、内眉上扬
│ ├── AU:1 + 4 + 15
│ └── 识别率:85%+
│
├── 愤怒(Anger)
│ ├── 特征:皱眉、瞪眼、压唇
│ ├── AU:4 + 5 + 7 + 23
│ └── 识别率:90%+
│
├── 惊讶(Surprise)
│ ├── 特征:眉眼睁、口张开
│ ├── AU:1 + 2 + 5 + 26
│ └── 识别率:95%+
│
├── 恐惧(Fear)
│ ├── 特征:眉眼睁、水平拉嘴
│ ├── AU:1 + 2 + 4 + 20
│ └── 识别率:80%+
│
└── 厌恶(Disgust)
├── 特征:皱鼻、上唇提升
├── AU:9 + 10
└── 识别率:90%+
争议表情:
├── 轻蔑(Contempt):单侧 AU14
├── 尴尬(Embarrassment):眼神回避
└── 骄傲(Pride):头部后仰、微笑
二、面部驱动技术
2.1 驱动方式对比
面部驱动技术对比
├── 电机驱动
│ ├── 原理:伺服电机 + 连杆
│ ├── 优点:精确、可靠
│ ├── 缺点:噪音、重量
│ └── 应用:Sophia、Ameca
│
├── 气动驱动
│ ├── 原理:气动肌肉 + 气管
│ ├── 优点:柔顺、安静
│ ├── 缺点:需要气源、响应慢
│ └── 应用:研究原型
│
├── SMA 驱动
│ ├── 原理:形状记忆合金丝
│ ├── 优点:紧凑、静音
│ ├── 缺点:响应慢、效率低
│ └── 应用:微型机器人
│
├── 液压驱动
│ ├── 原理:液体压力
│ ├── 优点:高功率密度
│ ├── 缺点:泄漏风险
│ └── 应用:高端仿生
│
└── 显示方案
├── 原理:LED 屏幕/投影
├── 优点:灵活、可编程
├── 缺点:非真实、分辨率限
└── 应用:Pepper、NAO
2.2 电机驱动方案
结构设计:
电机驱动面部结构
┌─────────────────────────────────────┐
│ 面部皮肤(硅胶) │
│ ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ │
│ │││││││││││││││││││││││ │
│ │ 面部机构层 │
│ │ ├── 连杆机构 │
│ │ ├── 滑轮系统 │
│ │ └── 线缆传动 │
│ │││││││││││││││││││││││ │
│ ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ │
├─────────────────────────────────────┤
│ 驱动层 │
│ ├── 伺服电机 × 20-40 │
│ ├── 减速器 │
│ └── 编码器 │
├─────────────────────────────────────┤
│ 头骨结构 │
│ ├── 3D 打印/ CNC │
│ └── 电机安装座 │
└─────────────────────────────────────┘
电机配置(典型 32 DOF 面部):
├── 眉部:4 电机(左右各 2)
├── 眼部:4 电机(眼皮、眼球)
├── 鼻部:2 电机
├── 口部:12 电机(嘴唇、嘴角、下巴)
├── 脸颊:4 电机
├── 颈部:4 电机
└── 其他:2 电机(耳朵等)
控制架构:
class FacialExpressionController:
"""
面部表情控制器
"""
def __init__(self, num_motors=32):
self.num_motors = num_motors
# 电机参数
self.motor_positions = np.zeros(num_motors)
self.motor_limits = self.load_motor_limits()
# 表情库
self.expression_library = self.load_expressions()
# 混合权重
self.blend_weights = np.zeros(len(self.expression_library))
def set_expression(self, expression_name, intensity=1.0):
"""
设置表情
"""
if expression_name not in self.expression_library:
return False
# 获取目标电机位置
target_positions = self.expression_library[expression_name].copy()
# 应用强度
current = self.motor_positions
target = current + (target_positions - current) * intensity
# 限制检查
target = np.clip(target, self.motor_limits[:, 0], self.motor_limits[:, 1])
# 发送命令
self.send_motor_commands(target)
return True
def blend_expressions(self, expressions):
"""
混合多个表情
expressions: [(name1, weight1), (name2, weight2), ...]
"""
blended = np.zeros(self.num_motors)
for name, weight in expressions:
if name in self.expression_library:
blended += self.expression_library[name] * weight
# 归一化
total_weight = sum(w for _, w in expressions)
if total_weight > 0:
blended /= total_weight
self.send_motor_commands(blended)
def transition(self, from_expr, to_expr, duration=1.0):
"""
表情平滑过渡
"""
start = self.expression_library.get(from_expr, np.zeros(self.num_motors))
end = self.expression_library.get(to_expr, np.zeros(self.num_motors))
# 插值
num_steps = int(duration / 0.016) # 60fps
for i in range(num_steps + 1):
t = i / num_steps
interpolated = start + (end - start) * t
self.send_motor_commands(interpolated)
time.sleep(0.016)
def load_expressions(self):
"""
加载预定义表情
基于 FACS 动作单元
"""
expressions = {
'neutral': np.zeros(self.num_motors),
'happy': self.create_expression(
mouth_corner_up=0.8, # AU12
cheek_raise=0.6, # AU6
eye_narrow=0.3
),
'sad': self.create_expression(
inner_brow_up=0.7, # AU1
brow_lower=0.5, # AU4
mouth_corner_down=0.8 # AU15
),
'angry': self.create_expression(
brow_lower=0.9, # AU4
upper_lid_raise=0.6, # AU5
lid_tighten=0.7, # AU7
lip_press=0.5 # AU23
),
'surprised': self.create_expression(
brow_raise=0.9, # AU1+2
upper_lid_raise=0.9, # AU5
jaw_drop=0.8 # AU26
),
'disgusted': self.create_expression(
nose_wrinkle=0.8, # AU9
upper_lip_raise=0.7 # AU10
),
'afraid': self.create_expression(
brow_raise=0.8, # AU1+2
brow_lower=0.5, # AU4
upper_lid_raise=0.7, # AU5
mouth_stretch=0.8 # AU20
),
}
return expressions
def create_expression(self, **aus):
"""
从 AU 创建表情
"""
motor_positions = np.zeros(self.num_motors)
# AU 到电机的映射(简化)
au_to_motor = {
'inner_brow_up': [0, 1],
'brow_raise': [0, 1, 2, 3],
'brow_lower': [2, 3],
'upper_lid_raise': [4, 5],
'lid_tighten': [4, 5, 6, 7],
'cheek_raise': [6, 7, 8, 9],
'nose_wrinkle': [10, 11],
'upper_lip_raise': [12, 13, 14],
'mouth_corner_up': [15, 16, 17, 18],
'mouth_corner_down': [15, 16, 17, 18],
'lip_press': [19, 20],
'jaw_drop': [21, 22, 23],
'mouth_stretch': [15, 16, 17, 18, 24, 25],
}
for au_name, intensity in aus.items():
if au_name in au_to_motor:
for motor_idx in au_to_motor[au_name]:
motor_positions[motor_idx] += intensity
# 限制在 0-1 范围
motor_positions = np.clip(motor_positions, 0, 1)
return motor_positions
2.3 气动驱动方案
气动肌肉面部:
气动面部驱动系统
┌─────────────────────────────────────┐
│ 面部皮肤(硅胶) │
│ - 内嵌气动通道 │
├─────────────────────────────────────┤
│ 气动肌肉层 │
│ ├── McKibben 肌肉 × 20 │
│ ├── 气管网络 │
│ └── 快速接头 │
├─────────────────────────────────────┤
│ 气源系统 │
│ ├── 空压机 │
│ ├── 储气罐 │
│ └── 比例阀 × 20 │
└─────────────────────────────────────┘
性能:
├── 响应时间:50-100 ms
├── 最大应变:20-30%
├── 噪音:<40 dB
└── 重量:500g(不含气源)
优势:
├── 柔顺(安全)
├── 安静(无电机声)
├── 仿人(类似肌肉)
└── 可拉伸
劣势:
├── 需要气源
├── 响应较慢
├── 控制复杂
└── 效率低
2.4 显示方案
LED 屏幕面部:
LED 屏幕面部方案
┌─────────────────────────────────────┐
│ 透明保护层 │
├─────────────────────────────────────┤
│ LED 矩阵 │
│ ├── 分辨率:64×64 或 128×128 │
│ ├── 像素间距:2-4mm │
│ └── 亮度:500-1000 cd/m² │
├─────────────────────────────────────┤
│ 扩散层 │
│ - 柔化像素边界 │
├─────────────────────────────────────┤
│ 头骨结构 │
└─────────────────────────────────────┘
优势:
├── 灵活(任意表情)
├── 可编程(动画、视频)
├── 可靠(无运动部件)
└── 成本可控
劣势:
├── 非真实(像素化)
├── 分辨率限制
├── 无 3D 深度
└── 功耗较高
应用:
├── SoftBank Pepper
├── NAO 机器人
└── 教育、展示机器人
三、表情生成
3.1 表情合成
基于规则的表情合成:
class ExpressionSynthesizer:
"""
表情合成器
"""
def __init__(self):
# 基本表情
self.basic_expressions = {
'neutral': [0.0] * 32,
'happy': [0.8, 0.6, 0.0, ...],
'sad': [0.7, 0.5, 0.8, ...],
'angry': [0.9, 0.6, 0.7, ...],
'surprised': [0.9, 0.9, 0.8, ...],
'disgusted': [0.8, 0.7, 0.0, ...],
'afraid': [0.8, 0.5, 0.7, ...],
}
# 强度映射
self.intensity_map = {
'slight': 0.3,
'mild': 0.5,
'moderate': 0.7,
'strong': 0.9,
'extreme': 1.0,
}
def synthesize(self, emotion, intensity='moderate'):
"""
合成表情
"""
base = self.basic_expressions.get(emotion, self.basic_expressions['neutral'])
scale = self.intensity_map.get(intensity, 0.7)
# 应用强度
result = [v * scale for v in base]
return result
def blend(self, expr1, expr2, weight=0.5):
"""
混合两个表情
weight: 0.0(全 expr1)到 1.0(全 expr2)
"""
blended = []
for v1, v2 in zip(expr1, expr2):
blended.append(v1 * (1 - weight) + v2 * weight)
return blended
def add_micro_expression(self, base_expr, micro_expr, duration=0.2):
"""
添加微表情
微表情:短暂、无意识的面部表情
"""
# 微表情强度较低
micro_intensity = 0.3
# 时间序列
frames = []
num_frames = int(duration / 0.016) # 60fps
for i in range(num_frames):
# 淡入
if i < num_frames / 4:
t = i / (num_frames / 4)
# 保持
elif i < 3 * num_frames / 4:
t = 1.0
# 淡出
else:
t = 1.0 - (i - 3 * num_frames / 4) / (num_frames / 4)
frame = self.blend(base_expr, micro_expr, t * micro_intensity)
frames.append(frame)
return frames
3.2 语音同步
唇形同步:
class LipSyncController:
"""
唇形同步控制器
"""
def __init__(self):
# 音素到口型的映射
self.viseme_map = {
'sil': [0.0, 0.0, 0.0], # 沉默
'ppp': [0.8, 0.0, 0.8], # p, b, m(闭唇)
'fff': [0.2, 0.8, 0.2], # f, v(上齿咬唇)
'thh': [0.3, 0.5, 0.3], # th
'ddd': [0.5, 0.3, 0.5], # t, d, n
'err': [0.4, 0.4, 0.4], # r
'sss': [0.3, 0.3, 0.6], # s, z
'shh': [0.4, 0.4, 0.7], # sh, ch, j
'aaa': [0.9, 0.0, 0.9], # a
'eee': [0.6, 0.0, 0.6], # e
'iii': [0.4, 0.0, 0.4], # i
'ooo': [0.2, 0.8, 0.2], # o
'uuu': [0.3, 0.6, 0.3], # u
}
# 口型参数:[张嘴、噘嘴、咧嘴]
def generate_lipsync(self, text, audio_duration):
"""
从文本生成唇形序列
"""
# 1. 文本→音素
phonemes = self.text_to_phonemes(text)
# 2. 音素→口型(viseme)
visemes = [self.phoneme_to_viseme(p) for p in phonemes]
# 3. 时间分配
total_phonemes = len(phonemes)
duration_per_phoneme = audio_duration / total_phonemes
# 4. 生成口型序列
lip_sequence = []
for viseme in visemes:
num_frames = int(duration_per_phoneme / 0.016)
for _ in range(num_frames):
lip_sequence.append(viseme)
return lip_sequence
def text_to_phonemes(self, text):
"""
文本→音素转换
可以使用 g2p_en 等库
"""
# 简化示例
phoneme_dict = {
'hello': ['hh', 'ax', 'l', 'ow'],
'world': ['w', 'er', 'l', 'd'],
'robot': ['r', 'ow', 'b', 'aa', 't'],
}
phonemes = []
for word in text.lower().split():
if word in phoneme_dict:
phonemes.extend(phoneme_dict[word])
else:
phonemes.extend(['sil']) # 未知词
return phonemes
def phoneme_to_viseme(self, phoneme):
"""
音素→口型转换
"""
# 音素分组到 viseme
groups = {
'ppp': ['p', 'b', 'm'],
'fff': ['f', 'v'],
'thh': ['th'],
'ddd': ['t', 'd', 'n'],
'err': ['r'],
'sss': ['s', 'z'],
'shh': ['sh', 'ch', 'j'],
'aaa': ['aa', 'ah'],
'eee': ['eh', 'ae'],
'iii': ['iy', 'ih'],
'ooo': ['ao', 'aw'],
'uuu': ['uw', 'uh'],
}
for viseme, phoneme_list in groups.items():
if phoneme in phoneme_list:
return self.viseme_map[viseme]
return self.viseme_map['sil']
四、表情识别
4.1 面部表情识别(FER)
深度学习方案:
import torch
import torch.nn as nn
import torchvision.models as models
class FacialExpressionRecognizer(nn.Module):
"""
面部表情识别模型
"""
def __init__(self, num_classes=7):
super().__init__()
# 使用预训练 ResNet
self.backbone = models.resnet18(pretrained=True)
# 替换最后的全连接层
num_features = self.backbone.fc.in_features
self.backbone.fc = nn.Sequential(
nn.Dropout(0.5),
nn.Linear(num_features, 256),
nn.ReLU(),
nn.Linear(256, num_classes)
)
# 7 类基本表情
self.classes = ['angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral']
def forward(self, x):
return self.backbone(x)
def predict(self, face_image):
"""
预测表情
"""
self.eval()
# 预处理
input_tensor = self.preprocess(face_image)
with torch.no_grad():
output = self.forward(input_tensor)
probabilities = nn.functional.softmax(output, dim=1)
# 获取预测
confidence, predicted = torch.max(probabilities, 1)
return {
'expression': self.classes[predicted.item()],
'confidence': confidence.item(),
'probabilities': dict(zip(self.classes, probabilities[0].tolist()))
}
def preprocess(self, image):
"""
预处理
"""
# 人脸检测、对齐、归一化
# 使用 dlib 或 MTCNN
pass
# 训练
def train_fer_model():
"""
训练表情识别模型
"""
# 数据集:FER2013, AffectNet, RAF-DB
dataset = FER2013Dataset()
model = FacialExpressionRecognizer()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 训练循环
for epoch in range(50):
for images, labels in dataset:
outputs = model(images)
loss = criterion(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
4.2 多模态情感识别
融合方案:
class MultimodalEmotionRecognizer:
"""
多模态情感识别
融合面部、语音、文本
"""
def __init__(self):
# 各模态模型
self.face_model = FacialExpressionRecognizer()
self.voice_model = VoiceEmotionRecognizer()
self.text_model = TextSentimentAnalyzer()
# 融合权重
self.weights = {
'face': 0.5,
'voice': 0.3,
'text': 0.2
}
def recognize(self, face_image, audio, text):
"""
多模态情感识别
"""
# 各模态识别
face_result = self.face_model.predict(face_image)
voice_result = self.voice_model.predict(audio)
text_result = self.text_model.predict(text)
# 融合
fused_probabilities = {}
for emotion in self.face_model.classes:
p_face = face_result['probabilities'].get(emotion, 0)
p_voice = voice_result['probabilities'].get(emotion, 0)
p_text = text_result['probabilities'].get(emotion, 0)
# 加权融合
fused = (
self.weights['face'] * p_face +
self.weights['voice'] * p_voice +
self.weights['text'] * p_text
)
fused_probabilities[emotion] = fused
# 归一化
total = sum(fused_probabilities.values())
fused_probabilities = {k: v/total for k, v in fused_probabilities.items()}
# 最终预测
predicted = max(fused_probabilities, key=fused_probabilities.get)
return {
'emotion': predicted,
'confidence': fused_probabilities[predicted],
'probabilities': fused_probabilities,
'modality_results': {
'face': face_result,
'voice': voice_result,
'text': text_result
}
}
五、应用案例
5.1 Sophia(Hanson Robotics)
面部系统:
Sophia 面部表情系统
├── 驱动方式
│ ├── 电机驱动 × 62
│ └── 硅胶皮肤
│
├── 自由度
│ ├── 眉部:6 DOF
│ ├── 眼部:8 DOF(含眼球)
│ ├── 鼻部:2 DOF
│ ├── 口部:20 DOF
│ ├── 脸颊:10 DOF
│ ├── 颈部:8 DOF
│ └── 其他:8 DOF
│
├── 性能
│ ├── 表情数量:50+
│ ├── 响应时间:100 ms
│ └── 识别率:90%+
│
└── 应用
├── 人机交互
├── 媒体露面
└── 研究平台
5.2 Ameca(Engineered Arts)
面部系统:
Ameca 面部表情系统
├── 驱动方式
│ ├── 电机驱动 × 40+
│ └── 模块化设计
│
├── 特点
│ ├── 超逼真表情
│ ├── 微表情支持
│ ├── 语音同步
│ └── 实时响应
│
├── 性能
│ ├── 刷新率:60 Hz
│ ├── 精度:0.1°
│ └── 延迟:<50 ms
│
└── 应用
├── 客户服务
├── 虚拟助手
└── 影视制作
5.3 小米 CyberOne
面部系统:
CyberOne 面部表情系统
├── 驱动方式
│ ├── 显示方案(OLED 屏幕)
│ └── 分辨率:128×128
│
├── 特点
│ ├── 可编程表情
│ ├── 动画播放
│ └── 情感识别反馈
│
├── 性能
│ ├── 表情数量:100+
│ ├── 响应时间:<10 ms
│ └── 成本:低
│
└── 应用
├── 家庭陪伴
├── 展示导览
└── 教育
六、总结
6.1 核心要点
- 面部是情感窗口:55% 情感信息通过面部传递
- 驱动方式多样:电机精确、气动柔顺、显示灵活
- FACS 是标准:基于动作单元的表情描述
- 多模态识别:面部 + 语音 + 文本融合
- 成本待降低:从$50,000 降至$5,000
6.2 技术成熟度
| 技术 | 成熟度 | 商用时间 |
|---|---|---|
| 电机驱动 | ⭐⭐⭐⭐ | 已商用 |
| 表情识别 | ⭐⭐⭐⭐ | 已商用 |
| 语音同步 | ⭐⭐⭐⭐ | 已商用 |
| 气动驱动 | ⭐⭐⭐ | 研究中 |
| 微表情 | ⭐⭐ | 研究中 |
6.3 长期展望
面部表情技术正朝着更逼真、更自然、更低成本的方向发展:
- 短期:电机驱动普及,表情识别成熟
- 中期:人工肌肉驱动,微表情支持
- 长期:神经接口,直接情感读取
面部表情是人形机器人**从”机器”到”伙伴”**的关键桥梁,将直接决定人机交互的自然度和情感连接。
参考资料:
- FACS 手册(Ekman)
- 面部表情识别论文
- Hanson Robotics 技术文档
- 各公司技术文档