news 2026/4/25 18:31:20

模拟激光雷达线束打在图像上的效果(c代码实时交互)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
模拟激光雷达线束打在图像上的效果(c代码实时交互)

<!DOCTYPE html>

<html lang="zh-CN">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>64线激光雷达投影模拟器</title>

<script src="https://cdn.tailwindcss.com"></script>

<style>

body { 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[type=range] { width: 100%; accent-color: #22c55e; }

</style>

</head>

<body class="flex flex-col h-screen overflow-hidden">

<!-- 顶部导航栏 -->

<div class="bg-gray-800 p-4 shadow-md z-10 flex justify-between items-center">

<h1 class="text-xl font-bold text-green-400 flex items-center gap-2">

<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">

<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />

</svg>

LiDAR 投影模拟器

</h1>

<label class="cursor-pointer bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded shadow transition">

<span>上传图片</span>

<input type="file" id="imageUpload" accept="image/*" class="hidden">

</label>

</div>

<div class="flex flex-1 overflow-hidden">

<!-- 左侧控制面板 -->

<div class="w-80 bg-gray-900 p-6 overflow-y-auto border-r border-gray-700 flex-shrink-0">

<h2 class="text-lg font-semibold mb-4 text-gray-300">参数控制</h2>

<!-- 深度控制 -->

<div class="slider-container">

<label class="block text-sm font-medium mb-1 flex justify-between">

模拟平面深度 (米)

<span id="val-depth" class="text-green-400">5.0</span>

</label>

<input type="range" id="depth" min="1" max="50" step="0.5" value="5.0">

<p class="text-xs text-gray-500 mt-1">模拟激光打在前方多远的平面上</p>

</div>

<!-- 焦距控制 -->

<div class="slider-container">

<label class="block text-sm font-medium mb-1 flex justify-between">

相机焦距系数 (Zoom)

<span id="val-focal" class="text-green-400">0.8</span>

</label>

<input type="range" id="focal" min="0.1" max="2.0" step="0.05" value="0.8">

<p class="text-xs text-gray-500 mt-1">值越大,视野越窄 (Telephoto)</p>

</div>

<!-- 点大小 -->

<div class="slider-container">

<label class="block text-sm font-medium mb-1 flex justify-between">

激光点大小

<span id="val-size" class="text-green-400">4</span>

</label>

<input type="range" id="pointSize" min="1" max="20" step="1" value="4">

</div>

<!-- 弯曲度控制 (新增) -->

<div class="slider-container">

<label class="block text-sm font-medium mb-1 flex justify-between">

线束弯曲度 (Curvature)

<span id="val-curvature" class="text-green-400">0.05</span>

</label>

<input type="range" id="curvature" min="0" max="0.3" step="0.01" value="0.05">

<p class="text-xs text-gray-500 mt-1">模拟广角畸变或扫描轨迹弯曲</p>

</div>

<!-- 垂直FOV微调 -->

<div class="slider-container border-t border-gray-700 pt-4 mt-4">

<label class="block text-sm font-medium mb-1 flex justify-between">

垂直视场角偏移 (Pitch)

<span id="val-pitch" class="text-green-400">2.0</span>

</label>

<input type="range" id="pitchOffset" min="-10" max="10" step="0.1" value="2.0">

<p class="text-xs text-gray-500 mt-1">模拟雷达安装的俯仰角微调</p>

</div>

<!-- 水平密度 -->

<div class="slider-container">

<label class="block text-sm font-medium mb-1 flex justify-between">

水平扫描密度

<span id="val-density" class="text-green-400">High</span>

</label>

<input type="range" id="density" min="0.1" max="1.0" step="0.1" value="0.2">

<p class="text-xs text-gray-500 mt-1">值越小点越密</p>

</div>

<div class="mt-6 p-3 bg-gray-800 rounded text-xs text-gray-400">

<p>💡 说明:本工具基于针孔相机模型,假设激光雷达与相机光心重合。</p>

</div>

</div>

<!-- 右侧画布区域 -->

<div class="flex-1 bg-black flex items-center justify-center p-4 relative overflow-auto" id="canvasContainer">

<div id="placeholderText" class="text-gray-500 text-center pointer-events-none">

<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 mx-auto mb-2 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">

<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 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" />

</svg>

<p>请点击右上角上传图片</p>

</div>

<canvas id="canvas" class="max-w-full max-h-full hidden"></canvas>

</div>

</div>

<script>

// 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轴向下为正,雷达仰角对应负Y

const 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>

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 18:29:22

dubbo系统学习总结

Dubbo 系统学习总结学习背景&#xff1a; 3–5 年开发经验项目中正在使用 Dubbo目标&#xff1a;从“会用”到“理解设计与架构取舍”一、Dubbo 的核心定位 一句话定义&#xff1a;Dubbo 是一个面向接口、以 RPC 为核心、内建服务治理能力的高性能分布式服务框架。关键特征&…

作者头像 李华
网站建设 2026/4/25 18:28:23

TMS = ERP?

它的本质是&#xff1a;TMS (Transportation Management System&#xff0c;运输管理系统) 是 ERP (Enterprise Resource Planning&#xff0c;企业资源计划) 的一个 垂直子集 (Vertical Subset) **或 ** 外挂模块 (Extended Module) 。ERP 是企业的“大脑”和“账本”&#xf…

作者头像 李华
网站建设 2026/4/25 18:26:25

如何用免费开源方案搭建Windows Syslog服务器实现实时网络监控

如何用免费开源方案搭建Windows Syslog服务器实现实时网络监控 【免费下载链接】visualsyslog Syslog Server for Windows with a graphical user interface 项目地址: https://gitcode.com/gh_mirrors/vi/visualsyslog Visual Syslog Server是一个专为Windows平台设计的…

作者头像 李华
网站建设 2026/4/25 18:22:21

STM32+LVGL触摸屏移植避坑指南:从轮询到中断,如何让UI响应更跟手?

STM32LVGL触摸屏移植性能优化实战&#xff1a;从轮询到中断的全流程改造 在嵌入式UI开发中&#xff0c;流畅的触摸响应往往决定着用户体验的成败。当开发者完成LVGL基础移植后&#xff0c;常会遇到这样的困境&#xff1a;按钮点击勉强可用&#xff0c;但列表滑动卡顿、进度条拖…

作者头像 李华