From e1ea0231cdaf844ccd7ae93f065ecdc06c0d14e9 Mon Sep 17 00:00:00 2001
From: XYTX0427 <481177497@qq.com>
Date: Fri, 19 Dec 2025 18:41:14 +0800
Subject: [PATCH 01/21] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E9=80=89=E9=A2=98?=
=?UTF-8?q?=E5=92=8C=E8=AF=95=E9=AA=8C=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/Unmanned_aerial_vehicle/README.md | 1 +
src/Unmanned_aerial_vehicle/main.py | 102 ++++++++++++++++++++++++++
2 files changed, 103 insertions(+)
create mode 100644 src/Unmanned_aerial_vehicle/README.md
create mode 100644 src/Unmanned_aerial_vehicle/main.py
diff --git a/src/Unmanned_aerial_vehicle/README.md b/src/Unmanned_aerial_vehicle/README.md
new file mode 100644
index 0000000000..b00460f809
--- /dev/null
+++ b/src/Unmanned_aerial_vehicle/README.md
@@ -0,0 +1 @@
+无人机导航
\ No newline at end of file
diff --git a/src/Unmanned_aerial_vehicle/main.py b/src/Unmanned_aerial_vehicle/main.py
new file mode 100644
index 0000000000..04a5ea1fdf
--- /dev/null
+++ b/src/Unmanned_aerial_vehicle/main.py
@@ -0,0 +1,102 @@
+import math
+
+# 定义无人机类,包含核心导航属性和方法
+class UAVAutonomousNavigation:
+ def __init__(self):
+ # 无人机当前位置(经纬度,单位:度)
+ self.current_lon = 0.0
+ self.current_lat = 0.0
+ # 预设航点列表(格式:[纬度, 经度])
+ self.waypoints = []
+ # 当前目标航点索引
+ self.target_waypoint_idx = 0
+ # 到达航点的判定阈值(米)
+ self.arrival_threshold = 5.0
+
+ def set_waypoints(self, waypoints_list):
+ """设置航点列表"""
+ self.waypoints = waypoints_list
+ self.target_waypoint_idx = 0 # 重置目标航点索引
+
+ def calculate_distance(self, lat1, lon1, lat2, lon2):
+ """
+ 计算两个经纬度点之间的地面距离(米)
+ 采用简化的Haversine公式,适用于近距离无人机导航
+ """
+ # 地球半径(米)
+ R = 6371000.0
+ # 角度转弧度
+ lat1_rad = math.radians(lat1)
+ lon1_rad = math.radians(lon1)
+ lat2_rad = math.radians(lat2)
+ lon2_rad = math.radians(lon2)
+
+ # 经纬度差值
+ dlat = lat2_rad - lat1_rad
+ dlon = lon2_rad - lon1_rad
+
+ # Haversine公式
+ a = math.sin(dlat / 2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon / 2)**2
+ c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
+ distance = R * c
+ return distance
+
+ def update_position(self, new_lat, new_lon):
+ """更新无人机当前位置"""
+ self.current_lat = new_lat
+ self.current_lon = new_lon
+
+ def navigate_to_waypoints(self):
+ """核心航点导航逻辑:依次飞向每个航点"""
+ if not self.waypoints:
+ print("未设置航点,导航终止")
+ return
+
+ # 获取当前目标航点
+ target_lat, target_lon = self.waypoints[self.target_waypoint_idx]
+ # 计算当前位置到目标航点的距离
+ distance_to_target = self.calculate_distance(
+ self.current_lat, self.current_lon, target_lat, target_lon
+ )
+
+ print(f"当前位置:({self.current_lat:.6f}, {self.current_lon:.6f})")
+ print(f"目标航点{self.target_waypoint_idx+1}:({target_lat:.6f}, {target_lon:.6f})")
+ print(f"到目标航点距离:{distance_to_target:.2f} 米")
+
+ # 判断是否到达当前航点
+ if distance_to_target < self.arrival_threshold:
+ print(f"已到达航点{self.target_waypoint_idx+1}")
+ self.target_waypoint_idx += 1
+ # 判断是否完成所有航点
+ if self.target_waypoint_idx >= len(self.waypoints):
+ print("所有航点导航完成!")
+ return
+ else:
+ print(f"切换至下一个航点:{self.target_waypoint_idx+1}")
+ else:
+ # 生成飞行指令(实际场景中需替换为无人机SDK的飞行控制指令)
+ print("飞行指令:向目标航点飞行(航向/速度需根据实际SDK调整)")
+
+# ------------------- 测试代码 -------------------
+if __name__ == "__main__":
+ # 初始化无人机导航实例
+ uav = UAVAutonomousNavigation()
+
+ # 设置航点列表(示例:3个航点)
+ waypoints = [
+ (39.908823, 116.397470), # 航点1:天安门
+ (39.997563, 116.337624), # 航点2:颐和园
+ (40.006401, 116.397029) # 航点3:圆明园
+ ]
+ uav.set_waypoints(waypoints)
+
+ # 模拟无人机位置更新与导航过程
+ # 模拟初始位置(靠近航点1)
+ uav.update_position(39.908820, 116.397468)
+ uav.navigate_to_waypoints()
+
+ print("-" * 50)
+
+ # 模拟无人机飞到航点1,更新位置
+ uav.update_position(39.908823, 116.397470)
+ uav.navigate_to_waypoints()
\ No newline at end of file
From ab1c3922464162dfa8852344f928dcd1edeb908b Mon Sep 17 00:00:00 2001
From: XYTX0427 <481177497@qq.com>
Date: Fri, 19 Dec 2025 20:59:18 +0800
Subject: [PATCH 02/21] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E9=80=89=E9=A2=98?=
=?UTF-8?q?=E5=92=8C=E8=AF=95=E9=AA=8C=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/Unmanned_aerial_vehicle/main.py | 295 ++++++++++++++++++++--------
1 file changed, 218 insertions(+), 77 deletions(-)
diff --git a/src/Unmanned_aerial_vehicle/main.py b/src/Unmanned_aerial_vehicle/main.py
index 04a5ea1fdf..3529838050 100644
--- a/src/Unmanned_aerial_vehicle/main.py
+++ b/src/Unmanned_aerial_vehicle/main.py
@@ -1,102 +1,243 @@
import math
+import logging
+from typing import List, Tuple
+
+# 配置日志,方便追踪运行状态
+logging.basicConfig(
+ level=logging.INFO,
+ format="%(asctime)s - %(levelname)s - %(message)s",
+ datefmt="%Y-%m-%d %H:%M:%S"
+)
+logger = logging.getLogger("UAV_Autonomous_Navigation")
+
-# 定义无人机类,包含核心导航属性和方法
class UAVAutonomousNavigation:
+ """无人机自主导航核心类:整合航点导航、避障、路径重规划"""
+
def __init__(self):
- # 无人机当前位置(经纬度,单位:度)
- self.current_lon = 0.0
- self.current_lat = 0.0
- # 预设航点列表(格式:[纬度, 经度])
- self.waypoints = []
- # 当前目标航点索引
- self.target_waypoint_idx = 0
- # 到达航点的判定阈值(米)
- self.arrival_threshold = 5.0
-
- def set_waypoints(self, waypoints_list):
- """设置航点列表"""
- self.waypoints = waypoints_list
- self.target_waypoint_idx = 0 # 重置目标航点索引
-
- def calculate_distance(self, lat1, lon1, lat2, lon2):
+ # 无人机核心状态
+ self.current_position: Tuple[float, float] = (0.0, 0.0) # (纬度, 经度)
+ self.current_heading: float = 0.0 # 当前航向(度,0=正北,90=正东)
+ self.safety_distance: float = 10.0 # 避障安全距离(米)
+ self.arrival_threshold: float = 5.0 # 到达航点判定阈值(米)
+
+ # 导航相关参数
+ self.waypoints: List[Tuple[float, float]] = [] # 航点列表
+ self.target_waypoint_idx: int = 0 # 当前目标航点索引
+ self.is_obstacle_detected: bool = False # 是否检测到障碍物
+ self.obstacle_distance: float = float("inf") # 障碍物距离(初始为无穷大)
+
+ def set_waypoints(self, waypoints: List[Tuple[float, float]]) -> None:
"""
- 计算两个经纬度点之间的地面距离(米)
- 采用简化的Haversine公式,适用于近距离无人机导航
+ 设置航点列表,增加输入验证
+ :param waypoints: 航点列表,每个元素为(纬度, 经度)元组
"""
- # 地球半径(米)
- R = 6371000.0
- # 角度转弧度
- lat1_rad = math.radians(lat1)
- lon1_rad = math.radians(lon1)
- lat2_rad = math.radians(lat2)
- lon2_rad = math.radians(lon2)
-
- # 经纬度差值
- dlat = lat2_rad - lat1_rad
- dlon = lon2_rad - lon1_rad
-
- # Haversine公式
- a = math.sin(dlat / 2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon / 2)**2
- c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
- distance = R * c
- return distance
-
- def update_position(self, new_lat, new_lon):
- """更新无人机当前位置"""
- self.current_lat = new_lat
- self.current_lon = new_lon
-
- def navigate_to_waypoints(self):
- """核心航点导航逻辑:依次飞向每个航点"""
- if not self.waypoints:
- print("未设置航点,导航终止")
+ try:
+ # 验证航点格式
+ if not isinstance(waypoints, list):
+ raise TypeError("航点必须为列表格式")
+ for idx, wp in enumerate(waypoints):
+ if not isinstance(wp, tuple) or len(wp) != 2:
+ raise ValueError(f"第{idx + 1}个航点格式错误,需为(纬度, 经度)元组")
+ if not (isinstance(wp[0], (int, float)) and isinstance(wp[1], (int, float))):
+ raise ValueError(f"第{idx + 1}个航点经纬度必须为数字")
+
+ self.waypoints = waypoints
+ self.target_waypoint_idx = 0
+ logger.info(f"成功设置{len(waypoints)}个航点,初始目标航点:1")
+
+ except (TypeError, ValueError) as e:
+ logger.error(f"设置航点失败:{str(e)}")
+ raise
+
+ def calculate_haversine_distance(self, lat1: float, lon1: float, lat2: float, lon2: float) -> float:
+ """
+ 高精度计算两个经纬度点的地面距离(米)
+ 采用Haversine公式,适配无人机低空导航场景
+ """
+ try:
+ # 地球平均半径(米)
+ EARTH_RADIUS = 6371008.8
+ # 角度转弧度
+ lat1_rad = math.radians(lat1)
+ lon1_rad = math.radians(lon1)
+ lat2_rad = math.radians(lat2)
+ lon2_rad = math.radians(lon2)
+
+ # 经纬度差值
+ dlat = lat2_rad - lat1_rad
+ dlon = lon2_rad - lon1_rad
+
+ # Haversine核心计算
+ a = math.sin(dlat / 2) ** 2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon / 2) ** 2
+ c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
+ distance = EARTH_RADIUS * c
+
+ logger.debug(f"距离计算:({lat1:.6f},{lon1:.6f}) → ({lat2:.6f},{lon2:.6f}) = {distance:.2f}米")
+ return distance
+
+ except Exception as e:
+ logger.error(f"距离计算失败:{str(e)}")
+ raise
+
+ def update_uav_state(self, new_lat: float, new_lon: float, new_heading: float = None) -> None:
+ """
+ 更新无人机位置和航向
+ :param new_lat: 新纬度
+ :param new_lon: 新经度
+ :param new_heading: 新航向(可选,不传入则保持原航向)
+ """
+ try:
+ if not (isinstance(new_lat, (int, float)) and isinstance(new_lon, (int, float))):
+ raise ValueError("经纬度必须为数字")
+
+ self.current_position = (new_lat, new_lon)
+ if new_heading is not None:
+ if not isinstance(new_heading, (int, float)) or new_heading < 0 or new_heading > 360:
+ raise ValueError("航向需为0-360之间的数字")
+ self.current_heading = new_heading
+
+ logger.info(f"无人机状态更新:位置({new_lat:.6f},{new_lon:.6f}),航向{self.current_heading:.1f}°")
+
+ except ValueError as e:
+ logger.error(f"更新无人机状态失败:{str(e)}")
+ raise
+
+ def update_obstacle_data(self, distance: float) -> None:
+ """
+ 更新障碍物检测数据(模拟激光雷达/超声波传感器输入)
+ :param distance: 前方障碍物距离(米),None表示未检测到
+ """
+ if distance is None:
+ self.is_obstacle_detected = False
+ self.obstacle_distance = float("inf")
+ logger.info("未检测到障碍物")
return
- # 获取当前目标航点
+ try:
+ if not isinstance(distance, (int, float)) or distance < 0:
+ raise ValueError("障碍物距离需为非负数字")
+
+ self.obstacle_distance = distance
+ self.is_obstacle_detected = distance < self.safety_distance
+
+ if self.is_obstacle_detected:
+ logger.warning(f"检测到前方障碍物!距离:{distance:.2f}米(安全阈值:{self.safety_distance}米)")
+ else:
+ logger.info(f"前方障碍物距离:{distance:.2f}米(安全)")
+
+ except ValueError as e:
+ logger.error(f"更新障碍物数据失败:{str(e)}")
+ raise
+
+ def replan_path(self) -> List[Tuple[float, float]]:
+ """
+ 路径重规划(简化版):遇到障碍时生成绕行航点
+ :return: 重规划后的航点列表
+ """
+ logger.info("触发路径重规划:生成绕行航点")
+
+ # 获取当前位置和目标航点
+ current_lat, current_lon = self.current_position
target_lat, target_lon = self.waypoints[self.target_waypoint_idx]
- # 计算当前位置到目标航点的距离
- distance_to_target = self.calculate_distance(
- self.current_lat, self.current_lon, target_lat, target_lon
+
+ # 计算绕行偏移量(向左偏移0.0001度,约11米,适配经纬度坐标系)
+ offset_lat = (target_lat - current_lat) * 0.1 # 纬度偏移
+ offset_lon = (target_lon - current_lon) * 0.1 # 经度偏移
+
+ # 生成绕行航点(当前位置 → 绕行点 → 原目标航点)
+ detour_point = (
+ current_lat + offset_lon, # 左偏移经度
+ current_lon - offset_lat # 左偏移纬度
+ )
+
+ # 重规划航点列表:保留已完成航点 + 绕行点 + 剩余航点
+ replanned_waypoints = (
+ self.waypoints[:self.target_waypoint_idx] +
+ [detour_point] +
+ self.waypoints[self.target_waypoint_idx:]
)
- print(f"当前位置:({self.current_lat:.6f}, {self.current_lon:.6f})")
- print(f"目标航点{self.target_waypoint_idx+1}:({target_lat:.6f}, {target_lon:.6f})")
- print(f"到目标航点距离:{distance_to_target:.2f} 米")
+ logger.info(f"路径重规划完成:新增绕行航点{detour_point}")
+ return replanned_waypoints
+
+ def navigate(self) -> None:
+ """
+ 核心导航逻辑:
+ 1. 检查航点是否为空
+ 2. 检测障碍物,必要时重规划路径
+ 3. 飞向目标航点,到达后切换下一个
+ """
+ # 检查航点
+ if not self.waypoints:
+ logger.error("导航终止:未设置航点")
+ return
+
+ # 检查是否完成所有航点
+ if self.target_waypoint_idx >= len(self.waypoints):
+ logger.info("导航完成:所有航点已到达")
+ return
+
+ # 障碍物检测与路径重规划
+ if self.is_obstacle_detected:
+ self.waypoints = self.replan_path()
+
+ # 获取目标航点
+ target_lat, target_lon = self.waypoints[self.target_waypoint_idx]
+ current_lat, current_lon = self.current_position
- # 判断是否到达当前航点
+ # 计算到目标航点的距离
+ distance_to_target = self.calculate_haversine_distance(
+ current_lat, current_lon, target_lat, target_lon
+ )
+
+ # 判断是否到达目标航点
if distance_to_target < self.arrival_threshold:
- print(f"已到达航点{self.target_waypoint_idx+1}")
+ logger.info(f"到达航点{self.target_waypoint_idx + 1}:({target_lat:.6f},{target_lon:.6f})")
self.target_waypoint_idx += 1
- # 判断是否完成所有航点
- if self.target_waypoint_idx >= len(self.waypoints):
- print("所有航点导航完成!")
- return
- else:
- print(f"切换至下一个航点:{self.target_waypoint_idx+1}")
+ # 重置障碍物状态
+ self.is_obstacle_detected = False
+ self.obstacle_distance = float("inf")
else:
- # 生成飞行指令(实际场景中需替换为无人机SDK的飞行控制指令)
- print("飞行指令:向目标航点飞行(航向/速度需根据实际SDK调整)")
+ # 生成飞行指令(实际场景替换为无人机SDK API)
+ logger.info(
+ f"飞向航点{self.target_waypoint_idx + 1}:"
+ f"当前距离{distance_to_target:.2f}米,航向{self.current_heading:.1f}°"
+ )
+
# ------------------- 测试代码 -------------------
if __name__ == "__main__":
- # 初始化无人机导航实例
- uav = UAVAutonomousNavigation()
+ # 初始化导航实例
+ uav_nav = UAVAutonomousNavigation()
- # 设置航点列表(示例:3个航点)
- waypoints = [
+ # 1. 设置航点(示例:3个真实地理坐标)
+ waypoints_list = [
(39.908823, 116.397470), # 航点1:天安门
(39.997563, 116.337624), # 航点2:颐和园
- (40.006401, 116.397029) # 航点3:圆明园
+ (40.006401, 116.397029) # 航点3:圆明园
]
- uav.set_waypoints(waypoints)
+ uav_nav.set_waypoints(waypoints_list)
+
+ # 2. 模拟无人机初始状态
+ uav_nav.update_uav_state(39.908820, 116.397468, 270.0) # 初始位置靠近航点1,航向270°(正西)
+
+ # 3. 模拟导航过程1:无障碍物,飞向航点1
+ logger.info("\n===== 导航阶段1:无障碍物 =====")
+ uav_nav.update_obstacle_data(20.0) # 障碍物距离20米(安全)
+ uav_nav.navigate()
- # 模拟无人机位置更新与导航过程
- # 模拟初始位置(靠近航点1)
- uav.update_position(39.908820, 116.397468)
- uav.navigate_to_waypoints()
+ # 4. 模拟到达航点1,更新位置
+ uav_nav.update_uav_state(39.908823, 116.397470)
+ uav_nav.navigate()
- print("-" * 50)
+ # 5. 模拟导航阶段2:检测到障碍物,触发路径重规划
+ logger.info("\n===== 导航阶段2:检测到障碍物 =====")
+ uav_nav.update_obstacle_data(8.0) # 障碍物距离8米(触发避障)
+ uav_nav.navigate()
- # 模拟无人机飞到航点1,更新位置
- uav.update_position(39.908823, 116.397470)
- uav.navigate_to_waypoints()
\ No newline at end of file
+ # 6. 模拟绕开障碍后继续导航
+ logger.info("\n===== 导航阶段3:绕开障碍后 =====")
+ uav_nav.update_obstacle_data(None) # 无障碍物
+ uav_nav.update_uav_state(39.950000, 116.360000) # 模拟飞到绕行点
+ uav_nav.navigate()
\ No newline at end of file
From 8867bfac303e182a253c2b11303bb7f9decd1224 Mon Sep 17 00:00:00 2001
From: XYTX0427 <481177497@qq.com>
Date: Fri, 19 Dec 2025 22:57:25 +0800
Subject: [PATCH 03/21] =?UTF-8?q?=E8=AF=95=E9=AA=8C=E6=96=B0=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/Unmanned_aerial_vehicle/main.py | 354 ++++++++++------------------
1 file changed, 118 insertions(+), 236 deletions(-)
diff --git a/src/Unmanned_aerial_vehicle/main.py b/src/Unmanned_aerial_vehicle/main.py
index 3529838050..679e2b45e0 100644
--- a/src/Unmanned_aerial_vehicle/main.py
+++ b/src/Unmanned_aerial_vehicle/main.py
@@ -1,243 +1,125 @@
import math
-import logging
-from typing import List, Tuple
-
-# 配置日志,方便追踪运行状态
-logging.basicConfig(
- level=logging.INFO,
- format="%(asctime)s - %(levelname)s - %(message)s",
- datefmt="%Y-%m-%d %H:%M:%S"
-)
-logger = logging.getLogger("UAV_Autonomous_Navigation")
-
-
-class UAVAutonomousNavigation:
- """无人机自主导航核心类:整合航点导航、避障、路径重规划"""
-
- def __init__(self):
- # 无人机核心状态
- self.current_position: Tuple[float, float] = (0.0, 0.0) # (纬度, 经度)
- self.current_heading: float = 0.0 # 当前航向(度,0=正北,90=正东)
- self.safety_distance: float = 10.0 # 避障安全距离(米)
- self.arrival_threshold: float = 5.0 # 到达航点判定阈值(米)
-
- # 导航相关参数
- self.waypoints: List[Tuple[float, float]] = [] # 航点列表
- self.target_waypoint_idx: int = 0 # 当前目标航点索引
- self.is_obstacle_detected: bool = False # 是否检测到障碍物
- self.obstacle_distance: float = float("inf") # 障碍物距离(初始为无穷大)
-
- def set_waypoints(self, waypoints: List[Tuple[float, float]]) -> None:
- """
- 设置航点列表,增加输入验证
- :param waypoints: 航点列表,每个元素为(纬度, 经度)元组
- """
- try:
- # 验证航点格式
- if not isinstance(waypoints, list):
- raise TypeError("航点必须为列表格式")
- for idx, wp in enumerate(waypoints):
- if not isinstance(wp, tuple) or len(wp) != 2:
- raise ValueError(f"第{idx + 1}个航点格式错误,需为(纬度, 经度)元组")
- if not (isinstance(wp[0], (int, float)) and isinstance(wp[1], (int, float))):
- raise ValueError(f"第{idx + 1}个航点经纬度必须为数字")
-
- self.waypoints = waypoints
- self.target_waypoint_idx = 0
- logger.info(f"成功设置{len(waypoints)}个航点,初始目标航点:1")
-
- except (TypeError, ValueError) as e:
- logger.error(f"设置航点失败:{str(e)}")
- raise
-
- def calculate_haversine_distance(self, lat1: float, lon1: float, lat2: float, lon2: float) -> float:
- """
- 高精度计算两个经纬度点的地面距离(米)
- 采用Haversine公式,适配无人机低空导航场景
- """
- try:
- # 地球平均半径(米)
- EARTH_RADIUS = 6371008.8
- # 角度转弧度
- lat1_rad = math.radians(lat1)
- lon1_rad = math.radians(lon1)
- lat2_rad = math.radians(lat2)
- lon2_rad = math.radians(lon2)
-
- # 经纬度差值
- dlat = lat2_rad - lat1_rad
- dlon = lon2_rad - lon1_rad
-
- # Haversine核心计算
- a = math.sin(dlat / 2) ** 2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon / 2) ** 2
- c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
- distance = EARTH_RADIUS * c
-
- logger.debug(f"距离计算:({lat1:.6f},{lon1:.6f}) → ({lat2:.6f},{lon2:.6f}) = {distance:.2f}米")
- return distance
-
- except Exception as e:
- logger.error(f"距离计算失败:{str(e)}")
- raise
-
- def update_uav_state(self, new_lat: float, new_lon: float, new_heading: float = None) -> None:
- """
- 更新无人机位置和航向
- :param new_lat: 新纬度
- :param new_lon: 新经度
- :param new_heading: 新航向(可选,不传入则保持原航向)
- """
- try:
- if not (isinstance(new_lat, (int, float)) and isinstance(new_lon, (int, float))):
- raise ValueError("经纬度必须为数字")
-
- self.current_position = (new_lat, new_lon)
- if new_heading is not None:
- if not isinstance(new_heading, (int, float)) or new_heading < 0 or new_heading > 360:
- raise ValueError("航向需为0-360之间的数字")
- self.current_heading = new_heading
-
- logger.info(f"无人机状态更新:位置({new_lat:.6f},{new_lon:.6f}),航向{self.current_heading:.1f}°")
-
- except ValueError as e:
- logger.error(f"更新无人机状态失败:{str(e)}")
- raise
-
- def update_obstacle_data(self, distance: float) -> None:
- """
- 更新障碍物检测数据(模拟激光雷达/超声波传感器输入)
- :param distance: 前方障碍物距离(米),None表示未检测到
- """
- if distance is None:
- self.is_obstacle_detected = False
- self.obstacle_distance = float("inf")
- logger.info("未检测到障碍物")
+import random
+import time
+
+
+class Drone:
+ """无人机核心类,模拟无人机的状态和行为"""
+
+ def __init__(self, start_x=0.0, start_y=0.0, start_z=0.0):
+ # 无人机当前位置 (x, y, z) 坐标
+ self.x = start_x
+ self.y = start_y
+ self.z = start_z
+ # 无人机飞行速度 (m/s)
+ self.speed = 2.0
+ # 无人机状态:idle(待机)/flying(飞行)/avoiding(避障)
+ self.status = "idle"
+ # 目标位置
+ self.target_x = 0.0
+ self.target_y = 0.0
+ self.target_z = 0.0
+ # 避障距离阈值 (米)
+ self.obstacle_threshold = 3.0
+
+ def set_target(self, target_x, target_y, target_z):
+ """设置无人机的目标位置"""
+ self.target_x = target_x
+ self.target_y = target_y
+ self.target_z = target_z
+ self.status = "flying"
+ print(f"无人机目标已设置:({target_x}, {target_y}, {target_z}),开始飞行")
+
+ def calculate_distance_to_target(self):
+ """计算当前位置到目标位置的直线距离"""
+ dx = self.target_x - self.x
+ dy = self.target_y - self.y
+ dz = self.target_z - self.z
+ return math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
+
+ def detect_obstacle(self, obstacles):
+ """模拟障碍物检测:检查是否有障碍物在避障阈值范围内"""
+ for (ox, oy, oz) in obstacles:
+ dx = ox - self.x
+ dy = oy - self.y
+ dz = oz - self.z
+ distance = math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
+ if distance < self.obstacle_threshold:
+ return True, (ox, oy, oz) # 检测到障碍物,返回障碍物位置
+ return False, None
+
+ def avoid_obstacle(self, obstacle_pos):
+ """简单避障策略:向障碍物右侧偏移飞行"""
+ ox, oy, _ = obstacle_pos
+ # 计算偏移方向(右侧1米)
+ self.x += (self.x - ox) * 0.5 # 远离障碍物x方向
+ self.y += (self.y - oy) * 0.5 # 远离障碍物y方向
+ print(f"检测到障碍物({ox}, {oy}),已避障,当前位置:({self.x:.2f}, {self.y:.2f})")
+ self.status = "avoiding"
+
+ def update_position(self):
+ """更新无人机位置,向目标点移动"""
+ if self.status != "flying":
return
- try:
- if not isinstance(distance, (int, float)) or distance < 0:
- raise ValueError("障碍物距离需为非负数字")
-
- self.obstacle_distance = distance
- self.is_obstacle_detected = distance < self.safety_distance
-
- if self.is_obstacle_detected:
- logger.warning(f"检测到前方障碍物!距离:{distance:.2f}米(安全阈值:{self.safety_distance}米)")
- else:
- logger.info(f"前方障碍物距离:{distance:.2f}米(安全)")
-
- except ValueError as e:
- logger.error(f"更新障碍物数据失败:{str(e)}")
- raise
-
- def replan_path(self) -> List[Tuple[float, float]]:
- """
- 路径重规划(简化版):遇到障碍时生成绕行航点
- :return: 重规划后的航点列表
- """
- logger.info("触发路径重规划:生成绕行航点")
-
- # 获取当前位置和目标航点
- current_lat, current_lon = self.current_position
- target_lat, target_lon = self.waypoints[self.target_waypoint_idx]
-
- # 计算绕行偏移量(向左偏移0.0001度,约11米,适配经纬度坐标系)
- offset_lat = (target_lat - current_lat) * 0.1 # 纬度偏移
- offset_lon = (target_lon - current_lon) * 0.1 # 经度偏移
-
- # 生成绕行航点(当前位置 → 绕行点 → 原目标航点)
- detour_point = (
- current_lat + offset_lon, # 左偏移经度
- current_lon - offset_lat # 左偏移纬度
- )
-
- # 重规划航点列表:保留已完成航点 + 绕行点 + 剩余航点
- replanned_waypoints = (
- self.waypoints[:self.target_waypoint_idx] +
- [detour_point] +
- self.waypoints[self.target_waypoint_idx:]
- )
-
- logger.info(f"路径重规划完成:新增绕行航点{detour_point}")
- return replanned_waypoints
-
- def navigate(self) -> None:
- """
- 核心导航逻辑:
- 1. 检查航点是否为空
- 2. 检测障碍物,必要时重规划路径
- 3. 飞向目标航点,到达后切换下一个
- """
- # 检查航点
- if not self.waypoints:
- logger.error("导航终止:未设置航点")
+ # 计算方向向量
+ dx = self.target_x - self.x
+ dy = self.target_y - self.y
+ dz = self.target_z - self.z
+ distance = self.calculate_distance_to_target()
+
+ # 如果距离小于步长,直接到达目标
+ if distance < self.speed:
+ self.x = self.target_x
+ self.y = self.target_y
+ self.z = self.target_z
+ self.status = "idle"
+ print(f"已到达目标位置:({self.x:.2f}, {self.y:.2f}, {self.z:.2f})")
return
- # 检查是否完成所有航点
- if self.target_waypoint_idx >= len(self.waypoints):
- logger.info("导航完成:所有航点已到达")
- return
+ # 归一化方向向量,按速度移动
+ self.x += (dx / distance) * self.speed
+ self.y += (dy / distance) * self.speed
+ self.z += (dz / distance) * self.speed
+
+ def get_status(self):
+ """获取无人机当前状态和位置"""
+ return {
+ "position": (round(self.x, 2), round(self.y, 2), round(self.z, 2)),
+ "status": self.status,
+ "distance_to_target": round(self.calculate_distance_to_target(), 2)
+ }
+
- # 障碍物检测与路径重规划
- if self.is_obstacle_detected:
- self.waypoints = self.replan_path()
-
- # 获取目标航点
- target_lat, target_lon = self.waypoints[self.target_waypoint_idx]
- current_lat, current_lon = self.current_position
-
- # 计算到目标航点的距离
- distance_to_target = self.calculate_haversine_distance(
- current_lat, current_lon, target_lat, target_lon
- )
-
- # 判断是否到达目标航点
- if distance_to_target < self.arrival_threshold:
- logger.info(f"到达航点{self.target_waypoint_idx + 1}:({target_lat:.6f},{target_lon:.6f})")
- self.target_waypoint_idx += 1
- # 重置障碍物状态
- self.is_obstacle_detected = False
- self.obstacle_distance = float("inf")
- else:
- # 生成飞行指令(实际场景替换为无人机SDK API)
- logger.info(
- f"飞向航点{self.target_waypoint_idx + 1}:"
- f"当前距离{distance_to_target:.2f}米,航向{self.current_heading:.1f}°"
- )
-
-
-# ------------------- 测试代码 -------------------
+# -------------------------- 主程序:无人机自主导航仿真 --------------------------
if __name__ == "__main__":
- # 初始化导航实例
- uav_nav = UAVAutonomousNavigation()
-
- # 1. 设置航点(示例:3个真实地理坐标)
- waypoints_list = [
- (39.908823, 116.397470), # 航点1:天安门
- (39.997563, 116.337624), # 航点2:颐和园
- (40.006401, 116.397029) # 航点3:圆明园
- ]
- uav_nav.set_waypoints(waypoints_list)
-
- # 2. 模拟无人机初始状态
- uav_nav.update_uav_state(39.908820, 116.397468, 270.0) # 初始位置靠近航点1,航向270°(正西)
-
- # 3. 模拟导航过程1:无障碍物,飞向航点1
- logger.info("\n===== 导航阶段1:无障碍物 =====")
- uav_nav.update_obstacle_data(20.0) # 障碍物距离20米(安全)
- uav_nav.navigate()
-
- # 4. 模拟到达航点1,更新位置
- uav_nav.update_uav_state(39.908823, 116.397470)
- uav_nav.navigate()
-
- # 5. 模拟导航阶段2:检测到障碍物,触发路径重规划
- logger.info("\n===== 导航阶段2:检测到障碍物 =====")
- uav_nav.update_obstacle_data(8.0) # 障碍物距离8米(触发避障)
- uav_nav.navigate()
-
- # 6. 模拟绕开障碍后继续导航
- logger.info("\n===== 导航阶段3:绕开障碍后 =====")
- uav_nav.update_obstacle_data(None) # 无障碍物
- uav_nav.update_uav_state(39.950000, 116.360000) # 模拟飞到绕行点
- uav_nav.navigate()
\ No newline at end of file
+ # 1. 初始化无人机(起点:0,0,10米高度)
+ drone = Drone(start_x=0.0, start_y=0.0, start_z=10.0)
+
+ # 2. 设置目标位置(终点:20, 15, 10米高度)
+ drone.set_target(target_x=20.0, target_y=15.0, target_z=10.0)
+
+ # 3. 模拟环境中的障碍物(坐标列表)
+ obstacles = [(10, 8, 10), (15, 12, 9)] # 两个障碍物位置
+
+ # 4. 自主导航主循环
+ print("\n=== 无人机自主导航开始 ===")
+ while drone.status != "idle":
+ # 检测障碍物
+ obstacle_detected, obstacle_pos = drone.detect_obstacle(obstacles)
+ if obstacle_detected:
+ drone.avoid_obstacle(obstacle_pos)
+ # 避障后恢复飞行状态
+ drone.status = "flying"
+
+ # 更新位置
+ drone.update_position()
+
+ # 打印实时状态
+ status = drone.get_status()
+ print(f"当前位置:{status['position']} | 距离目标:{status['distance_to_target']}米 | 状态:{status['status']}")
+
+ # 模拟实时刷新(每秒更新一次位置)
+ time.sleep(1)
+
+ print("\n=== 无人机自主导航完成 ===")
\ No newline at end of file
From 0788d1618563fbbc0bd4df80496907161d7b11be Mon Sep 17 00:00:00 2001
From: XYTX0427 <481177497@qq.com>
Date: Sun, 21 Dec 2025 16:24:42 +0800
Subject: [PATCH 04/21] =?UTF-8?q?=E8=AF=95=E9=AA=8C=E6=96=B0=E7=8E=AF?=
=?UTF-8?q?=E5=A2=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/Unmanned_aerial_vehicle/main.py | 275 ++++++++++++++++------------
1 file changed, 156 insertions(+), 119 deletions(-)
diff --git a/src/Unmanned_aerial_vehicle/main.py b/src/Unmanned_aerial_vehicle/main.py
index 679e2b45e0..5533b25647 100644
--- a/src/Unmanned_aerial_vehicle/main.py
+++ b/src/Unmanned_aerial_vehicle/main.py
@@ -1,125 +1,162 @@
import math
-import random
import time
-class Drone:
- """无人机核心类,模拟无人机的状态和行为"""
-
- def __init__(self, start_x=0.0, start_y=0.0, start_z=0.0):
- # 无人机当前位置 (x, y, z) 坐标
- self.x = start_x
- self.y = start_y
- self.z = start_z
- # 无人机飞行速度 (m/s)
- self.speed = 2.0
- # 无人机状态:idle(待机)/flying(飞行)/avoiding(避障)
- self.status = "idle"
+class DroneAutonomousNavigation:
+ def __init__(self):
+ """初始化无人机导航模拟器(无硬件依赖)"""
+ # 模拟无人机当前位置 [纬度, 经度, 高度(m)]
+ self.current_position = [39.908823, 116.397470, 10.0] # 初始位置(天安门附近)
# 目标位置
- self.target_x = 0.0
- self.target_y = 0.0
- self.target_z = 0.0
- # 避障距离阈值 (米)
- self.obstacle_threshold = 3.0
-
- def set_target(self, target_x, target_y, target_z):
- """设置无人机的目标位置"""
- self.target_x = target_x
- self.target_y = target_y
- self.target_z = target_z
- self.status = "flying"
- print(f"无人机目标已设置:({target_x}, {target_y}, {target_z}),开始飞行")
-
- def calculate_distance_to_target(self):
- """计算当前位置到目标位置的直线距离"""
- dx = self.target_x - self.x
- dy = self.target_y - self.y
- dz = self.target_z - self.z
- return math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
-
- def detect_obstacle(self, obstacles):
- """模拟障碍物检测:检查是否有障碍物在避障阈值范围内"""
- for (ox, oy, oz) in obstacles:
- dx = ox - self.x
- dy = oy - self.y
- dz = oz - self.z
- distance = math.sqrt(dx ** 2 + dy ** 2 + dz ** 2)
- if distance < self.obstacle_threshold:
- return True, (ox, oy, oz) # 检测到障碍物,返回障碍物位置
- return False, None
-
- def avoid_obstacle(self, obstacle_pos):
- """简单避障策略:向障碍物右侧偏移飞行"""
- ox, oy, _ = obstacle_pos
- # 计算偏移方向(右侧1米)
- self.x += (self.x - ox) * 0.5 # 远离障碍物x方向
- self.y += (self.y - oy) * 0.5 # 远离障碍物y方向
- print(f"检测到障碍物({ox}, {oy}),已避障,当前位置:({self.x:.2f}, {self.y:.2f})")
- self.status = "avoiding"
-
- def update_position(self):
- """更新无人机位置,向目标点移动"""
- if self.status != "flying":
- return
-
- # 计算方向向量
- dx = self.target_x - self.x
- dy = self.target_y - self.y
- dz = self.target_z - self.z
- distance = self.calculate_distance_to_target()
-
- # 如果距离小于步长,直接到达目标
- if distance < self.speed:
- self.x = self.target_x
- self.y = self.target_y
- self.z = self.target_z
- self.status = "idle"
- print(f"已到达目标位置:({self.x:.2f}, {self.y:.2f}, {self.z:.2f})")
- return
-
- # 归一化方向向量,按速度移动
- self.x += (dx / distance) * self.speed
- self.y += (dy / distance) * self.speed
- self.z += (dz / distance) * self.speed
-
- def get_status(self):
- """获取无人机当前状态和位置"""
- return {
- "position": (round(self.x, 2), round(self.y, 2), round(self.z, 2)),
- "status": self.status,
- "distance_to_target": round(self.calculate_distance_to_target(), 2)
- }
-
-
-# -------------------------- 主程序:无人机自主导航仿真 --------------------------
+ self.target_position = None
+ # 导航状态
+ self.is_navigating = False
+
+ def set_current_position(self, lat, lon, alt):
+ """手动设置当前位置(模拟GPS更新)"""
+ self.current_position = [lat, lon, alt]
+ print(f"✅ 更新当前位置:纬度{lat:.6f}, 经度{lon:.6f}, 高度{alt:.1f}m")
+
+ def calculate_gps_distance(self, pos1, pos2):
+ """
+ 纯Python实现GPS两点距离计算(半正矢公式)
+ :param pos1: [lat, lon, alt] 起点
+ :param pos2: [lat, lon, alt] 终点
+ :return: 地面距离(米)
+ """
+ # 地球半径(米)
+ EARTH_RADIUS = 6371000.0
+
+ # 转换为弧度
+ lat1, lon1 = math.radians(pos1[0]), math.radians(pos1[1])
+ lat2, lon2 = math.radians(pos2[0]), math.radians(pos2[1])
+
+ # 计算经纬度差值
+ dlat = lat2 - lat1
+ dlon = lon2 - lon1
+
+ # 半正矢公式核心计算
+ a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
+ c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
+ distance = EARTH_RADIUS * c
+
+ return round(distance, 2)
+
+ def generate_straight_path(self, start_pos, target_pos, waypoint_count=5):
+ """
+ 生成直线插值路径(无避障)
+ :param waypoint_count: 中间航点数量
+ :return: 航点列表 [[lat, lon, alt], ...]
+ """
+ path = []
+ # 计算每个维度的步长
+ lat_step = (target_pos[0] - start_pos[0]) / (waypoint_count + 1)
+ lon_step = (target_pos[1] - start_pos[1]) / (waypoint_count + 1)
+ alt_step = (target_pos[2] - start_pos[2]) / (waypoint_count + 1)
+
+ # 生成中间航点
+ for i in range(1, waypoint_count + 1):
+ lat = start_pos[0] + lat_step * i
+ lon = start_pos[1] + lon_step * i
+ alt = start_pos[2] + alt_step * i
+ path.append([round(lat, 6), round(lon, 6), round(alt, 1)])
+
+ # 添加最终目标点
+ path.append([target_pos[0], target_pos[1], target_pos[2]])
+ return path
+
+ def simulate_fly_to_waypoint(self, waypoint):
+ """
+ 模拟飞向单个航点(逐步更新位置)
+ :param waypoint: 目标航点 [lat, lon, alt]
+ """
+ # 每次移动的步长(模拟无人机飞行,每次移动0.00001度经纬度)
+ LAT_STEP = 0.00001
+ LON_STEP = 0.00001
+ ALT_STEP = 0.5 # 高度每次移动0.5米
+
+ # 持续移动直到到达航点(距离<1米)
+ while True:
+ distance = self.calculate_gps_distance(self.current_position, waypoint)
+ if distance < 1.0:
+ print(f"✅ 到达航点:{waypoint} (距离{distance}m)")
+ break
+
+ # 计算移动方向并更新位置
+ current_lat, current_lon, current_alt = self.current_position
+ target_lat, target_lon, target_alt = waypoint
+
+ # 纬度调整
+ if current_lat < target_lat:
+ new_lat = current_lat + LAT_STEP
+ elif current_lat > target_lat:
+ new_lat = current_lat - LAT_STEP
+ else:
+ new_lat = current_lat
+
+ # 经度调整
+ if current_lon < target_lon:
+ new_lon = current_lon + LON_STEP
+ elif current_lon > target_lon:
+ new_lon = current_lon - LON_STEP
+ else:
+ new_lon = current_lon
+
+ # 高度调整
+ if current_alt < target_alt:
+ new_alt = current_alt + ALT_STEP
+ elif current_alt > target_alt:
+ new_alt = current_alt - ALT_STEP
+ else:
+ new_alt = current_alt
+
+ # 更新位置
+ self.set_current_position(new_lat, new_lon, new_alt)
+ # 模拟飞行延迟
+ time.sleep(0.1)
+
+ def navigate_to_target(self, target_lat, target_lon, target_alt):
+ """
+ 自主导航主函数(纯算法模拟)
+ """
+ self.target_position = [target_lat, target_lon, target_alt]
+ self.is_navigating = True
+
+ print("\n🚀 开始自主导航任务")
+ print(f"📌 起点:{self.current_position}")
+ print(f"🎯 终点:{self.target_position}")
+
+ # 1. 生成路径
+ path = self.generate_straight_path(self.current_position, self.target_position)
+ print(f"\n🗺️ 生成路径完成,共{len(path)}个航点:")
+ for i, wp in enumerate(path):
+ print(f" 航点{i + 1}:{wp}")
+
+ # 2. 依次飞向每个航点
+ print("\n✈️ 开始飞向目标...")
+ for i, waypoint in enumerate(path):
+ print(f"\n--- 飞向第{i + 1}个航点 ---")
+ self.simulate_fly_to_waypoint(waypoint)
+
+ # 3. 导航完成
+ self.is_navigating = False
+ print("\n🎉 导航任务完成!已到达目标点")
+
+
+# ------------------- 测试代码 -------------------
if __name__ == "__main__":
- # 1. 初始化无人机(起点:0,0,10米高度)
- drone = Drone(start_x=0.0, start_y=0.0, start_z=10.0)
-
- # 2. 设置目标位置(终点:20, 15, 10米高度)
- drone.set_target(target_x=20.0, target_y=15.0, target_z=10.0)
-
- # 3. 模拟环境中的障碍物(坐标列表)
- obstacles = [(10, 8, 10), (15, 12, 9)] # 两个障碍物位置
-
- # 4. 自主导航主循环
- print("\n=== 无人机自主导航开始 ===")
- while drone.status != "idle":
- # 检测障碍物
- obstacle_detected, obstacle_pos = drone.detect_obstacle(obstacles)
- if obstacle_detected:
- drone.avoid_obstacle(obstacle_pos)
- # 避障后恢复飞行状态
- drone.status = "flying"
-
- # 更新位置
- drone.update_position()
-
- # 打印实时状态
- status = drone.get_status()
- print(f"当前位置:{status['position']} | 距离目标:{status['distance_to_target']}米 | 状态:{status['status']}")
-
- # 模拟实时刷新(每秒更新一次位置)
- time.sleep(1)
-
- print("\n=== 无人机自主导航完成 ===")
\ No newline at end of file
+ # 初始化导航模拟器
+ drone = DroneAutonomousNavigation()
+
+ # 设置目标点(比如:北京奥林匹克公园,高度50米)
+ target_lat = 39.990168
+ target_lon = 116.397204
+ target_alt = 50.0
+
+ # 执行自主导航
+ try:
+ drone.navigate_to_target(target_lat, target_lon, target_alt)
+ except KeyboardInterrupt:
+ print("\n🛑 导航任务被手动终止")
+ finally:
+ print("\n🛬 无人机已悬停/降落")
\ No newline at end of file
From 70ee8802909580025714081f4fbaccd6202fdd0f Mon Sep 17 00:00:00 2001
From: XYTX0427 <481177497@qq.com>
Date: Sun, 21 Dec 2025 21:00:21 +0800
Subject: [PATCH 05/21] =?UTF-8?q?=E9=87=8D=E6=96=B0=E6=8F=90=E4=BA=A4?=
=?UTF-8?q?=E9=80=89=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/Robotic_arm/README.md | 1 +
src/{Unmanned_aerial_vehicle => Robotic_arm}/main.py | 0
src/Unmanned_aerial_vehicle/README.md | 1 -
3 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 src/Robotic_arm/README.md
rename src/{Unmanned_aerial_vehicle => Robotic_arm}/main.py (100%)
delete mode 100644 src/Unmanned_aerial_vehicle/README.md
diff --git a/src/Robotic_arm/README.md b/src/Robotic_arm/README.md
new file mode 100644
index 0000000000..fc1e2dead9
--- /dev/null
+++ b/src/Robotic_arm/README.md
@@ -0,0 +1 @@
+机械臂
\ No newline at end of file
diff --git a/src/Unmanned_aerial_vehicle/main.py b/src/Robotic_arm/main.py
similarity index 100%
rename from src/Unmanned_aerial_vehicle/main.py
rename to src/Robotic_arm/main.py
diff --git a/src/Unmanned_aerial_vehicle/README.md b/src/Unmanned_aerial_vehicle/README.md
deleted file mode 100644
index b00460f809..0000000000
--- a/src/Unmanned_aerial_vehicle/README.md
+++ /dev/null
@@ -1 +0,0 @@
-无人机导航
\ No newline at end of file
From 7f88a0b42ac7cf08e71513641db12c145be20c1a Mon Sep 17 00:00:00 2001
From: XYTX0427 <481177497@qq.com>
Date: Sun, 21 Dec 2025 21:42:23 +0800
Subject: [PATCH 06/21] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=9F=BA=E7=A1=80?=
=?UTF-8?q?=E7=9A=84=E6=9C=BA=E6=A2=B0=E8=87=82=E5=BB=BA=E6=A8=A1.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/Robotic_arm/main.py | 314 ++++++++++++++++++++--------------------
1 file changed, 155 insertions(+), 159 deletions(-)
diff --git a/src/Robotic_arm/main.py b/src/Robotic_arm/main.py
index 5533b25647..0529a86787 100644
--- a/src/Robotic_arm/main.py
+++ b/src/Robotic_arm/main.py
@@ -1,162 +1,158 @@
-import math
-import time
-
-
-class DroneAutonomousNavigation:
- def __init__(self):
- """初始化无人机导航模拟器(无硬件依赖)"""
- # 模拟无人机当前位置 [纬度, 经度, 高度(m)]
- self.current_position = [39.908823, 116.397470, 10.0] # 初始位置(天安门附近)
- # 目标位置
- self.target_position = None
- # 导航状态
- self.is_navigating = False
-
- def set_current_position(self, lat, lon, alt):
- """手动设置当前位置(模拟GPS更新)"""
- self.current_position = [lat, lon, alt]
- print(f"✅ 更新当前位置:纬度{lat:.6f}, 经度{lon:.6f}, 高度{alt:.1f}m")
-
- def calculate_gps_distance(self, pos1, pos2):
- """
- 纯Python实现GPS两点距离计算(半正矢公式)
- :param pos1: [lat, lon, alt] 起点
- :param pos2: [lat, lon, alt] 终点
- :return: 地面距离(米)
- """
- # 地球半径(米)
- EARTH_RADIUS = 6371000.0
-
- # 转换为弧度
- lat1, lon1 = math.radians(pos1[0]), math.radians(pos1[1])
- lat2, lon2 = math.radians(pos2[0]), math.radians(pos2[1])
-
- # 计算经纬度差值
- dlat = lat2 - lat1
- dlon = lon2 - lon1
-
- # 半正矢公式核心计算
- a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
- c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
- distance = EARTH_RADIUS * c
-
- return round(distance, 2)
-
- def generate_straight_path(self, start_pos, target_pos, waypoint_count=5):
- """
- 生成直线插值路径(无避障)
- :param waypoint_count: 中间航点数量
- :return: 航点列表 [[lat, lon, alt], ...]
- """
- path = []
- # 计算每个维度的步长
- lat_step = (target_pos[0] - start_pos[0]) / (waypoint_count + 1)
- lon_step = (target_pos[1] - start_pos[1]) / (waypoint_count + 1)
- alt_step = (target_pos[2] - start_pos[2]) / (waypoint_count + 1)
-
- # 生成中间航点
- for i in range(1, waypoint_count + 1):
- lat = start_pos[0] + lat_step * i
- lon = start_pos[1] + lon_step * i
- alt = start_pos[2] + alt_step * i
- path.append([round(lat, 6), round(lon, 6), round(alt, 1)])
-
- # 添加最终目标点
- path.append([target_pos[0], target_pos[1], target_pos[2]])
- return path
-
- def simulate_fly_to_waypoint(self, waypoint):
- """
- 模拟飞向单个航点(逐步更新位置)
- :param waypoint: 目标航点 [lat, lon, alt]
- """
- # 每次移动的步长(模拟无人机飞行,每次移动0.00001度经纬度)
- LAT_STEP = 0.00001
- LON_STEP = 0.00001
- ALT_STEP = 0.5 # 高度每次移动0.5米
-
- # 持续移动直到到达航点(距离<1米)
- while True:
- distance = self.calculate_gps_distance(self.current_position, waypoint)
- if distance < 1.0:
- print(f"✅ 到达航点:{waypoint} (距离{distance}m)")
- break
-
- # 计算移动方向并更新位置
- current_lat, current_lon, current_alt = self.current_position
- target_lat, target_lon, target_alt = waypoint
-
- # 纬度调整
- if current_lat < target_lat:
- new_lat = current_lat + LAT_STEP
- elif current_lat > target_lat:
- new_lat = current_lat - LAT_STEP
- else:
- new_lat = current_lat
-
- # 经度调整
- if current_lon < target_lon:
- new_lon = current_lon + LON_STEP
- elif current_lon > target_lon:
- new_lon = current_lon - LON_STEP
- else:
- new_lon = current_lon
-
- # 高度调整
- if current_alt < target_alt:
- new_alt = current_alt + ALT_STEP
- elif current_alt > target_alt:
- new_alt = current_alt - ALT_STEP
- else:
- new_alt = current_alt
-
- # 更新位置
- self.set_current_position(new_lat, new_lon, new_alt)
- # 模拟飞行延迟
- time.sleep(0.1)
-
- def navigate_to_target(self, target_lat, target_lon, target_alt):
- """
- 自主导航主函数(纯算法模拟)
- """
- self.target_position = [target_lat, target_lon, target_alt]
- self.is_navigating = True
-
- print("\n🚀 开始自主导航任务")
- print(f"📌 起点:{self.current_position}")
- print(f"🎯 终点:{self.target_position}")
-
- # 1. 生成路径
- path = self.generate_straight_path(self.current_position, self.target_position)
- print(f"\n🗺️ 生成路径完成,共{len(path)}个航点:")
- for i, wp in enumerate(path):
- print(f" 航点{i + 1}:{wp}")
-
- # 2. 依次飞向每个航点
- print("\n✈️ 开始飞向目标...")
- for i, waypoint in enumerate(path):
- print(f"\n--- 飞向第{i + 1}个航点 ---")
- self.simulate_fly_to_waypoint(waypoint)
-
- # 3. 导航完成
- self.is_navigating = False
- print("\n🎉 导航任务完成!已到达目标点")
-
-
-# ------------------- 测试代码 -------------------
-if __name__ == "__main__":
- # 初始化导航模拟器
- drone = DroneAutonomousNavigation()
-
- # 设置目标点(比如:北京奥林匹克公园,高度50米)
- target_lat = 39.990168
- target_lon = 116.397204
- target_alt = 50.0
+import mujoco
+import mujoco.viewer
+import numpy as np
+import os
+import tempfile
+import time # 新增:备用的睡眠函数
+
+# ====================== 1. 定义机械臂 XML 模型 ======================
+# 6自由度机械臂的 MuJoCo XML 描述
+arm_xml = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+
+# ====================== 2. 模型加载和仿真控制 ======================
+def create_arm_simulation():
+ """创建并运行机械臂仿真"""
+ # 将XML字符串写入临时文件(MuJoCo需要文件路径加载)
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.xml', delete=False) as f:
+ f.write(arm_xml)
+ xml_path = f.name
- # 执行自主导航
try:
- drone.navigate_to_target(target_lat, target_lon, target_alt)
- except KeyboardInterrupt:
- print("\n🛑 导航任务被手动终止")
+ # 加载模型和数据
+ model = mujoco.MjModel.from_xml_path(xml_path)
+ data = mujoco.MjData(model)
+
+ print("✅ 机械臂模型加载成功!")
+ print(f"🔧 关节数量:{model.njnt}")
+ print(f"🔧 执行器数量:{model.nu}")
+
+ # 设置初始关节角度
+ initial_joint_angles = [0, 0.2, -0.5, 0, 0.3, 0]
+ data.qpos[:6] = initial_joint_angles
+
+ # 启动可视化界面
+ with mujoco.viewer.launch_passive(model, data) as viewer:
+ print("\n🎮 仿真已启动!按 Ctrl+C 退出")
+ print("💡 机械臂会自动缓慢运动,展示关节控制效果")
+
+ # 仿真循环
+ step = 0
+ while viewer.is_running():
+ # 控制频率:每20步更新一次关节目标
+ if step % 20 == 0:
+ # 生成周期性的关节控制指令(让机械臂缓慢摆动)
+ t = data.time
+ target_angles = [
+ 0.2 * np.sin(t * 0.5), # joint0: 基座旋转
+ 0.3 + 0.2 * np.sin(t), # joint1: 肩部
+ -0.6 + 0.2 * np.cos(t), # joint2: 肘部
+ 0.1 * np.sin(t * 1.2), # joint3: 前臂
+ 0.2 * np.cos(t * 0.8), # joint4: 腕部1
+ 0.1 * np.sin(t * 1.5) # joint5: 腕部2
+ ]
+ # 设置控制指令
+ data.ctrl[:6] = target_angles
+
+ # 运行一步仿真
+ mujoco.mj_step(model, data)
+
+ # 更新可视化
+ viewer.sync()
+
+ # 修复:兼容不同版本的睡眠函数
+ try:
+ # 尝试调用新版 MuJoCo 的 sleep 函数(归属到 utils)
+ mujoco.utils.mju_sleep(1 / 60)
+ except AttributeError:
+ try:
+ # 尝试调用旧版 MuJoCo 的 sleep 函数(主模块)
+ mujoco.mju_sleep(1 / 60)
+ except AttributeError:
+ # 终极备用:使用 Python 内置的 time.sleep
+ time.sleep(1 / 60)
+
+ step += 1
+
+ except Exception as e:
+ print(f"❌ 仿真出错:{e}")
finally:
- print("\n🛬 无人机已悬停/降落")
\ No newline at end of file
+ # 删除临时XML文件
+ os.unlink(xml_path)
+
+
+if __name__ == "__main__":
+ # 检查MuJoCo版本
+ print(f"🔍 MuJoCo 版本:{mujoco.__version__}")
+
+ # 启动仿真
+ create_arm_simulation()
\ No newline at end of file
From 7e1c538a21b1fb70bbd8675bda90dfed1e9dbfcb Mon Sep 17 00:00:00 2001
From: XYTX0427 <481177497@qq.com>
Date: Mon, 22 Dec 2025 09:35:35 +0800
Subject: [PATCH 07/21] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9C=BA=E6=A2=B0?=
=?UTF-8?q?=E8=87=82=E7=9A=84=E8=BF=90=E5=8A=A8=E8=BD=A8=E8=BF=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/Robotic_arm/main.py | 143 ++++++++++++++++++----------------------
1 file changed, 63 insertions(+), 80 deletions(-)
diff --git a/src/Robotic_arm/main.py b/src/Robotic_arm/main.py
index 0529a86787..b7ee4eee6a 100644
--- a/src/Robotic_arm/main.py
+++ b/src/Robotic_arm/main.py
@@ -3,58 +3,56 @@
import numpy as np
import os
import tempfile
-import time # 新增:备用的睡眠函数
+import time
+from scipy import interpolate
-# ====================== 1. 定义机械臂 XML 模型 ======================
-# 6自由度机械臂的 MuJoCo XML 描述
+
+# ====================== 轨迹优化核心(纯原生) ======================
+def quintic_polynomial(start, end, T, t):
+ """五次多项式插值:保证位置/速度/加速度连续"""
+ s0, v0, a0 = start, 0, 0
+ s1, v1, a1 = end, 0, 0
+
+ a = s0
+ b = v0
+ c = a0 / 2
+ d = (20 * (s1 - s0) - (8 * v1 + 12 * v0) * T - (3 * a0 - a1) * T ** 2) / (2 * T ** 3)
+ e = (30 * (s0 - s1) + (14 * v1 + 16 * v0) * T + (3 * a0 - 2 * a1) * T ** 2) / (2 * T ** 4)
+ f = (12 * (s1 - s0) - (6 * v1 + 6 * v0) * T - (a0 - a1) * T ** 2) / (2 * T ** 5)
+
+ return a + b * t + c * t ** 2 + d * t ** 3 + e * t ** 4 + f * t ** 5
+
+
+# ====================== 机械臂模型 ======================
arm_xml = """
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -65,8 +63,6 @@
-
-
@@ -79,80 +75,67 @@
"""
-# ====================== 2. 模型加载和仿真控制 ======================
-def create_arm_simulation():
- """创建并运行机械臂仿真"""
- # 将XML字符串写入临时文件(MuJoCo需要文件路径加载)
+# ====================== 仿真运行 ======================
+def run_simulation():
+ # 临时XML文件
with tempfile.NamedTemporaryFile(mode='w', suffix='.xml', delete=False) as f:
f.write(arm_xml)
xml_path = f.name
try:
- # 加载模型和数据
+ # 加载模型
model = mujoco.MjModel.from_xml_path(xml_path)
data = mujoco.MjData(model)
-
- print("✅ 机械臂模型加载成功!")
- print(f"🔧 关节数量:{model.njnt}")
- print(f"🔧 执行器数量:{model.nu}")
-
- # 设置初始关节角度
- initial_joint_angles = [0, 0.2, -0.5, 0, 0.3, 0]
- data.qpos[:6] = initial_joint_angles
-
- # 启动可视化界面
+ print("✅ 模型加载成功!")
+
+ # 轨迹关键点(关节空间)
+ waypoints = [
+ [0, 0.2, -0.5, 0, 0.3, 0],
+ [0.5, 0.5, -0.8, 0.2, 0.5, 0.3],
+ [0.8, 0.3, -0.6, 0.4, 0.2, 0.5],
+ [0.5, 0.5, -0.8, 0.2, 0.5, 0.3],
+ [0, 0.2, -0.5, 0, 0.3, 0]
+ ]
+ segment_time = 3.0 # 每段轨迹时长
+
+ # 启动可视化
with mujoco.viewer.launch_passive(model, data) as viewer:
- print("\n🎮 仿真已启动!按 Ctrl+C 退出")
- print("💡 机械臂会自动缓慢运动,展示关节控制效果")
-
- # 仿真循环
- step = 0
+ print("🎮 仿真启动!机械臂沿平滑轨迹运动(按Ctrl+C退出)")
while viewer.is_running():
- # 控制频率:每20步更新一次关节目标
- if step % 20 == 0:
- # 生成周期性的关节控制指令(让机械臂缓慢摆动)
- t = data.time
- target_angles = [
- 0.2 * np.sin(t * 0.5), # joint0: 基座旋转
- 0.3 + 0.2 * np.sin(t), # joint1: 肩部
- -0.6 + 0.2 * np.cos(t), # joint2: 肘部
- 0.1 * np.sin(t * 1.2), # joint3: 前臂
- 0.2 * np.cos(t * 0.8), # joint4: 腕部1
- 0.1 * np.sin(t * 1.5) # joint5: 腕部2
- ]
- # 设置控制指令
- data.ctrl[:6] = target_angles
-
- # 运行一步仿真
+ # 计算当前轨迹段
+ t_total = data.time
+ seg_idx = int(t_total // segment_time) % (len(waypoints) - 1)
+ t_seg = t_total % segment_time
+
+ # 生成平滑轨迹
+ target = []
+ for i in range(6):
+ pos = quintic_polynomial(
+ waypoints[seg_idx][i],
+ waypoints[seg_idx + 1][i],
+ segment_time,
+ t_seg
+ )
+ # 限制关节范围
+ pos = np.clip(pos, model.actuator_ctrlrange[i][0], model.actuator_ctrlrange[i][1])
+ target.append(pos)
+
+ # 控制机械臂
+ data.ctrl[:6] = target
mujoco.mj_step(model, data)
-
- # 更新可视化
viewer.sync()
- # 修复:兼容不同版本的睡眠函数
+ # 帧率控制
try:
- # 尝试调用新版 MuJoCo 的 sleep 函数(归属到 utils)
mujoco.utils.mju_sleep(1 / 60)
- except AttributeError:
- try:
- # 尝试调用旧版 MuJoCo 的 sleep 函数(主模块)
- mujoco.mju_sleep(1 / 60)
- except AttributeError:
- # 终极备用:使用 Python 内置的 time.sleep
- time.sleep(1 / 60)
-
- step += 1
+ except:
+ time.sleep(1 / 60)
except Exception as e:
- print(f"❌ 仿真出错:{e}")
+ print(f"❌ 错误:{e}")
finally:
- # 删除临时XML文件
os.unlink(xml_path)
if __name__ == "__main__":
- # 检查MuJoCo版本
- print(f"🔍 MuJoCo 版本:{mujoco.__version__}")
-
- # 启动仿真
- create_arm_simulation()
\ No newline at end of file
+ run_simulation() # 修复:删掉了多余的 + 号
\ No newline at end of file
From a186214a0ef62615251c8c331bc8d351fcf17837 Mon Sep 17 00:00:00 2001
From: XYTX0427 <481177497@qq.com>
Date: Mon, 22 Dec 2025 10:52:53 +0800
Subject: [PATCH 08/21] =?UTF-8?q?=E6=9C=BA=E6=A2=B0=E8=87=82=E8=BD=A8?=
=?UTF-8?q?=E8=BF=B9=E7=9A=84=E7=89=A9=E7=90=86=E7=BA=A6=E6=9D=9F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/Robotic_arm/main.py | 132 +++++++++++++++++++++++++++++++++-------
1 file changed, 109 insertions(+), 23 deletions(-)
diff --git a/src/Robotic_arm/main.py b/src/Robotic_arm/main.py
index b7ee4eee6a..71db2161ac 100644
--- a/src/Robotic_arm/main.py
+++ b/src/Robotic_arm/main.py
@@ -6,13 +6,33 @@
import time
from scipy import interpolate
-
-# ====================== 轨迹优化核心(纯原生) ======================
-def quintic_polynomial(start, end, T, t):
- """五次多项式插值:保证位置/速度/加速度连续"""
+# ====================== 1. 定义机械臂物理约束参数(核心) ======================
+# 参考UR5机械臂参数,可根据实际硬件调整
+CONSTRAINTS = {
+ "max_vel": [1.0, 0.8, 0.8, 1.2, 0.9, 1.2], # 各关节最大角速度 (rad/s)
+ "max_acc": [0.5, 0.4, 0.4, 0.6, 0.5, 0.6], # 各关节最大角加速度 (rad/s²)
+ "max_jerk": [0.3, 0.2, 0.2, 0.4, 0.3, 0.4], # 各关节最大加加速度 (rad/s³)
+ "ctrl_limit": [-10.0, 10.0] # 电机控制量限制
+}
+
+
+# ====================== 2. 带约束的五次多项式轨迹生成 ======================
+def constrained_quintic_polynomial(start, end, total_time, t, joint_idx):
+ """
+ 带约束的五次多项式插值
+ :param start: 起点角度
+ :param end: 终点角度
+ :param total_time: 轨迹段总时间
+ :param t: 当前段内时间 (0<=t<=total_time)
+ :param joint_idx: 关节索引(0-5)
+ :return: 约束后的位置、速度、加速度
+ """
+ # 基础边界条件(启停时速度/加速度为0)
s0, v0, a0 = start, 0, 0
s1, v1, a1 = end, 0, 0
+ T = total_time
+ # 五次多项式系数计算
a = s0
b = v0
c = a0 / 2
@@ -20,10 +40,53 @@ def quintic_polynomial(start, end, T, t):
e = (30 * (s0 - s1) + (14 * v1 + 16 * v0) * T + (3 * a0 - 2 * a1) * T ** 2) / (2 * T ** 4)
f = (12 * (s1 - s0) - (6 * v1 + 6 * v0) * T - (a0 - a1) * T ** 2) / (2 * T ** 5)
- return a + b * t + c * t ** 2 + d * t ** 3 + e * t ** 4 + f * t ** 5
+ # 计算原始位置、速度、加速度、加加速度
+ pos = a + b * t + c * t ** 2 + d * t ** 3 + e * t ** 4 + f * t ** 5
+ vel = b + 2 * c * t + 3 * d * t ** 2 + 4 * e * t ** 3 + 5 * f * t ** 4
+ acc = 2 * c + 6 * d * t + 12 * e * t ** 2 + 20 * f * t ** 3
+ jerk = 6 * d + 24 * e * t + 60 * f * t ** 2
+
+ # 应用约束(核心:超出则截断)
+ max_vel = CONSTRAINTS["max_vel"][joint_idx]
+ max_acc = CONSTRAINTS["max_acc"][joint_idx]
+ max_jerk = CONSTRAINTS["max_jerk"][joint_idx]
+
+ vel = np.clip(vel, -max_vel, max_vel)
+ acc = np.clip(acc, -max_acc, max_acc)
+ jerk = np.clip(jerk, -max_jerk, max_jerk)
+
+ # 可选:如果速度/加速度被截断,反向修正位置(更严谨)
+ # 这里简化处理,直接返回约束后的位置(基础场景足够)
+ return pos, vel, acc
+
+
+# ====================== 3. 闭环约束控制(实时修正) ======================
+def closed_loop_constraint_control(data, target_joints, joint_idx):
+ """
+ 闭环PD控制 + 约束检查,实时修正控制指令
+ """
+ # PD控制参数(可根据实际机械臂标定)
+ k_p = 8.0 # 比例系数
+ k_d = 0.2 # 微分系数
+
+ # 获取当前关节状态(仿真中读取,实际为编码器数据)
+ current_pos = data.qpos[joint_idx]
+ current_vel = data.qvel[joint_idx]
+
+ # 计算误差
+ pos_error = target_joints[joint_idx] - current_pos
+ vel_error = -current_vel # 速度误差:目标速度为0(启停阶段)
+ # 计算原始控制量
+ ctrl = k_p * pos_error + k_d * vel_error
-# ====================== 机械臂模型 ======================
+ # 约束控制量(避免电机过载)
+ ctrl = np.clip(ctrl, CONSTRAINTS["ctrl_limit"][0], CONSTRAINTS["ctrl_limit"][1])
+
+ return ctrl
+
+
+# ====================== 4. 机械臂模型(不变) ======================
arm_xml = """
@@ -75,18 +138,21 @@ def quintic_polynomial(start, end, T, t):
"""
-# ====================== 仿真运行 ======================
-def run_simulation():
+# ====================== 5. 带约束的仿真主逻辑 ======================
+def run_constrained_simulation():
# 临时XML文件
with tempfile.NamedTemporaryFile(mode='w', suffix='.xml', delete=False) as f:
f.write(arm_xml)
xml_path = f.name
try:
- # 加载模型
+ # 加载模型和数据
model = mujoco.MjModel.from_xml_path(xml_path)
data = mujoco.MjData(model)
- print("✅ 模型加载成功!")
+ print("✅ 带约束的机械臂模型加载成功!")
+ print("🔧 约束参数:")
+ print(f" 最大关节速度:{CONSTRAINTS['max_vel']} rad/s")
+ print(f" 最大关节加速度:{CONSTRAINTS['max_acc']} rad/s²")
# 轨迹关键点(关节空间)
waypoints = [
@@ -100,42 +166,62 @@ def run_simulation():
# 启动可视化
with mujoco.viewer.launch_passive(model, data) as viewer:
- print("🎮 仿真启动!机械臂沿平滑轨迹运动(按Ctrl+C退出)")
+ print("\n🎮 带约束的机械臂仿真启动!")
+ print("💡 特征:速度/加速度/控制量约束 + 闭环PD控制")
+ print("💡 按 Ctrl+C 退出")
+
while viewer.is_running():
- # 计算当前轨迹段
+ # 1. 计算当前轨迹段
t_total = data.time
seg_idx = int(t_total // segment_time) % (len(waypoints) - 1)
t_seg = t_total % segment_time
- # 生成平滑轨迹
- target = []
+ # 2. 生成带约束的目标关节角度
+ target_joints = []
+ joint_vels = [] # 记录约束后的速度(用于调试)
for i in range(6):
- pos = quintic_polynomial(
+ pos, vel, acc = constrained_quintic_polynomial(
waypoints[seg_idx][i],
waypoints[seg_idx + 1][i],
segment_time,
- t_seg
+ t_seg,
+ i
)
- # 限制关节范围
+ # 额外约束关节角度在可控范围
pos = np.clip(pos, model.actuator_ctrlrange[i][0], model.actuator_ctrlrange[i][1])
- target.append(pos)
+ target_joints.append(pos)
+ joint_vels.append(vel)
+
+ # 3. 闭环约束控制:修正控制指令
+ ctrl_signals = []
+ for i in range(6):
+ ctrl = closed_loop_constraint_control(data, target_joints, i)
+ ctrl_signals.append(ctrl)
+
+ # 4. 应用控制指令
+ data.ctrl[:6] = ctrl_signals
+
+ # 5. 打印关键状态(每50步打印一次,方便调试)
+ if int(data.time * 100) % 50 == 0:
+ print(f"\n⏱️ 时间:{data.time:.2f}s")
+ print(f" 关节0当前速度:{data.qvel[0]:.3f} rad/s (约束上限:{CONSTRAINTS['max_vel'][0]})")
+ print(f" 关节0控制量:{ctrl_signals[0]:.3f} (约束范围:{CONSTRAINTS['ctrl_limit']})")
- # 控制机械臂
- data.ctrl[:6] = target
+ # 6. 运行仿真步
mujoco.mj_step(model, data)
viewer.sync()
- # 帧率控制
+ # 7. 帧率控制
try:
mujoco.utils.mju_sleep(1 / 60)
except:
time.sleep(1 / 60)
except Exception as e:
- print(f"❌ 错误:{e}")
+ print(f"❌ 仿真出错:{e}")
finally:
os.unlink(xml_path)
if __name__ == "__main__":
- run_simulation() # 修复:删掉了多余的 + 号
\ No newline at end of file
+ run_constrained_simulation()
\ No newline at end of file
From cb3f4511ea57c3abc6c672f420a177e3d693c018 Mon Sep 17 00:00:00 2001
From: XYTX0427 <481177497@qq.com>
Date: Mon, 22 Dec 2025 11:03:37 +0800
Subject: [PATCH 09/21] =?UTF-8?q?=E6=9C=BA=E6=A2=B0=E8=87=82=E7=9A=84?=
=?UTF-8?q?=E8=90=BD=E5=9C=B0=E9=81=BF=E5=BC=80=E9=9A=9C=E7=A2=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/Robotic_arm/main.py | 284 ++++++++++++++++++++++++++--------------
1 file changed, 185 insertions(+), 99 deletions(-)
diff --git a/src/Robotic_arm/main.py b/src/Robotic_arm/main.py
index 71db2161ac..3ea2964733 100644
--- a/src/Robotic_arm/main.py
+++ b/src/Robotic_arm/main.py
@@ -6,33 +6,39 @@
import time
from scipy import interpolate
-# ====================== 1. 定义机械臂物理约束参数(核心) ======================
-# 参考UR5机械臂参数,可根据实际硬件调整
+# ====================== 1. 全局配置(物理约束+避障参数) ======================
CONSTRAINTS = {
- "max_vel": [1.0, 0.8, 0.8, 1.2, 0.9, 1.2], # 各关节最大角速度 (rad/s)
- "max_acc": [0.5, 0.4, 0.4, 0.6, 0.5, 0.6], # 各关节最大角加速度 (rad/s²)
- "max_jerk": [0.3, 0.2, 0.2, 0.4, 0.3, 0.4], # 各关节最大加加速度 (rad/s³)
- "ctrl_limit": [-10.0, 10.0] # 电机控制量限制
+ "max_vel": [1.0, 0.8, 0.8, 1.2, 0.9, 1.2],
+ "max_acc": [0.5, 0.4, 0.4, 0.6, 0.5, 0.6],
+ "max_jerk": [0.3, 0.2, 0.2, 0.4, 0.3, 0.4],
+ "ctrl_limit": [-10.0, 10.0]
}
+OBSTACLE_CONFIG = {
+ "k_att": 0.8,
+ "k_rep": 0.6,
+ "rep_radius": 0.3,
+ "obstacle_list": [
+ [0.6, 0.1, 0.5, 0.1],
+ [0.4, -0.1, 0.6, 0.08]
+ ]
+}
+
+CART_WAYPOINTS = [
+ [0.5, 0.0, 0.6],
+ [0.7, 0.2, 0.7],
+ [0.8, 0.1, 0.8],
+ [0.7, 0.2, 0.7],
+ [0.5, 0.0, 0.6]
+]
+
-# ====================== 2. 带约束的五次多项式轨迹生成 ======================
+# ====================== 2. 物理约束轨迹生成 ======================
def constrained_quintic_polynomial(start, end, total_time, t, joint_idx):
- """
- 带约束的五次多项式插值
- :param start: 起点角度
- :param end: 终点角度
- :param total_time: 轨迹段总时间
- :param t: 当前段内时间 (0<=t<=total_time)
- :param joint_idx: 关节索引(0-5)
- :return: 约束后的位置、速度、加速度
- """
- # 基础边界条件(启停时速度/加速度为0)
s0, v0, a0 = start, 0, 0
s1, v1, a1 = end, 0, 0
T = total_time
- # 五次多项式系数计算
a = s0
b = v0
c = a0 / 2
@@ -40,61 +46,111 @@ def constrained_quintic_polynomial(start, end, total_time, t, joint_idx):
e = (30 * (s0 - s1) + (14 * v1 + 16 * v0) * T + (3 * a0 - 2 * a1) * T ** 2) / (2 * T ** 4)
f = (12 * (s1 - s0) - (6 * v1 + 6 * v0) * T - (a0 - a1) * T ** 2) / (2 * T ** 5)
- # 计算原始位置、速度、加速度、加加速度
pos = a + b * t + c * t ** 2 + d * t ** 3 + e * t ** 4 + f * t ** 5
vel = b + 2 * c * t + 3 * d * t ** 2 + 4 * e * t ** 3 + 5 * f * t ** 4
acc = 2 * c + 6 * d * t + 12 * e * t ** 2 + 20 * f * t ** 3
- jerk = 6 * d + 24 * e * t + 60 * f * t ** 2
- # 应用约束(核心:超出则截断)
- max_vel = CONSTRAINTS["max_vel"][joint_idx]
- max_acc = CONSTRAINTS["max_acc"][joint_idx]
- max_jerk = CONSTRAINTS["max_jerk"][joint_idx]
+ vel = np.clip(vel, -CONSTRAINTS["max_vel"][joint_idx], CONSTRAINTS["max_vel"][joint_idx])
+ acc = np.clip(acc, -CONSTRAINTS["max_acc"][joint_idx], CONSTRAINTS["max_acc"][joint_idx])
- vel = np.clip(vel, -max_vel, max_vel)
- acc = np.clip(acc, -max_acc, max_acc)
- jerk = np.clip(jerk, -max_jerk, max_jerk)
-
- # 可选:如果速度/加速度被截断,反向修正位置(更严谨)
- # 这里简化处理,直接返回约束后的位置(基础场景足够)
return pos, vel, acc
-# ====================== 3. 闭环约束控制(实时修正) ======================
+# ====================== 3. 闭环PD控制 ======================
def closed_loop_constraint_control(data, target_joints, joint_idx):
- """
- 闭环PD控制 + 约束检查,实时修正控制指令
- """
- # PD控制参数(可根据实际机械臂标定)
- k_p = 8.0 # 比例系数
- k_d = 0.2 # 微分系数
+ k_p = 8.0
+ k_d = 0.2
- # 获取当前关节状态(仿真中读取,实际为编码器数据)
current_pos = data.qpos[joint_idx]
current_vel = data.qvel[joint_idx]
- # 计算误差
pos_error = target_joints[joint_idx] - current_pos
- vel_error = -current_vel # 速度误差:目标速度为0(启停阶段)
+ vel_error = -current_vel
- # 计算原始控制量
ctrl = k_p * pos_error + k_d * vel_error
-
- # 约束控制量(避免电机过载)
ctrl = np.clip(ctrl, CONSTRAINTS["ctrl_limit"][0], CONSTRAINTS["ctrl_limit"][1])
return ctrl
-# ====================== 4. 机械臂模型(不变) ======================
-arm_xml = """
-
+# ====================== 4. 人工势场法避障 ======================
+def artificial_potential_field(ee_pos, target_pos):
+ ee_pos = np.array(ee_pos)
+ target_pos = np.array(target_pos)
+ obstacle_list = OBSTACLE_CONFIG["obstacle_list"]
+ k_att = OBSTACLE_CONFIG["k_att"]
+ k_rep = OBSTACLE_CONFIG["k_rep"]
+ rep_radius = OBSTACLE_CONFIG["rep_radius"]
+
+ # 引力
+ att_force = k_att * (target_pos - ee_pos)
+
+ # 斥力
+ rep_force = np.zeros(3)
+ for obs in obstacle_list:
+ obs_pos = np.array(obs[:3])
+ obs_radius = obs[3]
+ dist = np.linalg.norm(ee_pos - obs_pos)
+
+ if dist < rep_radius + obs_radius:
+ if dist < 1e-6:
+ dist = 1e-6
+ rep_dir = (ee_pos - obs_pos) / dist
+ rep_force += k_rep * (1 / (dist - obs_radius) - 1 / rep_radius) * (1 / dist ** 2) * rep_dir
+
+ # 修正目标位置
+ corrected_target = ee_pos + att_force + rep_force
+ corrected_target = np.clip(corrected_target, [0.3, -0.4, 0.2], [0.9, 0.4, 1.0])
+
+ return corrected_target.tolist()
+
+
+# ====================== 5. 笛卡尔轨迹平滑 ======================
+def smooth_cartesian_trajectory(waypoints, num_points=200):
+ x = np.array([p[0] for p in waypoints])
+ y = np.array([p[1] for p in waypoints])
+ z = np.array([p[2] for p in waypoints])
+
+ t = np.linspace(0, 1, len(x))
+ t_new = np.linspace(0, 1, num_points)
+
+ spline_x = interpolate.CubicSpline(t, x, bc_type='natural')
+ spline_y = interpolate.CubicSpline(t, y, bc_type='natural')
+ spline_z = interpolate.CubicSpline(t, z, bc_type='natural')
+
+ return np.vstack((spline_x(t_new), spline_y(t_new), spline_z(t_new))).T
+
+
+# ====================== 6. 替代逆运动学:关节轨迹预生成 ======================
+def precompute_joint_waypoints(model, data, cart_waypoints):
+ """预计算笛卡尔轨迹对应的关节角度(兼容旧版MuJoCo)"""
+ joint_waypoints = []
+ ee_site_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_SITE, "ee_site")
+
+ for cart_pos in cart_waypoints:
+ # 重置数据
+ mujoco.mj_resetData(model, data)
+ # 设置目标位置到data中(旧版mj_inverse的要求)
+ data.site_xpos[ee_site_id] = cart_pos
+ # 调用逆运动学(仅model和data参数)
+ mujoco.mj_inverse(model, data)
+ # 保存关节角度
+ joint_waypoints.append(data.qpos[:6].copy())
+
+ return joint_waypoints
+
+
+# ====================== 7. 机械臂模型(带障碍物) ======================
+def get_arm_xml_with_obstacles():
+ arm_xml = """
+
+
@@ -118,6 +174,7 @@ def closed_loop_constraint_control(data, target_joints, joint_idx):
+
@@ -125,6 +182,15 @@ def closed_loop_constraint_control(data, target_joints, joint_idx):