动作记谱系统 movement-notation-systems

这个技能专注于开发和实施系统,用于编码、评估和生成人类动作序列,特别适用于舞蹈编舞、计算机动画和运动分析领域。关键技术包括Laban记谱法、计算几何、过程动画和Laban运动分析框架,帮助用户创建、记录和模拟复杂的动作模式,提升动作设计效率和精确性。关键词:动作记谱、Laban记谱、计算几何、过程动画、Laban运动分析、编舞系统、动作生成、运动分析、动画技术、骨骼表示。

其他 0 次安装 0 次浏览 更新于 3/7/2026

名称: 动作记谱系统 描述: 设计用于编码、评分和生成编舞动作的系统,使用Laban记谱、计算几何和过程动画原则。 许可证: MIT

动作记谱系统

这个技能提供指导,用于创建系统以编码、分析和生成人类动作,适用于编舞、动画和动作分析。

核心能力

  • 动作记谱: Labanotation、Benesh、Motif记谱
  • 计算几何: 骨骼表示、关节角度
  • 过程动画: 基于规则的动作生成
  • 努力-形状分析: Laban运动分析(LMA)
  • 时间结构: 节奏、分段、动态

动作记谱基础

动作的挑战

动作本质上是多维的:

  • 3D空间路径
  • 时间演化
  • 身体部位协调
  • 定性动态(努力)
  • 关系上下文(其他身体、对象、空间)

主要记谱系统

系统 优势 应用场景
Labanotation 完整、精确 归档、重建
Benesh 紧凑、视觉化 芭蕾、治疗
Motif 抽象、可读 教学、分析
动作捕捉 精确坐标 动画、研究

Laban运动分析框架

身体组件

哪些身体部位在移动:

身体组织:
├── 核心-远端(从中心向外)
├── 头-尾(脊柱连接)
├── 上-下(水平分割)
├── 身体半侧(左-右)
└── 交叉侧(对角线连接)

身体部位层次:
中心(骨盆)
├── 躯干(脊柱、胸部)
│   ├── 头
│   ├── 肩膀
│   └── 手臂 → 肘部 → 手 → 手指
└── 臀部
    └── 腿 → 膝盖 → 脚 → 脚趾

空间组件

身体移动的位置:

class KinesphereModel:
    """身体周围可达空间"""

    DIMENSIONS = {
        '垂直': {'上', '下'},
        '水平': {'左', '右'},
        '矢状': {'前', '后'}
    }

    LEVELS = ['低', '中', '高']

    # 动球中的27个方向
    DIRECTION_SYMBOLS = {
        '放置_高': (0, 1, 0),
        '放置_中': (0, 0, 0),
        '放置_低': (0, -1, 0),
        '前_高': (0, 1, 1),
        '前_中': (0, 0, 1),
        '前_低': (0, -1, 1),
        # ... 所有27种组合
    }

    # 空间尺度
    SCALES = {
        '近': 0.3,    # 接近身体中心
        '中': 0.6,     # 一般可达范围
        '远': 1.0      # 完全伸展
    }

努力组件

动作如何执行(定性动态):

class EffortFactors:
    """Laban努力品质"""

    FACTORS = {
        '重量': {
            '轻': {'感觉': '轻盈', '值': -1},
            '强': {'感觉': '有力', '值': 1}
        },
        '时间': {
            '持续': {'感觉': '悠闲', '值': -1},
            '快速': {'感觉': '紧急', '值': 1}
        },
        '空间': {
            '间接': {'感觉': '灵活', '值': -1},
            '直接': {'感觉': '专注', '值': 1}
        },
        '流动': {
            '自由': {'感觉': '流畅', '值': -1},
            '束缚': {'感觉': '控制', '值': 1}
        }
    }

    # 基本努力动作(重量、时间、空间的组合)
    ACTIONS = {
        '拳击': {'重量': '强', '时间': '快速', '空间': '直接'},
        '轻点': {'重量': '轻', '时间': '快速', '空间': '直接'},
        '挥砍': {'重量': '强', '时间': '快速', '空间': '间接'},
        '弹动': {'重量': '轻', '时间': '快速', '空间': '间接'},
        '按压': {'重量': '强', '时间': '持续', '空间': '直接'},
        '滑行': {'重量': '轻', '时间': '持续', '空间': '直接'},
        '扭转': {'重量': '强', '时间': '持续', '空间': '间接'},
        '漂浮': {'重量': '轻', '时间': '持续', '空间': '间接'}
    }

形状组件

身体如何改变形式:

class ShapeQualities:
    """身体形状变化"""

    MODES = {
        '形状流': {
            '描述': '内部塑形,自我导向',
            '示例': ['呼吸', '增长/缩小']
        },
        '方向性': {
            '描述': '与环境连接',
            '子类型': ['放射状', '弧状']
        },
        '雕刻': {
            '描述': '雕刻3D空间',
            '关系': '与环境互动'
        }
    }

    AFFINITIES = {
        '上升': {'努力': '轻', '方向': '上'},
        '下沉': {'努力': '强', '方向': '下'},
        '展开': {'努力': '间接', '方向': '水平'},
        '包围': {'努力': '直接', '方向': '内'},
        '前进': {'努力': '持续', '方向': '前'},
        '后退': {'努力': '快速', '方向': '后'}
    }

计算动作表示

骨骼数据结构

class Skeleton:
    """层次化骨骼表示"""

    def __init__(self):
        self.joints = {
            '骨盆': Joint('骨盆', parent=None),
            '脊柱': Joint('脊柱', parent='骨盆'),
            '胸部': Joint('胸部', parent='脊柱'),
            '颈部': Joint('颈部', parent='胸部'),
            '头': Joint('头', parent='颈部'),
            '左肩': Joint('左肩', parent='胸部'),
            '左肘': Joint('左肘', parent='左肩'),
            '左腕': Joint('左腕', parent='左肘'),
            '右肩': Joint('右肩', parent='胸部'),
            # ... 等等
        }

    def get_world_position(self, joint_name):
        """从局部变换计算全局位置"""
        joint = self.joints[joint_name]
        position = joint.local_position

        current = joint
        while current.parent:
            parent = self.joints[current.parent]
            position = parent.rotation.apply(position) + parent.local_position
            current = parent

        return position

    def compute_joint_angles(self):
        """提取关节角度用于分析"""
        angles = {}
        for name, joint in self.joints.items():
            if joint.parent:
                angles[name] = joint.rotation.as_euler('xyz')
        return angles


class Joint:
    """骨骼层次中的单个关节"""

    def __init__(self, name, parent=None):
        self.name = name
        self.parent = parent
        self.local_position = np.array([0, 0, 0])
        self.rotation = Rotation.identity()
        self.constraints = {}  # 关节限制

运动轨迹

class MotionTrajectory:
    """姿态的时间序列"""

    def __init__(self, fps=30):
        self.fps = fps
        self.frames = []  # Skeleton状态列表
        self.annotations = []  # 定性标记

    def duration(self):
        return len(self.frames) / self.fps

    def get_velocity(self, joint_name, frame_idx):
        """计算瞬时速度"""
        if frame_idx < 1:
            return np.zeros(3)

        pos_current = self.frames[frame_idx].get_world_position(joint_name)
        pos_prev = self.frames[frame_idx - 1].get_world_position(joint_name)

        return (pos_current - pos_prev) * self.fps

    def extract_effort_features(self, joint_name, window=10):
        """从运动估计Laban努力品质"""
        features = {
            '重量': self._compute_acceleration_magnitude(joint_name, window),
            '时间': self._compute_temporal_change_rate(joint_name, window),
            '空间': self._compute_path_directness(joint_name, window),
            '流动': self._compute_flow_continuity(joint_name, window)
        }
        return features

过程动作生成

基于规则的编舞

class ChoreographyGenerator:
    """从规则生成动作序列"""

    def __init__(self):
        self.vocabulary = self._load_movement_vocabulary()
        self.grammar = self._load_grammar_rules()

    def generate_phrase(self, theme, duration_beats=16):
        """生成编舞短语"""

        # 从主题开始基于动机
        motif = self._select_motif(theme)

        # 通过短语发展
        phrase = [motif]
        current_beats = motif.duration_beats

        while current_beats < duration_beats:
            # 应用发展规则
            if random.random() < 0.3:
                # 带变奏重复
                variation = self._vary_motif(phrase[-1])
                phrase.append(variation)
            elif random.random() < 0.5:
                # 对比
                contrast = self._generate_contrast(phrase[-1])
                phrase.append(contrast)
            else:
                # 过渡
                transition = self._smooth_transition(phrase[-1])
                phrase.append(transition)

            current_beats += phrase[-1].duration_beats

        return phrase

    def _vary_motif(self, motif):
        """创建动作变奏"""
        variations = [
            self._change_level,      # 相同动作,不同水平
            self._mirror,            # 左右反转
            self._change_size,       # 更大或更小
            self._change_tempo,      # 更快或更慢
            self._change_direction,  # 面向不同方向
            self._fragment,          # 动作的部分
            self._extend,            # 添加到动作
        ]
        return random.choice(variations)(motif)

努力驱动动画

class EffortAnimator:
    """生成具有指定努力品质的动作"""

    def animate_action(self, skeleton, target_position, effort_state):
        """以指定努力移动身体部位"""

        # 时间因子影响持续时间
        if effort_state['time'] == '快速':
            duration = 0.3
            ease_type = '指数'
        else:  # 持续
            duration = 1.2
            ease_type = '正弦'

        # 重量因子影响加速度
        if effort_state['weight'] == '强':
            acceleration_curve = self._strong_curve()
        else:  # 轻
            acceleration_curve = self._light_curve()

        # 空间因子影响路径
        if effort_state['space'] == '直接':
            path = self._linear_path(skeleton.current, target_position)
        else:  # 间接
            path = self._curved_path(skeleton.current, target_position)

        # 流动因子影响连续性
        if effort_state['flow'] == '束缚':
            path = self._add_micro_pauses(path)
        # 自由流动默认平滑

        return self._create_animation(
            path=path,
            duration=duration,
            acceleration=acceleration_curve
        )

空间模式生成

class FloorPatternGenerator:
    """生成空间路径"""

    def generate_path(self, pattern_type, space_bounds, duration):
        """生成地板模式"""

        patterns = {
            '圆形': self._circular_path,
            '螺旋': self._spiral_path,
            '八字形': self._figure_eight,
            '对角线交叉': self._diagonal_cross,
            '锯齿形': self._zigzag_path,
            '随机游走': self._random_walk
        }

        generator = patterns.get(pattern_type, self._random_walk)
        return generator(space_bounds, duration)

    def _spiral_path(self, bounds, duration, turns=3):
        """生成螺旋地板模式"""
        center = bounds.center
        max_radius = min(bounds.width, bounds.height) / 2

        points = []
        num_points = int(duration * 30)  # 每拍30个点

        for i in range(num_points):
            t = i / num_points
            angle = t * turns * 2 * np.pi
            radius = max_radius * (1 - t)  # 向内螺旋

            x = center[0] + radius * np.cos(angle)
            y = center[1] + radius * np.sin(angle)

            points.append((x, y, t * duration))

        return points

记谱输出

文本记谱格式

class MovementScore:
    """基于文本的动作乐谱"""

    def to_notation(self, phrase):
        """将短语转换为可读记谱"""
        score = []

        for movement in phrase:
            notation = {
                '拍': movement.start_beat,
                '持续时间': movement.duration_beats,
                '身体': self._notate_body(movement),
                '空间': self._notate_space(movement),
                '努力': self._notate_effort(movement),
                '描述': movement.description
            }
            score.append(notation)

        return score

    def _notate_effort(self, movement):
        """努力记谱符号"""
        symbols = {
            ('强', '快速', '直接'): '⚡',   # 拳击
            ('轻', '持续', '间接'): '☁️',  # 漂浮
            ('强', '持续', '间接'): '🌀',  # 扭转
            # ... 等等
        }
        effort_tuple = (
            movement.effort['重量'],
            movement.effort['时间'],
            movement.effort['空间']
        )
        return symbols.get(effort_tuple, '○')

视觉乐谱表示

时间 →
|    0    |    1    |    2    |    3    |    4    |
├─────────┼─────────┼─────────┼─────────┼─────────┤
│ ◊ 头   │         │    ○    │         │         │
│ ═ 手臂 │  ═══════│════     │   ═════ │═════    │
│ ║ 躯干 │    ║    │   ║║    │    ║    │  ║║║    │
│ ‖ 腿   │   ‖‖    │  ‖  ‖   │   ‖‖    │ ‖    ‖  │
├─────────┼─────────┼─────────┼─────────┼─────────┤
│ 水平:   │   高    │   中    │   低    │   中    │
│ 努力:   │   ⚡    │    ☁️   │    🌀   │   ⚡    │
└─────────┴─────────┴─────────┴─────────┴─────────┘

最佳实践

编码原则

  1. 保留意图: 捕捉编舞者想要的,不仅仅是位置
  2. 分层信息: 结构 > 形状 > 动态
  3. 允许解释: 除非需要,不要过度指定
  4. 支持变奏: 启用受控即兴

分析指南

  • 提取定量(位置)和定性(努力)特征
  • 考虑文化和风格上下文
  • 与从业者验证

参考

  • references/labanotation-symbols.md - Labanotation符号参考
  • references/lma-glossary.md - Laban运动分析术语
  • references/motion-capture-formats.md - 常见动作捕捉数据格式