在这里插入图片描述

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net


🎯 一、弹簧振子系统:数学之美

📚 1.1 简谐振动基础

简谐振动是最基本的振动形式,描述了物体在平衡位置附近的周期性运动。弹簧振子是简谐振动的经典模型。

胡克定律

弹簧的恢复力与位移成正比:

F = -kx

其中:
- F:恢复力
- k:弹簧劲度系数
- x:位移
- 负号:力与位移方向相反

运动方程

根据牛顿第二定律:F = ma

m(d²x/dt²) = -kx

整理得:
d²x/dt² + (k/m)x = 0

设 ω² = k/m,则:
d²x/dt² + ω²x = 0

这是简谐振动的微分方程

通解

x(t) = A·cos(ωt + φ)

其中:
- A:振幅(最大位移)
- ω:角频率 = √(k/m)
- φ:初相位
- T:周期 = 2π/ω = 2π√(m/k)
- f:频率 = 1/T = ω/2π

振动示意

        x(t) = A·cos(ωt)
        
    A  ────●─────────────────  最大位移
        ╱   ╲
       ╱     ╲
   0 ─●───────●───────●────  平衡位置
       ╲     ╱
        ╲   ╱
   -A ────●─────────────────  最小位移
        
        0   T/4  T/2  3T/4  T

📐 1.2 能量分析

弹簧振子系统涉及动能和势能的相互转换:

能量表达式

动能:
Ek = ½mv² = ½mω²A²sin²(ωt + φ)

势能:
Ep = ½kx² = ½kA²cos²(ωt + φ)

总能量:
E = Ek + Ep = ½kA² = 常数

能量守恒:振动过程中总能量不变

能量转换示意

位置        势能 Ep      动能 Ek      总能量 E
─────────────────────────────────────────────
+A (端点)   最大 (100%)  0            100%
   ↓        减小         增加         100%
 0 (中心)   0            最大 (100%)  100%
   ↓        增加         减小         100%
-A (端点)   最大 (100%)  0            100%

能量在动能和势能之间周期性转换

相空间表示

相空间是 (x, v) 或 (x, p) 构成的空间

对于简谐振动:
x = A·cos(ωt + φ)
v = -Aω·sin(ωt + φ)

相轨迹是椭圆:
(x/A)² + (v/Aω)² = 1

           v
           ↑
      ─────●─────
     ╱     │     ╲
   ●       │       ●
 ──────────┼─────────→ x
   ●       │       ●
     ╲     │     ╱
      ─────●─────

🔬 1.3 阻尼振动

实际振动系统总存在阻力,振幅会逐渐减小,这就是阻尼振动。

阻尼力

粘滞阻力与速度成正比:

F_d = -bv = -b(dx/dt)

其中 b 是阻尼系数

运动方程:
m(d²x/dt²) + b(dx/dt) + kx = 0

设 γ = b/(2m)(阻尼因子),ω₀ = √(k/m)(固有频率):
d²x/dt² + 2γ(dx/dt) + ω₀²x = 0

三种阻尼情况

类型 条件 特点
🟢 欠阻尼 γ < ω₀ x = Ae⁻ᵞᵗcos(ω’t + φ) 振荡衰减
🟡 临界阻尼 γ = ω₀ x = (A + Bt)e⁻ᵞᵗ 最快回到平衡
🔴 过阻尼 γ > ω₀ x = Ae⁻ʳ¹ᵗ + Be⁻ʳ²ᵗ 缓慢回到平衡

阻尼振动示意

欠阻尼振动:
    ────●─────────────────
       ╱ ╲ ╱╲ ╱╲ ╱╲
      ╱   ╲╱  ╲╱  ╲╱
   ─●────────────────●─
    振幅逐渐衰减,仍有振动

临界阻尼:
    ────●─────────────────
         ╲
          ╲
           ╲
   ─────────●───────────
    最快回到平衡位置,无振动

过阻尼:
    ────●─────────────────
          ╲
           ╲
            ╲_____________
   ───────────────────────
    缓慢回到平衡位置

🎯 1.4 受迫振动与共振

当系统受到周期性外力作用时,发生受迫振动。

受迫振动方程

m(d²x/dt²) + b(dx/dt) + kx = F₀cos(ωt)

其中 F₀ 是外力幅值,ω 是外力频率

稳态解:
x(t) = A·cos(ωt - φ)

振幅:
A = F₀ / √[(k - mω²)² + (bω)²]

相位差:
φ = arctan(bω / (k - mω²))

共振现象

当外力频率接近固有频率时,振幅急剧增大

共振频率:
ω_r = √(ω₀² - 2γ²)

当阻尼很小时,ω_r ≈ ω₀

共振曲线:
振幅 A
  ↑
  │      ╱╲
  │     ╱  ╲
  │    ╱    ╲
  │   ╱      ╲
  │──●────────●───→ ω
     ω₀  ω_r

阻尼越小,共振峰越尖锐

共振的应用与危害

应用 危害
🎵 乐器发声 🌉 桥梁坍塌
📻 无线电调谐 🏢 建筑物震动
🔬 核磁共振 ⚙️ 机械损坏
🎸 声学共振 🚗 汽车颠簸

🔧 二、弹簧振子系统的 Dart 实现

🧮 2.1 物理模拟引擎

import 'dart:math';

/// 弹簧振子系统
class SpringOscillator {
  double mass;
  double springConstant;
  double dampingCoefficient;
  
  double position;
  double velocity;
  
  double externalForceAmplitude;
  double externalForceFrequency;
  
  SpringOscillator({
    required this.mass,
    required this.springConstant,
    this.dampingCoefficient = 0.0,
    this.position = 0.0,
    this.velocity = 0.0,
    this.externalForceAmplitude = 0.0,
    this.externalForceFrequency = 0.0,
  });
  
  double get naturalFrequency => sqrt(springConstant / mass);
  double get dampingRatio => dampingCoefficient / (2 * sqrt(springConstant * mass));
  double get period => 2 * pi / naturalFrequency;
  
  double kineticEnergy() => 0.5 * mass * velocity * velocity;
  double potentialEnergy() => 0.5 * springConstant * position * position;
  double totalEnergy() => kineticEnergy() + potentialEnergy();
  
  void update(double dt) {
    final springForce = -springConstant * position;
    final dampingForce = -dampingCoefficient * velocity;
    final externalForce = externalForceAmplitude * cos(externalForceFrequency * _time);
    
    final acceleration = (springForce + dampingForce + externalForce) / mass;
    
    velocity += acceleration * dt;
    position += velocity * dt;
    
    _time += dt;
  }
  
  double _time = 0;
  
  void reset({double? initialPosition, double? initialVelocity}) {
    position = initialPosition ?? 0.0;
    velocity = initialVelocity ?? 0.0;
    _time = 0;
  }
  
  OscillationType get oscillationType {
    final ratio = dampingRatio;
    if (ratio < 1.0) return OscillationType.underdamped;
    if (ratio == 1.0) return OscillationType.critical;
    return OscillationType.overdamped;
  }
}

enum OscillationType {
  underdamped,
  critical,
  overdamped,
}

/// 多弹簧耦合系统
class CoupledSpringSystem {
  final List<SpringOscillator> oscillators;
  final List<double> couplingConstants;
  
  CoupledSpringSystem({
    required this.oscillators,
    required this.couplingConstants,
  });
  
  void update(double dt) {
    final accelerations = List<double>.filled(oscillators.length, 0);
    
    for (int i = 0; i < oscillators.length; i++) {
      final osc = oscillators[i];
      accelerations[i] = -osc.springConstant * osc.position / osc.mass;
      accelerations[i] -= osc.dampingCoefficient * osc.velocity / osc.mass;
      
      if (i > 0) {
        final coupling = couplingConstants[i - 1];
        accelerations[i] += coupling * (oscillators[i - 1].position - osc.position) / osc.mass;
      }
      if (i < oscillators.length - 1) {
        final coupling = couplingConstants[i];
        accelerations[i] += coupling * (oscillators[i + 1].position - osc.position) / osc.mass;
      }
    }
    
    for (int i = 0; i < oscillators.length; i++) {
      oscillators[i].velocity += accelerations[i] * dt;
      oscillators[i].position += oscillators[i].velocity * dt;
    }
  }
}

⚡ 2.2 可视化组件

import 'package:flutter/material.dart';

/// 弹簧可视化绘制器
class SpringVisualizer {
  static Path drawSpring({
    required Offset start,
    required Offset end,
    required int coils,
    required double amplitude,
  }) {
    final path = Path();
    path.moveTo(start.dx, start.dy);
    
    final dx = end.dx - start.dx;
    final dy = end.dy - start.dy;
    final length = sqrt(dx * dx + dy * dy);
    final angle = atan2(dy, dx);
    
    final segmentLength = length / (coils * 2 + 2);
    
    for (int i = 0; i <= coils * 2; i++) {
      final t = (i + 1) / (coils * 2 + 2);
      final x = start.dx + dx * t;
      final y = start.dy + dy * t;
      
      final offsetX = amplitude * sin(i * pi) * cos(angle + pi / 2);
      final offsetY = amplitude * sin(i * pi) * sin(angle + pi / 2);
      
      path.lineTo(x + offsetX, y + offsetY);
    }
    
    path.lineTo(end.dx, end.dy);
    
    return path;
  }
  
  static void drawMass(
    Canvas canvas,
    Offset center,
    double size,
    Color color,
  ) {
    final rect = Rect.fromCenter(center: center, width: size, height: size);
    
    canvas.drawRRect(
      RRect.fromRectAndRadius(rect, const Radius.circular(8)),
      Paint()..color = color,
    );
    
    canvas.drawRRect(
      RRect.fromRectAndRadius(rect, const Radius.circular(8)),
      Paint()
        ..color = Colors.white.withOpacity(0.3)
        ..style = PaintingStyle.stroke
        ..strokeWidth = 2,
    );
  }
  
  static void drawDamper(
    Canvas canvas,
    Offset start,
    Offset end,
    double width,
    Color color,
  ) {
    final paint = Paint()
      ..color = color
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;
    
    final mid = Offset((start.dx + end.dx) / 2, (start.dy + end.dy) / 2);
    
    canvas.drawLine(start, mid, paint);
    canvas.drawLine(end, mid, paint);
    
    final rect = Rect.fromCenter(center: mid, width: width, height: width * 0.6);
    canvas.drawRect(rect, paint);
  }
}

🎨 2.3 动画控制器

import 'package:flutter/material.dart';

/// 弹簧振子动画控制器
class SpringAnimationController extends ChangeNotifier {
  late SpringOscillator _oscillator;
  
  double _time = 0;
  bool _isRunning = true;
  double _timeScale = 1.0;
  
  final List<_HistoryPoint> _history = [];
  final int _maxHistoryLength = 500;
  
  SpringAnimationController({
    double mass = 1.0,
    double springConstant = 10.0,
    double dampingCoefficient = 0.5,
    double initialPosition = 1.0,
    double initialVelocity = 0.0,
  }) {
    _oscillator = SpringOscillator(
      mass: mass,
      springConstant: springConstant,
      dampingCoefficient: dampingCoefficient,
      position: initialPosition,
      velocity: initialVelocity,
    );
  }
  
  SpringOscillator get oscillator => _oscillator;
  double get time => _time;
  bool get isRunning => _isRunning;
  double get timeScale => _timeScale;
  List<_HistoryPoint> get history => _history;
  
  void update(double dt) {
    if (_isRunning) {
      _oscillator.update(dt * _timeScale);
      _time += dt * _timeScale;
      
      _history.add(_HistoryPoint(
        time: _time,
        position: _oscillator.position,
        velocity: _oscillator.velocity,
        kineticEnergy: _oscillator.kineticEnergy(),
        potentialEnergy: _oscillator.potentialEnergy(),
      ));
      
      if (_history.length > _maxHistoryLength) {
        _history.removeAt(0);
      }
      
      notifyListeners();
    }
  }
  
  void toggleRunning() {
    _isRunning = !_isRunning;
    notifyListeners();
  }
  
  void setTimeScale(double scale) {
    _timeScale = scale.clamp(0.1, 5.0);
    notifyListeners();
  }
  
  void setMass(double value) {
    _oscillator.mass = value.clamp(0.1, 10.0);
    notifyListeners();
  }
  
  void setSpringConstant(double value) {
    _oscillator.springConstant = value.clamp(0.1, 100.0);
    notifyListeners();
  }
  
  void setDampingCoefficient(double value) {
    _oscillator.dampingCoefficient = value.clamp(0.0, 10.0);
    notifyListeners();
  }
  
  void setInitialPosition(double value) {
    _oscillator.position = value;
    _oscillator.velocity = 0;
    _time = 0;
    _history.clear();
    notifyListeners();
  }
  
  void reset() {
    _oscillator.reset(initialPosition: 1.0, initialVelocity: 0.0);
    _time = 0;
    _history.clear();
    notifyListeners();
  }
}

class _HistoryPoint {
  final double time;
  final double position;
  final double velocity;
  final double kineticEnergy;
  final double potentialEnergy;
  
  _HistoryPoint({
    required this.time,
    required this.position,
    required this.velocity,
    required this.kineticEnergy,
    required this.potentialEnergy,
  });
}

📦 三、完整示例代码

以下是完整的弹簧振子系统可视化示例代码:

import 'package:flutter/material.dart';
import 'dart:math';

void main() {
  runApp(const SpringApp());
}

class SpringApp extends StatelessWidget {
  const SpringApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '弹簧振子系统',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.green, brightness: Brightness.dark),
        useMaterial3: true,
      ),
      home: const SpringHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class SpringHomePage extends StatelessWidget {
  const SpringHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('🎯 弹簧振子系统'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildCard(context, title: '简谐振动', description: '理想弹簧振子', icon: Icons.sync, color: Colors.green,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SimpleHarmonicDemo()))),
          _buildCard(context, title: '阻尼振动', description: '能量耗散过程', icon: Icons.trending_down, color: Colors.orange,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const DampedOscillationDemo()))),
          _buildCard(context, title: '共振现象', description: '受迫振动与共振', icon: Icons.graphic_eq, color: Colors.purple,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const ResonanceDemo()))),
          _buildCard(context, title: '耦合振子', description: '多弹簧系统', icon: Icons.link, color: Colors.teal,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const CoupledOscillatorDemo()))),
        ],
      ),
    );
  }

  Widget _buildCard(BuildContext context, {required String title, required String description, required IconData icon, required Color color, required VoidCallback onTap}) {
    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(16),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Row(children: [
            Container(width: 56, height: 56, decoration: BoxDecoration(color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12)),
                child: Icon(icon, color: color, size: 28)),
            const SizedBox(width: 16),
            Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
              Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
              const SizedBox(height: 4),
              Text(description, style: TextStyle(color: Colors.grey[600], fontSize: 14)),
            ])),
            Icon(Icons.chevron_right, color: Colors.grey[400]),
          ]),
        ),
      ),
    );
  }
}

/// 弹簧振子物理模型
class SpringOscillator {
  double mass, springConstant, dampingCoefficient, position, velocity;
  double externalAmplitude = 0, externalFrequency = 0;
  double _time = 0;
  
  SpringOscillator({required this.mass, required this.springConstant, this.dampingCoefficient = 0, this.position = 0, this.velocity = 0});
  
  double get naturalFrequency => sqrt(springConstant / mass);
  double get dampingRatio => dampingCoefficient / (2 * sqrt(springConstant * mass));
  double get period => 2 * pi / naturalFrequency;
  double kineticEnergy() => 0.5 * mass * velocity * velocity;
  double potentialEnergy() => 0.5 * springConstant * position * position;
  double totalEnergy() => kineticEnergy() + potentialEnergy();
  
  void update(double dt) {
    final springForce = -springConstant * position;
    final dampingForce = -dampingCoefficient * velocity;
    final externalForce = externalAmplitude * cos(externalFrequency * _time);
    final acceleration = (springForce + dampingForce + externalForce) / mass;
    velocity += acceleration * dt;
    position += velocity * dt;
    _time += dt;
  }
  
  void reset({double? pos, double? vel}) { position = pos ?? 0; velocity = vel ?? 0; _time = 0; }
}

/// 简谐振动演示
class SimpleHarmonicDemo extends StatefulWidget {
  const SimpleHarmonicDemo({super.key});
  
  State<SimpleHarmonicDemo> createState() => _SimpleHarmonicDemoState();
}

class _SimpleHarmonicDemoState extends State<SimpleHarmonicDemo> with SingleTickerProviderStateMixin {
  late AnimationController _ctrl;
  late SpringOscillator _osc;
  double _time = 0;
  final List<Offset> _trajectory = [];

  
  void initState() {
    super.initState();
    _osc = SpringOscillator(mass: 1, springConstant: 10, position: 1);
    _ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _ctrl.addListener(_update);
  }
  
  void _update() {
    _osc.update(0.016);
    _time += 0.016;
    _trajectory.add(Offset(_time, _osc.position));
    if (_trajectory.length > 300) _trajectory.removeAt(0);
    setState(() {});
  }

  
  void dispose() { _ctrl.removeListener(_update); _ctrl.dispose(); super.dispose(); }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('简谐振动')),
      body: Column(children: [
        Expanded(child: Row(children: [
          Expanded(flex: 2, child: _buildSpringView()),
          Expanded(child: _buildPhaseSpace()),
        ])),
        _buildGraph(),
        _buildControls(),
      ]),
    );
  }
  
  Widget _buildSpringView() {
    return CustomPaint(
      painter: SpringViewPainter(_osc.position, _time),
      size: Size.infinite,
    );
  }
  
  Widget _buildPhaseSpace() {
    return CustomPaint(
      painter: PhaseSpacePainter(_osc.position, _osc.velocity),
      size: Size.infinite,
    );
  }
  
  Widget _buildGraph() {
    return SizedBox(height: 120, child: CustomPaint(painter: TrajectoryPainter(_trajectory), size: Size.infinite));
  }

  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.grey[900], borderRadius: const BorderRadius.vertical(top: Radius.circular(20))),
      child: Column(mainAxisSize: MainAxisSize.min, children: [
        Row(children: [
          const Text('质量: ', style: TextStyle(color: Colors.white70)),
          Expanded(child: Slider(value: _osc.mass, min: 0.1, max: 5, onChanged: (v) => setState(() => _osc.mass = v))),
          Text(_osc.mass.toStringAsFixed(2), style: const TextStyle(color: Colors.green)),
        ]),
        Row(children: [
          const Text('劲度: ', style: TextStyle(color: Colors.white70)),
          Expanded(child: Slider(value: _osc.springConstant, min: 1, max: 50, onChanged: (v) => setState(() => _osc.springConstant = v))),
          Text(_osc.springConstant.toStringAsFixed(1), style: const TextStyle(color: Colors.green)),
        ]),
        Text('周期: ${_osc.period.toStringAsFixed(2)}s  能量: ${_osc.totalEnergy().toStringAsFixed(3)}J', style: const TextStyle(color: Colors.white70)),
      ]),
    );
  }
}

class SpringViewPainter extends CustomPainter {
  final double position;
  final double time;
  SpringViewPainter(this.position, this.time);

  
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final equilibriumX = size.width * 0.3;
    final massX = equilibriumX + position * 50;
    
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    // 固定墙
    canvas.drawRect(Rect.fromLTWH(10, center.dy - 60, 20, 120), Paint()..color = Colors.grey);
    
    // 弹簧
    _drawSpring(canvas, Offset(30, center.dy), Offset(massX - 30, center.dy));
    
    // 质量块
    final hue = 120 + sin(time * 2) * 30;
    canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromCenter(center: Offset(massX, center.dy), width: 60, height: 60), const Radius.circular(8)),
        Paint()..color = HSVColor.fromAHSV(1, hue, 0.7, 0.9).toColor());
    
    // 平衡位置标记
    canvas.drawLine(Offset(equilibriumX, center.dy - 80), Offset(equilibriumX, center.dy + 80), Paint()..color = Colors.white24..strokeWidth = 1);
    
    // 位移标注
    final tp = TextPainter(text: TextSpan(text: 'x = ${position.toStringAsFixed(2)}', style: const TextStyle(color: Colors.white70, fontSize: 12)), textDirection: TextDirection.ltr)..layout();
    tp.paint(canvas, Offset(massX - 20, center.dy + 50));
  }
  
  void _drawSpring(Canvas canvas, Offset start, Offset end) {
    final path = Path();
    path.moveTo(start.dx, start.dy);
    final coils = 12;
    final amplitude = 15.0;
    final length = end.dx - start.dx;
    
    for (int i = 0; i <= coils * 2; i++) {
      final t = i / (coils * 2);
      final x = start.dx + length * t;
      final y = start.dy + amplitude * sin(i * pi);
      path.lineTo(x, y);
    }
    path.lineTo(end.dx, end.dy);
    
    canvas.drawPath(path, Paint()..color = Colors.green..style = PaintingStyle.stroke..strokeWidth = 3);
  }

  
  bool shouldRepaint(covariant SpringViewPainter old) => true;
}

class PhaseSpacePainter extends CustomPainter {
  final double x, v;
  PhaseSpacePainter(this.x, this.v);

  
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    // 坐标轴
    canvas.drawLine(Offset(0, center.dy), Offset(size.width, center.dy), Paint()..color = Colors.white24);
    canvas.drawLine(Offset(center.dx, 0), Offset(center.dx, size.height), Paint()..color = Colors.white24);
    
    // 当前点
    final pointX = center.dx + x * 20;
    final pointY = center.dy - v * 20;
    canvas.drawCircle(Offset(pointX, pointY), 6, Paint()..color = Colors.green);
    
    // 标签
    final tp = TextPainter(text: const TextSpan(text: '相空间', style: TextStyle(color: Colors.white70, fontSize: 10)), textDirection: TextDirection.ltr)..layout();
    tp.paint(canvas, const Offset(8, 8));
  }

  
  bool shouldRepaint(covariant PhaseSpacePainter old) => true;
}

class TrajectoryPainter extends CustomPainter {
  final List<Offset> trajectory;
  TrajectoryPainter(this.trajectory);

  
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    if (trajectory.length < 2) return;
    
    final path = Path();
    final scaleX = size.width / 5;
    final scaleY = size.height / 4;
    final centerY = size.height / 2;
    
    for (int i = 0; i < trajectory.length; i++) {
      final x = (trajectory[i].dx - trajectory.first.dx) * scaleX / 60;
      final y = centerY - trajectory[i].dy * scaleY;
      
      if (i == 0) path.moveTo(x, y);
      else path.lineTo(x, y);
    }
    
    canvas.drawPath(path, Paint()..color = Colors.green..style = PaintingStyle.stroke..strokeWidth = 2);
  }

  
  bool shouldRepaint(covariant TrajectoryPainter old) => true;
}

/// 阻尼振动演示
class DampedOscillationDemo extends StatefulWidget {
  const DampedOscillationDemo({super.key});
  
  State<DampedOscillationDemo> createState() => _DampedOscillationDemoState();
}

class _DampedOscillationDemoState extends State<DampedOscillationDemo> with SingleTickerProviderStateMixin {
  late AnimationController _ctrl;
  late SpringOscillator _osc;
  double _damping = 0.5;
  final List<double> _energies = [];

  
  void initState() {
    super.initState();
    _osc = SpringOscillator(mass: 1, springConstant: 10, dampingCoefficient: _damping, position: 1);
    _ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _ctrl.addListener(_update);
  }
  
  void _update() {
    _osc.update(0.016);
    _energies.add(_osc.totalEnergy());
    if (_energies.length > 200) _energies.removeAt(0);
    setState(() {});
  }

  
  void dispose() { _ctrl.removeListener(_update); _ctrl.dispose(); super.dispose(); }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('阻尼振动')),
      body: Column(children: [
        Expanded(child: CustomPaint(painter: DampedPainter(_osc.position, _osc.dampingRatio), size: Size.infinite)),
        SizedBox(height: 100, child: CustomPaint(painter: EnergyPainter(_energies), size: Size.infinite)),
        _buildControls(),
      ]),
    );
  }

  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.grey[900], borderRadius: const BorderRadius.vertical(top: Radius.circular(20))),
      child: Column(mainAxisSize: MainAxisSize.min, children: [
        Row(children: [
          const Text('阻尼: ', style: TextStyle(color: Colors.white70)),
          Expanded(child: Slider(value: _damping, min: 0, max: 3, onChanged: (v) => setState(() { _damping = v; _osc.dampingCoefficient = v; _osc.reset(pos: 1); _energies.clear(); })),
          ),
          Text(_damping.toStringAsFixed(2), style: const TextStyle(color: Colors.orange)),
        ]),
        Text(_getTypeText(), style: TextStyle(color: _getTypeColor())),
      ]),
    );
  }
  
  String _getTypeText() {
    final r = _osc.dampingRatio;
    if (r < 1) return '欠阻尼 - 振荡衰减';
    if (r == 1) return '临界阻尼 - 最快回归';
    return '过阻尼 - 缓慢回归';
  }
  
  Color _getTypeColor() {
    final r = _osc.dampingRatio;
    if (r < 1) return Colors.green;
    if (r == 1) return Colors.yellow;
    return Colors.red;
  }
}

class DampedPainter extends CustomPainter {
  final double position;
  final double dampingRatio;
  DampedPainter(this.position, this.dampingRatio);

  
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    // 包络线
    final envelope = exp(-dampingRatio * 2);
    final paint = Paint()..color = Colors.orange.withOpacity(0.3)..style = PaintingStyle.stroke;
    canvas.drawLine(Offset(center.dx - 150, center.dy - 50 * envelope), Offset(center.dx + 150, center.dy - 50 * envelope), paint);
    canvas.drawLine(Offset(center.dx - 150, center.dy + 50 * envelope), Offset(center.dx + 150, center.dy + 50 * envelope), paint);
    
    // 质量块
    final massX = center.dx + position * 50;
    canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromCenter(center: Offset(massX, center.dy), width: 50, height: 50), const Radius.circular(8)),
        Paint()..color = Colors.orange);
  }

  
  bool shouldRepaint(covariant DampedPainter old) => true;
}

class EnergyPainter extends CustomPainter {
  final List<double> energies;
  EnergyPainter(this.energies);

  
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    if (energies.length < 2) return;
    
    final path = Path();
    final maxE = energies.reduce(max) * 1.2;
    
    for (int i = 0; i < energies.length; i++) {
      final x = i * size.width / 200;
      final y = size.height - energies[i] / maxE * size.height * 0.8;
      if (i == 0) path.moveTo(x, y);
      else path.lineTo(x, y);
    }
    
    canvas.drawPath(path, Paint()..color = Colors.purple..style = PaintingStyle.stroke..strokeWidth = 2);
  }

  
  bool shouldRepaint(covariant EnergyPainter old) => true;
}

/// 共振演示
class ResonanceDemo extends StatefulWidget {
  const ResonanceDemo({super.key});
  
  State<ResonanceDemo> createState() => _ResonanceDemoState();
}

class _ResonanceDemoState extends State<ResonanceDemo> with SingleTickerProviderStateMixin {
  late AnimationController _ctrl;
  late SpringOscillator _osc;
  double _driveFreq = 3.0;

  
  void initState() {
    super.initState();
    _osc = SpringOscillator(mass: 1, springConstant: 10, dampingCoefficient: 0.3, position: 0);
    _osc.externalAmplitude = 0.5;
    _osc.externalFrequency = _driveFreq;
    _ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _ctrl.addListener(_update);
  }
  
  void _update() { _osc.update(0.016); setState(() {}); }

  
  void dispose() { _ctrl.removeListener(_update); _ctrl.dispose(); super.dispose(); }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('共振现象')),
      body: Column(children: [
        Expanded(child: CustomPaint(painter: ResonancePainter(_osc.position, _osc.naturalFrequency, _driveFreq), size: Size.infinite)),
        _buildControls(),
      ]),
    );
  }

  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.grey[900], borderRadius: const BorderRadius.vertical(top: Radius.circular(20))),
      child: Column(mainAxisSize: MainAxisSize.min, children: [
        Row(children: [
          const Text('驱动频率: ', style: TextStyle(color: Colors.white70)),
          Expanded(child: Slider(value: _driveFreq, min: 0.5, max: 6, onChanged: (v) => setState(() { _driveFreq = v; _osc.externalFrequency = v; _osc.reset(); }))),
          Text('${_driveFreq.toStringAsFixed(2)} Hz', style: const TextStyle(color: Colors.purple)),
        ]),
        Text('固有频率: ${_osc.naturalFrequency.toStringAsFixed(2)} Hz', style: const TextStyle(color: Colors.white70)),
        Text(_isNearResonance() ? '⚠️ 接近共振!振幅增大' : '远离共振', style: TextStyle(color: _isNearResonance() ? Colors.red : Colors.green)),
      ]),
    );
  }
  
  bool _isNearResonance() => (_driveFreq - _osc.naturalFrequency).abs() < 0.5;
}

class ResonancePainter extends CustomPainter {
  final double position;
  final double naturalFreq;
  final double driveFreq;
  ResonancePainter(this.position, this.naturalFreq, this.driveFreq);

  
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    // 振幅指示
    final amplitude = position.abs() * 30;
    canvas.drawRect(Rect.fromLTWH(center.dx - 20, center.dy - amplitude, 40, amplitude * 2), Paint()..color = Colors.purple.withOpacity(0.5));
    
    // 质量块
    final massY = center.dy + position * 50;
    canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromCenter(center: Offset(center.dx, massY), width: 50, height: 30), const Radius.circular(8)),
        Paint()..color = Colors.purple);
    
    // 频率指示
    final tp = TextPainter(text: TextSpan(text: 'ω₀=${naturalFreq.toStringAsFixed(2)}  ω=${driveFreq.toStringAsFixed(2)}', style: const TextStyle(color: Colors.white70, fontSize: 12)), textDirection: TextDirection.ltr)..layout();
    tp.paint(canvas, const Offset(8, 8));
  }

  
  bool shouldRepaint(covariant ResonancePainter old) => true;
}

/// 耦合振子演示
class CoupledOscillatorDemo extends StatefulWidget {
  const CoupledOscillatorDemo({super.key});
  
  State<CoupledOscillatorDemo> createState() => _CoupledOscillatorDemoState();
}

class _CoupledOscillatorDemoState extends State<CoupledOscillatorDemo> with SingleTickerProviderStateMixin {
  late AnimationController _ctrl;
  final List<double> _positions = [1.0, 0.0, 0.0];
  final List<double> _velocities = [0.0, 0.0, 0.0];
  final double _k = 10.0, _coupling = 5.0, _m = 1.0;

  
  void initState() {
    super.initState();
    _ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _ctrl.addListener(_update);
  }
  
  void _update() {
    final acc = List<double>.filled(3, 0);
    for (int i = 0; i < 3; i++) {
      acc[i] = -_k * _positions[i] / _m;
      if (i > 0) acc[i] += _coupling * (_positions[i - 1] - _positions[i]) / _m;
      if (i < 2) acc[i] += _coupling * (_positions[i + 1] - _positions[i]) / _m;
    }
    for (int i = 0; i < 3; i++) {
      _velocities[i] += acc[i] * 0.016;
      _positions[i] += _velocities[i] * 0.016;
    }
    setState(() {});
  }

  
  void dispose() { _ctrl.removeListener(_update); _ctrl.dispose(); super.dispose(); }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('耦合振子')),
      body: Column(children: [
        Expanded(child: CustomPaint(painter: CoupledPainter(_positions), size: Size.infinite)),
        _buildControls(),
      ]),
    );
  }

  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.grey[900], borderRadius: const BorderRadius.vertical(top: Radius.circular(20))),
      child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
        ElevatedButton(onPressed: () => setState(() { _positions[0] = 1; _positions[1] = 0; _positions[2] = 0; for (int i = 0; i < 3; i++) _velocities[i] = 0; }), child: const Text('模式1')),
        ElevatedButton(onPressed: () => setState(() { _positions[0] = 1; _positions[1] = 0; _positions[2] = -1; for (int i = 0; i < 3; i++) _velocities[i] = 0; }), child: const Text('模式2')),
        ElevatedButton(onPressed: () => setState(() { _positions[0] = 1; _positions[1] = 1; _positions[2] = 1; for (int i = 0; i < 3; i++) _velocities[i] = 0; }), child: const Text('模式3')),
      ]),
    );
  }
}

class CoupledPainter extends CustomPainter {
  final List<double> positions;
  CoupledPainter(this.positions);

  
  void paint(Canvas canvas, Size size) {
    final centerY = size.height / 2;
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    final colors = [Colors.red, Colors.green, Colors.blue];
    
    for (int i = 0; i < 3; i++) {
      final x = size.width * (0.25 + i * 0.25) + positions[i] * 30;
      
      // 弹簧连接
      if (i < 2) {
        final nextX = size.width * (0.25 + (i + 1) * 0.25) + positions[i + 1] * 30;
        canvas.drawLine(Offset(x + 25, centerY), Offset(nextX - 25, centerY), Paint()..color = Colors.white30..strokeWidth = 2);
      }
      
      // 质量块
      canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromCenter(center: Offset(x, centerY), width: 50, height: 50), const Radius.circular(8)),
          Paint()..color = colors[i]);
    }
  }

  
  bool shouldRepaint(covariant CoupledPainter old) => true;
}

📝 四、数学原理深入解析

📐 4.1 微分方程求解

简谐振动方程求解

d²x/dt² + ω²x = 0

特征方程:r² + ω² = 0
特征根:r = ±iω

通解:
x(t) = C₁cos(ωt) + C₂sin(ωt)
     = A·cos(ωt + φ)

其中:
A = √(C₁² + C₂²)
φ = arctan(-C₂/C₁)

阻尼振动方程求解

d²x/dt² + 2γ(dx/dt) + ω₀²x = 0

特征方程:r² + 2γr + ω₀² = 0
特征根:r = -γ ± √(γ² - ω₀²)

三种情况:
1. 欠阻尼 (γ < ω₀):
   r = -γ ± iω'
   x = Ae⁻ᵞᵗcos(ω't + φ)
   ω' = √(ω₀² - γ²)

2. 临界阻尼 (γ = ω₀):
   r = -γ(重根)
   x = (A + Bt)e⁻ᵞᵗ

3. 过阻尼 (γ > ω₀):
   r = -γ ± √(γ² - ω₀²)
   x = Ae⁻ʳ¹ᵗ + Be⁻ʳ²ᵗ

🔄 4.2 品质因数 Q

品质因数定义

Q = ω₀ / (2γ) = √(km) / b

物理意义:
- Q 越大,阻尼越小
- Q 越大,共振峰越尖锐
- Q 越大,能量衰减越慢

能量衰减:
E(t) = E₀e⁻²ᵞᵗ = E₀e⁻ᵗ/τ

时间常数:τ = 1/(2γ) = Q/ω₀

振动次数:
在能量衰减到 1/e 之前,振动次数 ≈ Q/(2π)

🌸 4.3 傅里叶分析

振动的频谱表示

任何周期运动都可以分解为简谐振动的叠加:

x(t) = Σ Aₙcos(nωt + φₙ)

傅里叶变换:
X(ω) = ∫ x(t)e⁻ⁱᵒᵗdt

对于阻尼振动:
X(ω) ∝ 1 / [(ω₀² - ω²) + i(2γω)]

功率谱密度:
|X(ω)|² ∝ 1 / [(ω₀² - ω²)² + (2γω)²]

🎯 4.4 相空间与混沌

相空间动力学

对于受迫阻尼振子:
dx/dt = v
dv/dt = -ω₀²x - 2γv + (F₀/m)cos(ωt)

这是二维非自治系统
可以扩展为三维自治系统:
dx/dt = v
dv/dt = -ω₀²x - 2γv + (F₀/m)cos(θ)
dθ/dt = ω

当驱动力足够大时,可能出现混沌

🔬 五、高级应用场景

🎨 5.1 物理仿真

刚体动力学

class RigidBodySimulation {
  final List<SpringOscillator> oscillators;
  final List<List<double>> couplingMatrix;
  
  void update(double dt) {
    // 计算所有振子的加速度
    final accelerations = <double>[];
    for (int i = 0; i < oscillators.length; i++) {
      double acc = -oscillators[i].springConstant * oscillators[i].position / oscillators[i].mass;
      for (int j = 0; j < oscillators.length; j++) {
        if (couplingMatrix[i][j] != 0) {
          acc += couplingMatrix[i][j] * (oscillators[j].position - oscillators[i].position) / oscillators[i].mass;
        }
      }
      accelerations.add(acc);
    }
    
    // 更新速度和位置
    for (int i = 0; i < oscillators.length; i++) {
      oscillators[i].velocity += accelerations[i] * dt;
      oscillators[i].position += oscillators[i].velocity * dt;
    }
  }
}

🌐 5.2 音频合成

弹簧混响模型

class SpringReverb {
  final List<SpringOscillator> springs;
  
  SpringReverb(int count) : springs = List.generate(count, (i) => SpringOscillator(mass: 1, springConstant: 10 + i * 2, dampingCoefficient: 0.1));
  
  double process(double input) {
    double output = 0;
    for (final spring in springs) {
      spring.externalAmplitude = input;
      spring.externalFrequency = spring.naturalFrequency;
      spring.update(0.001);
      output += spring.position;
    }
    return output / springs.length;
  }
}

📱 5.3 鸿蒙多端适配

性能优化配置

class SpringSimulationConfig {
  static int getOptimalOscillatorCount(BuildContext context) {
    final performance = DevicePerformance.getLevel();
    switch (performance) {
      case PerformanceLevel.high: return 20;
      case PerformanceLevel.medium: return 10;
      case PerformanceLevel.low: return 5;
    }
  }
  
  static double getOptimalTimeStep() {
    return 0.016; // 60 FPS
  }
}

📊 六、性能优化策略

⚡ 6.1 数值积分优化

龙格-库塔方法

class RK4Integrator {
  static void integrate(SpringOscillator osc, double dt) {
    final k1v = _acceleration(osc, osc.position, osc.velocity);
    final k1x = osc.velocity;
    
    final k2v = _acceleration(osc, osc.position + k1x * dt / 2, osc.velocity + k1v * dt / 2);
    final k2x = osc.velocity + k1v * dt / 2;
    
    final k3v = _acceleration(osc, osc.position + k2x * dt / 2, osc.velocity + k2v * dt / 2);
    final k3x = osc.velocity + k2v * dt / 2;
    
    final k4v = _acceleration(osc, osc.position + k3x * dt, osc.velocity + k3v * dt);
    final k4x = osc.velocity + k3v * dt;
    
    osc.velocity += (k1v + 2 * k2v + 2 * k3v + k4v) * dt / 6;
    osc.position += (k1x + 2 * k2x + 2 * k3x + k4x) * dt / 6;
  }
  
  static double _acceleration(SpringOscillator osc, double x, double v) {
    return (-osc.springConstant * x - osc.dampingCoefficient * v) / osc.mass;
  }
}

💾 6.2 历史数据管理

环形缓冲区

class RingBuffer<T> {
  final List<T?> _buffer;
  int _head = 0;
  int _count = 0;
  
  RingBuffer(int size) : _buffer = List.filled(size, null);
  
  void add(T item) {
    _buffer[_head] = item;
    _head = (_head + 1) % _buffer.length;
    if (_count < _buffer.length) _count++;
  }
  
  List<T> toList() {
    final result = <T>[];
    for (int i = 0; i < _count; i++) {
      final idx = (_head - _count + i + _buffer.length) % _buffer.length;
      result.add(_buffer[idx]!);
    }
    return result;
  }
}

🎓 七、学习资源与拓展

📚 推荐阅读

主题 资源 难度
经典力学 《费曼物理学讲义》第一卷 ⭐⭐
振动理论 《振动理论》- 铁摩辛柯 ⭐⭐⭐
微分方程 《常微分方程》 ⭐⭐
混沌理论 《混沌:开创新科学》 ⭐⭐

🔗 相关项目

  • PhET Simulations:物理仿真教育项目
  • Algodoo:物理沙盒软件
  • Box2D:2D 物理引擎

📝 八、总结

本篇文章深入探讨了弹簧振子系统的数学原理及其在 Flutter 中的可视化实现。

✅ 核心知识点回顾

知识点 说明
🎯 简谐振动 胡克定律与周期运动
📉 阻尼振动 能量耗散与三种阻尼
📊 共振现象 受迫振动与振幅放大
🔗 耦合振子 多体系统与简正模式
数值模拟 欧拉法与龙格-库塔法

⭐ 最佳实践要点

  • ✅ 使用 RK4 积分提高精度
  • ✅ 合理设置阻尼系数
  • ✅ 注意共振频率匹配
  • ✅ 使用环形缓冲区管理历史数据

🚀 进阶方向

  • 🔮 非线性振动与混沌
  • ✨ 多自由度系统
  • 📊 实时频谱分析
  • 🎵 音频效果器应用

💡 提示:本文代码基于 Flutter for Harmony 开发,可在鸿蒙设备上流畅运行。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐