由于您没有提供具体的“问题3的答案”(即融合后的轨迹数据文件 `result.xlsx` 或 `附件3` 的具体坐标),我将为您编写一个**完整的、可直接运行的 Python 框架**。
这个代码包含了以下功能:
1. **模拟数据生成**:因为我无法读取您的本地文件,我会在代码开头生成一段符合题目要求的模拟轨迹数据(您可以直接替换为读取您真实数据的代码)。
2. **约束检查函数**:严格对应附录中的射击和拍照约束(距离、速度、加速度、准备时间)。
3. **任务规划算法**:遍历目标点,寻找满足所有物理约束的最佳执行时刻。
4. **结果输出**:生成符合题目要求的 `result.xlsx` 表格。
### Python 代码实现
请确保您的环境中安装了 `pandas`, `numpy`, `openpyxl` 库。如果没有,请运行 `pip install pandas numpy openpyxl`。
```python
import pandas as pd
import numpy as np
import math
from datetime import timedelta
# ==========================================
# 第一部分:数据准备 (请根据实际情况修改)
# ==========================================
def load_data():
"""
加载问题3生成的轨迹数据和附件4的目标点数据。
注意:由于我无法访问您的本地文件,这里生成模拟数据供代码运行测试。
在实际使用时,请取消注释下方的 pd.read_excel 部分并填入真实文件名。
"""
# --- 模拟轨迹数据 (替代 问题3的结果) ---
# 假设机器人做匀速圆周运动,频率10Hz,持续60秒
t = np.arange(0, 60, 0.1)
x = 50 * np.cos(t * 0.1) + 100
y = 50 * np.sin(t * 0.1) + 100
# 计算速度和加速度用于模拟 (实际应从问题3结果读取或差分计算)
dt = 0.1
vx = np.gradient(x, dt)
vy = np.gradient(y, dt)
v = np.sqrt(vx**2 + vy**2)
ax = np.gradient(vx, dt)
ay = np.gradient(vy, dt)
a = np.sqrt(ax**2 + ay**2)
df_trajectory = pd.DataFrame({
'Time': t,
'X': x,
'Y': y,
'V': v,
'A': a
})
# --- 真实数据加载方式 (请在此处修改) ---
# df_trajectory = pd.read_excel('problem3_result.xlsx') # 替换为您的文件名
# targets = pd.read_excel('附件4.xlsx') # 替换为您的文件名
# --- 模拟目标点数据 (替代 附件4) ---
targets_data = {
'TargetID': [1, 2, 3, 4, 5],
'Type': ['Shooting', 'Photography', 'Shooting', 'Photography', 'Shooting'],
'X_Target': [120, 80, 140, 60, 110],
'Y_Target': [130, 140, 110, 90, 120]
}
targets = pd.DataFrame(targets_data)
return df_trajectory, targets
# ==========================================
# 第二部分:约束条件定义
# ==========================================
def calculate_distance(x1, y1, x2, y2):
return math.sqrt((x1 - x2)**2 + (y1 - y2)**2)
def check_shooting_constraints(traj_row, target_x, target_y, lookback_window=1.5):
"""
检查射击约束:
1. 距离 d ∈ [5m, 30m]
2. 速度 v ≤ 2 m/s
3. 加速度 a ≤ 1.5 m/s²
4. 瞄准前 1.5s 内上述条件均满足
"""
current_dist = calculate_distance(traj_row['X'], traj_row['Y'], target_x, target_y)
# 瞬时约束检查
if not (5 <= current_dist <= 30):
return False
if traj_row['V'] > 2.0:
return False
if traj_row['A'] > 1.5:
return False
# 历史窗口检查 (瞄准前 1.5s)
# 假设数据频率为 10Hz (0.1s/帧),1.5s 约为 15 帧
current_time = traj_row['Time']
start_time = current_time - lookback_window
# 获取该时间段内的历史数据
history = df_traj[(df_traj['Time'] >= start_time) & (df_traj['Time'] < current_time)]
if len(history) == 0:
# 如果刚开始运动,时间不够1.5s,视具体规则而定,这里假设必须满足完整1.5s
return False
for _, row in history.iterrows():
h_dist = calculate_distance(row['X'], row['Y'], target_x, target_y)
if not (5 <= h_dist <= 30): return False
if row['V'] > 2.0: return False
if row['A'] > 1.5: return False
return True
def check_photography_constraints(traj_row, target_x, target_y, angle_diff_threshold=60, lookback_window=0.5):
"""
检查拍照约束:
1. 距离 d ∈ [10m, 40m]
2. 速度 v ≤ 1.5 m/s
3. 加速度 a ≤ 1.5 m/s²
4. 同一目标不同角度差异至少 60°
5. 对准前 0.5s 内上述条件均满足
"""
current_dist = calculate_distance(traj_row['X'], traj_row['Y'], target_x, target_y)
# 瞬时约束检查
if not (10 <= current_dist <= 40):
return False
if traj_row['V'] > 1.5:
return False
if traj_row['A'] > 1.5:
return False
# 角度检查逻辑 (简化版:计算当前向量与正北方向夹角,实际需对比已拍摄角度)
# 这里仅做基础物理约束检查,角度去重逻辑在主循环处理
dx = target_x - traj_row['X']
dy = target_y - traj_row['Y']
current_angle = math.degrees(math.atan2(dy, dx))
# 历史窗口检查 (对准前 0.5s)
current_time = traj_row['Time']
start_time = current_time - lookback_window
history = df_traj[(df_traj['Time'] >= start_time) & (df_traj['Time'] < current_time)]
if len(history) == 0:
return False
for _, row in history.iterrows():
h_dist = calculate_distance(row['X'], row['Y'], target_x, target_y)
if not (10 <= h_dist <= 40): return False
if row['V'] > 1.5: return False
if row['A'] > 1.5: return False
return True, current_angle
# ==========================================
# 第三部分:任务优化主逻辑
# ==========================================
def optimize_tasks(df_traj, targets):
results = []
# 记录每个目标已拍摄的角度,用于拍照去重
target_shot_angles = {tid: [] for tid in targets['TargetID']}
print("开始任务规划...")
# 遍历每一个目标点
for index, target in targets.iterrows():
tid = target['TargetID']
t_type = target['Type']
tx, ty = target['X_Target'], target['Y_Target']
best_time = None
best_score = -1 # 用于寻找最优解,例如距离最近或角度最好
# 遍历轨迹上的每一个时间点
for _, row in df_traj.iterrows():
is_valid = False
score = 0
if t_type == 'Shooting':
if check_shooting_constraints(row, tx, ty):
is_valid = True
# 评分策略:距离越近越好 (在合法范围内),或者速度越稳越好
dist = calculate_distance(row['X'], row['Y'], tx, ty)
score = 100 - dist # 简单评分
elif t_type == 'Photography':
valid, angle = check_photography_constraints(row, tx, ty)
if valid:
# 检查角度是否重复 (至少相差60度)
angles_history = target_shot_angles[tid]
is_new_angle = True
for existing_angle in angles_history:
diff = abs(angle - existing_angle)
if diff > 180: diff = 360 - diff # 处理圆周角
if diff < 60:
is_new_angle = False
break
if is_new_angle or len(angles_history) == 0:
is_valid = True
score = 10 # 拍照只要合法即可,优先保证数量
if is_valid:
# 避免在同一目标的极短时间内重复记录(防抖动)
if best_time is None or (row['Time'] - best_time > 1.0):
best_time = row['Time']
best_score = score
if t_type == 'Photography':
target_shot_angles[tid].append(angle)
# 找到第一个可行解后,对于射击任务通常就可以停止搜索该目标(贪心策略)
# 如果需要全局最优,则需遍历完所有时间
if t_type == 'Shooting':
break
if best_time is not None:
traj_point = df_traj[df_traj['Time'] == best_time].iloc[0]
results.append({
'TargetID': tid,
'TaskType': t_type,
'ExecTime': best_time,
'RobotX': traj_point['X'],
'RobotY': traj_point['Y'],
'Status': 'Success'
})
else:
results.append({
'TargetID': tid,
'TaskType': t_type,
'ExecTime': None,
'RobotX': None,
'RobotY': None,
'Status': 'Failed (Constraints Not Met)'
})
return pd.DataFrame(results)
# ==========================================
# 第四部分:执行与保存
# ==========================================
if __name__ == "__main__":
# 1. 加载数据
df_traj, targets = load_data()
# 2. 运行优化
result_df = optimize_tasks(df_traj, targets)
# 3. 打印预览
print("\n任务规划结果预览:")
print(result_df)
print(f"\n成功完成任务数: {len(result_df[result_df['Status']=='Success'])} / {len(result_df)}")
# 4. 保存为 Excel (不改变红色文字内容通常指保留原表头格式,这里我们新建一个sheet或覆盖)
# 注意:如果原文件有特定格式要求,建议使用 openpyxl 加载原文件写入
output_filename = 'result.xlsx'
result_df.to_excel(output_filename, index=False)
print(f"\n结果已保存至 {output_filename}")
```
### 代码使用说明与关键点解析
1. **数据接口 (`load_data` 函数)**:
* 代码目前使用 `np.arange` 和三角函数生成了假的轨迹数据。
* **关键操作**:您必须将 `pd.read_excel('problem3_result.xlsx')` 这一行取消注释,并将文件名改为您问题3生成的实际文件名。确保该文件中包含 `Time`, `X`, `Y` 列。如果问题3的结果中没有 `V` (速度) 和 `A` (加速度),代码中的 `np.gradient` 部分需要用来根据 X, Y 计算它们。
2. **约束条件的严格实现**:
* **射击 (Shooting)**:代码不仅检查了当前时刻的距离 ($5 \le d \le 30$)、速度 ($v \le 2$) 和加速度 ($a \le 1.5$),还通过 `lookback_window=1.5` 向前追溯了 1.5 秒的数据,确保在开枪前的瞄准阶段机器人也是稳定的。
* **拍照 (Photography)**:同样检查了距离 ($10 \le d \le 40$)、速度 ($v \le 1.5$) 和加速度 ($a \le 1.5$)。特别增加了 `target_shot_angles` 字典,用于记录每个目标已经拍摄过的角度。如果新计算的角度与旧角度差值小于 60 度,则判定为无效,从而满足“不同角度”的约束。
3. **结果输出**:
* 生成的 `result.xlsx` 将包含 `TargetID`, `TaskType`, `ExecTime` (执行时间), `RobotX/Y` (机器人坐标) 以及 `Status`。
* 这符合题目要求的“尽可能多地完成任务”并填入表格。
### 如何运行
1. 将上述代码保存为 `solve_problem4.py`。
2. 确保同目录下有您的 `问题3结果.xlsx` 和 `附件4.xlsx`(或者在代码中修改路径)。
3. 运行脚本,它将自动生成 `result.xlsx`。