!DOCTYPE htmlhtml langzh-CNheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0title64线激光雷达投影模拟器/titlescript srchttps://cdn.tailwindcss.com/scriptstylebody { font-family: Segoe UI, Roboto, Helvetica, Arial, sans-serif; background-color: #1a1a1a; color: #e0e0e0; }canvas { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.5); }.slider-container { margin-bottom: 1rem; }input[typerange] { width: 100%; accent-color: #22c55e; }/style/headbody classflex flex-col h-screen overflow-hidden!-- 顶部导航栏 --div classbg-gray-800 p-4 shadow-md z-10 flex justify-between items-centerh1 classtext-xl font-bold text-green-400 flex items-center gap-2svg xmlnshttp://www.w3.org/2000/svg classh-6 w-6 fillnone viewBox0 0 24 24 strokecurrentColorpath stroke-linecapround stroke-linejoinround stroke-width2 dM13 10V3L4 14h7v7l9-11h-7z //svgLiDAR 投影模拟器/h1label classcursor-pointer bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded shadow transitionspan上传图片/spaninput typefile idimageUpload acceptimage/* classhidden/label/divdiv classflex flex-1 overflow-hidden!-- 左侧控制面板 --div classw-80 bg-gray-900 p-6 overflow-y-auto border-r border-gray-700 flex-shrink-0h2 classtext-lg font-semibold mb-4 text-gray-300参数控制/h2!-- 深度控制 --div classslider-containerlabel classblock text-sm font-medium mb-1 flex justify-between模拟平面深度 (米)span idval-depth classtext-green-4005.0/span/labelinput typerange iddepth min1 max50 step0.5 value5.0p classtext-xs text-gray-500 mt-1模拟激光打在前方多远的平面上/p/div!-- 焦距控制 --div classslider-containerlabel classblock text-sm font-medium mb-1 flex justify-between相机焦距系数 (Zoom)span idval-focal classtext-green-4000.8/span/labelinput typerange idfocal min0.1 max2.0 step0.05 value0.8p classtext-xs text-gray-500 mt-1值越大视野越窄 (Telephoto)/p/div!-- 点大小 --div classslider-containerlabel classblock text-sm font-medium mb-1 flex justify-between激光点大小span idval-size classtext-green-4004/span/labelinput typerange idpointSize min1 max20 step1 value4/div!-- 弯曲度控制 (新增) --div classslider-containerlabel classblock text-sm font-medium mb-1 flex justify-between线束弯曲度 (Curvature)span idval-curvature classtext-green-4000.05/span/labelinput typerange idcurvature min0 max0.3 step0.01 value0.05p classtext-xs text-gray-500 mt-1模拟广角畸变或扫描轨迹弯曲/p/div!-- 垂直FOV微调 --div classslider-container border-t border-gray-700 pt-4 mt-4label classblock text-sm font-medium mb-1 flex justify-between垂直视场角偏移 (Pitch)span idval-pitch classtext-green-4002.0/span/labelinput typerange idpitchOffset min-10 max10 step0.1 value2.0p classtext-xs text-gray-500 mt-1模拟雷达安装的俯仰角微调/p/div!-- 水平密度 --div classslider-containerlabel classblock text-sm font-medium mb-1 flex justify-between水平扫描密度span idval-density classtext-green-400High/span/labelinput typerange iddensity min0.1 max1.0 step0.1 value0.2p classtext-xs text-gray-500 mt-1值越小点越密/p/divdiv classmt-6 p-3 bg-gray-800 rounded text-xs text-gray-400p 说明本工具基于针孔相机模型假设激光雷达与相机光心重合。/p/div/div!-- 右侧画布区域 --div classflex-1 bg-black flex items-center justify-center p-4 relative overflow-auto idcanvasContainerdiv idplaceholderText classtext-gray-500 text-center pointer-events-nonesvg xmlnshttp://www.w3.org/2000/svg classh-16 w-16 mx-auto mb-2 opacity-50 fillnone viewBox0 0 24 24 strokecurrentColorpath stroke-linecapround stroke-linejoinround stroke-width2 dM4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z //svgp请点击右上角上传图片/p/divcanvas idcanvas classmax-w-full max-h-full hidden/canvas/div/divscript// DOM 元素const canvas document.getElementById(canvas);const ctx canvas.getContext(2d);const uploadInput document.getElementById(imageUpload);const placeholder document.getElementById(placeholderText);// 参数元素const params {depth: document.getElementById(depth),focal: document.getElementById(focal),pointSize: document.getElementById(pointSize),pitchOffset: document.getElementById(pitchOffset),density: document.getElementById(density),curvature: document.getElementById(curvature) // 新增};// 显示数值的元素const displays {depth: document.getElementById(val-depth),focal: document.getElementById(val-focal),pointSize: document.getElementById(val-size),pitchOffset: document.getElementById(val-pitch),density: document.getElementById(val-density),curvature: document.getElementById(val-curvature) // 新增};// 状态let currentImage null;// 雷达常量const LIDAR_LINES 20;const V_FOV_UP 2.0;const V_FOV_DOWN -24.8;const H_FOV 90.0;// 初始化监听器uploadInput.addEventListener(change, handleImageUpload);// 为所有滑块添加监听Object.keys(params).forEach(key {params[key].addEventListener(input, (e) {// 更新数值显示displays[key].innerText e.target.value;if(key density) {displays[key].innerText parseFloat(e.target.value) 0.3 ? High : Low;}// 重绘draw();});});function handleImageUpload(e) {const file e.target.files[0];if (!file) return;const reader new FileReader();reader.onload function(event) {const img new Image();img.onload function() {currentImage img;placeholder.style.display none;canvas.classList.remove(hidden);// 设置画布尺寸与图片一致canvas.width img.width;canvas.height img.height;draw();}img.src event.target.result;}reader.readAsDataURL(file);}function deg2rad(deg) {return deg * Math.PI / 180;}function draw() {if (!currentImage) return;const width canvas.width;const height canvas.height;// 1. 清除画布并绘制原图ctx.clearRect(0, 0, width, height);ctx.drawImage(currentImage, 0, 0, width, height);// 获取参数const depthBase parseFloat(params.depth.value);const focalFactor parseFloat(params.focal.value);const pointSize parseInt(params.pointSize.value);const pitchOffset parseFloat(params.pitchOffset.value);const densityStep parseFloat(params.density.value);const curvature parseFloat(params.curvature.value); // 获取弯曲度// 相机内参const fx width * focalFactor;const fy width * focalFactor;const cx width / 2.0;const cy height / 2.0;// 绘制样式ctx.fillStyle #00ff00; // 激光绿色// 2. 模拟激光雷达投影const angleStepV (V_FOV_UP - V_FOV_DOWN) / (LIDAR_LINES - 1);for (let i 0; i LIDAR_LINES; i) {// 计算垂直角度 (加上用户的微调偏移)const angleV V_FOV_DOWN i * angleStepV pitchOffset;const radV deg2rad(angleV);// 水平扫描循环for (let angleH -H_FOV / 2.0; angleH H_FOV / 2.0; angleH densityStep) {const radH deg2rad(angleH);// --- 3D 坐标计算 (模拟平面投影) ---// Z轴向前X轴向右Y轴向下// 添加 /- 1.5% 的深度噪声const noiseFactor 0.015;const depthNoise depthBase * noiseFactor * (Math.random() - 0.5) * 2;const Z depthBase depthNoise;// 计算弯曲后的垂直角度// 随着水平角 radH 的增大让垂直角 radV 也稍微增大向上抬// 使用平方项来实现抛物线式的弯曲效果const radVCurved radV curvature * Math.pow(radH, 2);const X Z * Math.tan(radH);// 真实世界Y轴向上为正相机坐标系Y轴向下为正雷达仰角对应负Yconst Y -Z * Math.tan(radVCurved);// --- 投影到 2D 像素 ---const u fx * (X / Z) cx;const v fy * (Y / Z) cy;// 绘制点 (简单的边界检查)if (u 0 u width v 0 v height) {ctx.beginPath();ctx.arc(u, v, pointSize, 0, 2 * Math.PI);ctx.fill();}}}}/script/body/html