Raspberry Pi Build HATでおしゃべりラジコンカーをつくってみた

スポンサーリンク

前回作成した音声合成やら、レゴやら、何やらを組み合わせて「おしゃべりラジコンカー」を作成したときのメモです。


主な材料、環境

ハード: Raspberry Pi 3 model B、Raspberry Pi Build HAT、F710ワイヤレス ゲームパッド 、レゴ、 ラズパイを固定するメイカープレート(3Dプリンタ出力)、バッテリー、バッテリーとBuild HATをつなぐケーブル 、USBオーディオ変換アダプタ、スピーカー

OS: Raspberry Pi OS (32bit)

レゴ組み立て方メモ

ラズパイ周りの組み立て


次にモーター周りの組み立て


組み合わせる


モーターの補強部品を用意


横と下にくっつける


次にボール関連を用意


これもくっつける

車輪と補強部品を用意


車輪と前の補強をくっつけて


後ろの補強もくっつけたら、完成


Build HATとモーターの接続

ポート 接続するモーター
A 右車輪用 Lアンギュラーモーター
B 左車輪用 Lアンギュラーモーター

ゲームパッドでの動かし方

ボタン 内容
左アナログスティック 上下で左車輪回転
右アナログスティック 上下で右車輪回転
ボタン ボイス5再生
ボタン ボイス6再生
ボタン ボイス7再生

プログラム

voice_car.pyという名前でラズパイ上に保存して実行。

from io import BytesIO
import os
import pygame
from buildhat import Motor, Hat
from gtts import gTTS
from pydub import AudioSegment
from pygame.locals import *
import simpleaudio


def create_audio_segment(text, speed=2.0):
    fp = BytesIO()
    gTTS(text=text, lang='ja').write_to_fp(fp)
    fp.seek(0)
    return AudioSegment.from_file(fp, format="mp3").speedup(playback_speed=speed, chunk_size=50, crossfade=0)


def play_audio_segment(seg):
    simpleaudio.play_buffer(
        seg.raw_data,
        num_channels=seg.channels,
        bytes_per_sample=seg.sample_width,
        sample_rate=seg.frame_rate
    )


def get_joy_axes_data():
    axis_lx = round(joy.get_axis(0), 3)
    axis_ly = round(joy.get_axis(1), 3)
    axis_rx = round(joy.get_axis(3), 3)
    axis_ry = round(joy.get_axis(4), 3)
    hat_x = joy.get_hat(0)[0]
    hat_y = joy.get_hat(0)[1]
    axes_data = {
        "L": (axis_lx, axis_ly),
        "R": (axis_rx, axis_ry),
        "HAT_X": hat_x,
        "HAT_Y": hat_y,
    }
    print(axes_data)
    return axis_lx, axis_ly, axis_rx, axis_ry, hat_x, hat_y


def init_motor(motor):
    motor.release = False
    motor.set_default_speed(100)
    motor.plimit(1.0)
    motor.bias(0.5)


def get_state(axis_ly, axis_ry):
    if axis_ly < -0.5 and axis_ry < -0.5:
        return 1
    if axis_ly > 0.5 and axis_ry > 0.5:
        return 2
    if axis_ly < -0.5 and axis_ry > 0.5:
        return 3
    if axis_ly > 0.5 and axis_ry < -0.5:
        return 4
    return 0


os.environ["SDL_VIDEODRIVER"] = "dummy"

pygame.init()

pygame.joystick.init()
joy = pygame.joystick.Joystick(0)
joy.init()

 # 左側のモーターを基準にするので、反対側のモーターは回転方向を反転
rotation_correction_l = 1
rotation_correction_r = -1

hat = Hat()
print('Hat voltage = %f' % hat.get_vin())

motor_r = Motor('A')
motor_l = Motor('B')
init_motor(motor_r)
init_motor(motor_l)

print('setup audio segment ...')
audio_segment_1 = create_audio_segment('全速前進')
audio_segment_2 = create_audio_segment('バックします')
audio_segment_3 = create_audio_segment('右折します', speed=1.5)
audio_segment_4 = create_audio_segment('左折します', speed=1.5)
audio_segment_5 = create_audio_segment('どいてくださーい')
audio_segment_6 = create_audio_segment('ひいちゃいますよ?')
audio_segment_7 = create_audio_segment('安全運転中です!')
print('done')

old_state = 0
while True:
    pygame.time.wait(30)
    for e in pygame.event.get():
        if e.type == pygame.locals.JOYAXISMOTION or e.type == pygame.locals.JOYHATMOTION:
            (axis_lx, axis_ly, axis_rx, axis_ry, hat_x, hat_y) = get_joy_axes_data()
            if -0.3 <= axis_ly <= 0.3:
                motor_l.stop()
            else:
                motor_l.start(speed=100 * axis_ly * rotation_correction_l)
            if -0.3 <= axis_ry <= 0.3:
                motor_r.stop()
            else:
                motor_r.start(speed=100 * axis_ry * rotation_correction_r)
            state = get_state(axis_ly, axis_ry)
            if old_state != state:
                if state == 1:
                    play_audio_segment(audio_segment_1)
                if state == 2:
                    play_audio_segment(audio_segment_2)
                if state == 3:
                    play_audio_segment(audio_segment_3)
                if state == 4:
                    play_audio_segment(audio_segment_4)
            old_state = state
        if e.type == pygame.locals.JOYBUTTONDOWN:
            print('BUTTON %d DOWN' % e.button)
            if e.button == 0:
                play_audio_segment(audio_segment_5)
            if e.button == 1:
                play_audio_segment(audio_segment_6)
            if e.button == 2:
                play_audio_segment(audio_segment_7)
        elif e.type == pygame.locals.JOYBUTTONUP:
            print('BUTTON %d UP ' % e.button)

反省点

  • モーターが動いていると音声合成が聞こえない。。。
  • 音声合成を2倍速再生したものの、ノイズが目立つ
  • 撮影の問題なのか、動画だと音声合成が聞こえない

www.sato-susumu.com

www.sato-susumu.com