发帖
7 1 0

【安信可雷达模组Rd-03_V2】网页距离监控

无垠的广袤
论坛元老

45

主题

80

回帖

5270

积分

论坛元老

积分
5270
QQ
Rd-03系列 153 7 前天 11:40

【安信可雷达模组Rd-03_V2】网页距离监控

本文介绍了安信可 Rd-03 V2 雷达模组通过网页上位机实现距离监控的项目设计。

项目介绍

通过 Web 网页上位机接收和解析 Rd-03 V2 雷达测距信息、历史演化曲线、距离统计信息等,并优化显示界面,直观地反馈采集的数据,便于开发者在各种场景和环境条件下的快速应用。

  • 硬件连接
  • 流程图
  • 代码
  • 演示

硬件连接

使用 USB 转 TTL 工具连接雷达模块和电脑,接线方式如下

Rd-03 V2 序号(名称) USB 转 TTL 引脚 说明
1(3V3) 3V3 供电
2(GND) GND 接地
3(TXD) RXD 数据发送端
4(RXD) TXD 数据接收端
5(OT2) None 检测结果(高低电平)输出

实物图

usb-ttl.jpg

流程图

graph TD A[启动网页] --> D[枚举串口列表] D --> E[连接串口] E --> I[启动读取循环] I --> J{收到一行数据} J --> L[解析距离值] L --> M[更新当前距离] M --> N[加入曲线缓冲区] N --> O[绘制演化曲线] O --> P[更新统计值] P --> J

代码

新建 HTML 文件并添加如下代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>雷达模块距离监控Web上位机</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
    <style>
        :root {
            --bg: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
            --glass: rgba(255, 255, 255, 0.1);
            --accent: #4ade80;
        }
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body {
            font-family: Arial, sans-serif;
            background: var(--bg);
            color: #fff;
            min-height: 100vh;
            padding: 20px;
        }
        .container {
            max-width: 1200px;
            margin: auto;
            background: var(--glass);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 30px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.37);
            border: 1px solid rgba(255, 255, 255, 0.18);
        }
        h1 {
            text-align: center;
            margin-bottom: 20px;
            font-size: 2.2em;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
        }
        .controls {
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
            justify-content: center;
            margin-bottom: 20px;
        }
        .control-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        label {
            font-size: 0.9em;
            opacity: 0.9;
        }
        select, button {
            padding: 8px 16px;
            border: none;
            border-radius: 6px;
            background: rgba(255, 255, 255, 0.2);
            color: #fff;
            cursor: pointer;
            transition: background 0.3s;
        }
        select:hover, button:hover {
            background: rgba(255, 255, 255, 0.3);
        }
        button:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }
        .status {
            text-align: center;
            margin-bottom: 15px;
            font-size: 1.1em;
        }
        .connected {
            color: var(--accent);
        }
        .disconnected {
            color: #f87171;
        }
        .panels {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
            margin-bottom: 20px;
        }
        .panel {
            background: rgba(0, 0, 0, 0.25);
            border-radius: 12px;
            padding: 15px;
        }
        .panel h2 {
            font-size: 1.1em;
            margin-bottom: 10px;
            opacity: 0.9;
        }
        /* ===== 极简距离显示 ===== */
        #distanceValue {
            font-size: 5em;
            font-weight: bold;
            color: var(--accent);
            text-align: center;
            text-shadow: 0 0 12px var(--accent);
            margin-top: 20px;
        }
        #curveCanvas {
            max-height: 260px;
        }
        .stats {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
            gap: 15px;
            margin-bottom: 20px;
        }
        .stat-card {
            background: var(--glass);
            border-radius: 8px;
            padding: 12px;
            text-align: center;
        }
        .stat-label {
            font-size: 0.85em;
            opacity: 0.8;
            margin-bottom: 4px;
        }
        .stat-value {
            font-size: 1.4em;
            font-weight: bold;
            color: var(--accent);
        }
        .data-log {
            background: rgba(0, 0, 0, 0.5);
            border-radius: 10px;
            padding: 15px;
            height: 160px;
            overflow-y: auto;
            font-family: Courier New, monospace;
            font-size: 0.85em;
        }
        .log-entry {
            margin-bottom: 4px;
            opacity: 0.8;
        }
        .log-entry.recent {
            opacity: 1;
            color: var(--accent);
        }
        @media (max-width: 900px) {
            .panels {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🛰️ 安信可 Rd-03 V2 雷达模块距离监控</h1>

        <div class="controls">
            <div class="control-group">
                <label>波特率</label>
                <select id="baudRate">
                    <option value="9600">9600</option>
                    <option value="19200">19200</option>
                    <option value="38400">38400</option>
                    <option value="57600">57600</option>
                    <option value="115200" selected>115200</option>
                </select>
            </div>
            <div class="control-group">
                <label>&nbsp;</label>
                <button id="connectBtn" onclick="connectSerial()">连接串口</button>
            </div>
            <div class="control-group">
                <label>&nbsp;</label>
                <button id="disconnectBtn" onclick="disconnectSerial()" disabled>断开连接</button>
            </div>
        </div>

        <div class="status disconnected" id="status">未连接</div>

        <div class="panels">
            <div class="panel">
                <h2>当前距离</h2>
                <div id="distanceValue">--</div>
            </div>
            <div class="panel">
                <h2>距离演化曲线</h2>
                <canvas id="curveCanvas"></canvas>
            </div>
        </div>

        <div class="stats">
            <div class="stat-card">
                <div class="stat-label">平均距离</div>
                <div class="stat-value" id="avgDistance">--</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">最大距离</div>
                <div class="stat-value" id="maxDistance">--</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">最小距离</div>
                <div class="stat-value" id="minDistance">--</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">数据计数</div>
                <div class="stat-value" id="dataCount">0</div>
            </div>
        </div>

        <div class="data-log" id="dataLog">
            <div class="log-entry">等待数据…</div>
        </div>
    </div>

    <script>
        /* ================= 串口 & 业务逻辑 ================= */
        let port = null, reader = null;
        let dataHistory = [];
        const maxLogEntries = 100;
        const chartMaxPoints = 150;

        function updateStatus(connected) {
            const s = document.getElementById('status');
            const connectBtn = document.getElementById('connectBtn');
            const disconnectBtn = document.getElementById('disconnectBtn');
            s.textContent = connected ? '已连接' : '未连接';
            s.className = connected ? 'status connected' : 'status disconnected';
            connectBtn.disabled = connected;
            disconnectBtn.disabled = !connected;
        }

        function addLogEntry(msg, recent = false) {
            const log = document.getElementById('dataLog');
            const div = document.createElement('div');
            div.className = recent ? 'log-entry recent' : 'log-entry';
            div.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
            log.appendChild(div);
            while (log.children.length > maxLogEntries) log.removeChild(log.firstChild);
            log.scrollTop = log.scrollHeight;
        }

        function updateStats(dist) {
            dataHistory.push(dist);
            if (dataHistory.length > 300) dataHistory.shift();
            const avg = dataHistory.reduce((a, b) => a + b, 0) / dataHistory.length;
            document.getElementById('avgDistance').textContent = avg.toFixed(1) + ' mm';
            document.getElementById('maxDistance').textContent = Math.max(...dataHistory) + ' mm';
            document.getElementById('minDistance').textContent = Math.min(...dataHistory) + ' mm';
            document.getElementById('dataCount').textContent = dataHistory.length;
        }

        /* ================= 曲线 ================= */
        const ctx = document.getElementById('curveCanvas').getContext('2d');
        const chart = new Chart(ctx, {
            type: 'line',
            data: {
                labels: [],
                datasets: [{
                    label: '距离 mm',
                    data: [],
                    borderColor: '#4ade80',
                    backgroundColor: 'rgba(74,222,128,0.15)',
                    tension: 0.25,
                    fill: true,
                    pointRadius: 0,
                    borderWidth: 2
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                animation: false,
                scales: {
                    x: { title: { display: false }, ticks: { color: '#fff' }, grid: { color: 'rgba(255,255,255,0.15)' } },
                    y: { title: { display: false }, ticks: { color: '#fff' }, grid: { color: 'rgba(255,255,255,0.15)' } }
                },
                plugins: { legend: { display: false } }
            }
        });

        let sampleID = 0;
        function addCurvePoint(dist) {
            sampleID++;
            chart.data.labels.push(sampleID);
            chart.data.datasets[0].data.push(dist);
            if (chart.data.labels.length > chartMaxPoints) {
                chart.data.labels.shift();
                chart.data.datasets[0].data.shift();
            }
            chart.update();
        }

        /* ================= 串口读写 ================= */
        async function connectSerial() {
            try {
                port = await navigator.serial.requestPort();
                const baud = parseInt(document.getElementById('baudRate').value);
                await port.open({ baudRate: baud });
                updateStatus(true);
                addLogEntry(`串口已连接,波特率: ${baud}`);
                readLoop();
            } catch (e) {
                addLogEntry(`连接失败: ${e.message}`);
            }
        }
        async function disconnectSerial() {
            if (reader) { await reader.cancel(); reader = null; }
            if (port) { await port.close(); port = null; }
            updateStatus(false);
            addLogEntry('串口已断开');
        }
        async function readLoop() {
            const decoder = new TextDecoderStream();
            port.readable.pipeTo(decoder.writable);
            reader = decoder.readable.getReader();
            let buf = '';
            while (true) {
                const { value, done } = await reader.read();
                if (done) break;
                buf += value;
                const lines = buf.split('\n');
                buf = lines.pop();
                for (const ln of lines) {
                    const str = ln.trim();
                    if (str.startsWith('distance:')) {
                        const dist = parseInt(str.substring(9));
                        if (!isNaN(dist)) {
                            document.getElementById('distanceValue').textContent = dist + ' mm';
                            updateStats(dist);
                            addCurvePoint(dist);
                            addLogEntry(`收到距离: ${dist} mm`, true);
                        }
                    }
                }
            }
            reader.releaseLock();
        }

        /* ================= 串口枚举 ================= */
        async function getPorts() {
            if (!('serial' in navigator)) return;
            const ports = await navigator.serial.getPorts();
            const sel = document.getElementById('portSelect');
            sel.innerHTML = '<option value="">请选择串口</option>';
            ports.forEach((p, i) => {
                const opt = document.createElement('option');
                opt.value = i;
                opt.textContent = `串口 ${i + 1}`;
                sel.appendChild(opt);
            });
        }
        window.addEventListener('load', () => {
            getPorts();
            setInterval(getPorts, 5000);
        });
        window.addEventListener('beforeunload', disconnectSerial);
    </script>
</body>
</html>
  • 保存文件并双击打开 HTML 文件;

效果

  • 使用 USB 转 TTL 工具连接雷达模块和电脑;
  • 点击网页上方的 连接串口 按钮,弹出设备端口选项窗口,选择目标设备并连接;

radar-web.jpg

  • 网页显示实时距离以及动态历史演化曲线;

web-monitor.gif

总结

本文介绍了安信可 Rd-03 V2 雷达模组通过网页上位机实现距离监控的项目设计,包括硬件连接、流程图、代码和效果演示等,为相关产品的开发设计和快速应用提供了参考。

──── 1人觉得很赞 ────

使用道具 举报

UI很漂亮呢
前天 21:20
哇,这个网页好看
博士佬玩的花呀
博士高产~
昨天 10:31

感谢关注~网页设计稍显空旷了,可以加入更多控件,比如信号强度、人体感应方位、数据统计时段信息、自动分析等等;此外还可以结合步进电机,使雷达扫射面覆盖更大区域,实现360°循环扫描和探测
越来越高大上了
这个界面也太漂亮了吧!!!
您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 29855 个
  • 话题数: 43658 篇