mi-task/task_2/crawl_gait.py
2025-08-21 11:40:12 +08:00

397 lines
14 KiB
Python
Executable File
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.

import time
import sys
import os
import toml
import copy
import math
import lcm
# 添加父目录到路径以便能够导入utils
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# 添加当前目录到路径确保可以找到local文件
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from utils.log_helper import LogHelper, get_logger, section, info, debug, warning, error, success, timing
from file_send_lcmt import file_send_lcmt
# 创建本模块特定的日志记录器
logger = get_logger("低头匍匐步态")
observe = False
robot_cmd = {
'mode':0, 'gait_id':0, 'contact':0, 'life_count':0,
'vel_des':[0.0, 0.0, 0.0],
'rpy_des':[0.0, 0.0, 0.0],
'pos_des':[0.0, 0.0, 0.0],
'acc_des':[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
'ctrl_point':[0.0, 0.0, 0.0],
'foot_pose':[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
'step_height':[0.0, 0.0],
'value':0, 'duration':0
}
def low_crawl_gait(ctrl, msg, step_count=8, observe=True):
"""
执行低头匍匐前进步态
参数:
ctrl: Robot_Ctrl对象包含机器人控制接口
msg: 控制消息对象
step_count: 执行的步数默认8步两个完整步态周期
observe: 是否输出调试信息
返回:
bool: 是否成功执行步态
"""
if observe:
section('开始执行低头匍匐前进步态', "步态控制")
info(f"计划执行 {step_count} 步的匍匐步态", "步态")
# 切换步态
usergait_msg = file_send_lcmt()
try:
# 加载步态参数
gait_def_path = "./task_2/Gait_Def_crawl.toml"
gait_params_path = "./task_2/Gait_Params_crawl.toml"
if observe:
info(f"加载步态定义文件: {gait_def_path}", "加载")
info(f"加载步态参数文件: {gait_params_path}", "加载")
# 检查文件是否存在
if not os.path.exists(gait_def_path):
error(f"步态定义文件不存在: {gait_def_path}", "错误")
return False
if not os.path.exists(gait_params_path):
error(f"步态参数文件不存在: {gait_params_path}", "错误")
return False
# 加载步态参数
steps = toml.load(gait_params_path)
full_steps = {'step':[robot_cmd]}
k = 0
if observe:
info(f"成功加载 {len(steps['step'])} 个步态参数", "加载")
# 处理每个步态参数
for i in steps['step']:
cmd = copy.deepcopy(robot_cmd)
cmd['duration'] = i['duration']
if i['type'] == 'usergait':
cmd['mode'] = 11 # LOCOMOTION
cmd['gait_id'] = 110 # USERGAIT
cmd['vel_des'] = i['body_vel_des']
cmd['rpy_des'] = i['body_pos_des'][0:3] # roll, pitch, yaw
cmd['pos_des'] = i['body_pos_des'][3:6] # x, y, z
cmd['foot_pose'][0:2] = i['landing_pos_des'][0:2] # FR
cmd['foot_pose'][2:4] = i['landing_pos_des'][3:5] # FL
cmd['foot_pose'][4:6] = i['landing_pos_des'][6:8] # RR
cmd['ctrl_point'][0:2] = i['landing_pos_des'][9:11] # RL
# 步高编码(前两腿和后两腿)
cmd['step_height'][0] = math.ceil(i['step_height'][0] * 1e3) + math.ceil(i['step_height'][1] * 1e3) * 1e3
cmd['step_height'][1] = math.ceil(i['step_height'][2] * 1e3) + math.ceil(i['step_height'][3] * 1e3) * 1e3
cmd['acc_des'] = i['weight']
cmd['value'] = i['use_mpc_traj']
cmd['contact'] = math.floor(i['landing_gain'] * 1e1)
cmd['ctrl_point'][2] = i['mu']
if k == 0:
full_steps['step'] = [cmd]
else:
full_steps['step'].append(cmd)
k = k + 1
# 生成完整的步态参数文件
full_params_path = "./task_2/Gait_Params_crawl_full.toml"
with open(full_params_path, 'w') as f:
f.write("# Gait Params for Low Crawl - Generated\n")
f.writelines(toml.dumps(full_steps))
if observe:
info(f"生成完整步态参数文件: {full_params_path}", "生成")
# 发送步态定义文件
with open(gait_def_path, 'r') as file_obj_gait_def:
usergait_msg.data = file_obj_gait_def.read()
ctrl.lc_s.publish("user_gait_file", usergait_msg.encode())
if observe:
debug("发送步态定义文件到机器人", "发送")
time.sleep(0.5)
# 发送步态参数文件
with open(full_params_path, 'r') as file_obj_gait_params:
usergait_msg.data = file_obj_gait_params.read()
ctrl.lc_s.publish("user_gait_file", usergait_msg.encode())
if observe:
debug("发送步态参数文件到机器人", "发送")
time.sleep(0.1)
# 开始执行步态
msg.mode = 62 # 自定义步态模式
msg.value = 0
msg.contact = 15
msg.gait_id = 110 # USERGAIT ID
msg.duration = 1000
msg.life_count += 1
if observe:
info("开始执行低头匍匐步态...", "执行")
# 执行指定步数的步态
for i in range(step_count):
if observe:
debug(f"执行第 {i+1}/{step_count}", "步态")
ctrl.Send_cmd(msg)
time.sleep(1) # 每步间隔1秒
# 检查机器人状态(可选)
if hasattr(ctrl, 'odo_msg'):
current_pos = ctrl.odo_msg.xyz
current_rpy = ctrl.odo_msg.rpy
if observe and i % 2 == 0: # 每两步输出一次状态
debug(f"当前位置: x={current_pos[0]:.3f}, y={current_pos[1]:.3f}, z={current_pos[2]:.3f}", "状态")
debug(f"当前姿态: roll={math.degrees(current_rpy[0]):.1f}°, pitch={math.degrees(current_rpy[1]):.1f}°, yaw={math.degrees(current_rpy[2]):.1f}°", "状态")
if observe:
success(f"成功完成 {step_count} 步低头匍匐步态", "完成")
return True
except KeyboardInterrupt:
warning("用户中断,停止步态执行", "中断")
# 安全停止
msg.mode = 7 # PureDamper模式
msg.gait_id = 0
msg.duration = 0
msg.life_count += 1
ctrl.Send_cmd(msg)
return False
except Exception as e:
error(f"执行低头匍匐步态时发生错误: {str(e)}", "错误")
# 安全停止
try:
msg.mode = 7 # PureDamper模式
msg.gait_id = 0
msg.duration = 0
msg.life_count += 1
ctrl.Send_cmd(msg)
except:
pass
return False
def transition_to_crawl_position(ctrl, msg, observe=True):
"""
过渡到匍匐准备姿态
参数:
ctrl: Robot_Ctrl对象
msg: 控制消息对象
observe: 是否输出调试信息
返回:
bool: 是否成功过渡到匍匐姿态
"""
if observe:
section('过渡到低头匍匐准备姿态', "姿态调整")
try:
# 第一阶段:降低身体高度
if observe:
info("第一阶段:降低身体高度到匍匐位置", "调整")
msg.mode = 11 # Locomotion模式
msg.gait_id = 26 # 自变频步态
msg.vel_des = [0.0, 0.0, 0.0] # 停止移动
msg.rpy_des = [0.0, 0.15, 0.0] # 稍微抬头,为后续低头做准备
msg.pos_des = [0.0, 0.0, -0.03] # 身体降低3cm
msg.step_height = [0.04, 0.04] # 较低的步高
msg.duration = 0
msg.life_count += 1
ctrl.Send_cmd(msg)
time.sleep(2.0) # 等待姿态调整
# 第二阶段:调整到低头姿态
if observe:
info("第二阶段:调整到低头匍匐姿态", "调整")
msg.rpy_des = [0.0, 0.25, 0.0] # 低头姿态pitch角约14度
msg.pos_des = [0.0, 0.0, -0.05] # 身体再降低2cm
msg.life_count += 1
ctrl.Send_cmd(msg)
time.sleep(2.0)
# 第三阶段:稳定在匍匐姿态
if observe:
info("第三阶段:稳定在低头匍匐姿态", "调整")
msg.vel_des = [0.0, 0.0, 0.0] # 确保静止
msg.life_count += 1
ctrl.Send_cmd(msg)
time.sleep(1.0)
if observe:
success("成功过渡到低头匍匐准备姿态", "完成")
return True
except Exception as e:
error(f"过渡到匍匐姿态时发生错误: {str(e)}", "错误")
return False
def recover_from_crawl_position(ctrl, msg, observe=True):
"""
从匍匐姿态恢复到正常站立姿态
参数:
ctrl: Robot_Ctrl对象
msg: 控制消息对象
observe: 是否输出调试信息
返回:
bool: 是否成功恢复到正常姿态
"""
if observe:
section('从低头匍匐姿态恢复到正常站立', "姿态恢复")
try:
# 第一阶段:停止当前步态
if observe:
info("第一阶段:停止当前步态", "停止")
msg.mode = 7 # PureDamper模式安全停止
msg.gait_id = 0
msg.duration = 0
msg.life_count += 1
ctrl.Send_cmd(msg)
time.sleep(1.0)
# 第二阶段:逐渐抬高身体
if observe:
info("第二阶段:逐渐抬高身体", "恢复")
msg.mode = 11 # Locomotion模式
msg.gait_id = 26 # 自变频步态
msg.vel_des = [0.0, 0.0, 0.0]
msg.rpy_des = [0.0, 0.1, 0.0] # 减少低头角度
msg.pos_des = [0.0, 0.0, -0.02] # 稍微抬高
msg.step_height = [0.06, 0.06]
msg.duration = 0
msg.life_count += 1
ctrl.Send_cmd(msg)
time.sleep(2.0)
# 第三阶段:恢复到正常姿态
if observe:
info("第三阶段:恢复到正常站立姿态", "恢复")
msg.rpy_des = [0.0, 0.0, 0.0] # 恢复正常头部姿态
msg.pos_des = [0.0, 0.0, 0.0] # 恢复正常身体高度
msg.step_height = [0.08, 0.08] # 正常步高
msg.life_count += 1
ctrl.Send_cmd(msg)
time.sleep(2.0)
# 最终稳定
msg.life_count += 1
ctrl.Send_cmd(msg)
time.sleep(1.0)
if observe:
success("成功恢复到正常站立姿态", "完成")
return True
except Exception as e:
error(f"恢复姿态时发生错误: {str(e)}", "错误")
return False
def run_low_crawl_demo(ctrl, msg, distance=2.0, observe=True):
"""
运行完整的低头匍匐前进演示
参数:
ctrl: Robot_Ctrl对象
msg: 控制消息对象
distance: 期望的前进距离(米)
observe: 是否输出调试信息
返回:
bool: 是否成功完成演示
"""
if observe:
section('开始低头匍匐前进演示', "演示")
info(f"目标前进距离: {distance}", "目标")
try:
# 记录起始位置
start_position = None
if hasattr(ctrl, 'odo_msg'):
start_position = list(ctrl.odo_msg.xyz)
if observe:
info(f"起始位置: x={start_position[0]:.3f}, y={start_position[1]:.3f}, z={start_position[2]:.3f}", "位置")
# 步骤1: 过渡到匍匐姿态
success_transition = transition_to_crawl_position(ctrl, msg, observe)
if not success_transition:
error("过渡到匍匐姿态失败", "错误")
return False
# 步骤2: 估算需要的步数每步大约前进0.1-0.15米)
estimated_steps = max(4, int(distance / 0.12)) # 每步约12cm
if observe:
info(f"估算需要执行 {estimated_steps} 步来完成 {distance} 米的移动", "计划")
# 步骤3: 执行匍匐步态
success_crawl = low_crawl_gait(ctrl, msg, step_count=estimated_steps, observe=observe)
if not success_crawl:
error("执行匍匐步态失败", "错误")
# 尝试恢复姿态
recover_from_crawl_position(ctrl, msg, observe)
return False
# 步骤4: 恢复到正常姿态
success_recovery = recover_from_crawl_position(ctrl, msg, observe)
if not success_recovery:
error("恢复正常姿态失败", "错误")
return False
# 计算实际移动距离
if start_position and hasattr(ctrl, 'odo_msg'):
end_position = list(ctrl.odo_msg.xyz)
actual_distance = math.sqrt(
(end_position[0] - start_position[0])**2 +
(end_position[1] - start_position[1])**2
)
if observe:
success(f"演示完成!实际前进距离: {actual_distance:.3f}", "完成")
info(f"结束位置: x={end_position[0]:.3f}, y={end_position[1]:.3f}, z={end_position[2]:.3f}", "位置")
else:
if observe:
success("低头匍匐前进演示完成!", "完成")
return True
except Exception as e:
error(f"执行匍匐演示时发生错误: {str(e)}", "错误")
# 尝试安全恢复
try:
recover_from_crawl_position(ctrl, msg, observe)
except:
pass
return False