UNPKG

@foxglove/velodyne-cloud

Version:

TypeScript library for converting Velodyne LIDAR packet data to point clouds

137 lines (125 loc) 5.36 kB
// This Source Code Form is subject to the terms of the Mozilla Public // License, v2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/ import { CalibrationData, loadCalibrationData } from "./CalibrationData"; import { LaserCorrection, Model } from "./VelodyneTypes"; // Takes a calibration data file as input and computes cached lookup tables for // use in online point cloud conversion export class Calibration { static ROTATION_RESOLUTION = 0.01; // [deg] static ROTATION_MAX_UNITS = 36000; // [deg/100] static VLP16_FIRINGS_PER_BLOCK = 2; static VLP16_SCANS_PER_FIRING = 16; static VLP16_BLOCK_TDURATION = 110.592; // [µs] static VLP16_DSR_TOFFSET = 2.304; // [µs] static VLP16_FIRING_TOFFSET = 55.296; // [µs] static HDL32E_DSR_TOFFSET = 1.152; // [µs] static HDL32E_FIRING_TOFFSET = 46.08; // [µs] static VLS128_DSR_TOFFSET = 2.665; // [µs] static VLS128_FIRING_TOFFSET = 53.5; // [µs] readonly model: Model; readonly laserCorrections: LaserCorrection[]; readonly distanceResolution: number; // [m] readonly timingOffsets: number[][]; readonly sinRotTable: number[]; readonly cosRotTable: number[]; readonly vls128LaserAzimuthCache: number[]; constructor(model: Model, calibrationData: CalibrationData = loadCalibrationData(model)) { this.model = model; this.laserCorrections = calibrationData.lasers.map((v) => { return { laserId: v.laser_id, rotCorrection: v.rot_correction, vertCorrection: v.vert_correction, distCorrection: v.dist_correction, twoPtCorrectionAvailable: v.two_pt_correction_available ?? false, distCorrectionX: v.dist_correction_x, distCorrectionY: v.dist_correction_y, vertOffsetCorrection: v.vert_offset_correction, horizOffsetCorrection: v.horiz_offset_correction, maxIntensity: v.max_intensity ?? 255, minIntensity: v.min_intensity ?? 0, focalDistance: v.focal_distance, focalSlope: v.focal_slope, cosRotCorrection: Math.cos(v.rot_correction), sinRotCorrection: Math.sin(v.rot_correction), cosVertCorrection: Math.cos(v.vert_correction), sinVertCorrection: Math.sin(v.vert_correction), }; }); this.distanceResolution = calibrationData.distance_resolution; this.timingOffsets = Calibration.BuildTimingsFor(model); // Set up cached values for sin and cos of all the possible headings this.cosRotTable = Array<number>(Calibration.ROTATION_MAX_UNITS); this.sinRotTable = Array<number>(Calibration.ROTATION_MAX_UNITS); for (let i = 0; i < Calibration.ROTATION_MAX_UNITS; i++) { const rotation = deg2rad(Calibration.ROTATION_RESOLUTION * i); this.cosRotTable[i] = Math.cos(rotation); this.sinRotTable[i] = Math.sin(rotation); } this.vls128LaserAzimuthCache = Array<number>(16).fill(0); const VLS128_CHANNEL_TDURATION = 2.665; // [µs] Corresponds to one laser firing const VLS128_SEQ_TDURATION = 53.3; // [µs] A set of laser firings including recharging for (let i = 0; i < 16; i++) { this.vls128LaserAzimuthCache[i] = (VLS128_CHANNEL_TDURATION / VLS128_SEQ_TDURATION) * (i + i / 8); } } // Build a timing table with cells for each channel (laser) static BuildTimingsFor(model: Model): number[][] { const block1 = (x: number, _y: number) => x; const block16 = (x: number, y: number) => x * 2 + y / 16; const point1 = (_x: number, y: number) => y; const point2 = (_x: number, y: number) => y / 2; const point16 = (_x: number, y: number) => y % 16; switch (model) { case Model.VLP16: case Model.VLP16HiRes: { const full = Calibration.VLP16_FIRING_TOFFSET; const single = Calibration.VLP16_DSR_TOFFSET; return Calibration.BuildTimings(12, 32, full, single, 0, block16, point16); } case Model.VLP32C: { const full = Calibration.VLP16_FIRING_TOFFSET; const single = Calibration.VLP16_DSR_TOFFSET; return Calibration.BuildTimings(12, 32, full, single, 0, block1, point2); } case Model.HDL32E: { const full = Calibration.HDL32E_FIRING_TOFFSET; const single = Calibration.HDL32E_DSR_TOFFSET; return Calibration.BuildTimings(12, 32, full, single, 0, block1, point2); } case Model.VLS128: { const full = Calibration.VLS128_FIRING_TOFFSET; const single = Calibration.VLS128_DSR_TOFFSET; return Calibration.BuildTimings(3, 17, full, single, -8.7, block1, point1); } default: return []; } } static BuildTimings( rows: number, cols: number, fullFiringUs: number, // [µs] singleFiringUs: number, // [µs] offsetUs: number, // [µs] block: IndexCalc, point: IndexCalc, ): number[][] { const fullFiring = fullFiringUs * 1e-6; const singleFiring = singleFiringUs * 1e-6; const offset = offsetUs * 1e-6; return Array(rows) .fill(0) .map((_row, x) => Array(cols) .fill(0) .map((_col, y) => fullFiring * block(x, y) + singleFiring * point(x, y) + offset), ); } } type IndexCalc = (x: number, y: number) => number; function deg2rad(degrees: number): number { return degrees * (Math.PI / 180); }