|
再来一个增强版本的:
- import pymunk
- import pymunk.pygame_util
- import pygame
- import math
- import numpy as np
- import matplotlib.pyplot as plt
- import matplotlib.patches as patches
- from matplotlib.animation import FuncAnimation
- import time
- class BeltConveyorAnalyzer:
- def __init__(self, width=1000, height=700):
- """初始化皮带运输机分析器"""
- # 初始化pygame和pymunk
- pygame.init()
- self.width = width
- self.height = height
- self.screen = pygame.display.set_mode((width, height))
- pygame.display.set_caption("皮带运输机跑偏分析 - Pymunk物理模拟")
-
- # 物理空间设置
- self.space = pymunk.Space()
- self.space.gravity = (0, 0) # 水平运输,重力不影响跑偏
-
- # 系统参数
- self.roller_radius = 40
- self.roller_distance = 500
- self.belt_thickness = 8
- self.belt_segment_count = 30
-
- # 组件
- self.head_roller = None
- self.tail_roller = None
- self.belt_segments = []
- self.belt_joints = []
-
- # 分析数据
- self.lateral_forces = []
- self.deviation_history = []
- self.time_steps = []
-
- def clear_simulation(self):
- """清理现有模拟"""
- # 移除所有物体
- for body in self.space.bodies:
- self.space.remove(body)
- for shape in self.space.shapes:
- self.space.remove(shape)
- for constraint in self.space.constraints:
- self.space.remove(constraint)
-
- self.belt_segments = []
- self.belt_joints = []
- self.lateral_forces = []
- self.deviation_history = []
- self.time_steps = []
-
- def create_roller(self, x, y, angle_deg, is_power=False, angular_velocity=3.0):
- """创建辊筒"""
- body = pymunk.Body(body_type=pymunk.Body.KINEMATIC)
- body.position = x, y
- body.angle = math.radians(angle_deg)
-
- if is_power:
- body.angular_velocity = angular_velocity
-
- shape = pymunk.Circle(body, self.roller_radius)
- shape.friction = 0.9
- shape.collision_type = 1 # 辊筒碰撞类型
-
- self.space.add(body, shape)
- return shape
-
- def create_belt_system(self):
- """创建皮带系统"""
- # 计算皮带总长度(包括包裹辊筒的部分)
- straight_length = self.roller_distance
- curved_length = 2 * math.pi * self.roller_radius
- total_length = 2 * straight_length + curved_length
-
- segment_length = total_length / self.belt_segment_count
-
- # 创建上直段皮带
- for i in range(self.belt_segment_count // 2):
- x = 200 + i * (self.roller_distance / (self.belt_segment_count // 2))
- y = self.height // 2 - self.roller_radius - self.belt_thickness
-
- body = pymunk.Body(1, pymunk.moment_for_box(1, (segment_length, self.belt_thickness)))
- body.position = x, y
- body.velocity = (50, 0) # 初始速度
-
- shape = pymunk.Poly.create_box(body, (segment_length, self.belt_thickness))
- shape.friction = 0.7
- shape.collision_type = 2 # 皮带碰撞类型
-
- self.belt_segments.append(shape)
- self.space.add(body, shape)
-
- # 创建下直段皮带
- for i in range(self.belt_segment_count // 2):
- x = 200 + self.roller_distance - i * (self.roller_distance / (self.belt_segment_count // 2))
- y = self.height // 2 + self.roller_radius + self.belt_thickness
-
- body = pymunk.Body(1, pymunk.moment_for_box(1, (segment_length, self.belt_thickness)))
- body.position = x, y
- body.velocity = (-50, 0) # 反向速度
-
- shape = pymunk.Poly.create_box(body, (segment_length, self.belt_thickness))
- shape.friction = 0.7
- shape.collision_type = 2
-
- self.belt_segments.append(shape)
- self.space.add(body, shape)
-
- # 连接皮带段
- self.connect_belt_segments()
-
- def connect_belt_segments(self):
- """连接皮带段"""
- for i in range(len(self.belt_segments)):
- next_i = (i + 1) % len(self.belt_segments)
-
- # 使用弹性约束连接相邻段
- joint = pymunk.DampedSpring(
- self.belt_segments[i].body,
- self.belt_segments[next_i].body,
- (10, 0), (-10, 0), # 连接点
- 20, # 静止长度
- 1000, # 刚度
- 50 # 阻尼
- )
- self.belt_joints.append(joint)
- self.space.add(joint)
-
- def setup_case_A(self):
- """情况A:首辊为动力辊,首辊有偏角,尾辊垂直"""
- self.clear_simulation()
-
- # 创建辊筒
- head_x, head_y = 200, self.height // 2
- tail_x, tail_y = 700, self.height // 2
-
- self.head_roller = self.create_roller(head_x, head_y, 15, is_power=True, angular_velocity=3.0)
- self.tail_roller = self.create_roller(tail_x, tail_y, 0, is_power=False)
-
- # 创建皮带
- self.create_belt_system()
-
- return "情况A: 首辊为动力辊(15°偏角),尾辊垂直"
-
- def setup_case_B(self):
- """情况B:尾辊为动力辊,首辊有偏角,尾辊垂直"""
- self.clear_simulation()
-
- head_x, head_y = 200, self.height // 2
- tail_x, tail_y = 700, self.height // 2
-
- self.head_roller = self.create_roller(head_x, head_y, 15, is_power=False)
- self.tail_roller = self.create_roller(tail_x, tail_y, 0, is_power=True, angular_velocity=3.0)
-
- self.create_belt_system()
-
- return "情况B: 尾辊为动力辊,首辊偏角15°"
-
- def setup_case_C(self):
- """情况C:首辊为动力辊,尾辊有偏角,首辊垂直"""
- self.clear_simulation()
-
- head_x, head_y = 200, self.height // 2
- tail_x, tail_y = 700, self.height // 2
-
- self.head_roller = self.create_roller(head_x, head_y, 0, is_power=True, angular_velocity=3.0)
- self.tail_roller = self.create_roller(tail_x, tail_y, 15, is_power=False)
-
- self.create_belt_system()
-
- return "情况C: 首辊为动力辊,尾辊偏角15°"
-
- def setup_case_D(self):
- """情况D:尾辊为动力辊,尾辊有偏角,首辊垂直"""
- self.clear_simulation()
-
- head_x, head_y = 200, self.height // 2
- tail_x, tail_y = 700, self.height // 2
-
- self.head_roller = self.create_roller(head_x, head_y, 0, is_power=False)
- self.tail_roller = self.create_roller(tail_x, tail_y, 15, is_power=True, angular_velocity=3.0)
-
- self.create_belt_system()
-
- return "情况D: 尾辊为动力辊(15°偏角),首辊垂直"
-
- def calculate_lateral_deviation(self):
- """计算侧向偏移"""
- total_lateral_force = 0
- contact_count = 0
-
- # 检查与辊筒接触的皮带段
- for segment in self.belt_segments:
- pos = segment.body.position
-
- # 检查与首辊的接触
- head_dist = (pos - self.head_roller.body.position).length
- if head_dist < self.roller_radius + self.belt_thickness + 5:
- # 计算接触点的法向量和切向量
- contact_normal = (pos - self.head_roller.body.position).normalized()
- roller_tangent = pymunk.Vec2d(-math.sin(self.head_roller.body.angle),
- math.cos(self.head_roller.body.angle))
-
- # 计算侧向分量
- lateral_component = contact_normal.cross(roller_tangent)
- total_lateral_force += lateral_component * 100 # 缩放因子
- contact_count += 1
-
- # 检查与尾辊的接触
- tail_dist = (pos - self.tail_roller.body.position).length
- if tail_dist < self.roller_radius + self.belt_thickness + 5:
- contact_normal = (pos - self.tail_roller.body.position).normalized()
- roller_tangent = pymunk.Vec2d(-math.sin(self.tail_roller.body.angle),
- math.cos(self.tail_roller.body.angle))
-
- lateral_component = contact_normal.cross(roller_tangent)
- total_lateral_force += lateral_component * 100
- contact_count += 1
-
- return total_lateral_force / max(contact_count, 1)
-
- def run_simulation(self, case_title, duration=300):
- """运行物理模拟"""
- clock = pygame.time.Clock()
- draw_options = pymunk.pygame_util.DrawOptions(self.screen)
-
- # 重置数据
- self.lateral_forces = []
- self.deviation_history = []
- self.time_steps = []
-
- font = pygame.font.Font(None, 28)
- small_font = pygame.font.Font(None, 20)
-
- for step in range(duration):
- # 处理事件
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- pygame.quit()
- return
- elif event.type == pygame.KEYDOWN:
- if event.key == pygame.K_SPACE:
- return # 空格键跳过当前模拟
-
- # 物理步进
- dt = 1.0/60.0
- self.space.step(dt)
-
- # 计算偏移量
- lateral_deviation = self.calculate_lateral_deviation()
- self.lateral_forces.append(lateral_deviation)
- self.time_steps.append(step * dt)
-
- # 计算皮带整体偏移
- belt_center_y = np.mean([seg.body.position.y for seg in self.belt_segments[:len(self.belt_segments)//2]])
- deviation_from_center = belt_center_y - self.height // 2
- self.deviation_history.append(deviation_from_center)
-
- # 绘制
- self.screen.fill((240, 240, 240))
-
- # 绘制背景网格
- for i in range(0, self.width, 50):
- pygame.draw.line(self.screen, (220, 220, 220), (i, 0), (i, self.height), 1)
- for i in range(0, self.height, 50):
- pygame.draw.line(self.screen, (220, 220, 220), (0, i), (self.width, i), 1)
-
- # 绘制物理对象
- self.space.debug_draw(draw_options)
-
- # 绘制辊筒轴线
- if self.head_roller:
- head_pos = self.head_roller.body.position
- head_angle = self.head_roller.body.angle
- end_x = head_pos.x + 60 * math.cos(head_angle)
- end_y = head_pos.y + 60 * math.sin(head_angle)
- pygame.draw.line(self.screen, (255, 0, 0), head_pos, (end_x, end_y), 3)
-
- if self.tail_roller:
- tail_pos = self.tail_roller.body.position
- tail_angle = self.tail_roller.body.angle
- end_x = tail_pos.x + 60 * math.cos(tail_angle)
- end_y = tail_pos.y + 60 * math.sin(tail_angle)
- pygame.draw.line(self.screen, (0, 0, 255), tail_pos, (end_x, end_y), 3)
-
- # 显示信息
- title_surface = font.render(case_title, True, (0, 0, 0))
- self.screen.blit(title_surface, (10, 10))
-
- info_texts = [
- f"模拟时间: {step*dt:.1f}s",
- f"侧向力: {lateral_deviation:.2f}",
- f"皮带偏移: {deviation_from_center:.1f}px",
- "红线: 首辊轴线, 蓝线: 尾辊轴线",
- "按空格键跳过当前模拟"
- ]
-
- for i, text in enumerate(info_texts):
- surface = small_font.render(text, True, (0, 0, 0))
- self.screen.blit(surface, (10, 45 + i * 20))
-
- # 绘制实时图表
- if len(self.lateral_forces) > 1:
- self.draw_mini_chart()
-
- pygame.display.flip()
- clock.tick(60)
-
- def draw_mini_chart(self):
- """绘制小型实时图表"""
- chart_x, chart_y = self.width - 250, 50
- chart_w, chart_h = 200, 100
-
- # 背景
- pygame.draw.rect(self.screen, (255, 255, 255), (chart_x, chart_y, chart_w, chart_h))
- pygame.draw.rect(self.screen, (0, 0, 0), (chart_x, chart_y, chart_w, chart_h), 2)
-
- # 数据
- if len(self.lateral_forces) > 1:
- max_val = max(max(self.lateral_forces), abs(min(self.lateral_forces)), 1)
- points = []
-
- for i, val in enumerate(self.lateral_forces[-100:]): # 最近100个点
- x = chart_x + (i / 99) * chart_w if len(self.lateral_forces) >= 100 else chart_x + (i / (len(self.lateral_forces)-1)) * chart_w
- y = chart_y + chart_h//2 - (val / max_val) * (chart_h//2 - 10)
- points.append((x, y))
-
- if len(points) > 1:
- pygame.draw.lines(self.screen, (255, 0, 0), False, points, 2)
-
- # 标签
- font = pygame.font.Font(None, 16)
- label = font.render("侧向力", True, (0, 0, 0))
- self.screen.blit(label, (chart_x, chart_y - 20))
-
- def analyze_all_cases(self):
- """分析所有情况"""
- cases = [
- ('A', self.setup_case_A),
- ('B', self.setup_case_B),
- ('C', self.setup_case_C),
- ('D', self.setup_case_D)
- ]
-
- results = {}
-
- for case_name, setup_func in cases:
- print(f"\n开始分析情况{case_name}...")
- case_title = setup_func()
- self.run_simulation(case_title)
-
- # 保存结果
- results[case_name] = {
- 'title': case_title,
- 'lateral_forces': self.lateral_forces.copy(),
- 'deviation_history': self.deviation_history.copy(),
- 'time_steps': self.time_steps.copy()
- }
-
- return results
-
- def create_analysis_plots(self, results):
- """创建分析图表"""
- fig, axes = plt.subplots(2, 2, figsize=(15, 10))
- fig.suptitle('皮带运输机跑偏分析结果', fontsize=16, fontweight='bold')
-
- cases = ['A', 'B', 'C', 'D']
- colors = ['red', 'blue', 'green', 'orange']
-
- for i, case in enumerate(cases):
- if case in results:
- row, col = i // 2, i % 2
- ax = axes[row, col]
-
- # 绘制侧向力
- time_steps = results[case]['time_steps']
- lateral_forces = results[case]['lateral_forces']
-
- ax.plot(time_steps, lateral_forces, color=colors[i], linewidth=2, label='侧向力')
- ax.axhline(y=0, color='black', linestyle='--', alpha=0.5)
-
- ax.set_title(results[case]['title'], fontsize=12, fontweight='bold')
- ax.set_xlabel('时间 (s)')
- ax.set_ylabel('侧向力')
- ax.grid(True, alpha=0.3)
- ax.legend()
-
- # 分析结果
- avg_force = np.mean(lateral_forces[-50:]) if len(lateral_forces) >= 50 else np.mean(lateral_forces)
- if abs(avg_force) > 0.5:
- direction = "向右跑偏" if avg_force > 0 else "向左跑偏"
- severity = "严重" if abs(avg_force) > 2 else "轻微"
- else:
- direction = "基本无跑偏"
- severity = ""
-
- result_text = f"{severity} {direction}"
- ax.text(0.05, 0.95, result_text, transform=ax.transAxes,
- bbox=dict(boxstyle="round,pad=0.3", facecolor='yellow', alpha=0.7),
- fontsize=10, verticalalignment='top')
-
- plt.tight_layout()
- plt.savefig('belt_deviation_analysis.png', dpi=200, bbox_inches='tight')
- plt.show()
-
- def create_theoretical_analysis(self):
- """创建理论分析图"""
- fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))
- fig.suptitle('皮带跑偏理论分析', fontsize=16, fontweight='bold')
-
- # 绘制四种情况的示意图
- self.draw_case_diagram(ax1, "情况A: 首辊为动力辊,首辊偏角", head_power=True, head_angle=15, tail_angle=0)
- self.draw_case_diagram(ax2, "情况B: 尾辊为动力辊,首辊偏角", head_power=False, head_angle=15, tail_angle=0)
- self.draw_case_diagram(ax3, "情况C: 首辊为动力辊,尾辊偏角", head_power=True, head_angle=0, tail_angle=15)
- self.draw_case_diagram(ax4, "情况D: 尾辊为动力辊,尾辊偏角", head_power=False, head_angle=0, tail_angle=15)
-
- plt.tight_layout()
- plt.savefig('belt_deviation_theory.png', dpi=200, bbox_inches='tight')
- plt.show()
-
- def draw_case_diagram(self, ax, title, head_power, head_angle, tail_angle):
- """绘制单个情况的示意图"""
- ax.set_xlim(-2, 8)
- ax.set_ylim(-2, 2)
- ax.set_aspect('equal')
- ax.set_title(title, fontsize=12, fontweight='bold')
-
- # 首辊
- head_circle = patches.Circle((1, 0), 0.5, fill=False, linewidth=3,
- color='red' if head_power else 'blue')
- ax.add_patch(head_circle)
-
- # 首辊轴线
- head_rad = math.radians(head_angle)
- ax.arrow(1, 0, 0.8*math.cos(head_rad), 0.8*math.sin(head_rad),
- head_width=0.1, head_length=0.1, fc='red' if head_power else 'blue',
- ec='red' if head_power else 'blue')
-
- # 尾辊
- tail_circle = patches.Circle((5, 0), 0.5, fill=False, linewidth=3,
- color='red' if not head_power else 'blue')
- ax.add_patch(tail_circle)
-
- # 尾辊轴线
- tail_rad = math.radians(tail_angle)
- ax.arrow(5, 0, 0.8*math.cos(tail_rad), 0.8*math.sin(tail_rad),
- head_width=0.1, head_length=0.1, fc='red' if not head_power else 'blue',
- ec='red' if not head_power else 'blue')
-
- # 皮带
- belt_y_top = 0.6
- belt_y_bottom = -0.6
- ax.plot([1.5, 4.5], [belt_y_top, belt_y_top], 'k-', linewidth=4, label='皮带上段')
- ax.plot([1.5, 4.5], [belt_y_bottom, belt_y_bottom], 'k-', linewidth=4, label='皮带下段')
-
- # 添加标注
- if head_power:
- ax.text(1, -1.2, '动力辊', ha='center', fontsize=10, color='red', fontweight='bold')
- else:
- ax.text(1, -1.2, '被动辊', ha='center', fontsize=10, color='blue')
-
- if not head_power:
- ax.text(5, -1.2, '动力辊', ha='center', fontsize=10, color='red', fontweight='bold')
- else:
- ax.text(5, -1.2, '被动辊', ha='center', fontsize=10, color='blue')
-
- # 角度标注
- if head_angle != 0:
- ax.text(1, 1.2, f'偏角{head_angle}°', ha='center', fontsize=10,
- bbox=dict(boxstyle="round,pad=0.3", facecolor='yellow', alpha=0.7))
- if tail_angle != 0:
- ax.text(5, 1.2, f'偏角{tail_angle}°', ha='center', fontsize=10,
- bbox=dict(boxstyle="round,pad=0.3", facecolor='yellow', alpha=0.7))
-
- ax.grid(True, alpha=0.3)
- ax.set_xlabel('位置')
- ax.set_ylabel('高度')
-
- def print_comprehensive_analysis(self):
- """打印综合理论分析"""
- print("\n" + "="*80)
- print("皮带运输机跑偏问题综合分析报告")
- print("="*80)
-
- print("\n1. 基本原理:")
- print(" 皮带跑偏的根本原因是辊筒与皮带之间的摩擦力产生侧向分量。")
- print(" 当辊筒轴线与皮带运动方向不垂直时,接触点的摩擦力不再沿着")
- print(" 皮带运动方向,而是产生一个垂直于运动方向的侧向分力。")
-
- print("\n2. 四种情况详细分析:")
-
- print("\n 情况A - 首辊为动力辊,首辊有偏角(15°),尾辊垂直:")
- print(" ├─ 物理机制:动力辊的驱动力直接作用在皮带上")
- print(" ├─ 偏角影响:首辊偏角使驱动力产生侧向分量")
- print(" ├─ 跑偏方向:向偏角指向的一侧跑偏")
- print(" └─ 调整策略:调整首辊轴承座,使首辊轴线垂直于皮带方向")
-
- print("\n 情况B - 尾辊为动力辊,首辊有偏角(15°),尾辊垂直:")
- print(" ├─ 物理机制:动力从尾辊传递,首辊为被动辊")
- print(" ├─ 偏角影响:首辊偏角影响皮带张力分布和导向")
- print(" ├─ 跑偏方向:与情况A相反方向跑偏")
- print(" └─ 调整策略:调整首辊角度或适当增加皮带张力")
-
- print("\n 情况C - 首辊为动力辊,尾辊有偏角(15°),首辊垂直:")
- print(" ├─ 物理机制:动力辊驱动正常,尾辊偏角影响回程段")
- print(" ├─ 偏角影响:尾辊偏角主要影响皮带下回程的导向")
- print(" ├─ 跑偏方向:向尾辊偏角指向的一侧跑偏")
- print(" └─ 调整策略:调整尾辊轴承座,使尾辊轴线垂直于皮带方向")
-
- print("\n 情况D - 尾辊为动力辊,尾辊有偏角(15°),首辊垂直:")
- print(" ├─ 物理机制:动力辊本身有偏角,直接影响驱动方向")
- print(" ├─ 偏角影响:驱动力和导向力都产生侧向分量")
- print(" ├─ 跑偏方向:向偏角指向的一侧强烈跑偏")
- print(" └─ 调整策略:重点调整尾辊轴承座,消除偏角")
-
- print("\n3. 八字形喇叭口分析:")
- print(" 当首尾辊形成八字形时:")
- print(" ├─ 跑偏规律:皮带总是向口小(收缩)的一侧跑偏")
- print(" ├─ 物理原因:口小一侧的约束力更大,产生向心力")
- print(" ├─ 判断方法:观察两辊轴线延长线的交点位置")
- print(" └─ 调整原则:")
- print(" ├─ 口大的一侧:调紧(增大轴承座距离)")
- print(" ├─ 口小的一侧:调松(减小轴承座距离)")
- print(" └─ 目标:使两辊轴线平行")
-
- print("\n4. 调整方法总结:")
- print(" ├─ 首辊调整:影响皮带进入段的导向")
- print(" ├─ 尾辊调整:影响皮带输出段的导向")
- print(" ├─ 动力辊调整:对跑偏影响最大,需优先调整")
- print(" ├─ 被动辊调整:主要起辅助导向作用")
- print(" └─ 综合调整:通常需要两个辊筒配合调整")
-
- print("\n5. 预防措施:")
- print(" ├─ 安装精度:确保辊筒轴线平行且垂直于皮带方向")
- print(" ├─ 定期检查:监测轴承磨损和辊筒变形")
- print(" ├─ 张力控制:保持适当且均匀的皮带张力")
- print(" └─ 负载均匀:避免皮带上物料分布不均")
-
- print("\n" + "="*80)
- def main():
- """主函数"""
- print("皮带运输机跑偏分析系统启动中...")
-
- analyzer = BeltConveyorAnalyzer()
-
- try:
- # 打印理论分析
- analyzer.print_comprehensive_analysis()
-
- # 创建理论分析图
- print("\n生成理论分析图...")
- analyzer.create_theoretical_analysis()
-
- # 运行物理模拟
- print("\n开始物理模拟分析...")
- print("每个情况模拟5秒,按空格键可跳过当前模拟")
-
- results = analyzer.analyze_all_cases()
-
- # 生成分析图表
- print("\n生成分析结果图表...")
- analyzer.create_analysis_plots(results)
-
- print("\n" + "="*50)
- print("分析完成!生成的文件:")
- print("├─ belt_deviation_theory.png (理论分析图)")
- print("└─ belt_deviation_analysis.png (模拟结果图)")
- print("="*50)
-
- except Exception as e:
- print(f"分析过程中出现错误: {e}")
- import traceback
- traceback.print_exc()
-
- finally:
- pygame.quit()
- print("\n程序结束")
- if __name__ == "__main__":
- main()
复制代码
主要功能:
- 物理模拟引擎:使用pymunk模拟辊筒和皮带的物理交互
- 四种情况分析:分别模拟A、B、C、D四种跑偏情况
- 实时可视化:显示辊筒角度、皮带运动和受力分析
- 理论分析:详细的跑偏机制和调整方法分析
- 结果图表:生成分析图表和理论示意图
|
|