Adeept PiCar-B
Rewriting the Adeept PiCar-B kit in .NET/C# with a browser UI — ROS2, Lidar, and IMU next.

An Adeept PiCar-B I wanted under my own software, not the kit's Python defaults. Phase one is a .NET / C# 10 app with a web UI for lights, steering and motor, pan/tilt, ultrasonic ranging, and camera streaming — plus line following once that sensor is wired again. I'll link the GitHub repo here when it's actually running.
Phase two is ROS2: collision awareness and SLAM with the Lidar, and orientation from the IMU. Browser first; autonomy after that.
Current status
The browser teleop UI is up on the car: drive, steering, speed, camera pan/tilt joystick, WS2812 colour and effects, and a live status panel (ultrasonic, line sensor, steer/pan/tilt angles, CPU temperature).
The main view toggles between a 3D model of the robot and the live camera. The 3D view renders the PiCar-B in the browser — drag to orbit, scroll to zoom — with a point-cloud toggle ready for Lidar data later.

Camera streaming from the Arducam IMX708 works end to end in the same page. It's usable, but there's noticeable latency between the car moving and the picture catching up — fine for looking around, not yet good enough for driving by camera alone.

Next steps
- Cut camera stream latency — the biggest gap right now. Look at resolution/frame rate, encoder settings, buffering on both ends, and the transport (MJPEG vs. a lower-latency option like WebRTC).
- Line follower — rewire to the HAT V3.1 and replace the software stub.
- Phase two — ROS2 with the LD06 Lidar (feeding the point-cloud view) and BNO055 orientation.
Hardware
- Adeept PiCar-B — base kit. Kit docs · Original GitHub
- Adeept Robot HAT V3.1 — upgraded from the kit board; connectors differ from the older loom. HAT V3 docs · Intro PDF · PCA9685 datasheet
- Ultrasonic (HC-SR04) — kit ranging module on the HAT ultrasonic header. HC-SR04 datasheet
- 3-CH line follower — still disconnected after the HAT V3.1 rewire. HAT line-tracking notes
- Arducam Camera Module 3 (IMX708) — drop-in Camera Module 3 class sensor so current Raspberry Pi OS / libcamera works; chassis recess cut wider to fit. Camera Module 3 product brief · Pi camera docs
- GY-BNO055 — 9-axis absolute orientation IMU (phase two). BNO055 datasheet
- OKDO LD06 (LDRobot) — 360° DTOF Lidar for collision detection and SLAM (phase two). LD06 datasheet · Development manual
Parts photos



Challenges
OS choice mattered more than expected. Ubuntu-style hosts fought the Pi camera stack and package story, so the robot host settled on Raspberry Pi OS. ROS and heavier perception can come later — often in Docker — without owning the GPIO path on the native board.
The camera stayed black until I stopped treating it like a plain V4L2
device. On Pi OS the CSI path is libcamera / rpicam; OpenCV opening
/dev/video0 just fails. Streaming through rpicam fixed it. Newer Pi OS
had also dropped the original kit camera driver, so I swapped in an
Arducam Camera Module 3 (Sony IMX708) — same class as the official Pi
module, slightly larger than stock, which meant cutting a bigger recess
in the chassis.
Pi 5 was less forgiving than older boards. Opening GPIO and the WS2812 LED path in the wrong order could hard-lock the machine, and multiple GPIO openers fought over the chip (whose index also varied by image). One shared GPIO owner, LEDs off by default with WS2812, and a safe config for recovery.
The Robot HAT V3.1 upgrade was the other awkward bit. Connectors don't match the older loom one-for-one — most of the harness went back on after some patience, but the line follower is still disconnected, so line-follow stays a software stub for now. Old docs and config still pointed at HAT V2 ultrasonic pins; V3 uses different numbering and the ULTRASONIC header, which looked like a dead sensor until the pin map caught up. On Pi 5 the trigger pulse that worked on a Pi 3 was also too short, and a no-echo reading as Infinity blew up the status JSON — longer pulse, null for no echo.
Smaller software snags: the ASP.NET static-asset path served index.html
but / itself 404'd until that route was mapped explicitly. And coupling
teleop to ROS early slowed bring-up — host-first, ROS off by default,
Docker as a DDS peer later so GPIO stays on the native host.