mi-task/single_test/test_complex_movements.py

435 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
复杂运动测试脚本
测试连续弯道、复杂轨迹、组合运动等高级运动功能
"""
import time
import sys
import os
import math
# 添加父目录到路径以便能够导入utils
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from utils.log_helper import LogHelper, get_logger, section, info, debug, warning, error, success, timing
from base_move.turn_degree import turn_degree_v2
from base_move.go_straight import go_straight
from base_move.go_to_xy import go_to_xy_v2
from base_move.center_on_dual_tracks import center_on_dual_tracks
# 创建日志记录器
logger = get_logger("复杂运动测试")
def test_continuous_curves(ctrl, msg, observe=True):
"""测试连续弯道运动模拟任务2的三重弯道"""
section('测试:连续弯道运动', "复杂运动")
info('开始测试连续弯道运动...', "测试")
try:
# 记录起始位置
start_pos = ctrl.odo_msg.xyz
info(f"起始位置: x={start_pos[0]:.3f}, y={start_pos[1]:.3f}", "位置")
# 模拟任务2的连续弯道参数
# 简化版本的弯道参数
directions = [
# [vel_x, vel_y, vel_z, rpy_x, rpy_y, rpy_z, duration]
(0.4, 0, 0, 0, 0, 0, 1.2), # 直线走一段
(0.45, 0, 0.77, 0, 0, 0, 3.0), # 第一个圆弧(简化时间)
(0.3, 0, 0, 0, 0, 0, 0.4), # 微调路径
(0.4, 0, -0.73, 0, 0, 0, 3.0), # 第二个圆弧
(0.3, 0, 0, 0, 0, 0, 0.5), # 微调路径
(0.4, 0, 0.73, 0, 0, 0, 3.0), # 第三个圆弧
(0.3, 0, 0, 0, 0, 0, 0.5), # 微调路径
]
segment_names = [
"直线段", "左弯1", "直线调整1", "右弯", "直线调整2", "左弯2", "直线调整3"
]
for i, (vel_x, vel_y, vel_z, rpy_x, rpy_y, rpy_z, duration) in enumerate(directions):
info(f"执行第{i+1}段: {segment_names[i]}", "运动")
# 记录段开始位置
segment_start = ctrl.odo_msg.xyz
# 设置运动参数
msg.mode = 11 # Locomotion模式
msg.gait_id = 26 # 自变频步态
msg.vel_des = [vel_x, vel_y, vel_z]
msg.rpy_des = [rpy_x, rpy_y, rpy_z]
msg.duration = 0 # 持续运动
msg.step_height = [0.06, 0.06]
msg.life_count += 1
# 执行运动
start_time = time.time()
while time.time() - start_time < duration:
ctrl.Send_cmd(msg)
time.sleep(0.1)
# 停止运动
msg.vel_des = [0, 0, 0]
msg.rpy_des = [0, 0, 0]
msg.life_count += 1
ctrl.Send_cmd(msg)
# 记录段结束位置
segment_end = ctrl.odo_msg.xyz
segment_distance = ((segment_end[0] - segment_start[0])**2 +
(segment_end[1] - segment_start[1])**2)**0.5
info(f"{i+1}段完成,移动距离: {segment_distance:.3f}", "结果")
# 段间暂停
time.sleep(0.5)
# 记录总结果
end_pos = ctrl.odo_msg.xyz
total_distance = ((end_pos[0] - start_pos[0])**2 + (end_pos[1] - start_pos[1])**2)**0.5
info(f"结束位置: x={end_pos[0]:.3f}, y={end_pos[1]:.3f}", "位置")
info(f"总移动距离: {total_distance:.3f}", "距离")
success("连续弯道运动测试完成", "成功")
except Exception as e:
error(f"连续弯道运动测试失败: {str(e)}", "失败")
finally:
# 确保停止运动
msg.vel_des = [0, 0, 0]
msg.rpy_des = [0, 0, 0]
msg.life_count += 1
ctrl.Send_cmd(msg)
def test_figure_eight_movement(ctrl, msg, observe=True):
"""测试8字形运动"""
section('测试8字形运动', "复杂运动")
info('开始测试8字形运动...', "测试")
try:
# 记录起始位置
start_pos = ctrl.odo_msg.xyz
info(f"起始位置: x={start_pos[0]:.3f}, y={start_pos[1]:.3f}", "位置")
# 8字形运动参数
radius = 1.0 # 圆弧半径
speed = 0.3 # 运动速度
# 第一个圆(顺时针)
info("执行第一个圆(顺时针)", "运动")
msg.mode = 11
msg.gait_id = 26
msg.vel_des = [speed, 0, -0.3] # 右转
msg.duration = 0
msg.step_height = [0.06, 0.06]
msg.life_count += 1
# 执行第一个圆
start_time = time.time()
circle_duration = 2 * math.pi * radius / speed # 计算完成一个圆所需时间
while time.time() - start_time < circle_duration:
ctrl.Send_cmd(msg)
time.sleep(0.1)
# 暂停并记录中间位置
msg.vel_des = [0, 0, 0]
msg.life_count += 1
ctrl.Send_cmd(msg)
time.sleep(1)
mid_pos = ctrl.odo_msg.xyz
info(f"第一个圆完成,中间位置: x={mid_pos[0]:.3f}, y={mid_pos[1]:.3f}", "位置")
# 第二个圆(逆时针)
info("执行第二个圆(逆时针)", "运动")
msg.vel_des = [speed, 0, 0.3] # 左转
msg.life_count += 1
# 执行第二个圆
start_time = time.time()
while time.time() - start_time < circle_duration:
ctrl.Send_cmd(msg)
time.sleep(0.1)
# 停止运动
msg.vel_des = [0, 0, 0]
msg.life_count += 1
ctrl.Send_cmd(msg)
# 记录结束位置
end_pos = ctrl.odo_msg.xyz
total_distance = ((end_pos[0] - start_pos[0])**2 + (end_pos[1] - start_pos[1])**2)**0.5
info(f"结束位置: x={end_pos[0]:.3f}, y={end_pos[1]:.3f}", "位置")
info(f"与起始位置的距离: {total_distance:.3f}", "距离")
success("8字形运动测试完成", "成功")
except Exception as e:
error(f"8字形运动测试失败: {str(e)}", "失败")
finally:
# 确保停止运动
msg.vel_des = [0, 0, 0]
msg.life_count += 1
ctrl.Send_cmd(msg)
def test_spiral_movement(ctrl, msg, observe=True):
"""测试螺旋运动"""
section('测试:螺旋运动', "复杂运动")
info('开始测试螺旋运动...', "测试")
try:
# 记录起始位置
start_pos = ctrl.odo_msg.xyz
info(f"起始位置: x={start_pos[0]:.3f}, y={start_pos[1]:.3f}", "位置")
# 螺旋运动参数
initial_speed = 0.2
max_speed = 0.5
angular_velocity = 0.5
duration = 10 # 总持续时间
info(f"开始螺旋运动,持续{duration}", "运动")
msg.mode = 11
msg.gait_id = 26
msg.duration = 0
msg.step_height = [0.06, 0.06]
start_time = time.time()
while time.time() - start_time < duration:
# 计算当前时间比例
time_ratio = (time.time() - start_time) / duration
# 线性增加前进速度
current_speed = initial_speed + (max_speed - initial_speed) * time_ratio
# 设置运动参数
msg.vel_des = [current_speed, 0, angular_velocity]
msg.life_count += 1
ctrl.Send_cmd(msg)
if observe and int((time.time() - start_time) * 2) % 2 == 0:
current_pos = ctrl.odo_msg.xyz
distance_from_start = ((current_pos[0] - start_pos[0])**2 +
(current_pos[1] - start_pos[1])**2)**0.5
info(f"螺旋运动进行中,当前速度: {current_speed:.2f}m/s, 距起点: {distance_from_start:.3f}m", "状态")
time.sleep(0.1)
# 停止运动
msg.vel_des = [0, 0, 0]
msg.life_count += 1
ctrl.Send_cmd(msg)
# 记录结束位置
end_pos = ctrl.odo_msg.xyz
total_distance = ((end_pos[0] - start_pos[0])**2 + (end_pos[1] - start_pos[1])**2)**0.5
info(f"结束位置: x={end_pos[0]:.3f}, y={end_pos[1]:.3f}", "位置")
info(f"距离起始位置: {total_distance:.3f}", "距离")
success("螺旋运动测试完成", "成功")
except Exception as e:
error(f"螺旋运动测试失败: {str(e)}", "失败")
finally:
# 确保停止运动
msg.vel_des = [0, 0, 0]
msg.life_count += 1
ctrl.Send_cmd(msg)
def test_precision_positioning(ctrl, msg, observe=True):
"""测试精确定位"""
section('测试:精确定位', "复杂运动")
info('开始测试精确定位...', "测试")
try:
# 记录起始位置
start_pos = ctrl.odo_msg.xyz
info(f"起始位置: x={start_pos[0]:.3f}, y={start_pos[1]:.3f}", "位置")
# 定义目标位置序列
target_positions = [
(start_pos[0] + 1.0, start_pos[1] + 0.5), # 右前方
(start_pos[0] + 0.5, start_pos[1] + 1.0), # 前左方
(start_pos[0] - 0.5, start_pos[1] + 0.5), # 左前方
(start_pos[0], start_pos[1]), # 回到起点
]
for i, (target_x, target_y) in enumerate(target_positions, 1):
info(f"移动到目标位置{i}: x={target_x:.3f}, y={target_y:.3f}", "目标")
# 记录移动前位置
before_pos = ctrl.odo_msg.xyz
# 使用精确定位函数
go_to_xy_v2(ctrl, msg, target_x, target_y, speed=0.3, observe=observe)
# 记录移动后位置
after_pos = ctrl.odo_msg.xyz
# 计算定位误差
error_x = abs(after_pos[0] - target_x)
error_y = abs(after_pos[1] - target_y)
total_error = math.sqrt(error_x**2 + error_y**2)
info(f"实际位置: x={after_pos[0]:.3f}, y={after_pos[1]:.3f}", "位置")
info(f"定位误差: x={error_x:.3f}m, y={error_y:.3f}m, 总误差={total_error:.3f}m", "误差")
if total_error < 0.1: # 10cm以内认为精确
success(f"目标位置{i}到达精确", "成功")
else:
warning(f"目标位置{i}定位误差较大", "警告")
# 暂停
time.sleep(1)
# 最终位置检查
final_pos = ctrl.odo_msg.xyz
return_error = ((final_pos[0] - start_pos[0])**2 + (final_pos[1] - start_pos[1])**2)**0.5
info(f"最终位置: x={final_pos[0]:.3f}, y={final_pos[1]:.3f}", "位置")
info(f"回到起点误差: {return_error:.3f}", "误差")
success("精确定位测试完成", "成功")
except Exception as e:
error(f"精确定位测试失败: {str(e)}", "失败")
def test_dual_track_following(ctrl, msg, observe=True):
"""测试双轨道跟随"""
section('测试:双轨道跟随', "复杂运动")
info('开始测试双轨道跟随...', "测试")
try:
# 记录起始位置
start_pos = ctrl.odo_msg.xyz
info(f"起始位置: x={start_pos[0]:.3f}, y={start_pos[1]:.3f}", "位置")
# 测试双轨道居中
info("执行双轨道居中校准", "校准")
center_on_dual_tracks(ctrl, msg, max_deviation=10.0, observe=observe, detect_height=0.3)
# 记录校准后位置
centered_pos = ctrl.odo_msg.xyz
lateral_adjustment = abs(centered_pos[1] - start_pos[1])
info(f"校准后位置: x={centered_pos[0]:.3f}, y={centered_pos[1]:.3f}", "位置")
info(f"横向调整距离: {lateral_adjustment:.3f}", "调整")
# 沿轨道直线移动
info("沿双轨道直线移动", "移动")
go_straight(ctrl, msg, distance=2.0, speed=0.5, observe=observe)
# 记录移动后位置
moved_pos = ctrl.odo_msg.xyz
distance_moved = ((moved_pos[0] - centered_pos[0])**2 + (moved_pos[1] - centered_pos[1])**2)**0.5
info(f"移动后位置: x={moved_pos[0]:.3f}, y={moved_pos[1]:.3f}", "位置")
info(f"移动距离: {distance_moved:.3f}", "距离")
# 再次校准检查
info("再次执行双轨道居中校准", "校准")
center_on_dual_tracks(ctrl, msg, max_deviation=10.0, observe=observe, detect_height=0.3)
# 记录最终位置
final_pos = ctrl.odo_msg.xyz
final_adjustment = abs(final_pos[1] - moved_pos[1])
info(f"最终位置: x={final_pos[0]:.3f}, y={final_pos[1]:.3f}", "位置")
info(f"最终横向调整: {final_adjustment:.3f}", "调整")
success("双轨道跟随测试完成", "成功")
except Exception as e:
error(f"双轨道跟随测试失败: {str(e)}", "失败")
def test_combined_movements(ctrl, msg, observe=True):
"""测试组合运动"""
section('测试:组合运动', "复杂运动")
info('开始测试组合运动...', "测试")
try:
# 记录起始位置
start_pos = ctrl.odo_msg.xyz
info(f"起始位置: x={start_pos[0]:.3f}, y={start_pos[1]:.3f}", "位置")
# 组合运动序列
movements = [
("直线前进", lambda: go_straight(ctrl, msg, distance=1.0, speed=0.5, observe=observe)),
("右转90度", lambda: turn_degree_v2(ctrl, msg, degree=90, absolute=False, precision=True)),
("直线前进", lambda: go_straight(ctrl, msg, distance=1.0, speed=0.5, observe=observe)),
("左转180度", lambda: turn_degree_v2(ctrl, msg, degree=-180, absolute=False, precision=True)),
("直线前进", lambda: go_straight(ctrl, msg, distance=1.0, speed=0.5, observe=observe)),
("右转90度", lambda: turn_degree_v2(ctrl, msg, degree=90, absolute=False, precision=True)),
("返回起点", lambda: go_straight(ctrl, msg, distance=1.0, speed=0.5, observe=observe)),
]
for i, (movement_name, movement_func) in enumerate(movements, 1):
info(f"执行第{i}个动作: {movement_name}", "动作")
# 记录动作前位置
before_pos = ctrl.odo_msg.xyz
before_angle = ctrl.odo_msg.rpy[2]
# 执行动作
movement_func()
# 记录动作后位置
after_pos = ctrl.odo_msg.xyz
after_angle = ctrl.odo_msg.rpy[2]
# 计算变化
distance_change = ((after_pos[0] - before_pos[0])**2 + (after_pos[1] - before_pos[1])**2)**0.5
angle_change = after_angle - before_angle
info(f"动作完成,位移: {distance_change:.3f}m, 角度变化: {angle_change:.2f}°", "结果")
# 动作间暂停
time.sleep(0.5)
# 检查最终位置
final_pos = ctrl.odo_msg.xyz
final_angle = ctrl.odo_msg.rpy[2]
return_error = ((final_pos[0] - start_pos[0])**2 + (final_pos[1] - start_pos[1])**2)**0.5
info(f"最终位置: x={final_pos[0]:.3f}, y={final_pos[1]:.3f}, 角度: {final_angle:.2f}°", "位置")
info(f"回到起点误差: {return_error:.3f}", "误差")
if return_error < 0.2: # 20cm以内认为成功
success("组合运动测试成功完成", "成功")
else:
warning("组合运动测试完成,但回到起点误差较大", "警告")
except Exception as e:
error(f"组合运动测试失败: {str(e)}", "失败")
def run_complex_movement_tests(ctrl, msg):
"""运行所有复杂运动测试"""
section('复杂运动测试套件', "开始")
tests = [
("连续弯道运动", test_continuous_curves),
("8字形运动", test_figure_eight_movement),
("螺旋运动", test_spiral_movement),
("精确定位", test_precision_positioning),
("双轨道跟随", test_dual_track_following),
("组合运动", test_combined_movements),
]
for test_name, test_func in tests:
try:
info(f"开始执行测试: {test_name}", "测试")
test_func(ctrl, msg)
success(f"测试 {test_name} 成功完成", "成功")
except Exception as e:
error(f"测试 {test_name} 失败: {str(e)}", "失败")
# 每个测试之间暂停
time.sleep(3)
success("所有复杂运动测试完成", "完成")
if __name__ == "__main__":
# 这里可以添加独立运行的代码
print("复杂运动测试脚本")
print("使用方法:")
print("from single_test.test_complex_movements import run_complex_movement_tests")
print("run_complex_movement_tests(ctrl, msg)")