Setting Up Coral USB Accelerator on macOS (Apple Silicon M1 Pro)¶
This guide walks you through every step required to get a Google Coral USB Accelerator working on a MacBook Pro with an M1 (or later) Apple Silicon chip running macOS 14 Sonoma or newer. Because Google does not ship official ARM64 macOS builds of the Edge TPU runtime, there are a few extra steps compared to Linux or Intel Macs — but once set up, inference runs entirely natively without Docker or Rosetta 2.
Table of Contents¶
- Prerequisites
- Install libusb
- Install the Edge TPU Runtime
- ARM64-Native Build with feranick/libedgetpu
- Install tflite-runtime or TensorFlow
- Verify Device Detection
- Run First Inference
- Troubleshooting
- Known Issues on macOS Apple Silicon
1. Prerequisites¶
Before you begin, make sure your environment meets these requirements:
| Requirement | Minimum Version | How to Check |
|---|---|---|
| macOS | 14.0 (Sonoma) | sw_vers -productVersion |
| Homebrew | Latest | brew --version |
| Python | 3.9+ | python3 --version |
| Xcode CLI tools | Latest | xcode-select -p |
| Coral USB Accelerator | Any revision | Physical device |
Verify Python architecture¶
Your Python installation must be ARM64-native. The system Python that ships with macOS is already native, but if you installed Python via Homebrew or pyenv, verify the architecture:
If the output is x86_64, you are running under Rosetta 2. Switch to an
ARM64 Python build — for Homebrew, this is the default on Apple Silicon:
Install Xcode command-line tools (if not already present)¶
2. Install libusb¶
The Coral USB Accelerator communicates over USB via libusb. Install it
through Homebrew:
Verify the installation:
On Apple Silicon Macs, Homebrew installs to /opt/homebrew/ (not
/usr/local/). The PKG_CONFIG_PATH is usually set automatically, but if
you encounter build issues later, ensure it is configured:
3. Install the Edge TPU Runtime¶
The Edge TPU runtime (libedgetpu) is the shared library that the TFLite
interpreter loads as a delegate to offload computation to the Coral device.
Official Google runtime (x86_64)¶
Google ships a macOS x86_64 build that works under Rosetta 2. If you are using an x86_64 Python or do not mind the performance overhead of Rosetta translation, you can install it directly:
# Download the Edge TPU runtime package
curl -O https://github.com/google-coral/edgetpu/releases/download/release-16.0/edgetpu_runtime-16.0-macos-x86_64.zip
# Unzip the archive
unzip edgetpu_runtime-16.0-macos-x86_64.zip -d /tmp/edgetpu_runtime
# Install the library
cd /tmp/edgetpu_runtime
sudo bash install.sh
# Clean up
rm -rf /tmp/edgetpu_runtime edgetpu_runtime-16.0-macos-x86_64.zip
The install script copies libedgetpu.1.dylib to /usr/local/lib/ and
updates the dynamic linker cache.
Note: On Apple Silicon, this x86_64 dylib will only work if your Python process is also running under Rosetta 2. For native ARM64 performance, use the community build described in the next section.
Using the edgecompiler helper script¶
We provide a helper script that detects your architecture and installs the appropriate build automatically:
This script handles both ARM64 and x86_64 Macs, installs the runtime library,
sets up DYLD_LIBRARY_PATH, and installs the pycoral and
tflite-runtime Python packages.
4. ARM64-Native Build with feranick/libedgetpu¶
For true native ARM64 execution on Apple Silicon, use the community-maintained
build from feranick/libedgetpu.
This repository provides Makefiles that cross-compile libedgetpu for
darwin_arm64 without requiring Rosetta 2.
Prerequisites for building¶
Build steps¶
# Clone the repository
git clone https://github.com/feranick/libedgetpu.git
cd libedgetpu
# Build for ARM64 macOS
CPU=darwin_arm64 make
The build produces out/darwin_arm64/libedgetpu.1.dylib.
Install the built library¶
# Copy to a standard library path
sudo cp out/darwin_arm64/libedgetpu.1.dylib /usr/local/lib/
sudo ln -sf /usr/local/lib/libedgetpu.1.dylib /usr/local/lib/libedgetpu.dylib
# Update the dynamic linker cache
sudo update_dyld_shared_cache 2>/dev/null || true
Verify the architecture¶
Confirm that the dylib is ARM64:
file /usr/local/lib/libedgetpu.1.dylib
# Expected: Mach-O 64-bit dynamically linked shared library arm64
If the output mentions x86_64, you inadvertently installed the Intel build.
Remove it and rebuild with CPU=darwin_arm64.
Set library path¶
Add the library path to your shell configuration so the TFLite runtime can
find libedgetpu at load time:
# Add to ~/.zshrc (or ~/.bashrc)
echo 'export DYLD_LIBRARY_PATH="/usr/local/lib:${DYLD_LIBRARY_PATH:-}"' >> ~/.zshrc
source ~/.zshrc
Important: On macOS,
DYLD_LIBRARY_PATHmay be stripped by the system when launching apps from Finder or Spotlight. It works reliably when running Python from a terminal. If you encounter "library not found" errors from IDEs or GUI apps, set the variable in their launch configuration.
5. Install tflite-runtime or TensorFlow¶
You need either tflite-runtime (lightweight) or full tensorflow to run
TFLite models. Both work with the Edge TPU delegate.
Option A: tflite-runtime (recommended)¶
tflite-runtime is a minimal package that includes only the TFLite
interpreter — no TensorFlow dependency chain:
On Apple Silicon, tflite-runtime wheels for ARM64 macOS are available on
PyPI starting from version 2.14. If the latest version is not available for
your Python version, install a compatible wheel from
feranick/TFlite-builds:
# Example: install a specific ARM64 wheel
pip install https://github.com/feranick/TFlite-builds/releases/download/v2.15.0/tflite_runtime-2.15.0-cp312-cp312-macosx_11_0_arm64.whl
Check the feranick/TFlite-builds releases page for wheels matching your Python version (3.9, 3.10, 3.11, 3.12, 3.13).
Option B: Full TensorFlow¶
If you need TensorFlow for model conversion, training, or other tasks beyond inference:
As of TensorFlow 2.16+, native ARM64 macOS wheels are published on PyPI.
If you are on an older version, use tensorflow-macos:
Verify the installation¶
python3 -c "from tflite_runtime.interpreter import Interpreter; print('tflite-runtime OK')"
# Or:
python3 -c "import tensorflow as tf; print(f'TensorFlow {tf.__version__} OK')"
6. Verify Device Detection¶
Check the USB tree¶
Use system_profiler to confirm that macOS sees the Coral device:
Look for an entry resembling:
Google Coral:
Product ID: 0x9302
Vendor ID: 0x18d1 (Google Inc.)
Speed: Up to 5 Gb/s
Location ID: 0x...
If the device does not appear, try a different USB port (prefer USB-A via an adapter over a USB-C hub) and make sure the LED on the Coral Accelerator is lit.
Verify with Python¶
Use the CoralUSBRuntime class from edgecompiler to programmatically detect
the device:
from edgecompiler.runtime.coral_usb import CoralUSBRuntime
runtime = CoralUSBRuntime()
devices = runtime.detect_devices()
if devices:
for dev in devices:
print(f"Found: {dev.name} at {dev.path} (type: {dev.type})")
else:
print("No Coral USB Accelerator detected.")
You can also check using pycoral directly:
from pycoral.utils import edgetpu
devices = edgetpu.list_edge_tpus()
print(devices)
# Expected: [{'type': 'usb', 'path': '/dev/bus/usb/XXX/YYY'}]
7. Run First Inference¶
Once the device is detected, run a simple classification inference. First, download a test model and labels:
Then run inference in Python:
import numpy as np
from edgecompiler.runtime.coral_usb import CoralUSBRuntime
# Initialize runtime
with CoralUSBRuntime() as runtime:
# Check for device
devices = runtime.detect_devices()
if not devices:
print("No Coral USB device found!")
exit(1)
# Load the Edge TPU-compiled model
runtime.load_model("/tmp/coral_test/mobilenet_v1_1.0_224_quant_edgetpu.tflite")
# Create random input (UINT8, 224x224 RGB)
input_data = np.random.randint(0, 256, (1, 224, 224, 3), dtype=np.uint8)
# Run inference
result = runtime.infer(input_data, top_k=5)
print(f"Latency: {result.latency_ms:.1f} ms")
print("Top-5 predictions:")
for cls_id, score in result.top_classes:
print(f" Class {cls_id}: {score:.4f}")
# Benchmark performance
stats = runtime.benchmark(num_runs=100)
print(f"\nBenchmark ({stats['num_runs']} runs):")
print(f" Mean latency: {stats['mean_latency_ms']:.2f} ms")
print(f" P95 latency: {stats['p95_latency_ms']:.2f} ms")
print(f" Throughput: {stats['throughput_fps']:.1f} fps")
Alternatively, using the CLI:
edgecompiler run /tmp/coral_test/mobilenet_v1_1.0_224_quant_edgetpu.tflite \
--input /tmp/coral_test/parrot.jpg \
--target coral
# Benchmark
edgecompiler benchmark /tmp/coral_test/mobilenet_v1_1.0_224_quant_edgetpu.tflite \
--iterations 100 \
--target coral
Expected latency on an M1 Pro with Coral USB: 2–5 ms per inference for MobileNetV1.
8. Troubleshooting¶
"No device found"¶
Symptoms: detect_devices() returns an empty list, or
edgetpu.list_edge_tpus() returns [].
Solutions:
-
Check the physical connection. The LED on the Coral Accelerator should be lit (blue or white depending on model). If it is not lit, try a different USB cable or port.
-
Avoid USB-C hubs. Some USB-C hubs do not pass through the full USB 3.0 signaling required by the Coral. Use a direct USB-A port with a USB-C adapter, or a powered USB 3.0 hub.
-
Verify with system_profiler:
If the device does not appear at all, it may be a hardware issue.
- Re-plug the device. Sometimes the Coral needs to be physically disconnected and reconnected after the runtime is installed.
"libedgetpu not found"¶
Symptoms:
OSError: dlopen(libedgetpu.1.dylib, 0x0006): tried: 'libedgetpu.1.dylib' ...
Library not loaded: libedgetpu.1.dylib
Solutions:
- Check that the dylib exists:
- Set DYLD_LIBRARY_PATH:
- Verify with otool:
Ensure the install_name does not reference an absolute path that does not
exist on your system.
- Reinstall using the helper script:
"Architecture mismatch (x86_64 dylib on ARM64)"¶
Symptoms:
OSError: dlopen(...libedgetpu.1.dylib, 0x0006): no suitable image found.
Did find: .../libedgetpu.1.dylib: mach-o, but not compatible architecture
(expected arm64, found x86_64)
Solutions:
- Check the dylib architecture:
If it says x86_64, you have the Intel build installed.
-
Build the ARM64 version following the steps in Section 4.
-
Or use the edgecompiler helper which automatically selects the correct architecture:
- As a last resort, run Python under Rosetta 2 to match the x86_64 dylib:
This incurs a performance penalty and is not recommended for production use.
"Permission denied on USB device"¶
Symptoms:
Solutions:
-
On macOS, USB device permissions are typically managed automatically. If you are prompted to allow the device, approve it in System Settings > Privacy & Security > USB Devices.
-
On Linux, add a udev rule:
sudo bash -c 'echo "SUBSYSTEM==\"usb\", ATTR{idVendor}==\"18d1\", ATTR{idProduct}==\"9302\", MODE=\"0666\"" > /etc/udev/rules.d/99-edgetpu-accelerator.rules'
sudo udevadm control --reload-rules
sudo udevadm trigger
- Run with sudo (not recommended, but useful for debugging):
"Model not fully mapped to Edge TPU"¶
Symptoms: The model runs but latency is much higher than expected (e.g., 30+ ms for MobileNetV2).
Diagnosis: Some operations in the model are falling back to CPU execution.
Use edgecompiler inspect to see the operation mapping:
Or check the TFLite interpreter log at runtime:
Solutions:
-
Ensure the model file ends with
_edgetpu.tflite. Models compiled with the Edge TPU compiler include this suffix by convention. If you have a plain_quant.tflitemodel, it has not been compiled for the Edge TPU and all ops will run on CPU. -
Recompile the model using edgecompiler:
- Check unsupported ops. Operations like LSTM, Einsum, and ScatterND are not supported on the Edge TPU and will always fall back to CPU. See the supported operations table for details.
9. Known Issues on macOS Apple Silicon¶
flatbuffers via MacPorts¶
If you installed flatbuffers through MacPorts instead of Homebrew, the
flatc compiler may produce C++ bindings that differ slightly from what the
edgecompiler Python package expects. We recommend using Homebrew:
If you must use MacPorts, ensure the version matches the one used by
edgecompiler (check pyproject.toml for the pinned version).
Bazel temporary file patching¶
Building libedgetpu from source with Bazel on macOS Apple Silicon can fail
due to Bazel's temporary directory handling. The fix is to set the
TMPDIR environment variable to a path without spaces or special characters:
If you encounter "sandbox execution failed" errors, disable Bazel's sandbox:
export BAZEL_OPTS="--strategy=CppCompile=standalone --strategy=Genrule=standalone"
CPU=darwin_arm64 make
Python version compatibility¶
| Python Version | tflite-runtime | tensorflow | pycoral | Notes |
|---|---|---|---|---|
| 3.9 | ✅ | ✅ | ✅ | Minimum supported version |
| 3.10 | ✅ | ✅ | ✅ | Recommended for edgecompiler |
| 3.11 | ✅ | ✅ | ⚠️ | pycoral may need building from source |
| 3.12 | ✅ | ✅ | ⚠️ | pycoral may need building from source |
| 3.13 | ⚠️ | ✅ | ❌ | Not all packages have wheels yet |
For the smoothest experience, we recommend Python 3.10 or 3.11. If you need pycoral with Python 3.12+, install it from source:
Thermal throttling¶
Running sustained inference on both the Coral USB Accelerator and the M1 Pro GPU (Metal backend) simultaneously can cause thermal throttling. The M1 Pro has excellent thermal management, but in a fanless enclosure (e.g., MacBook Air), you may see latency increases of 20–30% after several minutes of continuous inference. This is a hardware limitation, not a software bug.
USB-C hub compatibility¶
Some USB-C hubs — particularly unpowered ones — cannot provide sufficient power for the Coral USB Accelerator (which draws up to 900 mA at 5V). If you experience intermittent disconnections or the device LED flickers:
- Use a powered USB 3.0 hub.
- Or connect directly to the MacBook's USB-C port using a USB-A to USB-C adapter.
- Avoid daisy-chaining multiple USB devices on the same hub.
DYLD_LIBRARY_PATH and SIP¶
macOS System Integrity Protection (SIP) strips DYLD_LIBRARY_PATH from
processes that are protected (e.g., apps launched from Finder, Spotlight, or
Launch Services). This means:
- Terminal-launched Python:
DYLD_LIBRARY_PATHworks correctly. - IDE-launched Python (VS Code, PyCharm): May work if the IDE itself was launched from the terminal.
- Jupyter notebooks: May not inherit the variable depending on how the kernel is launched.
Workaround: Set the library path in your IDE's run configuration, or install
libedgetpu.1.dylib to a location that the system searches by default (e.g.,
/usr/local/lib/).
Quick Reference: Complete Setup Commands¶
# 1. Install Homebrew dependencies
brew install libusb cmake wget flatbuffers
# 2. Build libedgetpu for ARM64
git clone https://github.com/feranick/libedgetpu.git
cd libedgetpu
CPU=darwin_arm64 make
sudo cp out/darwin_arm64/libedgetpu.1.dylib /usr/local/lib/
sudo ln -sf /usr/local/lib/libedgetpu.1.dylib /usr/local/lib/libedgetpu.dylib
cd ..
# 3. Set library path
echo 'export DYLD_LIBRARY_PATH="/usr/local/lib:${DYLD_LIBRARY_PATH:-}"' >> ~/.zshrc
source ~/.zshrc
# 4. Install Python packages
pip install tflite-runtime edgecompiler
# 5. Download test models
bash scripts/download_models.sh --output-dir /tmp/coral_test
# 6. Verify
python3 -c "
from edgecompiler.runtime.coral_usb import CoralUSBRuntime
runtime = CoralUSBRuntime()
devices = runtime.detect_devices()
print(f'Devices: {devices}')
"
If all steps succeed, you are ready to compile and run models on the Coral USB Accelerator with native ARM64 performance on your M1 Pro.