Skip to content

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

  1. Prerequisites
  2. Install libusb
  3. Install the Edge TPU Runtime
  4. ARM64-Native Build with feranick/libedgetpu
  5. Install tflite-runtime or TensorFlow
  6. Verify Device Detection
  7. Run First Inference
  8. Troubleshooting
  9. 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:

python3 -c "import platform; print(platform.machine())"
# Expected output: arm64

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:

brew install python@3.12

Install Xcode command-line tools (if not already present)

xcode-select --install

2. Install libusb

The Coral USB Accelerator communicates over USB via libusb. Install it through Homebrew:

brew install libusb

Verify the installation:

brew list libusb
# Should show: /opt/homebrew/lib/libusb-1.0.0.dylib, etc.

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:

export PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig:${PKG_CONFIG_PATH:-}"

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:

bash scripts/install_coral_runtime.sh

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

# Install build dependencies
brew install cmake wget

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_PATH may 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.

tflite-runtime is a minimal package that includes only the TFLite interpreter — no TensorFlow dependency chain:

pip install tflite-runtime

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:

pip install tensorflow

As of TensorFlow 2.16+, native ARM64 macOS wheels are published on PyPI. If you are on an older version, use tensorflow-macos:

# Fallback for older TF versions
pip install 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:

system_profiler SPUSBDataType

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:

# Download model and test data
bash scripts/download_models.sh --output-dir /tmp/coral_test

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:

  1. 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.

  2. 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.

  3. Verify with system_profiler:

system_profiler SPUSBDataType | rg -i "coral|google|edgetpu|18d1"

If the device does not appear at all, it may be a hardware issue.

  1. 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:

  1. Check that the dylib exists:
ls -la /usr/local/lib/libedgetpu*.dylib
ls -la /opt/homebrew/lib/libedgetpu*.dylib
  1. Set DYLD_LIBRARY_PATH:
export DYLD_LIBRARY_PATH="/usr/local/lib:${DYLD_LIBRARY_PATH:-}"
  1. Verify with otool:
otool -L /usr/local/lib/libedgetpu.1.dylib

Ensure the install_name does not reference an absolute path that does not exist on your system.

  1. Reinstall using the helper script:
bash scripts/install_coral_runtime.sh --force

"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:

  1. Check the dylib architecture:
file /usr/local/lib/libedgetpu.1.dylib

If it says x86_64, you have the Intel build installed.

  1. Build the ARM64 version following the steps in Section 4.

  2. Or use the edgecompiler helper which automatically selects the correct architecture:

bash scripts/install_coral_runtime.sh --force
  1. As a last resort, run Python under Rosetta 2 to match the x86_64 dylib:
arch -x86_64 python3 your_script.py

This incurs a performance penalty and is not recommended for production use.

"Permission denied on USB device"

Symptoms:

RuntimeError: Failed to open USB device: Permission denied

Solutions:

  1. 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.

  2. 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
  1. Run with sudo (not recommended, but useful for debugging):
sudo python3 your_script.py

"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:

edgecompiler inspect model_edgetpu.tflite

Or check the TFLite interpreter log at runtime:

import logging
logging.basicConfig(level=logging.DEBUG)
# ... load and run model

Solutions:

  1. 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.tflite model, it has not been compiled for the Edge TPU and all ops will run on CPU.

  2. Recompile the model using edgecompiler:

edgecompiler compile model.tflite --target coral --quantize -o model_edgetpu.tflite
  1. 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:

brew install flatbuffers

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:

export TMPDIR="/tmp/bazel_edgecompiler"
mkdir -p "$TMPDIR"
CPU=darwin_arm64 make

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:

pip install "pycoral @ git+https://github.com/google-coral/pycoral.git"

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:

  1. Use a powered USB 3.0 hub.
  2. Or connect directly to the MacBook's USB-C port using a USB-A to USB-C adapter.
  3. 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_PATH works 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.