From 19a21ef06728fa1a2ee28079f6517ac9c34de99c Mon Sep 17 00:00:00 2001 From: ZKX34 <3442977470@qq.com> Date: Mon, 22 Dec 2025 14:35:01 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E8=AE=A9=E6=97=A0=E4=BA=BA=E6=9C=BA?= =?UTF-8?q?=E9=A3=9E=E8=B5=B7=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/drone_simulation/MUJOCO_LOG.TXT | 3 + src/drone_simulation/main.py | 474 ++++++++++++++++++++++++++++ src/drone_simulation/model.xml | 164 ++++++++++ 3 files changed, 641 insertions(+) create mode 100644 src/drone_simulation/MUJOCO_LOG.TXT create mode 100644 src/drone_simulation/main.py create mode 100644 src/drone_simulation/model.xml diff --git a/src/drone_simulation/MUJOCO_LOG.TXT b/src/drone_simulation/MUJOCO_LOG.TXT new file mode 100644 index 0000000000..8d7def32c0 --- /dev/null +++ b/src/drone_simulation/MUJOCO_LOG.TXT @@ -0,0 +1,3 @@ +Mon Dec 22 14:33:32 2025 +WARNING: Nan, Inf or huge value in QACC at DOF 0. The simulation is unstable. Time = 0.0450. + diff --git a/src/drone_simulation/main.py b/src/drone_simulation/main.py new file mode 100644 index 0000000000..e13322b3e8 --- /dev/null +++ b/src/drone_simulation/main.py @@ -0,0 +1,474 @@ +""" +MuJoCo 四旋翼无人机仿真 - 修复纹理错误版本 +""" + +import mujoco +import mujoco.viewer +import numpy as np +import time +import math + + +class QuadrotorSimulation: + def __init__(self): + """初始化四旋翼无人机仿真""" + # 使用简化的XML字符串,避免纹理问题 + xml_string = self.create_minimal_quadrotor_xml() + + # 从XML字符串加载模型 + self.model = mujoco.MjModel.from_xml_string(xml_string) + print("✓ 模型加载成功") + + # 创建仿真数据 + self.data = mujoco.MjData(self.model) + + # 获取执行器数量 + self.n_actuators = self.model.nu + print(f"✓ 执行器数量: {self.n_actuators}") + + # 设置初始控制输入 + self.set_initial_control() + + def create_minimal_quadrotor_xml(self): + """创建最简单的四旋翼无人机XML配置""" + xml_string = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + return xml_string + + def set_initial_control(self): + """设置初始控制输入""" + # 设置初始推力 + hover_thrust = 500 # 悬停推力值 + self.data.ctrl[:] = [hover_thrust] * self.n_actuators + + def get_state(self): + """获取无人机状态""" + state = { + 'position': self.data.qpos[0:3].copy(), + 'orientation': self.data.qpos[3:7].copy(), + 'linear_velocity': self.data.qvel[0:3].copy(), + 'angular_velocity': self.data.qvel[3:6].copy(), + 'rotor_angles': self.data.qpos[7:11].copy(), + 'rotor_velocities': self.data.qvel[6:10].copy() + } + return state + + def print_state(self): + """打印无人机状态""" + state = self.get_state() + + print("\n" + "=" * 50) + print("四旋翼无人机状态:") + print("=" * 50) + print(f"位置: [{state['position'][0]:.3f}, {state['position'][1]:.3f}, {state['position'][2]:.3f}] m") + print(f"姿态四元数: [{state['orientation'][0]:.3f}, {state['orientation'][1]:.3f}, " + f"{state['orientation'][2]:.3f}, {state['orientation'][3]:.3f}]") + print(f"线速度: [{state['linear_velocity'][0]:.3f}, {state['linear_velocity'][1]:.3f}, " + f"{state['linear_velocity'][2]:.3f}] m/s") + print(f"角速度: [{state['angular_velocity'][0]:.3f}, {state['angular_velocity'][1]:.3f}, " + f"{state['angular_velocity'][2]:.3f}] rad/s") + print("=" * 50) + + def apply_control(self, ctrl_values): + """应用控制输入""" + if len(ctrl_values) != self.n_actuators: + print(f"⚠ 警告:控制值数量应为{self.n_actuators},使用默认值500") + ctrl_values = [500] * self.n_actuators + + # 应用控制值 + self.data.ctrl[:] = ctrl_values + + def altitude_controller(self, target_z=1.5): + """高度控制器""" + # PID参数 + Kp = 200.0 # 比例增益 + Kd = 50.0 # 微分增益 + + # 获取当前状态 + current_z = self.data.qpos[2] + current_vz = self.data.qvel[2] + + # 计算误差 + error_z = target_z - current_z + error_vz = 0 - current_vz + + # PID控制 + control_input = Kp * error_z + Kd * error_vz + + # 基础推力 + base_thrust = 500 + + # 计算推力 + thrust = base_thrust + control_input + + # 限制推力范围 + thrust = np.clip(thrust, 400, 600) + + # 应用到所有电机 + ctrl_values = [thrust] * self.n_actuators + self.apply_control(ctrl_values) + + return error_z, thrust + + def position_controller(self, target_pos=[0, 0, 1.5]): + """位置控制器""" + # PID参数 + Kp_pos = np.array([100.0, 100.0, 200.0]) + Kd_pos = np.array([30.0, 30.0, 50.0]) + + # 获取当前状态 + current_pos = self.data.qpos[0:3] + current_vel = self.data.qvel[0:3] + + # 计算误差 + pos_error = np.array(target_pos) - current_pos + vel_error = -current_vel + + # 位置控制 + pos_control = Kp_pos * pos_error + Kd_pos * vel_error + + # 基础推力 + base_thrust = 500 + + # 总推力 + total_thrust = base_thrust + pos_control[2] + + # 姿态控制 + roll_control = -pos_control[1] * 0.02 + pitch_control = pos_control[0] * 0.02 + + # 四旋翼混控 + ctrl_values = [ + total_thrust - pitch_control - roll_control, # 前右 + total_thrust - pitch_control + roll_control, # 前左 + total_thrust + pitch_control + roll_control, # 后左 + total_thrust + pitch_control - roll_control # 后右 + ] + + # 限制推力范围 + ctrl_values = np.clip(ctrl_values, 400, 600) + + self.apply_control(ctrl_values) + + return pos_error, ctrl_values + + def run_simulation(self, duration=10.0, use_viewer=True, controller_type="altitude"): + """运行仿真""" + print(f"\n▶ 开始仿真,时长: {duration}秒") + print(f"▶ 控制器类型: {controller_type}") + + if use_viewer: + print("▶ 使用可视化查看器 (按ESC退出)") + else: + print("▶ 无可视化模式") + + # 记录数据 + time_history = [] + height_history = [] + thrust_history = [] + + try: + if use_viewer: + with mujoco.viewer.launch_passive(self.model, self.data) as viewer: + # 设置相机 + viewer.cam.azimuth = 180 + viewer.cam.elevation = -20 + viewer.cam.distance = 5.0 + viewer.cam.lookat[:] = [0.0, 0.0, 1.0] + + self.simulation_loop(viewer, duration, controller_type, + time_history, height_history, thrust_history) + else: + self.simulation_loop(None, duration, controller_type, + time_history, height_history, thrust_history) + + except Exception as e: + print(f"⚠ 仿真错误: {e}") + + # 分析数据 + if time_history: + self.analyze_data(time_history, height_history, thrust_history) + + def simulation_loop(self, viewer, duration, controller_type, + time_history, height_history, thrust_history): + """仿真循环""" + start_time = time.time() + last_print_time = time.time() + step_count = 0 + + while (viewer is None or (viewer and viewer.is_running())) and (time.time() - start_time) < duration: + step_start = time.time() + step_count += 1 + + # 应用控制器 + if controller_type == "position": + # 移动目标点 + t = self.data.time + target_x = 1.0 * math.sin(t * 0.5) + target_y = 1.0 * math.cos(t * 0.5) + target_z = 1.5 + 0.3 * math.sin(t * 0.3) + + pos_error, thrusts = self.position_controller([target_x, target_y, target_z]) + control_info = f"位置误差: [{pos_error[0]:.2f}, {pos_error[1]:.2f}, {pos_error[2]:.2f}] m" + else: + error_z, thrust = self.altitude_controller(1.5) + thrusts = [thrust] * 4 + control_info = f"高度误差: {error_z:.2f} m" + + # 记录数据 + current_time = self.data.time + current_height = self.data.qpos[2] + time_history.append(current_time) + height_history.append(current_height) + thrust_history.append(np.mean(thrusts)) + + # 执行仿真步 + mujoco.mj_step(self.model, self.data) + + # 更新螺旋桨旋转(视觉效果) + rotor_speed = 80.0 + for i in range(4): + self.data.qpos[7 + i] += rotor_speed * self.model.opt.timestep + + # 更新查看器 + if viewer: + viewer.sync() + + # 打印状态信息 + if time.time() - last_print_time > 1.0: + print(f"\n时间: {current_time:.1f}s | 高度: {current_height:.2f}m") + print(f"推力: {np.mean(thrusts):.0f} | {control_info}") + print(f"步数: {step_count}") + last_print_time = time.time() + + # 控制仿真速度 + elapsed = time.time() - step_start + sleep_time = self.model.opt.timestep - elapsed + if sleep_time > 0: + time.sleep(sleep_time) + + def analyze_data(self, time_data, height_data, thrust_data): + """分析仿真数据""" + print("\n" + "=" * 50) + print("📊 仿真数据分析:") + print("=" * 50) + + if not time_data: + print("无数据") + return + + time_array = np.array(time_data) + height_array = np.array(height_data) + thrust_array = np.array(thrust_data) + + print(f"总步数: {len(time_array)}") + print(f"仿真时长: {time_array[-1]:.2f} 秒") + print(f"平均高度: {np.mean(height_array):.3f} m") + print(f"高度稳定性: ±{np.std(height_array):.3f} m") + print(f"高度范围: [{np.min(height_array):.3f}, {np.max(height_array):.3f}] m") + print(f"平均推力: {np.mean(thrust_array):.0f}") + print(f"推力范围: [{np.min(thrust_array):.0f}, {np.max(thrust_array):.0f}]") + + # 询问是否绘图 + try: + plot = input("\n是否绘制图表? (y/n): ").strip().lower() + if plot == 'y': + self.plot_results(time_array, height_array, thrust_array) + except: + pass + + def plot_results(self, time_data, height_data, thrust_data): + """绘制结果图表""" + try: + import matplotlib.pyplot as plt + + fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8)) + + # 高度图 + ax1.plot(time_data, height_data, 'b-', linewidth=2, label='实际高度') + ax1.axhline(y=1.5, color='r', linestyle='--', alpha=0.7, label='目标高度') + ax1.fill_between(time_data, 1.45, 1.55, color='r', alpha=0.1) + ax1.set_xlabel('时间 (秒)') + ax1.set_ylabel('高度 (米)') + ax1.set_title('四旋翼无人机高度控制') + ax1.legend() + ax1.grid(True, alpha=0.3) + + # 推力图 + ax2.plot(time_data, thrust_data, 'g-', linewidth=2, label='平均推力') + ax2.axhline(y=500, color='orange', linestyle='--', alpha=0.7, label='悬停推力') + ax2.set_xlabel('时间 (秒)') + ax2.set_ylabel('推力') + ax2.set_title('电机推力变化') + ax2.legend() + ax2.grid(True, alpha=0.3) + + plt.tight_layout() + plt.show() + + except ImportError: + print("⚠ 需要安装matplotlib: pip install matplotlib") + except Exception as e: + print(f"⚠ 绘图错误: {e}") + + +def main(): + """主函数""" + print("🚁 MuJoCo 四旋翼无人机仿真系统") + print("=" * 50) + + try: + # 创建仿真实例 + print("正在初始化...") + sim = QuadrotorSimulation() + print("✅ 初始化完成") + + # 用户选择 + print("\n请选择仿真模式:") + print("1. 高度控制 (简单)") + print("2. 位置控制 (高级)") + + choice = input("选择 (1/2): ").strip() + controller = "position" if choice == "2" else "altitude" + + # 仿真时长 + try: + duration = float(input("仿真时长(秒,默认10): ") or "10") + except: + duration = 10.0 + + # 是否使用可视化 + viz = input("使用可视化? (y/n,默认y): ").strip().lower() + use_viz = viz != "n" + + # 运行仿真 + sim.run_simulation( + duration=duration, + use_viewer=use_viz, + controller_type=controller + ) + + except KeyboardInterrupt: + print("\n\n⏹ 仿真被用户中断") + except Exception as e: + print(f"\n❌ 错误: {e}") + import traceback + traceback.print_exc() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/drone_simulation/model.xml b/src/drone_simulation/model.xml new file mode 100644 index 0000000000..717a287e45 --- /dev/null +++ b/src/drone_simulation/model.xml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 74ec5e5f15c61f1e26d2b9154bd5ab3d3ce089fb Mon Sep 17 00:00:00 2001 From: ZKX34 <3442977470@qq.com> Date: Mon, 22 Dec 2025 21:18:29 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E5=BB=BA=E7=AB=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=97=A0=E4=BA=BA=E6=9C=BA=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/drone_simulation/main.py | 511 +++++------------------------------ 1 file changed, 62 insertions(+), 449 deletions(-) diff --git a/src/drone_simulation/main.py b/src/drone_simulation/main.py index e13322b3e8..b6804d05bc 100644 --- a/src/drone_simulation/main.py +++ b/src/drone_simulation/main.py @@ -1,473 +1,86 @@ -""" -MuJoCo 四旋翼无人机仿真 - 修复纹理错误版本 -""" - +# simple_visible_drone.py import mujoco import mujoco.viewer import numpy as np import time -import math - - -class QuadrotorSimulation: - def __init__(self): - """初始化四旋翼无人机仿真""" - # 使用简化的XML字符串,避免纹理问题 - xml_string = self.create_minimal_quadrotor_xml() - - # 从XML字符串加载模型 - self.model = mujoco.MjModel.from_xml_string(xml_string) - print("✓ 模型加载成功") - - # 创建仿真数据 - self.data = mujoco.MjData(self.model) - - # 获取执行器数量 - self.n_actuators = self.model.nu - print(f"✓ 执行器数量: {self.n_actuators}") - - # 设置初始控制输入 - self.set_initial_control() - - def create_minimal_quadrotor_xml(self): - """创建最简单的四旋翼无人机XML配置""" - xml_string = """ - - - - - - +# 最小化但可见的模型 +MJCF_MODEL = """ + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + +""" - - - - - - - - - -""" - return xml_string - - def set_initial_control(self): - """设置初始控制输入""" - # 设置初始推力 - hover_thrust = 500 # 悬停推力值 - self.data.ctrl[:] = [hover_thrust] * self.n_actuators - - def get_state(self): - """获取无人机状态""" - state = { - 'position': self.data.qpos[0:3].copy(), - 'orientation': self.data.qpos[3:7].copy(), - 'linear_velocity': self.data.qvel[0:3].copy(), - 'angular_velocity': self.data.qvel[3:6].copy(), - 'rotor_angles': self.data.qpos[7:11].copy(), - 'rotor_velocities': self.data.qvel[6:10].copy() - } - return state - - def print_state(self): - """打印无人机状态""" - state = self.get_state() - - print("\n" + "=" * 50) - print("四旋翼无人机状态:") - print("=" * 50) - print(f"位置: [{state['position'][0]:.3f}, {state['position'][1]:.3f}, {state['position'][2]:.3f}] m") - print(f"姿态四元数: [{state['orientation'][0]:.3f}, {state['orientation'][1]:.3f}, " - f"{state['orientation'][2]:.3f}, {state['orientation'][3]:.3f}]") - print(f"线速度: [{state['linear_velocity'][0]:.3f}, {state['linear_velocity'][1]:.3f}, " - f"{state['linear_velocity'][2]:.3f}] m/s") - print(f"角速度: [{state['angular_velocity'][0]:.3f}, {state['angular_velocity'][1]:.3f}, " - f"{state['angular_velocity'][2]:.3f}] rad/s") - print("=" * 50) - - def apply_control(self, ctrl_values): - """应用控制输入""" - if len(ctrl_values) != self.n_actuators: - print(f"⚠ 警告:控制值数量应为{self.n_actuators},使用默认值500") - ctrl_values = [500] * self.n_actuators - - # 应用控制值 - self.data.ctrl[:] = ctrl_values - - def altitude_controller(self, target_z=1.5): - """高度控制器""" - # PID参数 - Kp = 200.0 # 比例增益 - Kd = 50.0 # 微分增益 - - # 获取当前状态 - current_z = self.data.qpos[2] - current_vz = self.data.qvel[2] - - # 计算误差 - error_z = target_z - current_z - error_vz = 0 - current_vz - - # PID控制 - control_input = Kp * error_z + Kd * error_vz - - # 基础推力 - base_thrust = 500 - - # 计算推力 - thrust = base_thrust + control_input - - # 限制推力范围 - thrust = np.clip(thrust, 400, 600) - - # 应用到所有电机 - ctrl_values = [thrust] * self.n_actuators - self.apply_control(ctrl_values) - - return error_z, thrust - - def position_controller(self, target_pos=[0, 0, 1.5]): - """位置控制器""" - # PID参数 - Kp_pos = np.array([100.0, 100.0, 200.0]) - Kd_pos = np.array([30.0, 30.0, 50.0]) - - # 获取当前状态 - current_pos = self.data.qpos[0:3] - current_vel = self.data.qvel[0:3] - - # 计算误差 - pos_error = np.array(target_pos) - current_pos - vel_error = -current_vel - - # 位置控制 - pos_control = Kp_pos * pos_error + Kd_pos * vel_error - - # 基础推力 - base_thrust = 500 - - # 总推力 - total_thrust = base_thrust + pos_control[2] - - # 姿态控制 - roll_control = -pos_control[1] * 0.02 - pitch_control = pos_control[0] * 0.02 - - # 四旋翼混控 - ctrl_values = [ - total_thrust - pitch_control - roll_control, # 前右 - total_thrust - pitch_control + roll_control, # 前左 - total_thrust + pitch_control + roll_control, # 后左 - total_thrust + pitch_control - roll_control # 后右 - ] - - # 限制推力范围 - ctrl_values = np.clip(ctrl_values, 400, 600) - - self.apply_control(ctrl_values) - - return pos_error, ctrl_values - - def run_simulation(self, duration=10.0, use_viewer=True, controller_type="altitude"): - """运行仿真""" - print(f"\n▶ 开始仿真,时长: {duration}秒") - print(f"▶ 控制器类型: {controller_type}") - - if use_viewer: - print("▶ 使用可视化查看器 (按ESC退出)") - else: - print("▶ 无可视化模式") - - # 记录数据 - time_history = [] - height_history = [] - thrust_history = [] - - try: - if use_viewer: - with mujoco.viewer.launch_passive(self.model, self.data) as viewer: - # 设置相机 - viewer.cam.azimuth = 180 - viewer.cam.elevation = -20 - viewer.cam.distance = 5.0 - viewer.cam.lookat[:] = [0.0, 0.0, 1.0] - - self.simulation_loop(viewer, duration, controller_type, - time_history, height_history, thrust_history) - else: - self.simulation_loop(None, duration, controller_type, - time_history, height_history, thrust_history) - - except Exception as e: - print(f"⚠ 仿真错误: {e}") - - # 分析数据 - if time_history: - self.analyze_data(time_history, height_history, thrust_history) - - def simulation_loop(self, viewer, duration, controller_type, - time_history, height_history, thrust_history): - """仿真循环""" - start_time = time.time() - last_print_time = time.time() - step_count = 0 - - while (viewer is None or (viewer and viewer.is_running())) and (time.time() - start_time) < duration: - step_start = time.time() - step_count += 1 - - # 应用控制器 - if controller_type == "position": - # 移动目标点 - t = self.data.time - target_x = 1.0 * math.sin(t * 0.5) - target_y = 1.0 * math.cos(t * 0.5) - target_z = 1.5 + 0.3 * math.sin(t * 0.3) - - pos_error, thrusts = self.position_controller([target_x, target_y, target_z]) - control_info = f"位置误差: [{pos_error[0]:.2f}, {pos_error[1]:.2f}, {pos_error[2]:.2f}] m" - else: - error_z, thrust = self.altitude_controller(1.5) - thrusts = [thrust] * 4 - control_info = f"高度误差: {error_z:.2f} m" - - # 记录数据 - current_time = self.data.time - current_height = self.data.qpos[2] - time_history.append(current_time) - height_history.append(current_height) - thrust_history.append(np.mean(thrusts)) - - # 执行仿真步 - mujoco.mj_step(self.model, self.data) - - # 更新螺旋桨旋转(视觉效果) - rotor_speed = 80.0 - for i in range(4): - self.data.qpos[7 + i] += rotor_speed * self.model.opt.timestep - - # 更新查看器 - if viewer: - viewer.sync() - - # 打印状态信息 - if time.time() - last_print_time > 1.0: - print(f"\n时间: {current_time:.1f}s | 高度: {current_height:.2f}m") - print(f"推力: {np.mean(thrusts):.0f} | {control_info}") - print(f"步数: {step_count}") - last_print_time = time.time() - - # 控制仿真速度 - elapsed = time.time() - step_start - sleep_time = self.model.opt.timestep - elapsed - if sleep_time > 0: - time.sleep(sleep_time) - - def analyze_data(self, time_data, height_data, thrust_data): - """分析仿真数据""" - print("\n" + "=" * 50) - print("📊 仿真数据分析:") - print("=" * 50) - - if not time_data: - print("无数据") - return - - time_array = np.array(time_data) - height_array = np.array(height_data) - thrust_array = np.array(thrust_data) - - print(f"总步数: {len(time_array)}") - print(f"仿真时长: {time_array[-1]:.2f} 秒") - print(f"平均高度: {np.mean(height_array):.3f} m") - print(f"高度稳定性: ±{np.std(height_array):.3f} m") - print(f"高度范围: [{np.min(height_array):.3f}, {np.max(height_array):.3f}] m") - print(f"平均推力: {np.mean(thrust_array):.0f}") - print(f"推力范围: [{np.min(thrust_array):.0f}, {np.max(thrust_array):.0f}]") - - # 询问是否绘图 - try: - plot = input("\n是否绘制图表? (y/n): ").strip().lower() - if plot == 'y': - self.plot_results(time_array, height_array, thrust_array) - except: - pass - - def plot_results(self, time_data, height_data, thrust_data): - """绘制结果图表""" - try: - import matplotlib.pyplot as plt - - fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8)) - - # 高度图 - ax1.plot(time_data, height_data, 'b-', linewidth=2, label='实际高度') - ax1.axhline(y=1.5, color='r', linestyle='--', alpha=0.7, label='目标高度') - ax1.fill_between(time_data, 1.45, 1.55, color='r', alpha=0.1) - ax1.set_xlabel('时间 (秒)') - ax1.set_ylabel('高度 (米)') - ax1.set_title('四旋翼无人机高度控制') - ax1.legend() - ax1.grid(True, alpha=0.3) - - # 推力图 - ax2.plot(time_data, thrust_data, 'g-', linewidth=2, label='平均推力') - ax2.axhline(y=500, color='orange', linestyle='--', alpha=0.7, label='悬停推力') - ax2.set_xlabel('时间 (秒)') - ax2.set_ylabel('推力') - ax2.set_title('电机推力变化') - ax2.legend() - ax2.grid(True, alpha=0.3) - plt.tight_layout() - plt.show() +def main(): + print("正在启动无人机仿真...") + print("按ESC退出窗口") + print("等待3秒...") - except ImportError: - print("⚠ 需要安装matplotlib: pip install matplotlib") - except Exception as e: - print(f"⚠ 绘图错误: {e}") + # 直接从字符串加载模型 + model = mujoco.MjModel.from_xml_string(MJCF_MODEL) + data = mujoco.MjData(model) + # 等待3秒 + time.sleep(3) -def main(): - """主函数""" - print("🚁 MuJoCo 四旋翼无人机仿真系统") - print("=" * 50) + print("开始飞行演示...") - try: - # 创建仿真实例 - print("正在初始化...") - sim = QuadrotorSimulation() - print("✅ 初始化完成") + with mujoco.viewer.launch_passive(model, data) as viewer: + # 设置相机视角 + viewer.cam.lookat[:] = [0, 0, 1] + viewer.cam.distance = 8.0 + viewer.cam.azimuth = 45 + viewer.cam.elevation = -30 - # 用户选择 - print("\n请选择仿真模式:") - print("1. 高度控制 (简单)") - print("2. 位置控制 (高级)") + t = 0 + while viewer.is_running() and t < 20: # 运行20秒 + # 简单的上下浮动 + force_z = 20 * np.sin(t * 2) + 50 - choice = input("选择 (1/2): ").strip() - controller = "position" if choice == "2" else "altitude" + # 应用力 + data.qfrc_applied[2] = force_z - # 仿真时长 - try: - duration = float(input("仿真时长(秒,默认10): ") or "10") - except: - duration = 10.0 + # 缓慢旋转 + data.qfrc_applied[5] = 5 * np.sin(t * 0.5) - # 是否使用可视化 - viz = input("使用可视化? (y/n,默认y): ").strip().lower() - use_viz = viz != "n" + mujoco.mj_step(model, data) + viewer.sync() - # 运行仿真 - sim.run_simulation( - duration=duration, - use_viewer=use_viz, - controller_type=controller - ) + t += 0.01 + time.sleep(0.01) - except KeyboardInterrupt: - print("\n\n⏹ 仿真被用户中断") - except Exception as e: - print(f"\n❌ 错误: {e}") - import traceback - traceback.print_exc() + print("仿真结束!") if __name__ == "__main__": From 222c8ee2ea82e32b40c19e8a41ecefa40d8f9123 Mon Sep 17 00:00:00 2001 From: ZKX34 <3442977470@qq.com> Date: Tue, 23 Dec 2025 15:18:47 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E5=BB=BA=E7=AB=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=97=A0=E4=BA=BA=E6=9C=BA=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/drone_simulation/main.py | 45 ++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/drone_simulation/main.py b/src/drone_simulation/main.py index b6804d05bc..1976d41ec0 100644 --- a/src/drone_simulation/main.py +++ b/src/drone_simulation/main.py @@ -3,6 +3,36 @@ import mujoco.viewer import numpy as np import time +import os +import glob + +# 定义日志文件路径(可根据实际情况修改) +LOG_FILE_PATHS = [ + "mujoco.log", + "drone_simulation.log", + "./logs/*.log" +] + + +def delete_log_files(): + """删除指定的日志文件(无控制台输出)""" + deleted_count = 0 + for path in LOG_FILE_PATHS: + if "*" in path: + for file in glob.glob(path): + try: + os.remove(file) + deleted_count += 1 + except (FileNotFoundError, PermissionError): + continue + else: + if os.path.exists(path): + try: + os.remove(path) + deleted_count += 1 + except (FileNotFoundError, PermissionError): + continue + # 最小化但可见的模型 MJCF_MODEL = """ @@ -43,19 +73,16 @@ def main(): - print("正在启动无人机仿真...") - print("按ESC退出窗口") - print("等待3秒...") + # 删除日志文件(无输出) + delete_log_files() - # 直接从字符串加载模型 + # 加载模型和数据 model = mujoco.MjModel.from_xml_string(MJCF_MODEL) data = mujoco.MjData(model) - # 等待3秒 + # 等待3秒(无提示) time.sleep(3) - print("开始飞行演示...") - with mujoco.viewer.launch_passive(model, data) as viewer: # 设置相机视角 viewer.cam.lookat[:] = [0, 0, 1] @@ -64,7 +91,7 @@ def main(): viewer.cam.elevation = -30 t = 0 - while viewer.is_running() and t < 20: # 运行20秒 + while viewer.is_running() and t < 20: # 简单的上下浮动 force_z = 20 * np.sin(t * 2) + 50 @@ -80,8 +107,6 @@ def main(): t += 0.01 time.sleep(0.01) - print("仿真结束!") - if __name__ == "__main__": main() \ No newline at end of file From 4fe6a0ee407a35c8b1fd89ec829139aa29fedd84 Mon Sep 17 00:00:00 2001 From: ZKX34 <3442977470@qq.com> Date: Tue, 23 Dec 2025 18:57:12 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E5=BB=BA=E7=AB=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=97=A0=E4=BA=BA=E6=9C=BA=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/drone_simulation/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drone_simulation/main.py b/src/drone_simulation/main.py index 1976d41ec0..2874d4690a 100644 --- a/src/drone_simulation/main.py +++ b/src/drone_simulation/main.py @@ -109,4 +109,5 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() +#qwerty \ No newline at end of file From 4995e85b91407a012f4fcd5962b73ae4fe6c520d Mon Sep 17 00:00:00 2001 From: ZKX34 <3442977470@qq.com> Date: Wed, 24 Dec 2025 20:34:58 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E5=92=8C=E7=94=9F=E6=88=90=E6=96=87=E4=BB=B6=EF=BC=8C=E4=BF=9D?= =?UTF-8?q?=E7=95=99xml=E6=96=87=E4=BB=B6=EF=BC=8C=E6=B7=BB=E5=8A=A0.gitig?= =?UTF-8?q?nore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | Bin 0 -> 382 bytes src/drone_simulation/MUJOCO_LOG.TXT | 3 --- 2 files changed, 3 deletions(-) delete mode 100644 src/drone_simulation/MUJOCO_LOG.TXT diff --git a/.gitignore b/.gitignore index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..357efa4d043105b21b6f83d6e7305adabfbae237 100644 GIT binary patch literal 382 zcmZvY%?`mp6ot~L?(BV zIp^MQ&W!&^B91cFn8Sh#2Qfs@ Date: Wed, 24 Dec 2025 20:35:33 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=B8=85=E7=90=86?= =?UTF-8?q?=EF=BC=9A=E5=88=A0=E9=99=A4TXT/mjb/stl=E6=96=87=E4=BB=B6?= =?UTF-8?q?=EF=BC=8C=E4=BF=9D=E7=95=99xml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | Bin 382 -> 126 bytes requirements.txt | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 357efa4d043105b21b6f83d6e7305adabfbae237..daba3febf6dd5b28f9852df7c8a9a8b1e0ddd154 100644 GIT binary patch delta 73 zcmeyzRA==6pE832!(IQ#Qay$WhFpdm2A=;4K`GP90~L?(BV zIp^MQ&W!&^B91cFn8Sh#2Qfs@=2.3.0 \ No newline at end of file From 3b63f0c2c8e38bb5450ce949e8dd9aa42c66ae73 Mon Sep 17 00:00:00 2001 From: ZKX34 <3442977470@qq.com> Date: Wed, 24 Dec 2025 20:56:14 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E5=BB=BA=E7=AB=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=97=A0=E4=BA=BA=E6=9C=BA=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/drone_simulation/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/drone_simulation/main.py b/src/drone_simulation/main.py index 2874d4690a..78f334b53d 100644 --- a/src/drone_simulation/main.py +++ b/src/drone_simulation/main.py @@ -110,4 +110,3 @@ def main(): if __name__ == "__main__": main() -#qwerty \ No newline at end of file