Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04fecf33f8 | |||
| acf4efde4c |
@@ -0,0 +1,77 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { lidar, type LidarPoint } from '$lib/stores/lidar'
|
||||
|
||||
function getIntersection(angle:number, size:number):number {
|
||||
const sinAngle = Math.sin(angle);
|
||||
const cosAngle = Math.cos(angle);
|
||||
|
||||
let x, y;
|
||||
if (Math.abs(cosAngle) > Math.abs(sinAngle)) {
|
||||
x = size * Math.sign(cosAngle);
|
||||
y = x * sinAngle / cosAngle;
|
||||
} else {
|
||||
y = size * Math.sign(sinAngle);
|
||||
x = y * cosAngle / sinAngle;
|
||||
}
|
||||
|
||||
return Math.sqrt(x**2 + y**2);
|
||||
}
|
||||
|
||||
|
||||
let canvas:HTMLCanvasElement
|
||||
let ctx
|
||||
|
||||
const DEG2RAD = 0.017453292519943;
|
||||
|
||||
onMount(() => {
|
||||
ctx = canvas.getContext("2d")
|
||||
resize()
|
||||
lidar.subscribe(lidar => {
|
||||
draw(lidar.points)
|
||||
})
|
||||
})
|
||||
|
||||
const draw = (points:LidarPoint[]) => {
|
||||
if(!points) return
|
||||
const centerX = canvas.width / 2
|
||||
const centerY = canvas.height / 2
|
||||
|
||||
const scale = 0.01//Math.max(centerX, centerY) / Math.max(...points.map((point) => point.distance))
|
||||
|
||||
if (!ctx) return
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
for (let i = 0; i < points.length; i++){
|
||||
const angle = points[i].angle
|
||||
const distance = points[i].distance
|
||||
const quality = points[i].quality
|
||||
|
||||
const endX = centerX + (distance * scale) * Math.cos(angle * DEG2RAD);
|
||||
const endY = centerY - (distance * scale) * Math.sin(angle * DEG2RAD);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX, centerY);
|
||||
ctx.lineTo(endX, endY);
|
||||
ctx.strokeStyle = "grey"
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(endX, endY, 3, 0, Math.PI * 2);
|
||||
ctx.fillStyle = "#1bfc06"
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
const resize = () => {
|
||||
const parentElement = canvas.parentElement;
|
||||
if (parentElement) {
|
||||
canvas.width = parentElement.clientWidth
|
||||
canvas.height = parentElement.clientHeight
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={resize}></svelte:window>
|
||||
|
||||
<canvas bind:this={canvas} class="w-full h-full"></canvas>
|
||||
@@ -0,0 +1,29 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export type LidarPoint = {
|
||||
distance: number;
|
||||
angle: number;
|
||||
quality: number;
|
||||
};
|
||||
|
||||
let lidar_data = {
|
||||
points: <LidarPoint[]>[]
|
||||
};
|
||||
|
||||
const maxLidarData = 600;
|
||||
|
||||
function createLidar() {
|
||||
const { subscribe, update } = writable(lidar_data);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
addData: (lidarPoint: LidarPoint) => {
|
||||
update((lidar_data) => ({
|
||||
...lidar_data,
|
||||
points: [...lidar_data.points, lidarPoint].slice(-maxLidarData)
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const lidar = createLidar();
|
||||
@@ -1,9 +1,15 @@
|
||||
<script lang="ts">
|
||||
import Visualization from "$lib/components/Visualization.svelte";
|
||||
import Lidar from "$lib/components/Lidar.svelte";
|
||||
</script>
|
||||
|
||||
<div class="grow flex">
|
||||
<div class="absolute h-screen w-full top-0">
|
||||
<div class="absolute h-screen w-full top-0 flex">
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<Visualization debug />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<Lidar />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -8,6 +8,7 @@
|
||||
import Devices from '~icons/mdi/devices'
|
||||
import Camera from '~icons/mdi/camera-outline';
|
||||
import Rotate3d from '~icons/mdi/rotate-3d';
|
||||
import MdiLandslideOutline from '~icons/mdi/landslide-outline';
|
||||
import MotorOutline from '~icons/mdi/motor-outline';
|
||||
import Health from '~icons/mdi/stethoscope';
|
||||
import Folder from '~icons/mdi/folder-outline';
|
||||
@@ -83,6 +84,12 @@
|
||||
icon: Rotate3d,
|
||||
href: '/peripherals/imu',
|
||||
feature: $page.data.features.imu || $page.data.features.mag || $page.data.features.bmp,
|
||||
},
|
||||
{
|
||||
title: 'Lidar',
|
||||
icon: MdiLandslideOutline,
|
||||
href: '/peripherals/lidar',
|
||||
feature: true//$page.data.features.lidar,
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Lidar from './lidar.svelte';
|
||||
</script>
|
||||
|
||||
<div class="mx-0 my-1 flex flex-col space-y-4 sm:mx-8 sm:my-8">
|
||||
<Lidar />
|
||||
</div>
|
||||
@@ -0,0 +1,95 @@
|
||||
<script lang="ts">
|
||||
import Lidar from "$lib/components/Lidar.svelte";
|
||||
import SettingsCard from "$lib/components/SettingsCard.svelte";
|
||||
import { lidar } from "$lib/stores/lidar";
|
||||
import { onMount } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
import { distance } from "three/examples/jsm/nodes/Nodes.js";
|
||||
|
||||
let port;
|
||||
let reader;
|
||||
let inputDone;
|
||||
let inputStream;
|
||||
let isConnected = false;
|
||||
let buffer = '';
|
||||
let lastLine = ""
|
||||
|
||||
onMount(() => {
|
||||
navigator.serial.addEventListener("connect", (e) => {
|
||||
console.log("Connected");
|
||||
});
|
||||
|
||||
navigator.serial.addEventListener("disconnect", (e) => {
|
||||
console.log("Disconnected");
|
||||
});
|
||||
|
||||
navigator.serial.getPorts().then((ports) => {
|
||||
// Initialize the list of available ports with `ports` on page load.
|
||||
});
|
||||
})
|
||||
|
||||
const connect = async () => {
|
||||
try {
|
||||
port = await navigator.serial.requestPort();
|
||||
await port.open({ baudRate: 115200 });
|
||||
const decoder = new TextDecoderStream();
|
||||
inputDone = port.readable.pipeTo(decoder.writable);
|
||||
inputStream = decoder.readable.pipeThrough(new TransformStream(new LineBreakTransformer()));
|
||||
reader = inputStream.getReader();
|
||||
readLoop();
|
||||
} catch (err) {
|
||||
console.error('Failed to open serial port:', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function readLoop() {
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) {
|
||||
console.log('[readLoop] DONE', done);
|
||||
reader.releaseLock();
|
||||
break;
|
||||
}
|
||||
if (value.split(",").length !== 3) continue
|
||||
|
||||
const [distance, angle, quality] = value.split(",").map((val:string) => parseFloat(val))
|
||||
const lidarData = { distance, angle, quality }
|
||||
|
||||
if (distance <1000 || distance > 40000 || quality < 40) continue
|
||||
lidar.addData(lidarData)
|
||||
}
|
||||
}
|
||||
|
||||
class LineBreakTransformer {
|
||||
container: string;
|
||||
constructor() {
|
||||
this.container = '';
|
||||
}
|
||||
|
||||
transform(chunk: any, controller: { enqueue: (arg0: any) => any; }) {
|
||||
let re = /\r\n|\n|\r/gm;
|
||||
this.container += chunk;
|
||||
const lines = this.container.split(re);
|
||||
this.container = lines.pop() || "";
|
||||
lines.forEach(line => controller.enqueue(line));
|
||||
}
|
||||
|
||||
flush(controller: { enqueue: (arg0: string) => void; }) {
|
||||
controller.enqueue(this.container);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<SettingsCard collapsible={false}>
|
||||
<!-- <MdiConnection slot="icon" class="lex-shrink-0 mr-2 h-6 w-6 self-end" /> -->
|
||||
<span slot="title">Lidar</span>
|
||||
<div>
|
||||
<button on:click={connect} class="btn">Connect</button>
|
||||
</div>
|
||||
</SettingsCard>
|
||||
|
||||
<div class="h-96 w-96">
|
||||
<div class="w-full h-full">
|
||||
<Lidar />
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user