Initial commit

This commit is contained in:
2026-07-13 09:25:40 +07:00
parent c08ff54676
commit bccfb156d7
1938 changed files with 641646 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
// Tim781S LiDAR Visualization JavaScript
let canvas, ctx;
let centerX, centerY;
let scale = 20; // pixels per meter
let maxDisplay = 20; // max display range in meters
function initLidarCanvas() {
canvas = document.getElementById('lidarCanvas');
if (!canvas) {
console.error('LiDAR canvas not found');
return;
}
ctx = canvas.getContext('2d');
centerX = canvas.width / 2;
centerY = canvas.height / 2;
// Draw initial grid
drawGrid();
console.log('LiDAR canvas initialized');
}
function drawGrid() {
if (!ctx) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw background
ctx.fillStyle = '#0a0a0a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw grid circles (range rings)
ctx.strokeStyle = '#2a2a2a';
ctx.lineWidth = 1;
for (let r = 5; r <= maxDisplay; r += 5) {
ctx.beginPath();
ctx.arc(centerX, centerY, r * scale, 0, 2 * Math.PI);
ctx.stroke();
// Draw range labels
ctx.fillStyle = '#666';
ctx.font = '12px monospace';
ctx.fillText(r + 'm', centerX + 5, centerY - r * scale + 15);
}
// Draw axes
ctx.strokeStyle = '#444';
ctx.lineWidth = 2;
// X axis
ctx.beginPath();
ctx.moveTo(0, centerY);
ctx.lineTo(canvas.width, centerY);
ctx.stroke();
// Y axis
ctx.beginPath();
ctx.moveTo(centerX, 0);
ctx.lineTo(centerX, canvas.height);
ctx.stroke();
// Draw robot position (center circle)
ctx.fillStyle = '#00ff88';
ctx.beginPath();
ctx.arc(centerX, centerY, 5, 0, 2 * Math.PI);
ctx.fill();
// Draw forward direction indicator
ctx.strokeStyle = '#00ff88';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(centerX, centerY - 30);
ctx.stroke();
}
function drawLidarScan(points) {
if (!ctx || !Array.isArray(points)) return;
// Redraw grid first
drawGrid();
// Draw scan points
points.forEach(point => {
const angle = point.angle;
const range = point.range;
// Convert polar to Cartesian (LiDAR coordinate frame)
// In LiDAR frame: 0° = forward (up on screen)
// We need to rotate 90° so forward matches up on screen
const x = centerX + (range * scale * Math.sin(angle));
const y = centerY - (range * scale * Math.cos(angle));
// Color based on distance (closer = brighter green, farther = darker)
const intensity = 1.0 - (range / maxDisplay);
const r = Math.floor(0 * intensity);
const g = Math.floor(255 * intensity);
const b = Math.floor(136 * intensity);
// Draw point
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
ctx.beginPath();
ctx.arc(x, y, 2, 0, 2 * Math.PI);
ctx.fill();
});
// Draw connection lines (optional - creates solid appearance)
if (points.length > 1) {
ctx.strokeStyle = 'rgba(0, 255, 136, 0.3)';
ctx.lineWidth = 1;
ctx.beginPath();
points.forEach((point, index) => {
const angle = point.angle;
const range = point.range;
const x = centerX + (range * scale * Math.sin(angle));
const y = centerY - (range * scale * Math.cos(angle));
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
}
}
// Make functions available globally
window.initLidarCanvas = initLidarCanvas;
window.drawLidarScan = drawLidarScan;

View File

@@ -0,0 +1,379 @@
// Velocity Chart Renderer
// Renders line charts for velocity data comparison (OdomVel vs CmdVel)
window.velocityChartRenderer = {
linearChartCtx: null,
angularChartCtx: null,
linearChartData: { odom: [], cmd: [], times: [] },
angularChartData: { odom: [], cmd: [], times: [] },
initLinearVelocityChart: function(canvas) {
if (!canvas || typeof canvas.getContext !== 'function') return;
try {
this.linearChartCtx = canvas.getContext('2d');
this.linearChartData = { odom: [], cmd: [], times: [] };
} catch (e) {
console.error('Error initializing linear velocity chart:', e);
}
},
initAngularVelocityChart: function(canvas) {
if (!canvas || typeof canvas.getContext !== 'function') return;
try {
this.angularChartCtx = canvas.getContext('2d');
this.angularChartData = { odom: [], cmd: [], times: [] };
} catch (e) {
console.error('Error initializing angular velocity chart:', e);
}
},
updateLinearVelocityChart: function(canvas, data) {
if (!canvas || !data || data.length === 0) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const width = canvas.width = canvas.offsetWidth;
const height = canvas.height = canvas.offsetHeight || 150;
// Clear canvas
ctx.clearRect(0, 0, width, height);
if (data.length === 0) return;
// Calculate min/max values for scaling
let dataMinVal = Math.min(...data.map(d => Math.min(d.odomVel, d.cmdVel)));
let dataMaxVal = Math.max(...data.map(d => Math.max(d.odomVel, d.cmdVel)));
// Set grid spacing to 0.1 m/s
const gridStep = 0.1;
// Round min/max to nearest 0.1 and add padding
let minVal = Math.floor(dataMinVal / gridStep) * gridStep;
let maxVal = Math.ceil(dataMaxVal / gridStep) * gridStep;
// Ensure at least some range
if (minVal === maxVal) {
minVal -= gridStep;
maxVal += gridStep;
}
// Add extra padding
minVal -= gridStep;
maxVal += gridStep;
// Calculate time range
const maxTime = Math.max(...data.map(d => d.time));
const minTime = Math.min(...data.map(d => d.time));
const timeRange = maxTime - minTime || 1;
// Padding
const padding = { top: 20, right: 20, bottom: 30, left: 50 };
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;
// Draw axes
ctx.strokeStyle = '#666';
ctx.lineWidth = 1;
// X axis
ctx.beginPath();
ctx.moveTo(padding.left, height - padding.bottom);
ctx.lineTo(width - padding.right, height - padding.bottom);
ctx.stroke();
// Y axis
ctx.beginPath();
ctx.moveTo(padding.left, padding.top);
ctx.lineTo(padding.left, height - padding.bottom);
ctx.stroke();
// Draw grid lines with 0.1 m/s spacing
ctx.strokeStyle = '#ddd';
ctx.lineWidth = 0.5;
// Horizontal grid lines at 0.1 m/s intervals
const valueRange = maxVal - minVal;
const numSteps = Math.round(valueRange / gridStep);
// Determine label step based on range to avoid overcrowding
// If range is large, show labels less frequently
let labelStep = 1; // Show every label by default
if (valueRange > 1.0) {
labelStep = 2; // Show every 2nd label if range > 1.0
}
if (valueRange > 2.0) {
labelStep = 5; // Show every 5th label if range > 2.0
}
if (valueRange > 5.0) {
labelStep = 10; // Show every 10th label if range > 5.0
}
for (let i = 0; i <= numSteps; i++) {
const value = minVal + i * gridStep;
const y = padding.top + chartHeight - ((value - minVal) / valueRange) * chartHeight;
ctx.beginPath();
ctx.moveTo(padding.left, y);
ctx.lineTo(width - padding.right, y);
ctx.stroke();
// Y axis labels - only show if it's a multiple of labelStep
if (i % labelStep === 0) {
ctx.fillStyle = '#666';
ctx.font = '10px Arial';
ctx.textAlign = 'right';
ctx.fillText(value.toFixed(2), padding.left - 5, y + 3);
}
}
// Vertical grid lines (time)
const xSteps = 5;
for (let i = 0; i <= xSteps; i++) {
const x = padding.left + (chartWidth / xSteps) * i;
ctx.beginPath();
ctx.moveTo(x, padding.top);
ctx.lineTo(x, height - padding.bottom);
ctx.stroke();
// X axis labels
const time = minTime + (timeRange / xSteps) * i;
ctx.fillStyle = '#666';
ctx.font = '10px Arial';
ctx.textAlign = 'center';
ctx.fillText(time.toFixed(1) + 's', x, height - padding.bottom + 15);
}
// Draw OdomVel line (blue - #2196f3)
if (data.length >= 1) {
ctx.strokeStyle = '#2196f3'; // Blue
ctx.lineWidth = 2;
if (data.length > 1) {
ctx.beginPath();
for (let i = 0; i < data.length; i++) {
const x = padding.left + ((data[i].time - minTime) / timeRange) * chartWidth;
const y = padding.top + chartHeight - ((data[i].odomVel - minVal) / (maxVal - minVal)) * chartHeight;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
}
// Draw point at current position
const lastX = padding.left + ((data[data.length - 1].time - minTime) / timeRange) * chartWidth;
const lastY = padding.top + chartHeight - ((data[data.length - 1].odomVel - minVal) / (maxVal - minVal)) * chartHeight;
ctx.fillStyle = '#2196f3';
ctx.beginPath();
ctx.arc(lastX, lastY, 3, 0, 2 * Math.PI);
ctx.fill();
}
// Draw CmdVel line (green - #4caf50)
if (data.length >= 1) {
ctx.strokeStyle = '#4caf50'; // Green
ctx.lineWidth = 2;
if (data.length > 1) {
ctx.beginPath();
for (let i = 0; i < data.length; i++) {
const x = padding.left + ((data[i].time - minTime) / timeRange) * chartWidth;
const y = padding.top + chartHeight - ((data[i].cmdVel - minVal) / (maxVal - minVal)) * chartHeight;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
}
// Draw point at current position
const lastX = padding.left + ((data[data.length - 1].time - minTime) / timeRange) * chartWidth;
const lastY = padding.top + chartHeight - ((data[data.length - 1].cmdVel - minVal) / (maxVal - minVal)) * chartHeight;
ctx.fillStyle = '#4caf50';
ctx.beginPath();
ctx.arc(lastX, lastY, 3, 0, 2 * Math.PI);
ctx.fill();
}
},
updateAngularVelocityChart: function(canvas, data) {
if (!canvas || !data || data.length === 0) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const width = canvas.width = canvas.offsetWidth;
const height = canvas.height = canvas.offsetHeight || 150;
// Clear canvas
ctx.clearRect(0, 0, width, height);
if (data.length === 0) return;
// Calculate min/max values for scaling
let dataMinVal = Math.min(...data.map(d => Math.min(d.odomVel, d.cmdVel)));
let dataMaxVal = Math.max(...data.map(d => Math.max(d.odomVel, d.cmdVel)));
// Set grid spacing to 0.1 rad/s
const gridStep = 0.1;
// Round min/max to nearest 0.1 and add padding
let minVal = Math.floor(dataMinVal / gridStep) * gridStep;
let maxVal = Math.ceil(dataMaxVal / gridStep) * gridStep;
// Ensure at least some range
if (minVal === maxVal) {
minVal -= gridStep;
maxVal += gridStep;
}
// Add extra padding
minVal -= gridStep;
maxVal += gridStep;
// Calculate time range
const maxTime = Math.max(...data.map(d => d.time));
const minTime = Math.min(...data.map(d => d.time));
const timeRange = maxTime - minTime || 1;
// Padding
const padding = { top: 20, right: 20, bottom: 30, left: 50 };
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;
// Draw axes
ctx.strokeStyle = '#666';
ctx.lineWidth = 1;
// X axis
ctx.beginPath();
ctx.moveTo(padding.left, height - padding.bottom);
ctx.lineTo(width - padding.right, height - padding.bottom);
ctx.stroke();
// Y axis
ctx.beginPath();
ctx.moveTo(padding.left, padding.top);
ctx.lineTo(padding.left, height - padding.bottom);
ctx.stroke();
// Draw grid lines with 0.1 rad/s spacing
ctx.strokeStyle = '#ddd';
ctx.lineWidth = 0.5;
// Horizontal grid lines at 0.1 rad/s intervals
const valueRange = maxVal - minVal;
const numSteps = Math.round(valueRange / gridStep);
// Determine label step based on range to avoid overcrowding
// If range is large, show labels less frequently
let labelStep = 1; // Show every label by default
if (valueRange > 1.0) {
labelStep = 2; // Show every 2nd label if range > 1.0
}
if (valueRange > 2.0) {
labelStep = 5; // Show every 5th label if range > 2.0
}
if (valueRange > 5.0) {
labelStep = 10; // Show every 10th label if range > 5.0
}
for (let i = 0; i <= numSteps; i++) {
const value = minVal + i * gridStep;
const y = padding.top + chartHeight - ((value - minVal) / valueRange) * chartHeight;
ctx.beginPath();
ctx.moveTo(padding.left, y);
ctx.lineTo(width - padding.right, y);
ctx.stroke();
// Y axis labels - only show if it's a multiple of labelStep
if (i % labelStep === 0) {
ctx.fillStyle = '#666';
ctx.font = '10px Arial';
ctx.textAlign = 'right';
ctx.fillText(value.toFixed(1), padding.left - 5, y + 3);
}
}
// Vertical grid lines (time)
const xSteps = 5;
for (let i = 0; i <= xSteps; i++) {
const x = padding.left + (chartWidth / xSteps) * i;
ctx.beginPath();
ctx.moveTo(x, padding.top);
ctx.lineTo(x, height - padding.bottom);
ctx.stroke();
// X axis labels
const time = minTime + (timeRange / xSteps) * i;
ctx.fillStyle = '#666';
ctx.font = '10px Arial';
ctx.textAlign = 'center';
ctx.fillText(time.toFixed(1) + 's', x, height - padding.bottom + 15);
}
// Draw OdomVel line (blue - #2196f3)
if (data.length >= 1) {
ctx.strokeStyle = '#2196f3'; // Blue
ctx.lineWidth = 2;
if (data.length > 1) {
ctx.beginPath();
for (let i = 0; i < data.length; i++) {
const x = padding.left + ((data[i].time - minTime) / timeRange) * chartWidth;
const y = padding.top + chartHeight - ((data[i].odomVel - minVal) / (maxVal - minVal)) * chartHeight;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
}
// Draw point at current position
const lastX = padding.left + ((data[data.length - 1].time - minTime) / timeRange) * chartWidth;
const lastY = padding.top + chartHeight - ((data[data.length - 1].odomVel - minVal) / (maxVal - minVal)) * chartHeight;
ctx.fillStyle = '#2196f3';
ctx.beginPath();
ctx.arc(lastX, lastY, 3, 0, 2 * Math.PI);
ctx.fill();
}
// Draw CmdVel line (green - #4caf50)
if (data.length >= 1) {
ctx.strokeStyle = '#4caf50'; // Green
ctx.lineWidth = 2;
if (data.length > 1) {
ctx.beginPath();
for (let i = 0; i < data.length; i++) {
const x = padding.left + ((data[i].time - minTime) / timeRange) * chartWidth;
const y = padding.top + chartHeight - ((data[i].cmdVel - minVal) / (maxVal - minVal)) * chartHeight;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
}
// Draw point at current position
const lastX = padding.left + ((data[data.length - 1].time - minTime) / timeRange) * chartWidth;
const lastY = padding.top + chartHeight - ((data[data.length - 1].cmdVel - minVal) / (maxVal - minVal)) * chartHeight;
ctx.fillStyle = '#4caf50';
ctx.beginPath();
ctx.arc(lastX, lastY, 3, 0, 2 * Math.PI);
ctx.fill();
}
}
};

File diff suppressed because it is too large Load Diff