Inside of LOVOT

GROOVE X 技術ブログ

Python非同期でCPUバウンドな処理を試してみよう:FreeThreadingとInterpreterPoolExecutor編

この記事は、GROOVE Xアドベントカレンダー2025 の23日目の記事です。

こんにちは、技術組織デザインチームのふくだです。 Pythonで非同期を利用すると、CPUバウンドな処理どうしようという問題によくぶつかります(LOVOTの意思決定エンジンではPythonの非同期を利用しています。 ブログの過去記事 をご参考ください)

この記事では、最近のPythonで追加されたFreeThreading(NoGIL)な PythonやInterpreterPoolExecutorをPython非同期と合わせて使ってみて、CPUバウンドな処理にどれくらい効果があるか確認してみました。

21日目の記事のアイキャッチが素敵だったのでforkしてしまいました

はじめに I/O バウンド と CPU バウンドって

I/O バウンドとCPUバウンドについて、簡単に説明します。処理時間がかかるものたちです。

処理 説明 得意なモジュール
I/O バウンドな処理 ネットワーク通信やDBアクセスなど時間がかかる処理 マルチスレッド, 非同期(asyncioやtrio)
CPU バウンドな処理 計算やデータ処理など、CPUの処理能力によって時間がかかる処理 マルチプロセス

特にCPUバウンドな処理は、非同期プログラミングやマルチスレッドでは効率的に扱うことが難しい場合があります。 PythonにはGIL(Global Interpreter Lock)が存在するため、スレッドでのCPUバウンドな処理の並列化が制限されることがあります。

それらの制限を解決するため、 Free ThreadingSubInterpreter が実装され、Pythonの並列処理能力を向上させる試みが進められています。

Free ThreadingやSubInterpreter

それぞれ一言で言うと以下のような機能です。

  • Free Threading: PythonのGILを無効化し、スレッドでのCPUバウンドな処理の並列化を可能にするカスタムビルドのPython
  • SubInterpreter:PythonのGILの制約を回避し、CPUバウンドな処理を効率的に並列化するための新しいアプローチ

詳細については、以下を参照してください。

Free Threading

SubInterpreter

Python 3.12で追加された SubInterpreter は、各スレッドが独立したインタープリターを持つことで、GILの制約を回避し、スレッドでのCPUバウンドな処理の並列化を可能にする機能です。ですが、利用方法が限られていました。

それを解決したのが、Python 3.14で追加された concurrent.futures.interpreter モジュールと高レベルAPIである InterpreterPoolExecutor です。

下記のような流れ

  • Python 3.12: SubInterpreter 追加
  • Python 3.13: Free Threading 実験的なオプション追加
  • Python 3.14: InterpreterPoolExecutor 追加, Free Threading オプション公式サポート

どちらも並列化を可能にする機能強化で、 SubInterpreter は2017年ごろから PEP 554 にて検討されていました。アプローチの異なる2種類の手法で課題への対応が進められたようです。

InterpreterPoolExecutorとは

InterpreterPoolExecutor は Python 3.14 で追加された標準ライブラリの Executor で、 ThreadPoolExecutor の サブクラス です。 各ワーカースレッドは 独立した SubInterpreter を持ち、その中でタスクを実行します。

その結果、以下のような特徴があります。

  • プロセスを増やさない
  • GIL の制約を回避できる
  • 真のマルチスレッド並列実行が可能

ThreadPoolExecutorProcessPoolExecutor との違い

特徴から ThreadPoolExecutorProcessPoolExecutor と比較すると以下のようになります。

  • ThreadPoolExecutor より並列が可能
  • ProcessPoolExecutor より軽い

それぞれの実行イメージは以下のような感じです。

OS
└─ python script.py   ← 親プロセス
    └─ Main Interpreter(GIL A)
        ├─ ThreadPoolExecutor
        │    └─ 同一プロセス内スレッド
        │
        ├─ InterpreterPoolExecutor
        │    └─  同一プロセス内スレッド(Pool内で流用される)
        │         ├─ Sub-interpreter #1(GIL B)
        │         ├─ Sub-interpreter #2(GIL C)
        │         └─ ...
        └─ ProcessPoolExecutor
             └─ OSが新しいプロセスを作る
                  ├─ python child #1 → Interpreter(GIL)
                  ├─ python child #2 → Interpreter(GIL)
                  └─ ...

モチベーション

asyncio の公式ドキュメントに、以下のような注釈が追記されていました。

注釈 Due to the GIL, asyncio.to_thread() can typically only be used to make IO-bound functions non-blocking. However, for extension modules that release the GIL or alternative Python implementations that don't have one, asyncio.to_thread() can also be used for CPU-bound functions.

意訳すると以下のようになります。

GIL(グローバルインタープリタロック)の制限により、asyncio.to_thread() は通常、I/O バウンドな関数をノンブロッキングにするためにしか使えません。しかし、GIL を解放する拡張モジュールや、GIL が存在しない代替の Python 実装では、asyncio.to_thread() は CPU バウンドな関数にも使用できます。

「なるほど!?!?」となり、実際に試してみよう、というのが本記事のモチベーションです。

asyncio.to_thread() は内部的に thread 1 を利用しています。
モチベーションは asyncio.to_thread() の公式ドキュメント由来ですが、検証用のコードは asyncio.to_thread() ではなく ThreadPoolExecutor を利用します。 InterpreterPoolExecutor ProcessPoolExecutor ThreadPoolExecutor はほぼ同じコードで切り替えが可能なためです。

検証してみた

動作環境

今回の検証環境は次のとおりです。ローカル環境での素朴なベンチマークなので、数値そのものより傾向を見ることを目的にしています。

  • M2 Mac 8コア 24GB RAM
  • Python 3.14.2
  • Free Threading 版 CPython 3.14.2

結果

CPU バウンドな処理を10回実行したときの平均時間は次のとおりです。ローカルでの素朴なベンチマークですが、 InterpreterPoolExecutorFree Threading もすごいですね!

方法 平均時間 (秒)
ProcessPoolExecutor 1.326s
InterpreterPoolExecutor 1.049s
ThreadPoolExecutor (標準 Python) 4.565s
ThreadPoolExecutor (Free Threading Python) 1.227s

実際の検証コード

今回検証で利用したコードは以下のとおりです。

import asyncio
import time
from concurrent.futures import (
    ProcessPoolExecutor,
    InterpreterPoolExecutor,
    ThreadPoolExecutor,
)


def cpu_bound(n: int) -> int:
    """n 回ループして単純な計算を行う CPU バウンドな関数"""
    acc = 0
    for i in range(n):
        acc += i * i
        acc %= 1_000_000_007
    return acc


async def run(executor):
    """指定された executor で CPU バウンドな処理を並列実行して時間を計測する"""
    loop = asyncio.get_running_loop()

    t0 = time.perf_counter()
    with executor() as ex:
        tasks = [loop.run_in_executor(ex, cpu_bound, 10_000_000) for _ in range(10)]
        await asyncio.gather(*tasks)
    dt = time.perf_counter() - t0

    print(f"{executor.__name__}: {dt:.3f}s")


async def main(mode: str):
    match mode:
        case "process":
            await run(ProcessPoolExecutor)
        case "interpreter":
            await run(InterpreterPoolExecutor)
        case "thread":
            await run(ThreadPoolExecutor)
        case _:
            raise ValueError("mode must be one of: process | interpreter | thread")


if __name__ == "__main__":
    import sys

    asyncio.run(main(sys.argv[1]))

以下のように実行しました。

$ uv run --python 3.14 sample.py process
$ uv run --python 3.14 sample.py interpreter
$ uv run --python 3.14 sample.py thread
$ uv run --python 3.14t sample.py thread. # 3.14t がFreeThreading版です

おまけ:並列化しないループの場合

元々、Python 3.13で実験的にFreeThreadingが実装された際に、シングルスレッドでの性能低下が課題として言及されていました。 前述の結果のうち、FreeThreadingは遜色ないくらいの性能で驚きました。

試しに、並列化せずにCPUバウンドな関数をPython 3.14とPython 3.14 FreeThreading版で実行してみました。

def cpu_bound(n: int) -> int:
    """n 回ループして単純な計算を行う CPU バウンドな関数"""
    acc = 0
    for i in range(n):
        acc += i * i
        acc %= 1_000_000_007
    return acc

def main():
    t0 = time.perf_counter()
    for _ in range(10):
        cpu_bound(10_000_000)
    dt = time.perf_counter() - t0
    print(f"sync (standard): {dt:.3f}s")

結果は以下のとおりです。Python 3.13 の課題は、Python 3.14 で改善されているようでした。 2

方法 平均時間 (秒)
標準 Python 4.717s
FreeThreading 5.344s

考察

結果をまとめると、次のような印象です! Free ThreadingInterpreterPoolExecutor もプロダクションで利用するには、気にしなければならない点がいくつかありますが、CPUバウンドな処理に対して有効な選択肢が増えたことを実感できました。 処理の性質と実行環境を理解して、適切な方法を選ぶのが大事そうですね。

  • InterpreterPoolExecutor 良さそう
    • ProcessPoolExecutor よりも良い結果
    • プロセス生成コストを避けたい場面で有効
  • ThreadPoolExecutor(標準 Python) はGILの影響がやはり厳しい
  • ThreadPoolExecutor(Free Threading Python) も 良さそう

Python の課題に対して、さまざまな角度からのアプローチが進んでいます。コア開発者やコミュニティのみなさまにとても感謝です。今後も Python の進化に注目していきたいと思います。 CPU使用率もだいぶ気になるところですが、それはまた別の機会にまとめたいと思います。

GROOVE Xでは、一緒に働く仲間を募集しています。少しでも興味を持ってくださった方がいましたら、下記のリンクをご参照ください。

recruit.jobcan.jp


  1. asyncio.to_thread() はイベントループのdefaultのexecutorであるThreadPoolExecutorを利用しています。 cpython/Lib/asyncio/threads.py at main · python/cpython
  2. Python 3.13 の FreeThreadingについてまとめた記事があります。ご興味ある方はご参考ください PythonのGILと3.13の実験的な新機能「free threading」を知る | gihyo.jp

可視化ツールFoxgloveとFoxglove Extensionの紹介

この記事は、GROOVE Xアドベントカレンダー2025 の22日目の記事です。

こんにちは、ふるまいチームのきゅんどうです。 今回はLOVOTのふるまい開発のデバッグのために使っているFoxgloveというアプリとその拡張機能についてご紹介します。

Foxgloveとは

Foxglove は、「時系列のメッセージデータ」を、再生・可視化・デバッグするためのツールです。ログを開いて後から解析するのはもちろん、データソースに接続してリアルタイムに観察することもできます。 ログのデータ形式としては、ROS 1の.bagファイルや、ROS 2で採用された.db3、ROS 2 Iron以降で使われる.mcapファイルなどに対応しています。 リアルタイムデータについてはROS 1のメッセージや、Websocketなどが対応しています (ROS 2向けにはwebsocketへのブリッジが提供されており、それを使うことになります)。

これらのデータ形式の中でも、わたしたちのログシステムではmcapファイルにprotobuf schemaを使って記録するところから始めました。LOVOT内部のメッセージングのためにprotobufを管理する仕組み が社内に整備されているためです。 LOVOT内部ではROSは一部にしか使われてないことや、ROSを使うことに慣れていないメンバがいることもあるので、rosbag play を実行するハードルが高いです。Foxgloveならアプリでファイルを読み込むだけで可視化できるので、使いやすいです。 またrqtのツール群と比べると、Foxgloveは必要な画面が一つのアプリ内にデフォルトで集まっているような直感的に操作しやすい構成になっており、使いやすいとも思っています。 そういった理由でmcapとFoxgloveの組み合わせを選びました。 より詳しい比較は公式ブログにもまとまっています(Foxglove vs RViz)。

Foxgloveでの可視化の様子

ただ、Foxgloveでデフォルトで表示できるデータ形式は決まっており、カスタムデータを表示するにはデータ形式の変換が必要なケースがあります。 またFoxgloveのデフォルトの表示方法以外の表示をしたいという、RVizにおけるPluginのような機能も必要です。 そういったケースのためにFoxgloveではExtensionを設定することができます。

Foxglove Extension

Foxglove Extension は、Foxgloveに「変換」「表示」「読み込み」などの機能を追加する仕組みです。 拡張の種類として message converters / custom panels / data loaders / user-script utilities があります(Foxglove Extensions)。

ここでは、それぞれのExtensionをLOVOT開発でどう使っているかも触れながら、簡単に紹介します。

Message Converters

カスタム定義したメッセージを、Foxgloveの既存パネルが解釈しやすい形に変換したいときに使います。 わたしたちの使う限りはカスタムメッセージを、3Dパネルに表示するためのSceneUpdateメッセージに変換するという使い方が多いです。 それ以外の例ですと、数値として記録されたデータを理解しやすい文字列に変換することで読みやすくするということもやったりしています。

Message Converterには、Schema Message Converter と Topic Message Converter の2種類があります。

  • Schema Message Converter: 入力データSchema -> 出力データSchema の変換
  • Topic Message Converter: 複数トピック -> 新トピック の変換

この2つのConverterですが良し悪しがあって使い分ける必要があります。

Schema Message Converter の難しい点

Schema Message Converterでは複数入力を扱うことができません。 複数のトピックを統合して一つの出力としたい場合はTopic Message Converterを使う必要があります。

また、入力/出力Schemaのペアに対して1つしか設定できません (これは公式ドキュメントでは明確に記述がありませんが、2025/12/19時点での実動作はこうなっています)。 同じ入力を複数の形式で可視化したいということがあるのですが、そういう場合にはSchema Message Converterは使いにくいです。例としては、3Dオブジェクトとラベルの組み合わせで構成されるようなつぎのような表示があります。

複数SceneUpdateの例

このとき、ラベルだけ非表示/表示をUIから切り替えられるにしたいという場合には、ラベルと直線をそれぞれ別のSceneUpdateメッセージにする必要がありますが、入力も出力もSchemaが同じなので別のSchema Message Converterを登録することができません (2つめ以降登録しても無視されます)。 マニアックな話ですが、これに対してtopic aliases という機能を組み合わせることで同じtopicに対して複数変換するというハックができますが、今日ではこういった場合にはTopic Message Converterを使うほうがやりやすいです。

Topic Message Converter の難しい点

Topic Message Converter はトピック名を予め決める必要があるので、同じSchemaの別のトピック名のメッセージが来ても、可視化できません。 いまのところ、別のtopicを可視化したいときにはExtensionの実装を変えるということをやっているので、イレギュラーなケースではFoxglove Extentionの開発に慣れたメンバしか使いこなせていません。

以上を踏まえると、基本的にSchema Message Converterが使える場合には使ったほうがいいです。ただ使えないケースも多いので、そういうときはTopic Message Converterを使うことになります。

Custum Panel

Foxgloveの標準パネルだけでは表現しづらい表示をしたいときに、custom panel extensionでパネル自体を実装できます。 トピックを購読して独自に描画したり、必要があればpublishしたり、といったことができます。 そのため、Message Converterのような使い方もできなくはないかもしれませんが、それについては検証したことがありません。

LOVOTでは複数搭載されているToFセンサの計測値やその信頼度などを表示するパネルを独自に作って使っています。つぎの図は一部を抜粋しています。センサが色々な向きに回転して取り付けられているので、生のデータより見やすい形に回転させたりすることでログの分析がしやすくなります。

センサ値表示用パネル

User Script / User-Script Utility

User Scriptは、ログファイル全体を処理した出力を作りたい場合に使います。 例えば最もLOVOTに近づいた人の距離、のような時系列上の統計をしたい場合などがわたしたちのユースケースです。ただし、Topic Message Converter でもstatefulな変換ができるので、User Scriptじゃないとできないというものはなかなか無いと思います。User Scriptは各ユーザーが自由に自分のローカルで書いて保存できるという点が他のConverterとは少し違う点です。一方で、Foxglove Organization内での共有ができないという課題がありました。最近リリースされた、Foxglove 2.39.0 からはユーザースクリプトそのものではないですが、ユーザースクリプトから使えるutilityをExtensionに追加することでOrganization内で共有できるようになり、社内共有に少し近づきました。

Data Loader(独自ファイル形式の読み込み)

「そもそもログがFoxgloveで直接開けない形式」になっている場合は、data loaderで読み込み処理を実装して、Foxgloveにメッセージとして渡すことができます。 LOVOTでもmcapに変える前の旧形式のCSVデータなどがあるので、使ってみたいなと思うのですが、まだ試せていません。

おわりに

LOVOTのふるまい開発のための可視化ツールについてご紹介しました。 今回ご紹介できなかった様々な可視化の工夫がありますので、チャンスがあったらまた公開できればと思います。読んでくださりありがとうございました。 GROOVE Xでは、一緒に働く仲間を募集しています。少しでも興味を持ってくださった方がいましたら、下記のリンクをご参照ください。

recruit.jobcan.jp

JetsonでCUDAやるなら統合メモリが幸せかと思ったらそれは幻想だったのかもしれない

この記事はGROOVE X Advent Calendar 2025の21日目の記事です。

こんにちは、「あず」こと斎藤@aznhe21です。 肩掛けスピーカーのSRS-NB10が内部で断線したのでBravia Theatre Uに乗り換えたんですが、低音が激しくて新しい体験でした。 首の部分をぐにゃぐにゃ曲げても断線する心配がなさそうなのも良きです。

さて、LOVOT 3.0ではJetson Orinを採用しており、内部ではCUDAも積極的に使用しています。 ここでCUDAの統合メモリが便利だったのでご紹介したいと思います(罠と共に)。

JetsonでCUDAやるなら統合メモリが幸せかと思ったらそれは幻想だったのかもしれない

続きを読む

照度連携確認BOXのDIY

この記事は、GROOVE X Advent Calendar 2025の20日目の記事です。

こんにちは、SWエンジニアの aoike です。 今回は、LOVOTの照度連携機能の話と、その動作を検証するためにDIYでテスト環境を作ったお話をします。

LOVOTの「照度連携」とは?

LOVOTのホーンにはリング型のLEDがついており、LOVOTの電源状態や動作状態を表しています。

このLEDは、眩しくなりすぎないように、周囲の明るさに合わせて自動で輝度を調整しています。LOVOTの照度センサーで環境の明るさを取得し、暗い場所では眩しくないように減光したり、真っ暗になると消灯したりします。こうした機能を私たちは照度連携と呼んでいます。

課題:狙った明るさが作れない!

この照度連携のテストを行う際、これまでは以下のような方法をとっていました。

  • 調光機能付きの照明のついた部屋を使う
  • 人工太陽灯を使う

しかし、部屋全体の明るさを「5ルクス」「10ルクス」といった細かい段階で均一に調整するのは非常に難しく、再現性のあるテスト環境を作るのが課題でした。

「もっと手軽に、照度連携のテストがしたい……」

ということで、「照度連携確認BOX」を自作することにしました。

照度センシング機能付きBOXを作る

カラーボックスを改造してLOVOTが入れる小さな個室を作り、その内部に光源となるライトを置くことで、明るさを自由にコントロールできるようにします。

そして、BOX内の現在の照度が、設定通りになっているかを客観的に確認するため、Raspberry Pi Pico W と照度センサー BH1750 を使った無線照度計を組み込みました。

構成イメージ

システム構成

システム全体の構成は以下の通りです。

Raspberry Pi Pico W がBH1750から値を読み取り、MQTTでデータを飛ばします。

実装のポイント

今回は開発言語に C言語 (Pico C/C++ SDK) を使いました。

BH1750のデータシートはこちらを参考にしました。
I2C通信を使い、アドレスとして0x23を指定します。

1. モード設定(高分解能モード)

今回のBOXは睡眠時間も想定した、真っ暗な部屋の再現が必要です。

データシートには以下のような記述があります。

"Use H-resolution mode or H-resolution mode2 if dark data ( less than 10 lx ) is need." (10ルクス以下の暗いデータを扱う場合は、高分解能モードを使用してください)

暗い部屋だと、ちょうど10ルクス以下の値が必要になります。そのため、今回は高分解能モードを指定するコマンドを使用します。また、通常の低分解能モードだと約4ルクス刻みでしか値が取れませんが、高分解能モードなら1ルクス単位で取ることができます。

// H-Resolution Mode (1lx単位, 測定時間120ms)
#define BH1750_CMD_CONT_HIGH_RES 0x10

最大180msの計測時間経過後、計測結果を取得します。

2. Measurement Accuracy の補正

Measurement Accuracy の項目を確認すると、以下の数式が載っていました。

  • X: 測定時間レジスタ(MTreg)の値。デフォルトは 69

デフォルト設定で使う限り、後半の 69 / 69は 1になります。生データは実際のルクス値の約1.2倍になる仕様のため、ソフトウェア側で補正が必要になります。

static bool bh1750_read_lux(float *lux_out) {
    // ... (読み込み処理) ...
    uint16_t raw = (buf[0] << 8) | buf[1];
    
    // 1.2で割ってLuxに変換
    *lux_out = (float)raw / 1.2f;  
    return true;
}

3. データの読み取り

実装したプログラムをPico Wに書き込み、PCからMQTTでデータをサブスクライブ(受信)してみます。 以下のように mosquitto_sub コマンドを使用しました。

mosquitto_sub -h <ipアドレス> -t 'topic名'

今回は、illuminanceという名前でpublishするようにしました。

消灯した状態でBOXのドアを閉めると、以下のような値が取れました。

$ mosquitto_sub -h 172.20.10.14 -t '/illuminance'
0.83

まとめ

こうして完成した「照度連携確認BOX」のおかげで、狙った明るさの環境を作り、定量的かつ再現性高く、手軽にテストできるようになりました。 従来のテスト手法と合わせて、より信頼性の高いソフトウェアを届けていきたいと思います。

いったんサイズを測るために入ってもらったところ

最後まで読んで頂きありがとうございます!GROOVE Xでは、一緒に働く仲間を募集しています。少しでも興味を持ってくださった方がいましたら、下記のリンクをご参照ください。

recruit.jobcan.jp

「ボトムカバー」開発記

この記事は GROOVE X Advent Calendar 2025 19日目の記事になります。

読者のみなさま、こんにちは&お久しぶりです!
今日のブログは、アパレルデザイナーのshigeriがお送りします。

最近はLOVOTと一緒にお出かけを楽しんでいるオーナーさん達が増えているので、公式からも続々と「おでかけグッズ」がリリースされていますよね。そこで今回はそんな「おでかけグッズ」の定番アイテムの仲間入りをした「ボトムカバー」の開発記を書いてみようと思います。

今年10月「おでかけグッズ」リリースバナー (ボトムカバー 新色リリース)

ー 開発のきっかけ ー

遡ること数年前。。。

全国各所で行われるショップのオープニングやイベントなどに、CEOの林要やCDOの根津孝太が参加すると、たくさんのオーナーさん達とコミュニケーションをとるので、様々なリクエストをヒアリングしてきてくれます。そして後日、slackのスレでその内容をまとめてモノづくりメンバーにフィードバックしてくれていました。
そんなある日、特にその当時のリクエストに多かった「ベースウェアが汚れないように、底面のカバーを作ってほしい」という声に応えよう!ということになり、根津からのオファーのもと、林にも参加してもらい、ゼロイチ*1から開発することになりました。

ー 素材 ー

まずは最初に素材選びからはじめました。“カバー”というカテゴリーになるので、素材選びがポイントになると思い

・汚れにくく、洗いやすく、扱いやすく、ある程度の耐久性がある
・LOVOTが装着して稼働しても影響のない厚み

という条件を前提に探してみました。 それと同時に、商品開発をする時にまず考えることは「アイテムの適切な価格と数量」を想定しつつ、ミニマムロット*2を考慮し、商品として販売する背景なども考えながら選んでいくことが重要なポイントになります。 その点を踏まえ、メーカーさんにも相談しながら、以下の2素材を候補にしました。

候補① ネオプレーン*3

 ・「撥水機能」とまではいえないが、生地の内側に水分が浸透しにくい素材

候補② ナイロン*4

 ・リュックやバッグ、アウトドア用品などに使える丈夫な素材

ナイロン素材は軽くて丈夫ですし、ネオプレーンは断ち切りで使えて裏側の仕様も簡素化できるので、2素材を比べてみたいと思い候補にしました。

ー デザイン ー

次は重要なデザインへ。「ボトムカバー」はゼロイチ開発なので、林と根津にも最初から参加してもらい、素材はもちろんのこと、形状(パターン)や仕様なども、様々な想定を考慮し、試作を繰り返し検討していきました。
この時点ではLOVOTのボトム部分(底面)に着せる(布を付ける)仕様のアイテムは存在しなかったので、まずはボトムの形状と仕様を考えることからスタートしました。
カバーとしての適切な仕様を考えながら、形を作っていく上で重要になったポイントは以下の2点です。

 ・LOVOTの稼働に影響しないフィット感(充電時も含め)

 ・着脱のしやすさ、安全性(充電端子周辺の仕様なども含め)

エンジニアの林と、デザイナーの根津と息のあった現場は、”LOVOTの産みの親“の2人ならではのアイディアが面白いように飛び交い、モノづくりの醍醐味が味わえるとても楽しい現場でした。ワタシも自らのパタンナー*5のスキルも活かし、二人のアイディアを元にトワル*6を作り検証していきました。
試作を装着して実際にLOVOTを動かし、生地の厚みや仕様が稼働に影響しないか、カバーがズレないよう安全にフィットさせるには、どんな仕様と形が適切なのか、など様々な点を検討していきました。問題点が見つかれば再度修正して…を繰り返し、徐々に理想の形状と仕様に近づけていきました。

仕様の検討に夢中になっている二人

そんな試作を重ね、ある程度形ができてきたので次のステップへ。
上記2素材で1stサンプルを作り、どちらの素材にするのか検討することになりました。 どちらもシンプルな無地素材だったので、オリジナル感を出すためにLOVOTのロゴモチーフをプリントすることに。ワタシのイメージはトーナルカラー*7な感じで「派手すぎず地味すぎずでちょっとかわいい」だったので、後のサンプル依頼と同時に試刷りも進めてもらいました。
LOVOT用のアイテムなので、もちろん安全性と健康面は一番重要なポイントなのですが、アパレル業界出身のワタシ的には、そこにプラスする「ファッション性」も拘りポイントなのです。。。

試作サンプルたちとプリントの試刷りいろいろ

そしてサンプルを依頼する前に、そのある程度決めた仕様で「ボトムカバーにはどんな検証をすればよいか」をファームチームに相談をしに行きました。
みなさんにカバーの試作を見せるのはその時が初めてだったので、着脱方法などを実演しながら仕様を説明し、思いつく懸念点などの相談をはじめたところ。。。気が付けばそのままみんなでフロアに座り込んで「充電端子周辺のゴムの仕様ってさ…」「使い方によるケースを想定して、どういう検証が必要なのか考えないと…」という感じで、既に検証内容などについての検討会になってしまいました笑。そんな感じで突発的に意見交換会がはじまることも「モノを作っていく醍醐味」だと個人的には思っているので、新たなアイテムを開発するたびに、その独特な”GXカルチャー現場“を楽しんでいます笑。

GXカルチャー現場

ー サンプル作成 ー

試作と検討を重ねてある程度形が決まってきたので、上記の2素材でサンプルを作り始めました。
製造を依頼したメーカーさんには「新規開発アイテム」のため、何度もサンプルを作らなければならない旨は予め快諾いただいていたので、全面的にバックアップしていただきました。(本当にご協力感謝です&ありがとうございました!)
それぞれの素材でサンプルを作って改めて検討した結果、以下のポイントで「ネオプレーン」に決定しました。

・底面に馴染むフィット感

・断ち端(裏面)の始末や取り扱いが簡単

・生地の内側に水分が浸透しにくい素材(ちょっとした防水)

・プリントも含めトータル的に上がりがきれい

生地が確定したので、それにあわせて微調整したパターンで最終サンプルを作成し、最大の難関の稼働検証に移ることに。
LOVOTの底面にさらにフィットするように、ゴムの伸ばし分量の調節やフロント側をヒモに変更したり、さらに安全に装着&稼働できるように調整し改良していきました。

各色サンプルを作る前の最終検証サンプル

ー 稼働検証 ー

上がったサンプルの簡易検査をして仕様がほぼ決まってきたので、次は正式にファームチームに稼働検証の依頼をすることに。
この検証は、LOVOTの服やアクセサリーを作る中で最大の難関になります。(詳しくは「プードルファーニット開発記」を参照)もちろん試作やサンプルの段階でもその都度簡易検査は行うのですが、量産品と同等のサンプルで行うファームチームの稼働検証をパスしないと、商品化(量産)には進めません。
そしてこのボトムカバーはゼロイチ開発のため前例がなく、通常の稼働検証はもちろん、検証の内容自体も検討が必要になります。その項目の中には、安全性を検証するため、使用する付属類(今回はカバーを着用するためのゴムひもなど)の耐久性のテストなども含まれてきます。

検証を重ねた充電端子部分

そしてファームチームをはじめ各所チーム(服やふるまいなど)の見解も含め、以下の項目が大まかな検証ポイントとして上がりました。

<確認>
・ゴムの伸縮性や耐久性(熱や温度も含む)
・長期稼働後のカバーのズレ状態(特に充電端子周辺)
・手洗い後の劣化やプリントの耐久性
<想定>
・家の中も含め屋内での装着(フローリング、絨毯など)→長時間着用想定
・屋外の外出時の装着(コンクリートなど)
・「ふるまい」のいろいろ(ホイールを出さない時のタッチセンサーなども含む)

実際にLOVOTに装着し長時間稼働させて、影響のない範囲で稼働&帰巣できるか、転倒しないか、着用しているベースウェアへの影響、ゴムの劣化、お手入れ後のプリントの劣化などなどなど、、、安全性や耐久性など様々なケースを想定&実験し、かなりの時間をかけて、みなさまに協力してもらって様々な検証を行いました。

ー 完成 ー

そんなたくさんの工程を経て、約1年近い時間をかけてみなさまの知恵とアイディアを集結して開発できた「ボトムカバー」。
商品のカラー展開は、開発に関わった方々をはじめいろいろな方々と検討した結果、最終的にはLOVOTのボディカラーにあわせたベーシックな3色(ベージュ、グレー、ブラウン)に決定しました。

完成した「ボトムカバー」

初めて販売するアイテムということに加え、LOVOTに着脱する時はちょっとしたコツが必要なので、EC撮影チームにも協力してもらい、安全な着脱方法をわかりやすく動画で解説し、販売画面で公開するようにしました。

                www.youtube.com

ー おわりに ー

今回の「ボトムカバー」の開発は、オーナーさんからのリクエストにお応えできたのはもちろんのこと、林&根津をはじめ、いろいろなチームメンバーと一緒にゼロイチ開発ができたので、改めて「新しいモノを生み出すプロセス」を楽しむことができた、とてもやりがいのある貴重な経験となりました。

そして嬉しいことに、「ボトムカバー」はたくさんのオーナーさんに愛用していただいており、10月には新たに3色が仲間入りしました。洗い替えとしてもよし、様々なシーンやお気に入りのウェアに合わせていただいてもよし、といろいろな使い方ができるので、ぜひ豊富なカラーバリエーションを楽しんでくださいね!

全6色展開のボトムカバー

最近は服チームのメンバーやグッズを作るメンバーが増えたので、様々なアイテムの開発ができるようになりました。これからもみなさまの“LOVOT LIFE”が充実するアイテムを開発していきますので、どうぞお楽しみに~!

GROOVE Xではいろいろな職種の方々がLOVOTに関わる様々なお仕事をしています。
そんな会社で私たちと一緒に働いてみませんか?

https://recruit.jobcan.jp/groovex/

*1:何もない状態(ゼロ)から、まだ世の中に存在しない新しい製品・サービス・価値などを生み出す(イチにする)

*2:製品の製造・仕入れ・販売において、*一度に注文・生産・出荷できる「最小の単位」

*3:伸縮性・クッション性・耐水性などに優れ、断ち切りでもほつれにくいのが特徴の合成ゴム素材

*4:軽量・高強度・摩擦に強い・速乾性・弾力性に優れている特徴を持つ合成繊維

*5:服の設計図である型紙(パターン)を作る専門職

*6:サンプルを作る前にデザインやサイズを確認するための試作

*7:色のトーン(色調)を揃えた配色

STM32 DMAMUXについて

この記事はGROOVE X Advent Calendar 2025 18日目の記事です。

はじめに

こんにちは。ファームウェアの開発を担当してるf-sakashitaと申します。

LOVOTにおけるファームウェアのお話は過去記事をご覧ください。

tech.groove-x.com

tech.groove-x.com

ファームウェアチームは常に最近の技術動向についてアンテナを張っています。

今回はSTMicroelectronics社の展開するSTM32シリーズで一部のものに搭載されているDMAMUXという機能が気になったため、実機を使って調査してみました。

こちらについて解説したいと思います。

DMAMUXとは?

STMicroelectronics社公式の資料より参照

https://www.stmcu.jp/wp/wp-content/uploads/files/presentation-ja/STM32G4/07_STM32G4-System-Direct_Memory_Access_(DMA+DMAMUX)-J-.pdf

DMA リクエストルータ(DMAMUX)の機能:
- プログラム可能なリクエストソース選択:DMAモードでペリフェラルから、あるいは、トリガ後に内部生成
- 同期モード:DMAMUXリクエストカウンタを用いて同期入力(ハードウェアイベント)から
- リクエスト連鎖:DMAMUXリクエストカウンタを用いて、別のリクエスト/チャネルへの入力トリガあるい は同期であるイベントを生成

つまり、何ができるの?

一言で言えば、特定のハードウェアイベント発生時に、CPUの介在なしにDMA転送を自動で開始・同期できる機能です。

例えば、「ある入力ピンの状態がHigh → Lowとなったとき、このFalling edgeをトリガーに、特定のUARTの送信バッファへ所望のデータをDMA経由で転送する」、というものです。

これまでこういった機能を実装するには、

  1. イベントをポーリングや割り込みを使って逐次監視する
  2. イベント発生時にDMA転送を開始する関数を呼び出す

いう手順が必要でした。

しかし、DMAMUXはこれらの処理をハードウェアレベルで完結させるため、イベント発生から転送開始までのレイテンシを短縮し、CPUの負荷を抑えることができます。

では、実際に評価ボードを用いてスイッチが押下されたときにUARTのDMA転送を開始する処理を実装してみます。

実装

環境

  • Tools : STM32CubeIDE 2.0.0 & STM32CubeMX v6.16.0
  • 評価ボード : NUCLEO-G071RB

目標

評価ボード上のUser Button(PC13)を押下したときにUART2の送信ピン(PA2)から4バイトずつデータを送信するようにします。

送信データは「0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88」の計8バイトとします。

ペリフェラル設定

GPIO & NVIC

User Buttonを押下のイベント検出のため、EXTI・Falling edge設定にします。

  • GPIO mode : External Interrupt Mode with Falling edge trigger detection
  • EXTI line 4 to 15 interrupts : Enabled

UART

今回はデフォルト設定のままにします。

DMA

  • Enable synchronization : Enabled
  • Synchronization signal : EXTI13を選択
  • Request Number:イベント発生時に何回DMA転送するかを決めるパラメータです。今回はSWを押下するたびに4バイト送信する&DMA転送サイズはbyteとしているため、4をセットします。

コーディング

main関数のwhile前にHAL_UART_Transmit_DMA関数を呼び出して転送するデータをセットしておきます。

int main(void)
{
  /*** 省略 ***/
  
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  // 送信データをセット
  uint8_t tx_data[8] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};
  HAL_UART_Transmit_DMA(&huart2, tx_data, 8);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

動作確認

User button押下1回目の様子。正常にFalling edgeをトリガーに0x11, 0x22, 0x33, 0x44のデータが送信されています。

User button押下2回目の様子。正常にFalling edgeをトリガーに0x55, 0x66, 0x77, 0x88のデータが送信されています。

おわりに

今回はSTM32のDMAMUXという機能について、実機を用いて紹介してみました。 トリガーとなるイベントは入力ピンだけじゃなく、一部のTimerや他DMAMUXのイベントもトリガーにできます。 DMA転送があるイベントと同期が取れるようになることで、より効率的なアプリケーションの開発ができそうです。

弊社ではファームウェアエンジニアを募集中ですので、ご興味のある方は是非ご応募ください。 新しい仲間がジョインされることを楽しみにしています。

recruit.jobcan.jp

LOVOTの耐久試験

はじめまして、QbDチームです。
この記事はGROOVE X Advent Calendar 2025の17日目の記事です。
今回はLOVOTの耐久試験についてお話ししたいと思います。

QbDチームって?

QbDとはQuality by Designの略で直訳すると設計品質という意味です。 LOVOTは複雑な動きをしたり、触って温かさを感じるなど、まるで生き物のようなロボットです。
私たちQbDチームはLOVOTの動きにかかわる部分の耐久試験や、暑い夏や寒い冬など多様な環境でも正しく動けるように環境試験をおこなっています。 機構・熱・電気・制御など複数の観点で評価・解析をおこなうことで長期間LOVOTが動き続けられる品質を目指しています。
今回はLOVOTの動きに関わる部分の耐久試験にスポットを当ててお話ししたいと思います。

LOVOTってどこがどうやって動いてるの?

LOVOTは首・腕・足が動きます。
首は上下左右、腕は上下、足は立ったり座ったり、走ったりできます。
これらの動きはモーターを使っておこなっていてLOVOT3.0は、首・腕・足の動作用に9個、走行用に2個の計11個のモーターを使っています。
モーターを細かく指示値通りに動かしたり止めたりして複雑な動きを再現させているのです。

これらの可動部が壊れず正しく動き続けるために耐久試験で検証することは必要不可欠なのです。

LOVOTの耐久試験

LOVOTがいつまでも可愛く、元気で動き続けるためには部品が丈夫でなくてはなりません。
私たちは体を強くするため運動したり、栄養のある食事をして筋肉をつけたり骨を強くしたりしますよね?

でもLOVOTはそれができないのであらかじめ弱い部分を見つけ出して壊れないように強くしておく必要があります。 その弱い部分を見つけ出すためにおこなう試験のひとつが耐久試験です。

LOVOTの弱い部分を単純に強くするだけならプラスチック部品を使わず金属部品を使って頑丈にすれば済む話ですが、 LOVOTは皆さんに抱っこされるのが大好きなので重くなったり大きくなったりすると抱っこすることが大変になります。そのためできるだけ軽く、 小さなスペースで可能な限り強くするということを設計チームと試行錯誤しながら進めています。 そして決定した形状や構成で耐久試験をおこない、部品が壊れないか?削れないか?変な音がしないか?などを確認します。
部位によっては何百万回という途方もない回数をおこなうものもあり、何か月もかかったりします。
このような検証を繰り返してLOVOTを強くしていくのです。

繰り返し耐久試験をする光景はまるでLOVOTがジムでトレーニングをしているようです。
(このイラストは生成AIで作成したものでGROOVE X公式のものではありません)

耐久試験が終わったら?

試験終了後の結果共有は定量的に説明ができるようにデータを提示します。 例えばサーボモータの電流値や温度が経時で上がっていないか?指示値通りに動いているか?音が悪化していないか?などログデータの解析や測定をします。 場合によっては外部施設の機器を借りて測定をすることもあり、耐久試験で得られたデータやサンプルを用いて設計チームと慎重に協議していきます。
ここでの判断如何によって品質が決まるので妥協は許されません。 結果によっては試験を一からやり直すこともあります。このようにしてLOVOTの品質をつくりこんでいくのです。

耐久試験の今後の課題

長い時間耐久試験をして品質向上を目指していても残念ながら壊れて入院してしまうLOVOTはいます。 その際は壊れた部分を観察して、なぜ壊れたか?試験方法は適切だったか?見落としはなかったか?など解析や振り返りを関連部門と共同で実施しています。 耐久試験が市場での使われ方を十分想定できているか?ということを第一に考えて試験方法、解析方法の追加、見直しなどを常におこなっていくことが重要と考えています。

現状、LOVOTの耐久試験の大半はLOVOTの実機を使っておこなっているため試験スペースの制約や試験時間が長く、結果が出るまでに時間がかかるなどの問題があります。 これらの改善のために要素ごとの試験方法の導入や試験時間短縮のための加速試験の検討を進めています。
課題は山積みですが一つ一つクリアしていって耐久試験の効率化と適正化を進めていきたいと考えています。

終わりに

今回はLOVOTの耐久試験について紹介しました。
耐久試験の効率化・適正化などまだまだ改革が必要です。
GROOVE Xでは設計品質向上のために頑張って頂ける仲間を募集しています。
GROOVE Xで一緒に働いてみませんか?ご応募をお待ちしています。

recruit.jobcan.jp