script.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. // 伺服驱动选型工具 - 核心计算逻辑
  2. class ServoSizer {
  3. constructor() {
  4. this.currentType = 'ballScrew';
  5. this.init();
  6. }
  7. init() {
  8. this.bindEvents();
  9. this.updateUI();
  10. this.loadDefaults();
  11. }
  12. bindEvents() {
  13. // 传动类型切换
  14. document.querySelectorAll('.transmission-type').forEach(btn => {
  15. btn.addEventListener('click', (e) => {
  16. this.currentType = e.target.dataset.type;
  17. this.updateUI();
  18. this.clearResults();
  19. });
  20. });
  21. // 输入验证
  22. document.querySelectorAll('input[type="number"]').forEach(input => {
  23. input.addEventListener('input', () => {
  24. this.validateInput(input);
  25. });
  26. });
  27. // 计算按钮
  28. document.getElementById('calculateBtn').addEventListener('click', () => {
  29. this.calculate();
  30. });
  31. // 重置按钮
  32. document.getElementById('resetBtn').addEventListener('click', () => {
  33. this.resetForm();
  34. });
  35. // 导出按钮
  36. document.getElementById('exportBtn').addEventListener('click', () => {
  37. this.exportResults();
  38. });
  39. }
  40. validateInput(input) {
  41. const value = parseFloat(input.value);
  42. const min = parseFloat(input.min) || -Infinity;
  43. const max = parseFloat(input.max) || Infinity;
  44. if (value < min || value > max) {
  45. input.classList.add('error');
  46. } else {
  47. input.classList.remove('error');
  48. }
  49. }
  50. loadDefaults() {
  51. // 加载默认值
  52. const defaults = {
  53. friction: 0.1,
  54. efficiency: 0.9,
  55. safetyFactor: 1.5,
  56. inertiaRatio: 10
  57. };
  58. Object.keys(defaults).forEach(key => {
  59. const input = document.querySelector(`[name="${key}"]`);
  60. if (input) input.value = defaults[key];
  61. });
  62. }
  63. updateUI() {
  64. // 隐藏所有传动类型表单
  65. document.querySelectorAll('.transmission-form').forEach(form => {
  66. form.style.display = 'none';
  67. });
  68. // 显示当前选中的传动类型表单
  69. const currentForm = document.getElementById(`${this.currentType}Form`);
  70. if (currentForm) currentForm.style.display = 'block';
  71. // 更新按钮状态
  72. document.querySelectorAll('.transmission-type').forEach(btn => {
  73. btn.classList.remove('active');
  74. });
  75. document.querySelector(`[data-type="${this.currentType}"]`).classList.add('active');
  76. // 更新标题
  77. const titles = {
  78. ballScrew: '滚珠丝杠传动',
  79. timingBelt: '同步带传动',
  80. gearbox: '减速机传动',
  81. turntable: '转盘传动',
  82. winding: '收放卷传动',
  83. directDrive: '直接驱动'
  84. };
  85. document.getElementById('currentTypeTitle').textContent = titles[this.currentType] || '伺服驱动选型';
  86. }
  87. // 滚珠丝杠计算
  88. calculateBallScrew(params) {
  89. const {
  90. loadMass, screwDiameter, screwLead, friction, efficiency,
  91. maxSpeed, accelerationTime, travelDistance
  92. } = params;
  93. // 转换为标准单位
  94. const lead = screwLead / 1000; // mm to m
  95. const diameter = screwDiameter / 1000; // mm to m
  96. // 1. 负载转矩计算
  97. const gravity = 9.81;
  98. const frictionForce = loadMass * gravity * friction;
  99. const frictionTorque = (frictionForce * diameter) / 2;
  100. // 推力转矩 (忽略效率先)
  101. const thrustTorque = (loadMass * gravity * lead) / (2 * Math.PI);
  102. const totalTorque = (thrustTorque + frictionTorque) / efficiency;
  103. // 2. 转速计算
  104. const maxRpm = (maxSpeed * 60) / (lead * 1000); // mm/s to rpm
  105. // 3. 惯量计算
  106. const screwInertia = (Math.PI * 7800 * Math.pow(diameter, 4) * travelDistance) / (32 * Math.pow(lead, 2));
  107. const loadInertia = loadMass * Math.pow(lead / (2 * Math.PI), 2);
  108. const totalInertia = screwInertia + loadInertia;
  109. // 4. 加速度转矩
  110. const angularAccel = (maxRpm * 2 * Math.PI / 60) / accelerationTime;
  111. const accelTorque = totalInertia * angularAccel;
  112. // 5. 峰值转矩
  113. const peakTorque = totalTorque + accelTorque;
  114. // 6. 功率计算
  115. const power = (peakTorque * maxRpm * 2 * Math.PI) / 60;
  116. return {
  117. speedRange: { min: 0, max: maxRpm },
  118. torque: { continuous: totalTorque, peak: peakTorque },
  119. inertia: { motor: 0, load: totalInertia, ratio: totalInertia / 0.001 }, // 假设电机惯量
  120. power: { required: power, rated: power * 1.2 }
  121. };
  122. }
  123. // 同步带计算
  124. calculateTimingBelt(params) {
  125. const {
  126. loadMass, pulleyDiameter, friction, efficiency,
  127. maxSpeed, accelerationTime
  128. } = params;
  129. const radius = pulleyDiameter / 2000; // mm to m
  130. // 负载转矩
  131. const gravity = 9.81;
  132. const frictionForce = loadMass * gravity * friction;
  133. const loadTorque = (loadMass * gravity * radius + frictionForce * radius) / efficiency;
  134. // 转速
  135. const maxRpm = (maxSpeed * 60) / (Math.PI * pulleyDiameter);
  136. // 惯量
  137. const pulleyInertia = 0.5 * 0.1 * Math.pow(radius, 2); // 假设滑轮质量0.1kg
  138. const loadInertia = loadMass * Math.pow(radius, 2);
  139. const totalInertia = pulleyInertia + loadInertia;
  140. // 加速度转矩
  141. const angularAccel = (maxRpm * 2 * Math.PI / 60) / accelerationTime;
  142. const accelTorque = totalInertia * angularAccel;
  143. const peakTorque = loadTorque + accelTorque;
  144. const power = (peakTorque * maxRpm * 2 * Math.PI) / 60;
  145. return {
  146. speedRange: { min: 0, max: maxRpm },
  147. torque: { continuous: loadTorque, peak: peakTorque },
  148. inertia: { motor: 0, load: totalInertia, ratio: totalInertia / 0.001 },
  149. power: { required: power, rated: power * 1.2 }
  150. };
  151. }
  152. // 减速机计算
  153. calculateGearbox(params) {
  154. const {
  155. loadInertia, loadTorque, gearRatio, efficiency,
  156. maxSpeed, accelerationTime
  157. } = params;
  158. // 折算到电机侧
  159. const reflectedInertia = loadInertia / Math.pow(gearRatio, 2);
  160. const reflectedTorque = loadTorque / (gearRatio * efficiency);
  161. // 电机转速
  162. const motorRpm = maxSpeed * gearRatio;
  163. // 加速度转矩
  164. const angularAccel = (motorRpm * 2 * Math.PI / 60) / accelerationTime;
  165. const accelTorque = reflectedInertia * angularAccel;
  166. const peakTorque = reflectedTorque + accelTorque;
  167. const power = (peakTorque * motorRpm * 2 * Math.PI) / 60;
  168. return {
  169. speedRange: { min: 0, max: motorRpm },
  170. torque: { continuous: reflectedTorque, peak: peakTorque },
  171. inertia: { motor: 0, load: reflectedInertia, ratio: reflectedInertia / 0.001 },
  172. power: { required: power, rated: power * 1.2 }
  173. };
  174. }
  175. // 转盘计算
  176. calculateTurntable(params) {
  177. const {
  178. tableMass, tableRadius, friction, efficiency,
  179. maxSpeed, accelerationTime
  180. } = params;
  181. const radius = tableRadius / 1000; // mm to m
  182. // 转盘惯量 (实心圆盘)
  183. const tableInertia = 0.5 * tableMass * Math.pow(radius, 2);
  184. // 摩擦转矩
  185. const gravity = 9.81;
  186. const frictionTorque = tableMass * gravity * friction * radius;
  187. // 转速
  188. const maxRpm = maxSpeed;
  189. // 加速度转矩
  190. const angularAccel = (maxRpm * 2 * Math.PI / 60) / accelerationTime;
  191. const accelTorque = tableInertia * angularAccel;
  192. const peakTorque = (frictionTorque + accelTorque) / efficiency;
  193. const power = (peakTorque * maxRpm * 2 * Math.PI) / 60;
  194. return {
  195. speedRange: { min: 0, max: maxRpm },
  196. torque: { continuous: frictionTorque / efficiency, peak: peakTorque },
  197. inertia: { motor: 0, load: tableInertia, ratio: tableInertia / 0.001 },
  198. power: { required: power, rated: power * 1.2 }
  199. };
  200. }
  201. // 收放卷计算
  202. calculateWinding(params) {
  203. const {
  204. materialDensity, materialWidth, initialDiameter, finalDiameter,
  205. lineSpeed, tension, accelerationTime
  206. } = params;
  207. const initialRadius = initialDiameter / 2000; // mm to m
  208. const finalRadius = finalDiameter / 2000;
  209. const width = materialWidth / 1000;
  210. // 卷筒惯量 (空心圆柱)
  211. const initialMass = materialDensity * Math.PI * (Math.pow(finalRadius, 2) - Math.pow(initialRadius, 2)) * width;
  212. const initialInertia = 0.5 * initialMass * (Math.pow(finalRadius, 2) + Math.pow(initialRadius, 2));
  213. // 张力转矩
  214. const tensionTorque = tension * finalRadius;
  215. // 转速范围
  216. const minRpm = (lineSpeed * 60) / (Math.PI * finalDiameter);
  217. const maxRpm = (lineSpeed * 60) / (Math.PI * initialDiameter);
  218. // 加速度转矩 (使用最大惯量)
  219. const angularAccel = (maxRpm * 2 * Math.PI / 60) / accelerationTime;
  220. const accelTorque = initialInertia * angularAccel;
  221. const peakTorque = tensionTorque + accelTorque;
  222. const power = (peakTorque * maxRpm * 2 * Math.PI) / 60;
  223. return {
  224. speedRange: { min: minRpm, max: maxRpm },
  225. torque: { continuous: tensionTorque, peak: peakTorque },
  226. inertia: { motor: 0, load: initialInertia, ratio: initialInertia / 0.001 },
  227. power: { required: power, rated: power * 1.2 }
  228. };
  229. }
  230. // 直接驱动计算
  231. calculateDirectDrive(params) {
  232. const {
  233. loadInertia, frictionTorque, maxSpeed, accelerationTime
  234. } = params;
  235. // 直接驱动,无传动比
  236. const motorRpm = maxSpeed;
  237. // 加速度转矩
  238. const angularAccel = (motorRpm * 2 * Math.PI / 60) / accelerationTime;
  239. const accelTorque = loadInertia * angularAccel;
  240. const peakTorque = frictionTorque + accelTorque;
  241. const power = (peakTorque * motorRpm * 2 * Math.PI) / 60;
  242. return {
  243. speedRange: { min: 0, max: motorRpm },
  244. torque: { continuous: frictionTorque, peak: peakTorque },
  245. inertia: { motor: 0, load: loadInertia, ratio: loadInertia / 0.001 },
  246. power: { required: power, rated: power * 1.2 }
  247. };
  248. }
  249. getFormValues() {
  250. const form = document.getElementById(`${this.currentType}Form`);
  251. const inputs = form.querySelectorAll('input[type="number"]');
  252. const values = {};
  253. inputs.forEach(input => {
  254. const name = input.name;
  255. const value = parseFloat(input.value) || 0;
  256. values[name] = value;
  257. });
  258. // 获取通用参数
  259. const friction = parseFloat(document.querySelector('[name="friction"]').value) || 0.1;
  260. const efficiency = parseFloat(document.querySelector('[name="efficiency"]').value) || 0.9;
  261. const safetyFactor = parseFloat(document.querySelector('[name="safetyFactor"]').value) || 1.5;
  262. const inertiaRatio = parseFloat(document.querySelector('[name="inertiaRatio"]').value) || 10;
  263. return { ...values, friction, efficiency, safetyFactor, inertiaRatio };
  264. }
  265. calculate() {
  266. const params = this.getFormValues();
  267. // 验证输入
  268. if (!this.validateInputs(params)) {
  269. this.showAlert('请检查输入参数是否有效', 'error');
  270. return;
  271. }
  272. let results;
  273. try {
  274. switch (this.currentType) {
  275. case 'ballScrew':
  276. results = this.calculateBallScrew(params);
  277. break;
  278. case 'timingBelt':
  279. results = this.calculateTimingBelt(params);
  280. break;
  281. case 'gearbox':
  282. results = this.calculateGearbox(params);
  283. break;
  284. case 'turntable':
  285. results = this.calculateTurntable(params);
  286. break;
  287. case 'winding':
  288. results = this.calculateWinding(params);
  289. break;
  290. case 'directDrive':
  291. results = this.calculateDirectDrive(params);
  292. break;
  293. default:
  294. throw new Error('未知的传动类型');
  295. }
  296. // 应用安全系数
  297. results.torque.continuous *= params.safetyFactor;
  298. results.torque.peak *= params.safetyFactor;
  299. results.power.rated *= params.safetyFactor;
  300. this.displayResults(results);
  301. this.showAlert('计算完成!', 'success');
  302. } catch (error) {
  303. console.error('计算错误:', error);
  304. this.showAlert('计算出错: ' + error.message, 'error');
  305. }
  306. }
  307. validateInputs(params) {
  308. // 基本验证
  309. for (let key in params) {
  310. if (params[key] < 0) return false;
  311. if (isNaN(params[key])) return false;
  312. }
  313. return true;
  314. }
  315. displayResults(results) {
  316. const resultsDiv = document.getElementById('results');
  317. resultsDiv.innerHTML = `
  318. <div class="result-card">
  319. <h3>计算结果</h3>
  320. <div class="result-grid">
  321. <div class="result-item">
  322. <label>转速范围 (RPM)</label>
  323. <div class="result-value">${results.speedRange.min.toFixed(1)} - ${results.speedRange.max.toFixed(1)}</div>
  324. </div>
  325. <div class="result-item">
  326. <label>连续转矩 (N·m)</label>
  327. <div class="result-value">${results.torque.continuous.toFixed(3)}</div>
  328. </div>
  329. <div class="result-item">
  330. <label>峰值转矩 (N·m)</label>
  331. <div class="result-value">${results.torque.peak.toFixed(3)}</div>
  332. </div>
  333. <div class="result-item">
  334. <label>负载惯量 (kg·m²)</label>
  335. <div class="result-value">${results.inertia.load.toExponential(3)}</div>
  336. </div>
  337. <div class="result-item">
  338. <label>惯量比</label>
  339. <div class="result-value">${results.inertia.ratio.toFixed(1)}</div>
  340. </div>
  341. <div class="result-item">
  342. <label>所需功率 (W)</label>
  343. <div class="result-value">${results.power.required.toFixed(1)}</div>
  344. </div>
  345. <div class="result-item">
  346. <label>额定功率 (W)</label>
  347. <div class="result-value">${results.power.rated.toFixed(1)}</div>
  348. </div>
  349. </div>
  350. </div>
  351. `;
  352. resultsDiv.style.display = 'block';
  353. }
  354. clearResults() {
  355. document.getElementById('results').style.display = 'none';
  356. }
  357. resetForm() {
  358. document.querySelectorAll('input[type="number"]').forEach(input => {
  359. input.value = input.defaultValue || '';
  360. input.classList.remove('error');
  361. });
  362. this.clearResults();
  363. this.loadDefaults();
  364. }
  365. exportResults() {
  366. const resultsDiv = document.getElementById('results');
  367. if (resultsDiv.style.display === 'none') {
  368. this.showAlert('请先进行计算', 'warning');
  369. return;
  370. }
  371. const results = resultsDiv.innerText;
  372. const blob = new Blob([results], { type: 'text/plain' });
  373. const url = URL.createObjectURL(blob);
  374. const a = document.createElement('a');
  375. a.href = url;
  376. a.download = `servo_sizing_results_${new Date().toISOString().slice(0, 10)}.txt`;
  377. document.body.appendChild(a);
  378. a.click();
  379. document.body.removeChild(a);
  380. URL.revokeObjectURL(url);
  381. }
  382. showAlert(message, type = 'info') {
  383. const alertDiv = document.createElement('div');
  384. alertDiv.className = `alert alert-${type}`;
  385. alertDiv.textContent = message;
  386. document.body.appendChild(alertDiv);
  387. setTimeout(() => {
  388. alertDiv.remove();
  389. }, 3000);
  390. }
  391. }
  392. // 初始化应用
  393. document.addEventListener('DOMContentLoaded', () => {
  394. new ServoSizer();
  395. });