<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Aron Petau - coral</title>
    <link rel="self" type="application/atom+xml" href="https://aron.petau.net/tags/coral/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://aron.petau.net/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2024-01-30T00:00:00+00:00</updated>
    <id>https://aron.petau.net/tags/coral/atom.xml</id>
    <entry xml:lang="en">
        <title>AIRASPI Build Log</title>
        <published>2024-01-30T00:00:00+00:00</published>
        <updated>2024-01-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            Aron Petau
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://aron.petau.net/project/airaspi-build-log/"/>
        <id>https://aron.petau.net/project/airaspi-build-log/</id>
        
        <content type="html" xml:base="https://aron.petau.net/project/airaspi-build-log/">&lt;h2 id=&quot;AI-Raspi_Build_Log&quot;&gt;AI-Raspi Build Log&lt;&#x2F;h2&gt;
&lt;p&gt;This document chronicles the process of building a custom edge computing device for real-time image recognition and object detection. The goal was to create a portable, self-contained system that could operate independently of cloud infrastructure.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Project Goals:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Build an edge device with image recognition and object detection capabilities that can process video in real-time, targeting 30fps at 720p resolution. Portability and autonomous operation are critical requirements—the device must function without an active internet connection and maintain a compact form factor suitable for installation environments. All computation happens locally on the device itself, making it a true edge computing solution with no cloud dependency.&lt;&#x2F;p&gt;
&lt;p&gt;This project was inspired by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;MauiJerry&#x2F;Pose2Art&quot;&gt;pose2art&lt;&#x2F;a&gt;, which demonstrated the creative potential of real-time pose detection for interactive installations.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Hardware&quot;&gt;Hardware&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raspberrypi.com&#x2F;products&#x2F;raspberry-pi-5&#x2F;&quot;&gt;Raspberry Pi 5&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raspberrypi.com&#x2F;documentation&#x2F;accessories&#x2F;camera.html&quot;&gt;Raspberry Pi Camera Module v1.3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raspberrypi.com&#x2F;documentation&#x2F;accessories&#x2F;camera.html&quot;&gt;Raspberry Pi GlobalShutter Camera&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;2x CSI FPC Cable (needs one compact side to fit pi 5)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pineberrypi.com&#x2F;products&#x2F;hat-ai-for-raspberry-pi-5&quot;&gt;Pineberry AI Hat (m.2 E key)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.coral.ai&#x2F;products&#x2F;m2-accelerator-dual-edgetpu&quot;&gt;Coral Dual Edge TPU (m.2 E key)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Raspi Official 5A Power Supply&lt;&#x2F;li&gt;
&lt;li&gt;Raspi active cooler&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;Setup&quot;&gt;Setup&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;Primary_Resources&quot;&gt;Primary Resources&lt;&#x2F;h3&gt;
&lt;p&gt;This build wouldn&#x27;t have been possible without the excellent documentation and troubleshooting guides from the community. The primary sources I relied on throughout this project were:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.coral.ai&#x2F;docs&#x2F;m2&#x2F;get-started&#x2F;#requirements&quot;&gt;coral.ai official documentation&lt;&#x2F;a&gt; - Google&#x27;s official setup guide for the M.2 Edge TPU&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jeffgeerling.com&#x2F;blog&#x2F;2023&#x2F;pcie-coral-tpu-finally-works-on-raspberry-pi-5&quot;&gt;Jeff Geerling&#x27;s blog&lt;&#x2F;a&gt; - Critical PCIe configuration insights for Raspberry Pi 5&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.frigate.video&quot;&gt;Frigate NVR documentation&lt;&#x2F;a&gt; - Comprehensive guide for the network video recorder software&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;Raspberry_Pi_OS_Installation&quot;&gt;Raspberry Pi OS Installation&lt;&#x2F;h3&gt;
&lt;p&gt;I used the Raspberry Pi Imager to flash the latest Raspberry Pi OS to an SD card. The OS choice is critical for camera compatibility.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;markdown-alert-important&quot;&gt;
&lt;p&gt;Needs to be Debian Bookworm.
Needs to be the full arm64 image (with desktop), otherwise you will get into camera
driver hell.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Initial Configuration Settings:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Using the Raspberry Pi Imager&#x27;s advanced settings, I configured the following before flashing:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Used the default arm64 image (with desktop) - critical for camera driver compatibility&lt;&#x2F;li&gt;
&lt;li&gt;Enabled custom settings for headless operation&lt;&#x2F;li&gt;
&lt;li&gt;Enabled SSH for remote access&lt;&#x2F;li&gt;
&lt;li&gt;Configured WiFi country code for legal compliance&lt;&#x2F;li&gt;
&lt;li&gt;Set WiFi SSID and password for automatic network connection&lt;&#x2F;li&gt;
&lt;li&gt;Configured locale settings for proper timezone and keyboard layout&lt;&#x2F;li&gt;
&lt;li&gt;Set custom hostname: &lt;code&gt;airaspi&lt;&#x2F;code&gt; for easy network identification&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;System_Update&quot;&gt;System Update&lt;&#x2F;h3&gt;
&lt;p&gt;After the initial boot, updating the system is essential.
This process can take considerable time with the full desktop image, but ensures all packages are current and security patches are applied.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade -y &amp;amp;&amp;amp; sudo reboot
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;Preparing_the_System_for_Coral_TPU&quot;&gt;Preparing the System for Coral TPU&lt;&#x2F;h3&gt;
&lt;p&gt;The Raspberry Pi 5&#x27;s PCIe interface requires specific configuration to work with the Coral Edge TPU. This section was the most technically challenging, involving kernel modifications and device tree changes. A huge thanks to Jeff Geerling for documenting this process—without his detailed troubleshooting, this would have been nearly impossible.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;# check kernel version
uname -a
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;# modify config.txt
sudo nano &#x2F;boot&#x2F;firmware&#x2F;config.txt
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;While in the file, add the following lines:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;config&quot;&gt;kernel=kernel8.img
dtparam=pciex1
dtparam=pciex1_gen=2
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Save and reboot:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;sudo reboot
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;# check kernel version again
uname -a
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;should be different now, with a -v8 at the end&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;edit &#x2F;boot&#x2F;firmware&#x2F;cmdline.txt&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;sudo nano &#x2F;boot&#x2F;firmware&#x2F;cmdline.txt
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;add pcie_aspm=off before rootwait&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;sudo reboot
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;Modifying_the_Device_Tree&quot;&gt;Modifying the Device Tree&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;Initial_Script_Attempt_(Deprecated)&quot;&gt;Initial Script Attempt (Deprecated)&lt;&#x2F;h4&gt;
&lt;p&gt;Initially, there was an automated script available that was supposed to handle the device tree modifications. However, this script proved problematic and caused issues during my build.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;markdown-alert-warning&quot;&gt;
&lt;p&gt;maybe this script is the issue?
i will try again without it&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;curl https:&#x2F;&#x2F;gist.githubusercontent.com&#x2F;dataslayermedia&#x2F;714ec5a9601249d9ee754919dea49c7e&#x2F;raw&#x2F;32d21f73bd1ebb33854c2b059e94abe7767c3d7e&#x2F;coral-ai-pcie-edge-tpu-raspberrypi-5-setup | sh
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Yes, it was the problematic script. I left a comment documenting the issue on the original gist:
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;dataslayermedia&#x2F;714ec5a9601249d9ee754919dea49c7e?permalink_comment_id=4860232#gistcomment-4860232&quot;&gt;My comment on the gist&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;Manual_Device_Tree_Modification_(Recommended)&quot;&gt;Manual Device Tree Modification (Recommended)&lt;&#x2F;h4&gt;
&lt;p&gt;Instead of relying on the automated script, I followed Jeff Geerling&#x27;s manual approach.
This method gives you complete control over the process and helps understand what&#x27;s
actually happening under the hood.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;In the meantime the Script got updated and it is now recommended again.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The device tree modification process involves backing up the current device tree blob
(DTB), decompiling it to a readable format, editing the MSI parent reference to fix
PCIe compatibility issues, and then recompiling it back to binary format. Here&#x27;s the
step-by-step process:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. Back up and Decompile the Device Tree&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;# Back up the current dtb
sudo cp &#x2F;boot&#x2F;firmware&#x2F;bcm2712-rpi-5-b.dtb &#x2F;boot&#x2F;firmware&#x2F;bcm2712-rpi-5-b.dtb.bak

# Decompile the current dtb (ignore warnings)
dtc -I dtb -O dts &#x2F;boot&#x2F;firmware&#x2F;bcm2712-rpi-5-b.dtb -o ~&#x2F;test.dts

# Edit the file
nano ~&#x2F;test.dts

# Change the line: msi-parent = &amp;lt;0x2f&amp;gt;; (under `pcie@110000`)
# To: msi-parent = &amp;lt;0x66&amp;gt;;
# Then save the file.

# Recompile the dtb and move it back to the firmware directory
dtc -I dts -O dtb ~&#x2F;test.dts -o ~&#x2F;test.dtb
sudo mv ~&#x2F;test.dtb &#x2F;boot&#x2F;firmware&#x2F;bcm2712-rpi-5-b.dtb

# Reboot for changes to take effect
sudo reboot
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;Note: msi-parent seems to carry the value &amp;lt;0x2c&amp;gt; nowadays, cost me a few hours.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;2. Verify the Changes&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;After rebooting, check that the Coral TPU is recognized by the system:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;lspci -nn | grep 089a
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You should see output similar to: &lt;code&gt;0000:01:00.0 System peripheral [0880]: Global Unichip Corp. Coral Edge TPU [1ac1:089a]&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Installing_the_Apex_Driver&quot;&gt;Installing the Apex Driver&lt;&#x2F;h3&gt;
&lt;p&gt;With the device tree properly configured, the next step is installing Google&#x27;s Apex
driver for the Coral Edge TPU. This driver enables communication between the operating
system and the TPU hardware.&lt;&#x2F;p&gt;
&lt;p&gt;Following the official instructions from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;coral.ai&#x2F;docs&#x2F;m2&#x2F;get-started#2a-on-linux&quot;&gt;coral.ai&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;echo &amp;quot;deb https:&#x2F;&#x2F;packages.cloud.google.com&#x2F;apt coral-edgetpu-stable main&amp;quot; | sudo tee &#x2F;etc&#x2F;apt&#x2F;sources.list.d&#x2F;coral-edgetpu.list

curl https:&#x2F;&#x2F;packages.cloud.google.com&#x2F;apt&#x2F;doc&#x2F;apt-key.gpg | sudo apt-key add -

sudo apt-get update

sudo apt-get install gasket-dkms libedgetpu1-std

sudo sh -c &amp;quot;echo &amp;#39;SUBSYSTEM==\&amp;quot;apex\&amp;quot;, MODE=\&amp;quot;0660\&amp;quot;, GROUP=\&amp;quot;apex\&amp;quot;&amp;#39; &amp;gt;&amp;gt; &#x2F;etc&#x2F;udev&#x2F;rules.d&#x2F;65-apex.rules&amp;quot;

sudo groupadd apex

sudo adduser $USER apex

sudo reboot
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This sequence:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Adds Google&#x27;s package repository and GPG key&lt;&#x2F;li&gt;
&lt;li&gt;Installs the gasket DKMS module (kernel driver) and Edge TPU runtime library&lt;&#x2F;li&gt;
&lt;li&gt;Creates udev rules for device permissions&lt;&#x2F;li&gt;
&lt;li&gt;Creates an &lt;code&gt;apex&lt;&#x2F;code&gt; group and adds your user to it&lt;&#x2F;li&gt;
&lt;li&gt;Reboots to load the driver&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;After the reboot, verify the installation:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;lspci -nn | grep 089a
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This should display the connected Coral TPU as a PCIe device.&lt;&#x2F;p&gt;
&lt;p&gt;Next, confirm the device node exists with proper permissions:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;ls -l &#x2F;dev&#x2F;apex_0
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If the output shows &lt;code&gt;&#x2F;dev&#x2F;apex_0&lt;&#x2F;code&gt; with appropriate group permissions, the installation
was successful. If not, review the udev rules and group membership.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Testing_with_Example_Models&quot;&gt;Testing with Example Models&lt;&#x2F;h3&gt;
&lt;p&gt;To verify the TPU is functioning correctly, we&#x27;ll use Google&#x27;s example classification
script with a pre-trained MobileNet model:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;# Install Python packages
sudo apt-get install python3-pycoral

# Download example code and models
mkdir -p ~&#x2F;coral &amp;amp;&amp;amp; cd ~&#x2F;coral
git clone https:&#x2F;&#x2F;github.com&#x2F;google-coral&#x2F;pycoral.git
cd pycoral

# Run bird classification example
python3 examples&#x2F;classify_image.py \
  --model test_data&#x2F;mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite \
  --labels test_data&#x2F;inat_bird_labels.txt \
  --input test_data&#x2F;parrot.jpg
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The output should show inference results with confidence scores, confirming the Edge
TPU is working correctly.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Docker_Installation&quot;&gt;Docker Installation&lt;&#x2F;h3&gt;
&lt;p&gt;Docker provides containerization for the applications we&#x27;ll be running (Frigate,
MediaMTX, etc.). This keeps dependencies isolated and makes deployment much cleaner.&lt;&#x2F;p&gt;
&lt;p&gt;Install Docker using the official convenience script from
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;engine&#x2F;install&#x2F;debian&#x2F;#install-using-the-convenience-script&quot;&gt;docker.com&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;curl -fsSL https:&#x2F;&#x2F;get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After installation, log out and back in for group membership changes to take effect.&lt;&#x2F;p&gt;
&lt;p&gt;Configure Docker to start automatically on boot:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;sudo systemctl enable docker.service
sudo systemctl enable containerd.service
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;Test_the_Edge_TPU_(Optional)&quot;&gt;Test the Edge TPU (Optional)&lt;&#x2F;h3&gt;
&lt;p&gt;To verify the Edge TPU works inside a Docker container, we can build a test image.
This is particularly useful if you plan to use the TPU with containerized applications.&lt;&#x2F;p&gt;
&lt;p&gt;Create a test directory and Dockerfile:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;mkdir coraltest
cd coraltest
sudo nano Dockerfile
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Into the new file, paste:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;Dockerfile&quot;&gt;FROM debian:10

WORKDIR &#x2F;home
ENV HOME &#x2F;home
RUN cd ~
RUN apt-get update
RUN apt-get install -y git nano python3-pip python-dev pkg-config wget usbutils curl

RUN echo &amp;quot;deb https:&#x2F;&#x2F;packages.cloud.google.com&#x2F;apt coral-edgetpu-stable main&amp;quot; \
| tee &#x2F;etc&#x2F;apt&#x2F;sources.list.d&#x2F;coral-edgetpu.list
RUN curl https:&#x2F;&#x2F;packages.cloud.google.com&#x2F;apt&#x2F;doc&#x2F;apt-key.gpg | apt-key add -
RUN apt-get update
RUN apt-get install -y edgetpu-examples
RUN apt-get install libedgetpu1-std
CMD &#x2F;bin&#x2F;bash
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Build and run the test container, passing through the Coral device:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;# build the docker container
docker build -t &amp;quot;coral&amp;quot; .

# run the docker container
docker run -it --device &#x2F;dev&#x2F;apex_0:&#x2F;dev&#x2F;apex_0 coral &#x2F;bin&#x2F;bash
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Inside the container, run an inference example:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;# run an inference example from within the container
python3 &#x2F;usr&#x2F;share&#x2F;edgetpu&#x2F;examples&#x2F;classify_image.py --model &#x2F;usr&#x2F;share&#x2F;edgetpu&#x2F;examples&#x2F;models&#x2F;mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite --label &#x2F;usr&#x2F;share&#x2F;edgetpu&#x2F;examples&#x2F;models&#x2F;inat_bird_labels.txt --image &#x2F;usr&#x2F;share&#x2F;edgetpu&#x2F;examples&#x2F;images&#x2F;bird.bmp
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You should see inference results with confidence values from the Edge TPU. If not, try
a clean restart of the system.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Portainer_(Optional)&quot;&gt;Portainer (Optional)&lt;&#x2F;h3&gt;
&lt;p&gt;Portainer provides a web-based GUI for managing Docker containers, images, and volumes.
While not required, it makes container management significantly more convenient.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;This is optional, gives you a browser GUI for your various docker containers.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Install Portainer:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;docker volume create portainer_data
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v &#x2F;var&#x2F;run&#x2F;docker.sock:&#x2F;var&#x2F;run&#x2F;docker.sock -v portainer_data:&#x2F;data portainer&#x2F;portainer-ce:latest
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Access Portainer in your browser and set an admin password:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;airaspi.local:9443&quot;&gt;https:&#x2F;&#x2F;airaspi.local:9443&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;VNC_Setup_(Optional)&quot;&gt;VNC Setup (Optional)&lt;&#x2F;h3&gt;
&lt;p&gt;VNC provides remote desktop access to your headless Raspberry Pi. This is particularly
useful for testing cameras and debugging visual issues without connecting a physical
monitor.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;This is optional, useful to test your cameras on your headless device. You could attach
a monitor, but I find VNC more convenient.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Enable VNC through the Raspberry Pi configuration tool:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;sudo raspi-config
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Navigate to: &lt;strong&gt;Interface Options&lt;&#x2F;strong&gt; → &lt;strong&gt;VNC&lt;&#x2F;strong&gt; → &lt;strong&gt;Enable&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Connecting_through_VNC_Viewer&quot;&gt;Connecting through VNC Viewer&lt;&#x2F;h3&gt;
&lt;p&gt;Install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.realvnc.com&#x2F;en&#x2F;connect&#x2F;download&#x2F;viewer&#x2F;&quot;&gt;RealVNC Viewer&lt;&#x2F;a&gt; on your
computer (available for macOS, Windows, and Linux).&lt;&#x2F;p&gt;
&lt;p&gt;Connect using the address: &lt;code&gt;airaspi.local:5900&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ll be prompted for your Raspberry Pi username and password. Once connected, you&#x27;ll
have full remote desktop access for testing cameras and debugging.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Frigate_NVR_Setup&quot;&gt;Frigate NVR Setup&lt;&#x2F;h2&gt;
&lt;p&gt;Frigate is a complete Network Video Recorder (NVR) with real-time object detection
powered by the Coral Edge TPU. It&#x27;s the heart of this edge AI system.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Docker_Compose_Configuration&quot;&gt;Docker Compose Configuration&lt;&#x2F;h3&gt;
&lt;p&gt;This setup uses Docker Compose to define the Frigate container with all necessary
configurations. If you&#x27;re using Portainer, you can add this as a custom stack.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;markdown-alert-important&quot;&gt;
&lt;p&gt;Important: you need to change the paths to your own paths.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre&gt;&lt;code data-lang=&quot;yaml&quot;&gt;version: &amp;quot;3.9&amp;quot;
services:
  frigate:
    container_name: frigate
    privileged: true # this may not be necessary for all setups
    restart: unless-stopped
    image: ghcr.io&#x2F;blakeblackshear&#x2F;frigate:stable
    shm_size: &amp;quot;64mb&amp;quot; # update for your cameras based on calculation above
    devices:
      - &#x2F;dev&#x2F;apex_0:&#x2F;dev&#x2F;apex_0 # passes a PCIe Coral, follow driver instructions here https:&#x2F;&#x2F;coral.ai&#x2F;docs&#x2F;m2&#x2F;get-started&#x2F;#2a-on-linux

    volumes:
      - &#x2F;etc&#x2F;localtime:&#x2F;etc&#x2F;localtime:ro
      - &#x2F;home&#x2F;aron&#x2F;frigate&#x2F;config.yml:&#x2F;config&#x2F;config.yml # replace with your config file
      - &#x2F;home&#x2F;aron&#x2F;frigate&#x2F;storage:&#x2F;media&#x2F;frigate # replace with your storage directory
      - type: tmpfs # Optional: 1GB of memory, reduces SSD&#x2F;SD Card wear
        target: &#x2F;tmp&#x2F;cache
        tmpfs:
          size: 1000000000
    ports:
      - &amp;quot;5000:5000&amp;quot;
      - &amp;quot;8554:8554&amp;quot; # RTSP feeds
      - &amp;quot;8555:8555&#x2F;tcp&amp;quot; # WebRTC over tcp
      - &amp;quot;8555:8555&#x2F;udp&amp;quot; # WebRTC over udp
    environment:
      FRIGATE_RTSP_PASSWORD: &amp;quot;******&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Key configuration points in this Docker Compose file:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Privileged mode&lt;&#x2F;strong&gt; and &lt;strong&gt;device mappings&lt;&#x2F;strong&gt;: Required for accessing hardware (TPU,
cameras)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Shared memory size&lt;&#x2F;strong&gt;: Allocated for processing video frames efficiently&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Port mappings&lt;&#x2F;strong&gt;: Exposes Frigate&#x27;s web UI (5000) and RTSP streams (8554)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Volume mounts&lt;&#x2F;strong&gt;: Persists recordings, config, and database&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;Frigate_Configuration_File&quot;&gt;Frigate Configuration File&lt;&#x2F;h3&gt;
&lt;p&gt;Frigate requires a YAML configuration file to define cameras, detectors, and detection
zones. Create this file at the path you specified in the docker-compose file (e.g.,
&lt;code&gt;&#x2F;home&#x2F;aron&#x2F;frigate&#x2F;config.yml&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;This is necessary just once. Afterwards, you will be able to change the config in the
GUI.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Here&#x27;s a working configuration using the Coral TPU:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;yaml&quot;&gt;mqtt:
  enabled: False

detectors:
  cpu1:
    type: cpu
    num_threads: 3
  coral_pci:
    type: edgetpu
    device: pci

cameras:
  cam1: # &amp;lt;++++++ Name the camera
    ffmpeg:
      hwaccel_args: preset-rpi-64-h264
      inputs:
        - path: rtsp:&#x2F;&#x2F;192.168.1.58:8900&#x2F;cam1
          roles:
            - detect
  cam2: # &amp;lt;++++++ Name the camera
    ffmpeg:
      hwaccel_args: preset-rpi-64-h264
      inputs:
        - path: rtsp:&#x2F;&#x2F;192.168.1.58:8900&#x2F;cam2
          roles:
            - detect
    detect:
      enabled: True # &amp;lt;+++- disable detection until you have a working camera feed
      width: 1280 # &amp;lt;+++- update for your camera&amp;#39;s resolution
      height: 720 # &amp;lt;+++- update for your camera&amp;#39;s resolution
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This configuration:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disables MQTT&lt;&#x2F;strong&gt;: Simplifies setup for local-only operation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Defines two detectors&lt;&#x2F;strong&gt;: A Coral TPU detector (&lt;code&gt;coral&lt;&#x2F;code&gt;) and a CPU fallback&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Uses default detection model&lt;&#x2F;strong&gt;: Frigate includes a pre-trained model&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Configures two cameras&lt;&#x2F;strong&gt;: Both set to 1280x720 resolution&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Uses hardware acceleration&lt;&#x2F;strong&gt;: &lt;code&gt;preset-rpi-64-h264&lt;&#x2F;code&gt; for Raspberry Pi 5&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Detection zones&lt;&#x2F;strong&gt;: Enable only when camera feeds are working properly&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;MediaMTX_Setup&quot;&gt;MediaMTX Setup&lt;&#x2F;h2&gt;
&lt;p&gt;MediaMTX is a real-time media server that handles streaming from the Raspberry Pi
cameras to Frigate. It&#x27;s necessary because Frigate doesn&#x27;t directly support &lt;code&gt;libcamera&lt;&#x2F;code&gt;
(the modern Raspberry Pi camera stack).&lt;&#x2F;p&gt;
&lt;p&gt;Install MediaMTX directly on the system (not via Docker - the Docker version has
compatibility issues with libcamera).&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;markdown-alert-warning&quot;&gt;
&lt;p&gt;Double-check the chip architecture when downloading - this caused me significant
headaches during setup.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Download and install MediaMTX:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;mkdir mediamtx
cd mediamtx
wget https:&#x2F;&#x2F;github.com&#x2F;bluenviron&#x2F;mediamtx&#x2F;releases&#x2F;download&#x2F;v1.5.0&#x2F;mediamtx_v1.5.0_linux_arm64v8.tar.gz

tar xzvf mediamtx_v1.5.0_linux_arm64v8.tar.gz &amp;amp;&amp;amp; rm mediamtx_v1.5.0_linux_arm64v8.tar.gz
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;MediaMTX_Configuration&quot;&gt;MediaMTX Configuration&lt;&#x2F;h3&gt;
&lt;p&gt;Edit the &lt;code&gt;mediamtx.yml&lt;&#x2F;code&gt; file to configure camera streams. The configuration below uses
&lt;code&gt;rpicam-vid&lt;&#x2F;code&gt; (Raspberry Pi&#x27;s modern camera tool) piped through FFmpeg to create RTSP
streams.&lt;&#x2F;p&gt;
&lt;p&gt;Add the following to the &lt;code&gt;paths&lt;&#x2F;code&gt; section in &lt;code&gt;mediamtx.yml&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;yaml&quot;&gt;paths:
 cam1:
   runOnInit: bash -c &amp;#39;rpicam-vid -t 0 --camera 0 --nopreview --codec yuv420 --width 1280 --height 720 --inline --listen -o - | ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 1280x720 -i &#x2F;dev&#x2F;stdin -c:v libx264 -preset ultrafast -tune zerolatency -f rtsp rtsp:&#x2F;&#x2F;localhost:$RTSP_PORT&#x2F;$MTX_PATH&amp;#39;
   runOnInitRestart: yes
 cam2:
   runOnInit: bash -c &amp;#39;rpicam-vid -t 0 --camera 1 --nopreview --codec yuv420 --width 1280 --height 720 --inline --listen -o - | ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 1280x720 -i &#x2F;dev&#x2F;stdin -c:v libx264 -preset ultrafast -tune zerolatency -f rtsp rtsp:&#x2F;&#x2F;localhost:$RTSP_PORT&#x2F;$MTX_PATH&amp;#39;
   runOnInitRestart: yes
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This configuration:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cam1&lt;&#x2F;code&gt; and &lt;code&gt;cam2&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: Define two camera paths&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;rpicam-vid&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: Captures YUV420 video from Raspberry Pi cameras&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ffmpeg&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: Transcodes the raw video to H.264 RTSP stream&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;runOnInitRestart: yes&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: Automatically restarts the stream if it fails&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;Port_Configuration&quot;&gt;Port Configuration&lt;&#x2F;h3&gt;
&lt;p&gt;Change the default RTSP port to avoid conflicts with Frigate:&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;code&gt;mediamtx.yml&lt;&#x2F;code&gt;, change:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;yaml&quot;&gt;rtspAddress: :8554
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;yaml&quot;&gt;rtspAddress: :8900
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Otherwise there will be a port conflict with Frigate.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Start_MediaMTX&quot;&gt;Start MediaMTX&lt;&#x2F;h3&gt;
&lt;p&gt;Run MediaMTX in the foreground to verify it&#x27;s working:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;zsh&quot;&gt;.&#x2F;mediamtx
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If there are no errors, verify your streams using VLC or another RTSP client:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rtsp:&#x2F;&#x2F;airaspi.local:8900&#x2F;cam1&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;rtsp:&#x2F;&#x2F;airaspi.local:8900&#x2F;cam2&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Note: Default RTSP port is 8554, but we changed it to 8900 in the config.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Current_Status_and_Performance&quot;&gt;Current Status and Performance&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;What&amp;#39;s_Working&quot;&gt;What&#x27;s Working&lt;&#x2F;h3&gt;
&lt;p&gt;The system successfully streams from both cameras at 30fps and 720p resolution. The
Coral Edge TPU performs object detection with minimal latency - the TPU itself is not
breaking a sweat, maintaining consistently high performance.&lt;&#x2F;p&gt;
&lt;p&gt;According to Frigate documentation, the TPU can handle up to 10 cameras, so there&#x27;s
significant headroom for expansion.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;Current_Issues&quot;&gt;Current Issues&lt;&#x2F;h3&gt;
&lt;p&gt;However, there are several significant problems hampering the system:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;1. Frigate Display Limitations&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Frigate limits the display FPS to 5, which is depressing to watch, especially since the
TPU doesn&#x27;t even break a sweat. The hardware is clearly capable of much more, but
software limitations hold it back.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;2. Stream Stability Problems&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The stream is completely errant and drops frames constantly. I&#x27;ve sometimes observed
detect FPS as low as 0.2, but the TPU speed should definitely not be the bottleneck
here. One potential solution might be to attach the cameras to a separate device and
stream from there.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;3. Coral Software Abandonment&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The biggest issue is that Google seems to have abandoned the Coral ecosystem, even
though they just released new hardware for it. Their most recent Python build supports
only Python 3.9.&lt;&#x2F;p&gt;
&lt;p&gt;Specifically, &lt;code&gt;pycoral&lt;&#x2F;code&gt; appears to be the problem - without a decent update, I&#x27;m
confined to Debian 10 with Python 3.7.3. That sucks. There are custom wheels available,
but nothing that seems plug-and-play.&lt;&#x2F;p&gt;
&lt;p&gt;This severely limits the ability to use modern software and libraries with the system.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Reflections_and_Lessons_Learned&quot;&gt;Reflections and Lessons Learned&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;Hardware_Decisions&quot;&gt;Hardware Decisions&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;The M.2 E Key Choice&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The decision to go for the M.2 E key version to save money, instead of spending more on
the USB version, was a huge mistake. Please do yourself a favor and spend the extra 40
bucks.&lt;&#x2F;p&gt;
&lt;p&gt;Technically, it&#x27;s probably faster and better for continuous operation, but I have yet to
feel the benefit of that. The USB version would have offered far more flexibility and
easier debugging.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;Future_Development&quot;&gt;Future Development&lt;&#x2F;h2&gt;
&lt;p&gt;Several improvements and experiments are planned to enhance this system:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Documentation and Visual Aids&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Add images and screenshots to this build log to make it easier to follow&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Mobile Stream Integration&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Check whether &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;vdo.ninja&quot;&gt;vdo.ninja&lt;&#x2F;a&gt; is a viable way to add mobile streams,
enabling smartphone camera integration and evaluation&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;MediaMTX libcamera Support&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Reach out to the MediaMTX developers about bumping libcamera support, which would
eliminate the current &lt;code&gt;rpicam-vid&lt;&#x2F;code&gt; workaround. I suspect there&#x27;s quite a lot of
performance lost in the current pipeline.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Frigate Configuration Refinement&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Tweak the Frigate config to enable snapshots and potentially build an image&#x2F;video
database for training custom models later&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Storage Expansion&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Worry about attaching an external SSD and saving the video files on it for long-term
storage and analysis&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Data Export Capabilities&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Find a way to export the landmark points from Frigate, potentially sending them via
OSC (like in my &lt;a href=&quot;&#x2F;project&#x2F;pose2art&#x2F;&quot;&gt;pose2art&lt;&#x2F;a&gt; project) for creative applications&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Dual TPU Access&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Find a different HAT that lets me access the other TPU - I have the dual version, but
can currently only access 1 of the 2 TPUs due to hardware restrictions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
</feed>
