Rover written with RUST
Pascal UDS Server
2025.11.11 04:36
Now, my RUST program generates around 3,000 of 3d points of data clouds around every 0.2 seconds.
Now, that data is saved in as RAM folder as /dev/shm/dots.bin file. Right after the file is written, my RUST program will send out a character 'R' to server as "Ready". Then, my UDS server will quickly read the file, then send back 'D' as "Done". This message method prevents overwriting of the data file while reading it. This is typical synchronization problem for IPC.
-----------------------
After implementation

It has been a long journey, but you now have a very powerful, modular system:
-
High-Speed Engine (Rust): A highly optimized, multi-threaded visual odometry engine that runs at a stable 5 FPS, handles all the complex computer vision math, and outputs raw, clean data.
-
Robust IPC (UDS): A reliable Unix Domain Socket pipeline that uses a handshake protocol (
Ready->Done) to ensure zero data loss and zero tearing, all while using shared memory speeds. -
Flexible Viewer (Pascal): A Pascal server that sits and waits for data, ready for you to build your custom visualization logic on top of it.
You have successfully decoupled the "math" from the "view," which will make your future development much easier. You can now restart the Rust engine endlessly without crashing your viewer, and you can tweak your viewer's OpenGL code without stopping the robot.
If you need help setting up the OpenGL rendering in Lazarus/Pascal to actually see those dots in 3D (instead of just printing text), feel free to ask. Otherwise, congratulations on getting this pipeline stable!
=================================================
Since BGRABitmap runs entirely on the CPU (software rasterization), it has zero dependencies on a GPU, X Server, or OpenGL context. It will never crash because of a missing monitor. It simply manipulates bytes in RAM, which is exactly what you need for a headless robot.
Since we aren't using OpenGL, we have to calculate the 3D-to-2D Projection manually. This is actually quite simple: it's just high-school trigonometry (similar triangles).
Here is the math we will use:
-
Shift: Move the point based on where we want the "Camera" to be.
-
Project:
ScreenX = (PointX / PointZ) * Zoom.
Here is the Pascal code to render your dots.bin data onto a BGRABitmap.
Pascal Rendering Unit (VoRender.pas)
You can add this function to your Pascal project. It takes your TPointCloud array and paints it onto a bitmap.
uses
BGRABitmap, BGRABitmapTypes, Math;
// Matches your Rust data
type
TPoint3D = packed record
X: Int32;
Y: Int32;
Z: Int32;
end;
TPointCloud = array of TPoint3D;
const
// --- VIRTUAL CAMERA SETTINGS ---
// We place the camera High (-500 Y) and Behind (-1000 Z) the origin
// to look "Down and Forward" at the data.
CAM_X = 0;
CAM_Y = -500;
CAM_Z = -1000;
FOCAL_LENGTH = 600; // Acts like "Zoom"
procedure RenderCloudToBitmap(Cloud: TPointCloud; TargetBmp: TBGRABitmap);
var
i: Integer;
WX, WY, WZ: Integer; // World Coordinates (shifted)
SX, SY: Integer; // Screen Coordinates
CenterX, CenterY: Integer;
DepthColor: Byte;
P: PBGRAPixel;
begin
// 1. Clear Background (Black)
TargetBmp.Fill(BGRA(0, 0, 0, 255));
CenterX := TargetBmp.Width div 2;
CenterY := TargetBmp.Height div 2;
// 2. Iterate over all points
for i := 0 to High(Cloud) do
begin
// --- A. CAMERA TRANSFORM ---
// Shift the world so it is relative to our camera position
WX := Cloud[i].X - CAM_X;
WY := Cloud[i].Y - CAM_Y;
WZ := Cloud[i].Z - CAM_Z;
// prevent division by zero or drawing points behind us
if WZ <= 10 then Continue;
// --- B. PERSPECTIVE PROJECTION ---
// The fundamental formula: Screen = (World / Depth) * FocalLength
SX := CenterX + (WX * FOCAL_LENGTH) div WZ;
SY := CenterY + (WY * FOCAL_LENGTH) div WZ;
// --- C. DRAWING ---
// Check bounds
if (SX >= 0) and (SX < TargetBmp.Width) and
(SY >= 0) and (SY < TargetBmp.Height) then
begin
// Depth Shading: Make close points bright, far points dark
// Map Z (e.g. 1000 to 5000) to Brightness (255 to 50)
DepthColor := EnsureRange(255 - (WZ div 30), 50, 255);
// Set the pixel (White/Gray based on depth)
TargetBmp.SetPixel(SX, SY, BGRA(DepthColor, DepthColor, DepthColor, 255));
end;
end;
end;
How to Integrate into your Server
In your RunUdsServer loop, right after you load the points:
-
Create a global
MyBitmap := TBGRABitmap.Create(800, 600); -
Call
RenderCloudToBitmap(MyCloud, MyBitmap); -
Save or Transmit
MyBitmap.
Example snippet for the inner loop:
if Cmd = 'R' then
begin
// 1. Load Data
LoadPointCloud(SHM_FILE_PATH, MyCloud);
// 2. Render to Memory (CPU only, no OpenGL)
RenderCloudToBitmap(MyCloud, MyBitmap);
// 3. Visualize (Since you are headless)
// Option A: Save to file for checking
// MyBitmap.SaveToFile('/dev/shm/render_debug.bmp');
// Option B: If running VNC, draw to a Form's Image
// MyBitmap.Draw(Form1.Image1.Canvas, 0, 0);
// 4. Handshake Done
Cmd := 'D';
fpSend(ClientHandle, @Cmd, 1, 0);
end;
This approach is 100% headless safe. It treats the 3D rendering purely as a math problem, processing arrays of integers into pixels in RAM.