12 Commits

Author SHA1 Message Date
Björn Otgaar
4688a8fe17 chore: cleanup for merging/ switching to later branches 2025-10-30 17:07:20 +01:00
Björn Otgaar
8990af88fb Merge remote-tracking branch 'origin/dev' into feat/ri2cb-robot-connections 2025-10-22 11:48:21 +02:00
Björn Otgaar
da14a67791 feat: made ping system for automatic disconnection
events, and fixed some issues.

ref: N25B-150
2025-10-08 14:30:55 +02:00
Björn Otgaar
ae60105b4d feat: send connection message when starting up
issue: N25B-150
2025-10-08 12:11:24 +02:00
Twirre Meulenbelt
c634e4b516 chore: replace print with logging and make robot conditional
All print statements in the main program, and components used by the main program, have been replaced with appropriate logging statements. The connection to the robot now only gets made when it's possible, otherwise only the microphone will be run.

ref: N25B-119
2025-10-02 16:13:39 +02:00
Twirre Meulenbelt
2132a74321 fix: allow access to state's exit_event while exiting
When exiting, the state's `is_initialized` flag is unset. Noticeable on Windows, when a thread tried to access the state's `exit_event` property to check whether it had been set, it would complain that the state was no longer initialized. Now, even when no longer initialized, if the `exit_event` is set, it will not raise an error when accessing this attribute.

ref: N25B-119
2025-10-01 17:34:51 +02:00
Twirre Meulenbelt
d21c7fa423 fix: always use 1 audio channel
Before, I chose the number of audio channels that the microphone supports. Should be 1.

ref: N25B-119
2025-10-01 13:41:53 +02:00
Twirre Meulenbelt
afae6fc331 feat: stream audio to CB
Uses PyAudio and ZeroMQ to publish audio chunks.

ref: N25B-119
2025-10-01 10:50:53 +02:00
da99b5cd62 chore: update README 2025-09-30 13:26:42 +02:00
d48ea930a1 chore: complete installation instructions
ref: N25B-115
2025-09-30 13:16:33 +02:00
9e001da685 chore: update README and gitignore
Add installation instructions for the development environment.

ref: N25B-115
2025-09-30 13:14:52 +02:00
a41552f7c6 feat: basic implementation of CB2RI
Nothing fancy yet. When we receive a message through ZeroMQ's PUB/SUB
architecture, we tell the robot to say it out loud.
2025-09-27 20:41:03 +02:00
5 changed files with 96 additions and 1 deletions

View File

@@ -1,4 +1,6 @@
# PepperPlus-RI ## Development environment
### Linux (or WSL)
Start off by installing [Pyenv](https://github.com/pyenv/pyenv?tab=readme-ov-file#installation) and walk through the steps outlined there (be sure to also add it to PATH). Also install the [Python build requirements](https://github.com/pyenv/pyenv/wiki#suggested-build-environment). Afterwards, install Python 2.7 and activate it for your current shell:
The robot interface is a high-level API for controlling the robot. It implements the API as designed: https://utrechtuniversity.youtrack.cloud/articles/N25B-A-14/RI-CB-Communication. The robot interface is a high-level API for controlling the robot. It implements the API as designed: https://utrechtuniversity.youtrack.cloud/articles/N25B-A-14/RI-CB-Communication.

0
src/__init__.py Normal file
View File

BIN
src/__init__.pyc Normal file

Binary file not shown.

93
src/audio_streaming.py Normal file
View File

@@ -0,0 +1,93 @@
import threading
import pyaudio
import zmq
from state import state
def choose_mic_interactive(audio):
"""Choose a microphone to use. The `audio` parameter is an instance of PyAudio. Returns a dict."""
device_count = audio.get_device_count()
print("Found {} audio devices:".format(device_count))
for i in range(device_count):
print("- {}: {}".format(i, audio.get_device_info_by_index(i)["name"]))
microphone_index = None
while microphone_index is None:
chosen = input("Which device would you like to use?\n> ")
try:
chosen = int(chosen)
if chosen < 0 or chosen > device_count: raise ValueError()
microphone_index = chosen
except ValueError:
print("Please enter a number between 0 and {}".format(device_count))
chosen_microphone = audio.get_device_info_by_index(microphone_index)
print("Chose microphone \"{}\"".format(chosen_microphone["name"]))
return chosen_microphone
def choose_mic_default(audio):
"""Choose a microphone to use based on defaults. The `audio` parameter is a PyAudio. Returns a dict."""
default_device = audio.get_default_input_device_info()
return default_device
class AudioStreaming:
def __init__(self, port=5557):
self.port = port
self.audio = pyaudio.PyAudio()
self.microphone = choose_mic_default(self.audio)
self.thread = None
def run(self):
self.thread = threading.Thread(target=self._stream)
self.thread.start()
def wait_until_done(self):
if not self.thread: return
self.thread.join()
def _stream(self):
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:{}".format(self.port))
chunk = 512 # 320 at 16000 Hz is 20ms, 512 is required for Silero-VAD
stream = self.audio.open(
format=pyaudio.paFloat32,
channels=1,
rate=16000,
input=True,
input_device_index=self.microphone["index"],
frames_per_buffer=chunk,
)
try:
while not state.exit_event.is_set():
data = stream.read(chunk)
socket.send(data)
finally:
stream.stop_stream()
stream.close()
if __name__ == "__main__":
state.initialize()
try:
audio = AudioStreaming()
print("Starting audio streaming...")
audio.run()
import time
end = time.time() + 10
while not state.exit_event.is_set() and time.time() < end:
print "\rExiting in {:.2f} seconds".format(end - time.time()),
time.sleep(0.05)
state.exit_event.set()
audio.wait_until_done()
finally:
state.deinitialize()

BIN
src/audio_streaming.pyc Normal file

Binary file not shown.