mi-task/base-move-demo.py

208 lines
7.0 KiB
Python
Raw Normal View History

'''
This demo show the communication interface of MR813 motion control board based on Lcm.
Dependency:
- robot_control_cmd_lcmt.py
- robot_control_response_lcmt.py
'''
import lcm
from threading import Thread, Lock
import sys
import os
import time
import sys
import rclpy
from utils.robot_control_cmd_lcmt import robot_control_cmd_lcmt
from utils.robot_control_response_lcmt import robot_control_response_lcmt
from utils.localization_lcmt import localization_lcmt
# from utils.image_raw import ImageProcessor
from utils.base_msg import BaseMsg
from base_move.turn_degree import turn_degree_v2
from base_move.go_straight import go_straight
2025-08-18 11:06:42 +08:00
from base_move.move_base_hori_line import align_to_horizontal_line
from utils.log_helper import info
pass_marker = True
TIME_SLEEP = 3000
def main():
rclpy.init() # 新增:在主程序中统一初始化 ROS 2 上下文
Ctrl = Robot_Ctrl()
Ctrl.run()
msg = robot_control_cmd_lcmt()
2025-08-18 11:06:42 +08:00
print(msg.pos_des[0],msg.pos_des[1],msg.pos_des[2])
try:
2025-08-18 11:06:42 +08:00
# info("Recovery stand", "info")
Ctrl.base_msg.stand_up()
2025-08-18 11:06:42 +08:00
# Ctrl.base_msg.stop()
# # GO STRAIGHT TEST
align_to_horizontal_line(Ctrl, msg, observe=False, max_attempts=3, detect_func_version=1)
2025-08-18 11:06:42 +08:00
# # TURN TEST
# turn_degree_v2(Ctrl, msg, degree=90)
2025-08-18 11:06:42 +08:00
Ctrl.base_msg.stop()
# msg.mode = 12 # Recovery stand
# msg.gait_id = 0
# msg.life_count += 1
# Ctrl.Send_cmd(msg)
# Ctrl.Wait_finish(12, 0)
# msg.mode = 11
# msg.gait_id = 26
# msg.vel_des = [0.9, 0, 0] # X轴线速度前进
# msg.life_count += 1
# Ctrl.Send_cmd(msg)
# time.sleep(2.5)
except KeyboardInterrupt:
print("\n程序被用户中断")
except Exception as e:
print(f"发生错误: {e}")
finally:
print("正在清理资源...")
Ctrl.quit()
rclpy.shutdown() # 新增:在主程序中统一销毁 ROS 2 上下文
print("程序已退出")
sys.exit()
class Robot_Ctrl(object):
def __init__(self):
self.rec_thread = Thread(target=self.rec_responce)
self.send_thread = Thread(target=self.send_publish)
self.odo_thread = Thread(target=self.rec_responce_o)
self.lc_r = lcm.LCM("udpm://239.255.76.67:7670?ttl=255")
self.lc_s = lcm.LCM("udpm://239.255.76.67:7671?ttl=255")
self.lc_o = lcm.LCM("udpm://239.255.76.67:7667?ttl=255")
self.cmd_msg = robot_control_cmd_lcmt()
self.rec_msg = robot_control_response_lcmt()
self.odo_msg = localization_lcmt()
# self.image_processor = ImageProcessor()
# DEBUG
# self.marker_runner = MarkerRunner(pass_flag=pass_marker)
self.send_lock = Lock()
self.delay_cnt = 0
self.mode_ok = 0
self.gait_ok = 0
self.runing = 1
# 新增: 校准相关变量
self.is_calibrated = False # 是否已完成校准
self.calibration_offset = [0, 0, 0] # 校准偏移量
# 新增: 基础消息
self.base_msg = BaseMsg(self, self.cmd_msg)
def run(self):
self.lc_r.subscribe("robot_control_response", self.msg_handler)
self.lc_o.subscribe("global_to_robot", self.msg_handler_o)
self.send_thread.start()
self.rec_thread.start()
self.odo_thread.start()
# self.image_processor.run()
# self.marker_runner.run()
def msg_handler(self, channel, data):
self.rec_msg = robot_control_response_lcmt().decode(data)
if self.rec_msg.order_process_bar >= 95:
self.mode_ok = self.rec_msg.mode
else:
self.mode_ok = 0
2025-08-18 11:06:42 +08:00
# print("[rec_msg]", self.rec_msg.mode, self.rec_msg.gait_id, self.rec_msg.contact, self.rec_msg.order_process_bar, self.rec_msg.switch_status, self.rec_msg.ori_error, self.rec_msg.footpos_error, self.rec_msg.motor_error)
def msg_handler_o(self, channel, data):
self.odo_msg = localization_lcmt().decode(data)
# 如果尚未校准,记录第一帧数据作为校准基准
if not self.is_calibrated:
self.calibration_offset = self.odo_msg.xyz
self.is_calibrated = True
print(f"校准完成,基准值: {self.calibration_offset}")
# 将接收到的 odo 数据减去校准基准值
self.odo_msg.xyz = [
self.odo_msg.xyz[0] - self.calibration_offset[0],
self.odo_msg.xyz[1] - self.calibration_offset[1],
self.odo_msg.xyz[2] - self.calibration_offset[2]
]
2025-08-18 11:06:42 +08:00
if self.odo_msg.timestamp % 1000 == 0:
2025-08-17 23:10:47 +08:00
print("[odo_msg]", self.odo_msg.xyz, self.odo_msg.rpy, self.odo_msg.vxyz, self.odo_msg.omegaBody, self.odo_msg.vBody)
2025-08-18 11:06:42 +08:00
# print(f"LCM URL: {self.lc_s.get_url()}") # 确认与硬件文档一致
def odo_reset(self):
self.calibration_offset = self.odo_msg.xyz
def rec_responce(self):
while self.runing:
self.lc_r.handle()
time.sleep(0.002)
def rec_responce_o(self):
while self.runing:
self.lc_o.handle()
time.sleep(0.002)
def Wait_finish(self, mode, gait_id):
count = 0
while self.runing and count < 2000: # 10s
if self.mode_ok == mode and self.gait_ok == gait_id:
return True
else:
time.sleep(0.005)
count += 1
print("Wait_finish timeout.")
return False
def send_publish(self):
while self.runing:
self.send_lock.acquire()
if self.delay_cnt > 20: # Heartbeat signal 10HZ
self.lc_s.publish("robot_control_cmd", self.cmd_msg.encode())
self.delay_cnt = 0
self.delay_cnt += 1
self.send_lock.release()
time.sleep(0.005)
def Send_cmd(self, msg):
self.send_lock.acquire()
self.delay_cnt = 50
self.cmd_msg = msg
self.cmd_msg.life_count %= 127
self.send_lock.release()
def place_marker(self, x, y, z, color, observe=False):
return None
"""调用 MarkerRunner 放置标志物"""
if self.marker_runner is None or self.marker_runner.marker_client is None:
print("MarkerRunner 未初始化,无法放置标志物")
return None
try:
response = self.marker_runner.send_request(x, y, z, color, observe=observe)
print(f"放置标志物结果: {response.success}, 消息: {response.message}")
return response
except Exception as e:
print(f"放置标志物时发生异常: {e}")
return None
def quit(self):
self.runing = 0
self.rec_thread.join()
self.send_thread.join()
# self.image_processor.destroy()
# 销毁 MarkerRunner
if hasattr(self, 'marker_runner') and self.marker_runner is not None:
try:
self.marker_runner.destroy()
except Exception as e:
print(f"MarkerRunner 销毁失败: {e}")
# Main function
if __name__ == '__main__':
main()