【Alpha阶段项目展示】Time Shaft·时间轴

Time Shaft是一个旨在提高团队效率的项目,以时间轴为核心功能,突出团队协作与沟通。项目使用GitHub进行管理,通过飞书、微信群和腾讯会议进行即时沟通。其杀手级功能是独特的时间轴模式,便于记录和查看事件。项目已进行150位测试用户的反馈收集,目前正努力达到预期的500用户目标。团队在文档、测试用例和代码标准方面做了详尽的工作,确保软件工程质量。

Time Shaft·时间轴 Alpha阶段项目展示

一. 项目与团队亮点

1 关于我们👪
1.1 成员与分工
姓名 任务
黄炜 Project Manager、前端开发
徐家乐 前端开发
颜月 前端开发
周子颖 后端开发及后端负责人
陈子睿 后端开发
戴啸天 后端开发、测试
王子萱 Program Manager、前端开发
1.2 项目管理

使用github进行管理,主分支主要进行文档的管理,分支front_dev与分支back_dev分别是前、后端的代码仓库,其他分支用于不同团队成员自己开发所使用,分支reconstruction用于代码重构。

请添加图片描述

每日任务都会新建相应的"issues",每晚由PM统计各成员完成的任务,并关闭相应的issues。

请添加图片描述


使用飞书+微信群+腾讯会议进行即时讨论与项目跟进。

飞书:重要文档的发布、小组会议(e.g. 前/后端)

微信群:即时任务与问题的沟通

腾讯会议:进行全体成员的会议


  • 典型场景1:好友间交互

用户 周自赢、紫睿智、颜王爷、黄上
职业 学生
知识层次和能力 大三计算机系学生,使用手机电脑频繁
动机、目的、困难 写大作业合作项目时候需要频繁交流,发送文件不便,没有团队日程管理
典型场景 对刚发布的大作业项目进行讨论
典型描述 0. 自赢同学在他同为计算机专业的表哥,黄上的推荐下打开了“时间轴·Time Shaft”
1. 自赢同学用自己的邮箱注册了一个账号并且给自己起了一个响亮的名字:擎天柱,并邀请团队所有成员在TimeShaft上注册了账号;
2. 大家在团队中讨论项目内容,在聊天框中输入自己的想法,时不时点击“开启会议”组织小组交流,并将内容保留在时间轴上;
3. 如果忘记了上周三开会发的一篇文档,大家可以打开时间轴找到这次会议并查询相关记录;
4. 最后大家完成了作业,罗老师给了99分,美滋滋!
  • 典型场景2:群组会议记录

用户 徐假乐,戴笑甜
职业 公司员工
知识层次和能力 大学毕业,某个合作项目领导者
动机、目的、困难 对于每次会议记录查找不便,文件需要自己每次分类
典型场景 需要根据上一次会议的内容制定进一步详细的计划;团队中其他成员能够在任意时候快速找到此前某次会议的文件
典型描述 0. 甜甜同学在他同为计算机专业的表弟,黄上的推荐下打开了“时间轴·Time Shaft”
1. 甜甜用自己的邮箱注册了一个账号并且给自己起了一个响亮的名字:啸天;
2. 甜甜和乐乐在团队的整体氛围中成长了不少,在每次会议中积极发言,在时间轴上留下自己展现业绩的机会;
3. 通过时间轴上的日程安排,甜甜和乐乐的工作完成的井井有条;
4. 甜甜和乐乐也不会因为男同事的头像很帅就觉得它是个帅哥,因为Time Shaft的头像都是统一生成的,在alpha阶段删除了上传头像的功能,通过一套脚本根据名称自动生成用户头像,没有被照骗渣男欺骗的可能。

3 杀手功能🥷
  • 独特的时间轴模式:添加记录和好友的动态事件,展现在我们的时间轴当中。时间轴模式的出现让团队推进更加高效、清晰,强调时间结点性,从软需求上提升团队效率。

时间轴样式:


添加时间轴:


  • 对比竞品

说明:❤️表示完全具备,❓表示不完全具备,☠️表示不具备

特色模块 微信 飞书 Time Shaft
创建团队 ❤️ ❤️ ❤️
好友/团队列表 ❤️ ❤️ ❤️
好友/团队邀请、添加 ❤️ ❤️ ❤️
聊天列表 ❤️ ❤️ ❤️
即时通讯 ❤️ ❤️ ❤️
好友时间轴 ☠️ ☠️ ❤️
静态日历 ☠️ ❤️

4 项目发布💻
4.1 宣传产品制作

4.2 测试用户

本阶段共150位测试用户。
请添加图片描述

4.3 用户反馈

我们通过邮箱收集到了来自用户的反馈信息,挑选了几则展示:

在此感谢提供反馈的用户,为我们接下来的开发带来了宝贵性意见。


5 软件工程质量💯

团队代码的软件工程质量如何?如何用数据来证明?

5.1 文档

项目有完善的API设计文档和数据库设计文档,Alpha阶段共6篇。Beta阶段将继续扩充架构文档。

请添加图片描述

5.2 测试用例

测试思路一:人工测试

1、页面名称title是否正确
2、当前位置是否可见 您的位置:xxx>xxxx
3、文字格式统一性
4、排版是否整齐
5、列表项显示字段是否齐全,列表项字段名称是否跟表单统一
6、同一页面,是否出现 字段名称相同、值取不同的问
7、不输入的查询
8、模糊查询(输入部分字段,或者说,输入英文字母,查询到相关中文数据)
9、输入不存在的查询
10、页面是否被挤出的测试(都输入长英文字符串,是否断行)
11、数据库最大字符(都输入汉字、都输入英文、混合……)
12、添加动作检查范围:失败:是否提示


测试思路二:脚本测试

​ 助firebug工具,在Script Tab上,在$(“#nextButton”).attr(“disabled”, “disabled”);这行脚本设置disable, 点击nextButton,检查运行到断点处停止,按钮无法再次点击。运行断点后, disable解除。

JS:Ajax 应用程序通常涉及JavaScript、XML和按需信息检索。它们的规模常常超过正常应用程序,并且运行起来很像是桌面应用程序。因为大量使用 JavaScript,所以您将发现标准漏洞估计程序将无法覆盖所有可能的攻击方式。就像二进制程序测试一样,我们需要使用调试器来跟踪代码、分析程序结 构和调查潜在的问题,Firebug为我们提供了所有这些功能

DOM:DOM是存放Web 应用程序的内容的地方。DOM结构提供了动态编辑页面的所有必要功能,比如去除和插入html 元素、初始化计时器、创建和删除Cookie等等。DOM 是所有的Web 应用程序最为复杂的组件,所以对它的检查也是最难的。然而,Firebug 能够提供有用的DOM 视图,它的使用跟DOM Inspector一样


测试思路三:压力测试

​ 使用webbench,Webbench能测试处

import numpy as np import pandas as pd from deap import base, tools, algorithms, creator import matplotlib.pyplot as plt import matplotlib.animation as animation # 设置中文字体支持 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # ================== 参数设置 ================== input_damage_file = 'C:/Users/1/Desktop/附件6-问题二答案表.xlsx' output_result_file = '优化后的功率分配_问题三加权损伤_含噪声约束.xlsx' # S-N曲线参数 S0_shaft = 1e8 m_shaft = 3 S0_tower = 1.2e8 m_tower = 4 # 权重 weight_shaft = 0.6 weight_tower = 0.4 # 新增文件路径 true_file = r'C:/Users/1/Desktop/问题四_延时补偿后数据.xlsx' measured_file = r'C:/Users/1/Desktop/附件3-噪声和延迟作用下的采集数据.xlsx' # 每台风机数据行数 rows_per_turbine = 2000 num_turbines = 100 num_time_steps = 2000 # ================== 读取问题二输出的载荷数据 ================== def load_damage_data(file_path): shaft_df = pd.read_excel(file_path, sheet_name='主轴扭矩', header=0) shaft_torques = shaft_df.iloc[:, 1:].values.T tower_df = pd.read_excel(file_path, sheet_name='塔架推力', header=0) tower_forces = tower_df.iloc[:, 1:].values.T return shaft_torques, tower_forces # ================== 新增:读取真实功率与风速 + 测量功率与风速 ================== def load_true_and_measured_data(true_path, measured_path): true_data = pd.read_excel(true_path, header=None).values measured_data = pd.read_excel(measured_path, header=None).values true_power_list = [] true_wind_list = [] measured_power_list = [] measured_wind_list = [] for i in range(num_turbines): start = i * rows_per_turbine end = start + rows_per_turbine true_power_list.append(true_data[start:end, 1]) true_wind_list.append(true_data[start:end, 2]) measured_power_list.append(measured_data[start:end, 1]) measured_wind_list.append(measured_data[start:end, 2]) return { 'true_power': np.array(true_power_list), 'true_wind': np.array(true_wind_list), 'measured_power': np.array(measured_power_list), 'measured_wind': np.array(measured_wind_list) } # ================== 疲劳损伤计算函数 ================== def calculate_shaft_damage(torque): stress = abs(torque) return (stress / S0_shaft) ** m_shaft def calculate_tower_damage(force): stress = abs(force) return (stress / S0_tower) ** m_tower # ================== 目标函数(新增:噪声约束惩罚项) ================== def objective_function(individual, shaft_torques, tower_forces, total_power, true_power, true_wind, measured_power, measured_wind): powers = np.clip(individual, 0, 5e6) if abs(np.sum(powers) - total_power) > 1e4: return 1e6, shaft_damages = [calculate_shaft_damage(t) for t in shaft_torques] tower_damages = [calculate_tower_damage(f) for f in tower_forces] avg_shaft = np.mean(shaft_damages) avg_tower = np.mean(tower_damages) total_damage = weight_shaft * avg_shaft + weight_tower * avg_tower power_error = np.abs(powers - measured_power) <= 0.1 * np.abs(true_power) wind_error = np.abs(powers - measured_wind) <= 0.1 * np.abs(true_wind) power_penalty = 1e3 * np.sum(~power_error) wind_penalty = 1e3 * np.sum(~wind_error) final_objective = total_damage + power_penalty + wind_penalty return final_objective, # ================== 功率限制函数 ================== def get_bounds(avg_power): low = max(0, avg_power - 1e6) high = min(5e6, avg_power + 1e6) return [(low, high)] * 100 # ================== 遗传算法优化器 ================== def optimize_ga(shaft_torques, tower_forces, power_initial, true_power, true_wind, measured_power, measured_wind): total_power = np.sum(power_initial) avg_power = total_power / 100 bounds = get_bounds(avg_power) creator.create("FitnessMin", base.Fitness, weights=(-1.0,)) creator.create("Individual", list, fitness=creator.FitnessMin) toolbox = base.Toolbox() for i in range(100): toolbox.register(f"attr_{i}", np.random.uniform, bounds[i][0], bounds[i][1]) toolbox.register("individual", tools.initCycle, creator.Individual, [getattr(toolbox, f"attr_{i}") for i in range(100)], n=1) toolbox.register("population", tools.initRepeat, list, toolbox.individual) toolbox.register("evaluate", objective_function, shaft_torques=shaft_torques, tower_forces=tower_forces, total_power=total_power, true_power=true_power, true_wind=true_wind, measured_power=measured_power, measured_wind=measured_wind) toolbox.register("mate", tools.cxSimulatedBinaryBounded, low=[b[0] for b in bounds], up=[b[1] for b in bounds], eta=20.0) toolbox.register("mutate", tools.mutPolynomialBounded, low=[b[0] for b in bounds], up=[b[1] for b in bounds], eta=20.0, indpb=0.1) toolbox.register("select", tools.selTournament, tournsize=3) pop = toolbox.population(n=50) hof = tools.HallOfFame(1) stats = tools.Statistics(lambda ind: ind.fitness.values) stats.register("avg", np.mean) stats.register("min", np.min) algorithms.eaSimple(pop, toolbox, cxpb=0.7, mutpb=0.3, ngen=50, stats=stats, halloffame=hof, verbose=False) min_bounds = np.array([b[0] for b in bounds]) max_bounds = np.array([b[1] for b in bounds]) best_powers = np.clip(hof[0], min_bounds, max_bounds) best_damage = objective_function(best_powers, shaft_torques, tower_forces, total_power, true_power, true_wind, measured_power, measured_wind)[0] return best_powers, best_damage # ================== 主程序入口 ================== if __name__ == "__main__": # Step 1: 加载问题二的数据 shaft_torques, tower_forces = load_damage_data(input_damage_file) # Step 2: 加载真实功率与风速、测量功率与风速 data_dict = load_true_and_measured_data(true_file, measured_file) true_power = data_dict['true_power'] true_wind = data_dict['true_wind'] measured_power = data_dict['measured_power'] measured_wind = data_dict['measured_wind'] optimized_results = [] raw_power_data = [] # 用于保存原始测量功率 # 存储历史数据用于动画展示 history = { 'time_step': [], 'total_power': [], 'damage_before': [], 'damage_after': [], 'constraint_ok': [] } print("开始逐时间步进行功率优化分配...") fig, axs = plt.subplots(3, 1, figsize=(10, 8)) def animate(i): if i >= len(history['time_step']): return axs[0].clear() axs[1].clear() axs[2].clear() time_steps = history['time_step'][:i+1] powers = history['total_power'][:i+1] damage_before = history['damage_before'][:i+1] damage_after = history['damage_after'][:i+1] constraints = history['constraint_ok'][:i+1] axs[0].plot(time_steps, powers, label='总功率', color='blue') axs[0].set_title(f"时间步: {time_steps[-1]} | 总功率: {powers[-1]:.2f} W") axs[0].legend() axs[1].plot(time_steps, damage_before, label='优化前损伤', color='orange') axs[1].plot(time_steps, damage_after, label='优化后损伤', color='green') axs[1].set_title("主轴+塔架加权损伤") axs[1].legend() status = "满足" if constraints[-1] else "不满足" color = "green" if constraints[-1] else "red" axs[2].text(0.5, 0.5, f"约束条件: {status}", fontsize=20, ha='center', va='center', color=color) axs[2].axis('off') plt.tight_layout() def run_animation(t): print(f"处理第 {t + 1} 时间步...") shaft_t = shaft_torques[:, t] tower_f = tower_forces[:, t] power_initial = measured_power[:, t] raw_power_data.append(power_initial.copy()) # 保存原始测量功率 true_p = true_power[:, t] true_w = true_wind[:, t] measured_p = measured_power[:, t] measured_w = measured_wind[:, t] opt_powers, damage_after = optimize_ga(shaft_t, tower_f, power_initial, true_p, true_w, measured_p, measured_w) total_power_after = np.sum(opt_powers) constraint_ok = abs(np.sum(opt_powers) - np.sum(power_initial)) < 1e4 damage_before_val = weight_shaft * np.mean([calculate_shaft_damage(t) for t in shaft_t]) + \ weight_tower * np.mean([calculate_tower_damage(f) for f in tower_f]) damage_after_val = objective_function(opt_powers, shaft_t, tower_f, np.sum(power_initial), true_p, true_w, measured_p, measured_w)[0] history['time_step'].append(t + 1) history['total_power'].append(total_power_after) history['damage_before'].append(damage_before_val) history['damage_after'].append(damage_after_val) history['constraint_ok'].append(constraint_ok) optimized_results.append(opt_powers) for t in range(num_time_steps): run_animation(t) # 保存动画为 GIF ani = animation.FuncAnimation(fig, animate, frames=num_time_steps, interval=1000, repeat=False) ani.save('real_time_optimization_with_noise_constraint.gif', writer='pillow') plt.close() # 保存结果 optimized_array = np.array(optimized_results) raw_array = np.array(raw_power_data) # 构建 DataFrame time_column = np.arange(1, num_time_steps + 1).reshape(-1, 1) output_data = np.hstack([time_column, optimized_array]) columns = ['时间'] + [f'风机{i+1}' for i in range(100)] result_df = pd.DataFrame(output_data, columns=columns) result_df.to_excel(output_result_file, index=False) print(f"优化完成,结果已保存至 {output_result_file}") # ================== 新增:绘制对比图 ================== # 随机挑选两个风机 turbine_indices = np.random.choice(num_turbines, 2, replace=False) # 1. 功率分配对比图 plt.figure(figsize=(14, 6)) for i, idx in enumerate(turbine_indices): plt.subplot(2, 1, i + 1) plt.plot(raw_array[:, idx], label='原始功率(含噪声)', alpha=0.7) plt.plot(optimized_array[:, idx], label='优化后功率', linewidth=2) plt.title(f'风机 {idx + 1} 功率分配对比') plt.xlabel('时间步') plt.ylabel('功率 (W)') plt.legend() plt.tight_layout() plt.savefig('功率分配对比图.png') plt.show() # 2. 累计疲劳值对比图 def calculate_cumulative_damage(power_array): damage = [] for t in range(num_time_steps): shaft_damages = [calculate_shaft_damage(shaft_torques[i, t]) for i in range(num_turbines)] tower_damages = [calculate_tower_damage(tower_forces[i, t]) for i in range(num_turbines)] total = weight_shaft * np.mean(shaft_damages) + weight_tower * np.mean(tower_damages) damage.append(total) return np.cumsum(damage) raw_damage = calculate_cumulative_damage(raw_array) opt_damage = calculate_cumulative_damage(optimized_array) plt.figure(figsize=(10, 6)) plt.plot(raw_damage, label='原始功率下的累计疲劳值', alpha=0.8) plt.plot(opt_damage, label='优化后功率下的累计疲劳值', linewidth=2) plt.title('降噪与延迟处理前后的累计疲劳值对比') plt.xlabel('时间步') plt.ylabel('累计疲劳值') plt.legend() plt.grid(True) plt.tight_layout() plt.savefig('累计疲劳值对比图.png') plt.show() 累计疲劳的对比图中没有显示原始功率下的累计疲劳总值曲线
最新发布
07-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值