通过深度分析发现debug_execution.py中机械臂无法移动的根本原因: 缺少p.stepSimulation()调用导致PyBullet控制命令无法执行。 修复内容: - 在插值循环中添加stepSimulation调用,让每步控制命令生效 - 在收敛等待中添加stepSimulation调用,确保最终位置收敛 - 删除过时的测试文件和debug文件,保持项目整洁 修复效果: - 关节误差从0.8000降至0.0049(降低99%) - 位置误差从1.4720m降至0.0070m(降低99.5%) - 路径长度从0.0000m恢复至1.5020m(接近理论值1.5095m) 这个修复也为PathExecutor在无GUI环境下的独立使用提供了重要参考。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
236 lines
10 KiB
Python
236 lines
10 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
路径执行调试脚本
|
||
专门测试路径执行过程中的坐标对应关系
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||
|
||
from src.config_loader import ConfigLoader
|
||
from src.robot.arm_controller import create_arm_controller
|
||
from src.simulation.environment import Environment
|
||
from src.planning.ai_rrt_star import AIRRTStarPlanner
|
||
from src.planning.collision_checker import CollisionChecker
|
||
from src.planning.path_executor import PathExecutor
|
||
import pybullet as p
|
||
import pybullet_data
|
||
import numpy as np
|
||
import time
|
||
|
||
def debug_path_execution():
|
||
"""调试路径执行的具体步骤"""
|
||
print("=== 路径执行调试分析 ===")
|
||
|
||
# 初始化仿真
|
||
physics_client = p.connect(p.GUI)
|
||
p.setAdditionalSearchPath(pybullet_data.getDataPath())
|
||
p.setGravity(0, 0, 0) # 关闭重力
|
||
|
||
try:
|
||
# 加载配置和组件
|
||
config_loader = ConfigLoader()
|
||
arm_controller = create_arm_controller(config_loader, physics_client)
|
||
environment = Environment(config_loader, physics_client)
|
||
collision_checker = CollisionChecker(arm_controller, environment, config_loader)
|
||
path_executor = PathExecutor(arm_controller, config_loader)
|
||
|
||
print("=== 步骤1: 生成简单测试路径 ===")
|
||
|
||
# 获取当前位置
|
||
current_joints = arm_controller.get_current_joint_positions()
|
||
print(f"起始关节配置: {[f'{x:.3f}' for x in current_joints]}")
|
||
|
||
# 计算当前末端位置
|
||
current_pos, current_ori = arm_controller.forward_kinematics(current_joints)
|
||
print(f"起始末端位置: {[f'{x:.3f}' for x in current_pos]}")
|
||
|
||
# 生成一个简单的测试路径:只修改第一个关节
|
||
test_path = []
|
||
for i in range(5): # 5个路径点
|
||
test_config = current_joints.copy()
|
||
test_config[0] += i * 0.2 # 每步增加0.2弧度
|
||
test_path.append(test_config)
|
||
|
||
print(f"测试路径包含 {len(test_path)} 个waypoint")
|
||
|
||
print("\n=== 步骤2: 分析路径的理论末端位置 ===")
|
||
theoretical_positions = []
|
||
for i, config in enumerate(test_path):
|
||
pos, ori = arm_controller.forward_kinematics(config)
|
||
theoretical_positions.append(pos)
|
||
print(f"Waypoint {i}: 关节{config[0]:.3f} -> 末端位置{[f'{x:.3f}' for x in pos]}")
|
||
|
||
print("\n=== 步骤3: 可视化理论路径 ===")
|
||
# 绘制理论路径线条
|
||
line_ids = []
|
||
for i in range(len(theoretical_positions) - 1):
|
||
line_id = p.addUserDebugLine(
|
||
theoretical_positions[i],
|
||
theoretical_positions[i + 1],
|
||
lineColorRGB=[0, 1, 0], # 绿色 - 理论路径
|
||
lineWidth=3,
|
||
physicsClientId=physics_client
|
||
)
|
||
line_ids.append(line_id)
|
||
|
||
print("\n=== 步骤4: 执行路径并记录实际位置 ===")
|
||
actual_positions = []
|
||
|
||
# 重置机械臂到起始位置
|
||
arm_controller.set_joint_positions(current_joints)
|
||
for _ in range(50): # 等待稳定
|
||
p.stepSimulation(physicsClientId=physics_client)
|
||
|
||
# 模拟主程序的路径执行器插值方式
|
||
timestep = 0.01 # 和主程序相同的timestep
|
||
velocity_scaling = 1.0 # 和主程序默认值相同
|
||
position_tolerance = 0.01 # 和主程序默认值相同
|
||
|
||
for i, target_config in enumerate(test_path):
|
||
print(f"\n--- 执行Waypoint {i} (模拟路径执行器插值) ---")
|
||
print(f"目标关节配置: {[f'{x:.3f}' for x in target_config]}")
|
||
|
||
# 计算理论末端位置
|
||
theoretical_pos, _ = arm_controller.forward_kinematics(target_config)
|
||
print(f"理论末端位置: {[f'{x:.3f}' for x in theoretical_pos]}")
|
||
|
||
# === 模拟路径执行器的插值过程 ===
|
||
current_config = arm_controller.get_current_joint_positions()
|
||
|
||
# 计算距离和插值步数
|
||
distance = np.linalg.norm(np.array(target_config) - np.array(current_config))
|
||
move_time = distance / velocity_scaling
|
||
num_steps = int(move_time / timestep) + 1
|
||
|
||
print(f" 插值参数: 距离{distance:.4f}, 移动时间{move_time:.3f}s, 插值步数{num_steps}")
|
||
|
||
# 执行插值
|
||
for step in range(1, num_steps + 1):
|
||
ratio = step / num_steps
|
||
interpolated = (
|
||
np.array(current_config) * (1 - ratio) +
|
||
np.array(target_config) * ratio
|
||
)
|
||
|
||
# 设置插值位置
|
||
arm_controller.set_joint_positions(interpolated.tolist())
|
||
|
||
# 执行仿真步进(关键修复:让PyBullet执行控制命令)
|
||
p.stepSimulation(physicsClientId=physics_client)
|
||
|
||
# 等待timestep时间(和主程序相同)
|
||
time.sleep(timestep)
|
||
|
||
# 记录状态(每10步一次)
|
||
if step % max(1, num_steps // 5) == 0:
|
||
actual_joints = arm_controller.get_current_joint_positions()
|
||
actual_pos, _ = arm_controller.get_current_pose()
|
||
|
||
interp_error = np.linalg.norm(interpolated - np.array(actual_joints))
|
||
pos_error = np.linalg.norm(np.array(theoretical_pos) - np.array(actual_pos))
|
||
|
||
print(f" 插值步{step}/{num_steps}: 插值误差{interp_error:.4f}, 位置误差{pos_error:.4f}")
|
||
|
||
# 最终设置目标位置
|
||
arm_controller.set_joint_positions(target_config)
|
||
|
||
# 等待收敛(和主程序相同的逻辑)
|
||
wait_start = time.time()
|
||
max_wait = max(timestep * 10, move_time * 2)
|
||
final_converged = False
|
||
|
||
while time.time() - wait_start < max_wait:
|
||
final_config = arm_controller.get_current_joint_positions()
|
||
final_distance = np.linalg.norm(np.array(target_config) - np.array(final_config))
|
||
|
||
if final_distance <= position_tolerance:
|
||
final_converged = True
|
||
break
|
||
|
||
# 执行仿真步进让机械臂继续收敛
|
||
p.stepSimulation(physicsClientId=physics_client)
|
||
time.sleep(timestep)
|
||
|
||
# 记录最终实际位置
|
||
final_joints = arm_controller.get_current_joint_positions()
|
||
final_pos, _ = arm_controller.get_current_pose()
|
||
actual_positions.append(final_pos)
|
||
|
||
final_joint_error = np.linalg.norm(np.array(target_config) - np.array(final_joints))
|
||
final_pos_error = np.linalg.norm(np.array(theoretical_pos) - np.array(final_pos))
|
||
|
||
print(f"最终关节位置: {[f'{x:.3f}' for x in final_joints]}")
|
||
print(f"最终末端位置: {[f'{x:.3f}' for x in final_pos]}")
|
||
print(f"最终关节误差: {final_joint_error:.4f}")
|
||
print(f"最终位置误差: {final_pos_error:.4f}")
|
||
|
||
if final_pos_error > 0.02: # 2cm容差
|
||
print(f"⚠️ Waypoint {i} 存在显著位置误差!")
|
||
|
||
print("\n=== 步骤5: 绘制实际执行路径 ===")
|
||
# 绘制实际执行路径
|
||
for i in range(len(actual_positions) - 1):
|
||
line_id = p.addUserDebugLine(
|
||
actual_positions[i],
|
||
actual_positions[i + 1],
|
||
lineColorRGB=[1, 0, 0], # 红色 - 实际路径
|
||
lineWidth=3,
|
||
physicsClientId=physics_client
|
||
)
|
||
line_ids.append(line_id)
|
||
|
||
print("\n=== 步骤6: 对比分析 ===")
|
||
total_theoretical_length = 0
|
||
total_actual_length = 0
|
||
max_waypoint_error = 0
|
||
|
||
for i in range(len(test_path)):
|
||
theoretical_pos = theoretical_positions[i]
|
||
actual_pos = actual_positions[i]
|
||
waypoint_error = np.linalg.norm(np.array(theoretical_pos) - np.array(actual_pos))
|
||
max_waypoint_error = max(max_waypoint_error, waypoint_error)
|
||
|
||
print(f"Waypoint {i}:")
|
||
print(f" 理论: {[f'{x:.3f}' for x in theoretical_pos]}")
|
||
print(f" 实际: {[f'{x:.3f}' for x in actual_pos]}")
|
||
print(f" 误差: {waypoint_error:.4f}m")
|
||
|
||
if i > 0:
|
||
theoretical_segment = np.linalg.norm(
|
||
np.array(theoretical_positions[i]) - np.array(theoretical_positions[i-1])
|
||
)
|
||
actual_segment = np.linalg.norm(
|
||
np.array(actual_positions[i]) - np.array(actual_positions[i-1])
|
||
)
|
||
total_theoretical_length += theoretical_segment
|
||
total_actual_length += actual_segment
|
||
|
||
print(f"\n总结:")
|
||
print(f"理论路径长度: {total_theoretical_length:.4f}m")
|
||
print(f"实际路径长度: {total_actual_length:.4f}m")
|
||
print(f"路径长度差异: {abs(total_theoretical_length - total_actual_length):.4f}m")
|
||
print(f"最大waypoint误差: {max_waypoint_error:.4f}m")
|
||
|
||
if max_waypoint_error > 0.02:
|
||
print("❌ 发现显著的路径执行误差")
|
||
else:
|
||
print("✅ 路径执行精度良好")
|
||
|
||
print(f"\n绿色线条 = 理论路径(基于正向运动学)")
|
||
print(f"红色线条 = 实际执行路径(基于实际机械臂位置)")
|
||
print(f"如果两条线重合,说明路径执行准确")
|
||
|
||
input("按回车键关闭...")
|
||
|
||
except Exception as e:
|
||
print(f"调试过程中发生错误: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
|
||
finally:
|
||
p.disconnect(physicsClientId=physics_client)
|
||
|
||
if __name__ == "__main__":
|
||
debug_path_execution() |