Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
# Troubleshooting


## No Microphone Input
*For beta only*

There is a known issue where the microphone may not initialize correctly. The best way to resolve this is to reboot the microphone array using [xvf_host](https://github.com/respeaker/reSpeaker_XVF3800_USB_4MIC_ARRAY/tree/master/host_control):

```bash
xvf_host(.exe) REBOOT 1
```
There is a known issue where the microphone may not initialize correctly. Please update to [firmware 2.1.3](../src/reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin). You may need to run the [update script](../src/reachy_mini/assets/firmware/update.sh).

Then run [examples/debug/sound_record.py](../examples/debug/sound_record.py) to check that everything is working properly.
Afterwards, run [examples/debug/sound_record.py](../examples/debug/sound_record.py) to check that everything is working properly.

If the problem persists, check the connection of the flex cables ([see slides 45 to 47](https://huggingface.co/spaces/pollen-robotics/Reachy_Mini_Assembly_Guide)).


## Sound Direction of Arrival Not Working
*For beta only*

The microphone array requires firmware version 2.1.0 or higher to support this feature. The firmware is located in `src/reachy_mini/assets/firmware/*.bin`.
The microphone array requires firmware version 2.1.0 or higher to support this feature. The firmware files are located in `src/reachy_mini/assets/firmware/*.bin`.

Refer to the [Seeed documentation](https://wiki.seeedstudio.com/respeaker_xvf3800_introduction/#update-firmware) for the upgrade process.

A [helper script](../src/reachy_mini/assets/firmware/update.sh) is available for Unix users.


## Volume Is Too Low
*Linux only*

Check with `alsamixer` that PCM1 is set to 100%. Then use PCM,0 to adjust the volume.
Check in `alsamixer` that PCM1 is set to 100%. Then use PCM,0 to adjust the volume.

To make this change permanent:
```bash
Expand All @@ -32,6 +33,7 @@ amixer -c "$CARD" set PCM,1 100%
sudo alsactl store "$CARD"
```


## Circular Buffer Overrun Warning

When starting a client with `with ReachyMini() as mini:` in Mujoco (--sim mode), you may see the following warning:
Expand Down
2 changes: 1 addition & 1 deletion examples/debug/sound_doa.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def main() -> None:
doa = mini.media.audio.get_DoA()
print(f"DOA: {doa}")
if doa[1] and np.abs(doa[0] - last_doa) > THRESHOLD:
print(f" Speech detected at {doa[0]:.1f}°")
print(f" Speech detected at {doa[0]:.1f} radians")
p_head = [np.sin(doa[0]), np.cos(doa[0]), 0.0]
print(
f" Pointing to x={p_head[0]:.2f}, y={p_head[1]:.2f}, z={p_head[2]:.2f}"
Expand Down

This file was deleted.

Git LFS file not shown
9 changes: 9 additions & 0 deletions src/reachy_mini/assets/firmware/update.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Firmware update script for Reachy Mini
# Usage: ./update.sh <firmware_file>
firmware="$1"
if [ -z "$firmware" ]; then
echo "Usage: $0 <firmware_file>"
exit 1
fi
dfu-util -R -e -a 1 -D "$firmware"
79 changes: 7 additions & 72 deletions src/reachy_mini/media/audio_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,30 @@
"""

import logging
import struct
from abc import ABC, abstractmethod
from typing import List, Optional
from typing import Optional

import numpy as np
import numpy.typing as npt
import usb
from libusb_package import get_libusb1_backend

from reachy_mini.media.audio_control_utils import ReSpeaker, init_respeaker_usb


class AudioBase(ABC):
"""Abstract class for opening and managing audio devices."""

SAMPLE_RATE = 16000 # respeaker samplerate
TIMEOUT = 100000
PARAMETERS = {
"VERSION": (48, 0, 4, "ro", "uint8"),
"AEC_AZIMUTH_VALUES": (33, 75, 16 + 1, "ro", "radians"),
"DOA_VALUE": (20, 18, 4 + 1, "ro", "uint16"),
"DOA_VALUE_RADIANS": (20, 19, 8 + 1, "ro", "radians"),
}

def __init__(self, log_level: str = "INFO") -> None:
"""Initialize the audio device."""
self.logger = logging.getLogger(__name__)
self.logger.setLevel(log_level)
self._respeaker = self._init_respeaker_usb()
# name, resid, cmdid, length, type
self._respeaker: Optional[ReSpeaker] = init_respeaker_usb()

def __del__(self) -> None:
"""Destructor to ensure resources are released."""
if self._respeaker:
usb.util.dispose_resources(self._respeaker)
self._respeaker.close()

@abstractmethod
def start_recording(self) -> None:
Expand Down Expand Up @@ -79,63 +70,6 @@ def play_sound(self, sound_file: str) -> None:
"""
pass

def _init_respeaker_usb(self) -> Optional[usb.core.Device]:
try:
dev = usb.core.find(
idVendor=0x2886, idProduct=0x001A, backend=get_libusb1_backend()
)
return dev
except usb.core.NoBackendError:
self.logger.error(
"No USB backend was found ! Make sure libusb_package is correctly installed with `pip install libusb_package`."
)
return None

def _read_usb(self, name: str) -> Optional[List[int] | List[float]]:
try:
data = self.PARAMETERS[name]
except KeyError:
self.logger.error(f"Unknown parameter: {name}")
return None

if not self._respeaker:
self.logger.warning("ReSpeaker device not found.")
return None

resid = data[0]
cmdid = 0x80 | data[1]
length = data[2]

response = self._respeaker.ctrl_transfer(
usb.util.CTRL_IN
| usb.util.CTRL_TYPE_VENDOR
| usb.util.CTRL_RECIPIENT_DEVICE,
0,
cmdid,
resid,
length,
self.TIMEOUT,
)

self.logger.debug(f"Response for {name}: {response}")

result: Optional[List[float] | List[int]] = None
if data[4] == "uint8":
result = response.tolist()
elif data[4] == "radians":
byte_data = response.tobytes()
num_values = (data[2] - 1) / 4
match_str = "<"
for i in range(int(num_values)):
match_str += "f"
result = [
float(x) for x in struct.unpack(match_str, byte_data[1 : data[2]])
]
elif data[4] == "uint16":
result = response.tolist()

return result

def get_DoA(self) -> tuple[float, bool] | None:
"""Get the Direction of Arrival (DoA) value from the ReSpeaker device.

Expand All @@ -153,7 +87,8 @@ def get_DoA(self) -> tuple[float, bool] | None:
if not self._respeaker:
self.logger.warning("ReSpeaker device not found.")
return None
result = self._read_usb("DOA_VALUE_RADIANS")

result = self._respeaker.read("DOA_VALUE_RADIANS")
if result is None:
return None
return float(result[0]), bool(result[1])
Loading