运动符号系统 movement-notation-systems

这个技能用于设计和开发系统,以编码、分析和生成人类运动,应用于编舞、动画和运动分析。它结合了Labanotation符号、计算几何和程序动画原则,支持运动编码、评分和生成。关键词包括:运动符号、Labanotation、计算运动分析、编舞生成、程序动画、骨骼表示、努力分析。

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

name: 运动符号系统 description: 设计系统以编码、评分和生成编舞运动,使用Laban符号、计算几何和程序动画原则。 license: MIT

运动符号系统

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

核心能力

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

运动符号基础

运动的挑战

运动本质上是多维的:

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

主要符号系统

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

Laban运动分析框架

身体组件

哪些身体部位在运动:

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

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

空间组件

身体在哪里运动:

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

    DIMENSIONS = {
        'vertical': {'up', 'down'},
        'horizontal': {'left', 'right'},
        'sagittal': {'forward', 'backward'}
    }

    LEVELS = ['low', 'middle', 'high']

    # 动球中的27个方向
    DIRECTION_SYMBOLS = {
        'place_high': (0, 1, 0),
        'place_middle': (0, 0, 0),
        'place_low': (0, -1, 0),
        'forward_high': (0, 1, 1),
        'forward_middle': (0, 0, 1),
        'forward_low': (0, -1, 1),
        # ... 所有27种组合
    }

    # 空间尺度
    SCALES = {
        'near': 0.3,    # 靠近身体中心
        'mid': 0.6,     # 一般可及范围
        'far': 1.0      # 完全伸展
    }

努力组件

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

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

    FACTORS = {
        'weight': {
            'light': {'sensation': 'buoyant', 'value': -1},
            'strong': {'sensation': 'powerful', 'value': 1}
        },
        'time': {
            'sustained': {'sensation': 'leisurely', 'value': -1},
            'quick': {'sensation': 'urgent', 'value': 1}
        },
        'space': {
            'indirect': {'sensation': 'flexible', 'value': -1},
            'direct': {'sensation': 'focused', 'value': 1}
        },
        'flow': {
            'free': {'sensation': 'fluent', 'value': -1},
            'bound': {'sensation': 'controlled', 'value': 1}
        }
    }

    # 基本努力动作(重量、时间、空间的组合)
    ACTIONS = {
        'punch': {'weight': 'strong', 'time': 'quick', 'space': 'direct'},
        'dab': {'weight': 'light', 'time': 'quick', 'space': 'direct'},
        'slash': {'weight': 'strong', 'time': 'quick', 'space': 'indirect'},
        'flick': {'weight': 'light', 'time': 'quick', 'space': 'indirect'},
        'press': {'weight': 'strong', 'time': 'sustained', 'space': 'direct'},
        'glide': {'weight': 'light', 'time': 'sustained', 'space': 'direct'},
        'wring': {'weight': 'strong', 'time': 'sustained', 'space': 'indirect'},
        'float': {'weight': 'light', 'time': 'sustained', 'space': 'indirect'}
    }

形状组件

身体如何改变形式:

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

    MODES = {
        'shape_flow': {
            'description': '内部塑形,自我导向',
            'examples': ['呼吸', '生长/收缩']
        },
        'directional': {
            'description': '与环境桥接',
            'subtypes': ['辐射状', '弧状']
        },
        'carving': {
            'description': '雕刻3D空间',
            'relationship': '与环境互动'
        }
    }

    AFFINITIES = {
        'rising': {'effort': 'light', 'direction': 'up'},
        'sinking': {'effort': 'strong', 'direction': 'down'},
        'spreading': {'effort': 'indirect', 'direction': 'horizontal'},
        'enclosing': {'effort': 'direct', 'direction': 'in'},
        'advancing': {'effort': 'sustained', 'direction': 'forward'},
        'retreating': {'effort': 'quick', 'direction': 'back'}
    }

计算运动表示

骨骼数据结构

class Skeleton:
    """分层骨骼表示"""

    def __init__(self):
        self.joints = {
            'pelvis': Joint('pelvis', parent=None),
            'spine': Joint('spine', parent='pelvis'),
            'chest': Joint('chest', parent='spine'),
            'neck': Joint('neck', parent='chest'),
            'head': Joint('head', parent='neck'),
            'l_shoulder': Joint('l_shoulder', parent='chest'),
            'l_elbow': Joint('l_elbow', parent='l_shoulder'),
            'l_wrist': Joint('l_wrist', parent='l_elbow'),
            'r_shoulder': Joint('r_shoulder', parent='chest'),
            # ... 等
        }

    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 = []  # 骨骼状态列表
        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 = {
            'weight': self._compute_acceleration_magnitude(joint_name, window),
            'time': self._compute_temporal_change_rate(joint_name, window),
            'space': self._compute_path_directness(joint_name, window),
            'flow': 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'] == 'quick':
            duration = 0.3
            ease_type = 'exponential'
        else:  # sustained
            duration = 1.2
            ease_type = 'sine'

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

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

        # 流动因子影响连续性
        if effort_state['flow'] == 'bound':
            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 = {
            'circular': self._circular_path,
            'spiral': self._spiral_path,
            'figure_eight': self._figure_eight,
            'diagonal_cross': self._diagonal_cross,
            'zigzag': self._zigzag_path,
            'random_walk': 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 = {
                'beat': movement.start_beat,
                'duration': movement.duration_beats,
                'body': self._notate_body(movement),
                'space': self._notate_space(movement),
                'effort': self._notate_effort(movement),
                'description': movement.description
            }
            score.append(notation)

        return score

    def _notate_effort(self, movement):
        """努力符号标记"""
        symbols = {
            ('strong', 'quick', 'direct'): '⚡',   # 击打
            ('light', 'sustained', 'indirect'): '☁️',  # 漂浮
            ('strong', 'sustained', 'indirect'): '🌀',  # 扭绞
            # ... 等
        }
        effort_tuple = (
            movement.effort['weight'],
            movement.effort['time'],
            movement.effort['space']
        )
        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 - 常见动作捕捉数据格式