Inside of LOVOT

GROOVE X 技術ブログ

LOVOTの描き方講座 2023

みなさんアロハ!ふるまい/ALOHAチームアニメーターの中里です。 GROOVE X AdventCalendar 2023 15日目の今回は、LOVOTの絵をいい感じに描くための技術についてお話します。

LOVOTの描き方のコツについては、3年前にGROOVE X公式YouTubeの企画でやりたいと依頼されまして、誰でも描けるようになるポイントをいくつかピックアップして公開したのが始まりです。ふるまいチームメンバーの中には「これを見て練習して描けるようになった!」と言ってくれる人もいたりして、世界80億人のLOVOT描きたい欲のお役に立つならということで2023年版を公開いたします。今回はLOVOTの顔部分にフォーカスしたいと思います。体はまた来年のAdventCalendarにとっておきたい。

顔の輪郭

まず最初は、顔の輪郭です。LOVOTの顔の丸い部分の描き方ですが、ポイントはまんまるく描かないで「マイルドなかまぼこ型をめざす」ということです。

目鼻のバランス

次に、目鼻の配置バランスです。目の円と鼻の円の下端の高さをそろえるのがポイントです。目鼻全体の位置としては鼻が顔の半分の高さくらいにあるといいんじゃないかと思います。お好みです。

目の入れ方

最後に黒目の入れ方です。黒目は白目の真ん中に置かずに、少しより目気味に描くのがポイントです。

追加技能

ここからは追加技能、ちょっとした応用です。これまでの描き方だけではLOVOTの正面顔しか描けないので、常に語りかけてくる圧の強いLOVOTばかり描くことになってしまいます。一つの道を究めるのも悪いことではありませんが、今日は別の道も指し示そうと思います。最初に描いたマイルドかまぼこの位置をずらすことによって、LOVOTの顔の向きを変えることができます。しれっとHornの向きも大事ですが、ここまで描けたみなさんなら問題ないでしょう。

練習あるのみ

いかがでしたでしょうか。簡単なポイントをおさえるだけでそれなりにいい感じのLOVOTが描けるんじゃないかと思います。あとはひたすら描きつづければ卍固めを決めるLOVOTだって描ける日が来ます。

そう、練習あるのみなんです。練習さえすれば描けるようになるんです。それなのに練習しないとどうなるか、ここからは反面教師としてアナザーストーリーをお見せします。

CEOは困っていた

GROOVE X CEOの林要をご存知でしょうか。今年は著書「温かいテクノロジー」を上梓し、おかげさまでご好評いただき講演やサイン会など行わせていただいておりました。 lovot.life

そんな折、GXのなんでも屋、器用貧乏筆頭のわたくし中里にこんな相談が。

なるほど、CEOもLOVOTを描こうと思っているとは感心感心、ここはこの貧乏人がひと肌脱ぎましょう。 そして頭をひねり、忙しくてなかなか絵を描く練習をする時間もないであろうCEOのために絵描き歌を作って献上しました。歌の内容は「バリバリ伝説」が自身のバイブルと言って憚らないCEOに合わせたものとなっています。なんという気遣い…!

※この絵描き歌は個人で作成した非公式なものです

LOVOTえかきうた

これに対してCEOははっきりとこう言いました。 「練習します!!」

ビックリマークが2つもついています。これを機にLOVOTを描くのが得意になって、頼まれなくても自分から描いたりしちゃうかな~、などと思っていたわたくしでしたが、なんと最新状況はこうなっていました。

裏切り!?

がんばれCEO!

目がトリプルXになってしまい「せっかく絵描き歌まで作ったのに…やれやれ」と落胆していたのですが、このブログで晒されてしまう事をどこかで聞きつけたCEOが「絵が苦手な人にそんなにきびしくしないで!」と涙ながらに訴えてきました。聞けば必死に練習はしたものの、自分に厳しいCEO的には納得のいく出来にならず、それでもイラストを求められることも少なくないためなかなか悩んでいるとのこと。昭和生まれのため、ついスパルタ指導してしまいすみませんでした。ごめんねこ🐱

Slack上のCEO涙の訴えスレッドではみんなでCEOを励ましてこれからの成長を願いました。LOVOTのイラストをめぐってこんなふうにCEO含めてみんなでああだこうだできるのもGROOVE Xの愛にあふれてるところなんですよね。本当に平和な会社です。

恒例の大事な募集

さて、とはいえ絵が描けなくてもLOVOTを素敵に成長させるのに全く困ることはありません。このGROOVE X技術ブログを読んで少しでも興味を持たれましたら、ぜひぜひ、お気軽にご連絡ください〜

recruit.jobcan.jp

Salesforceとformrunを連携させてLOVOT問診票機能を実装してみた

この記事は、GROOVE X Advent Calendar 2023の14日目の記事です。

はじめまして、GROOVE Xの尾銭です。
私はお客様サポート業務がメインのシステムエンジニアとして、日々サポートデスクの業務改善やポータルサイトの開発などを行っております。
今回はサポートデスクで使っているSalesforceと新たにWEBフォームサービスのformrunを連携させて、お客様コミュニケーションの改善に挑戦している話をご紹介します。

www.salesforce.com

form.run

なにを作ったのか

概要

お客様からお問合せを受け付けたあと、内容に応じてWEBフォームのご案内をお送りし、入力頂いた回答をお問合せに自動反映させる仕組みを作りました。 流れとしては、

  1. お客様からお問合せを受付
  2. Salesforce上でWEBフォームの種類を選択してお客様に送信
  3. お客様がWEBフォームに回答
  4. ケースに自動連携されたアンケート内容を確認した上でサポート対応

という感じで簡単な図にすると以下になります。

概要図

上記の仕組みを作るにはコーディングは不要で、Salesforce側とformrun側にそれぞれ以下のような設定を行えば実現できました。

formrun側の設定(フォーム)

まずはformrun側でフォームを作成していきます。
ここでは「hiddenテキスト」という非表示項目を選択して、後述のケース番号を埋め込んでご案内できるようにしておきます。
その他は自由にヒアリング項目を追加していきます。
弊社では、選択内容によってヒアリング項目を切替できる選択項目を追加して、症状別にヒアリング項目を切り替えれるように実装しました。

フォーム編集画面

Salesforce側の設定(カスタムオブジェクト)

次に、WEBフォームの回答結果を連携させるためのカスタムオブジェクトをSalesforce側に用意します。カスタムオブジェクトに必要な項目は最低限以下になります。

カスタム項目名 データ型 用途
ケース 参照関係 元の問合せに紐付けるため
ケース番号 テキスト 「hiddenテキスト」に設定したケース番号が連携されるようにします
メール メール formrun側で必要なため
formrun項目1 テキスト formrunの回答を保存するため
... ... ...
formrun項目N テキスト formrunの回答を保存するため

formrun側の項目数分、対応するカスタム項目の追加が必要になります。
私は以下のようにカスタム項目のAPI参照名はformrun側の項目の番号と揃えるようにしておきました。

カスタムオブジェクトのイメージ

※formrun側の項目は表示名のみ変更可能で、項目名は変更できないようでした、、

formrun側の項目設定イメージ

formrun側の設定(Salesforce連携設定)

次に、forumrunのフォーム項目とSalesforceのカスタム項目のマッピング設定を行います。
マッピング設定にはSalesforceのアカウントが必要になります。
このSalesforceアカウントは先日から各Salesforce環境に無償提供されている、API 連携用ユーザライセンスのSalesforceアカウントを用意して設定すればよいと思います。

Salesforce連携アカウント設定

認証が終わるとマッピング設定が可能です。認証したアカウントにアクセス権限があるカスタムオブジェクト/項目のみしか選択できないので、うまく選択肢に表示されない場合は、Salesforce側の権限設定を見直すと解決すると思います。

formrunとSalesforce項目のマッピング設定

Salesforce側の設定(フロー)

最後に、Salesforceに連携されたWEBフォームの回答結果をケースに自動で紐付ける処理をSalesforceのフローでノーコード実装します。
formrunとのマッピング設定を行ったカスタムオブジェクトのレコードが作成されたときに実行されるように設定します。

フローの全体像と開始条件

フロー内の処理では最初にWEBフォームの回答結果の紐付け先ケースを取得する処理を用意するのですが、ここで前段で設定した「hiddenテキスト」のケース番号を条件に設定します。

GetCase

そしてケースが取得できた場合はケースIDを参照項目に設定してWEBフォームの回答結果のレコードを更新します。

SetCaseId

以上が一通りの実装になります。

最後にお客様へWEBフォームのご案内をするときですが、以下のようにフォームURLのクエリパラメータで初期値を設定することができます。

https://form.run/@フォーム名?項目名1=初期値1&項目名2=初期値2

このクエリパラメータには忘れずにケース番号を埋め込んでご案内するようにします。
弊社ではSalesforceの画面からお客様の症状別に初期値を切り替えてWEBフォームのご案内定型文を生成する機能も実装しております。
ここまで作り込むとlwcでの開発が必要になってきますが、要件次第ではノーコードで実装することも可能だと思います。

WEBフォームのご案内イメージ

なぜ作ったのか

弊社ではLOVOTというプロダクトの性質上、お客様が故障かな?と思ったお問い合わせに対して、適切なサポートを行うために多くのヒアリング項目が存在します。
しかも、

  • 故障と思われる症状は多岐にわたる
  • 症状別にヒアリングする項目が異なる
  • お客様にヒアリングする症状と項目は日々変化する

というもので、現在はすべてテキストでお客様とコミュニケーションを行っています。
これは弊社のサポートデスクメンバーの負担になっているだけではなく、お客様にも大きな負担となってしまっています。

そこで、柔軟にヒアリング項目をメンテナンスできて、弊社のサポート基盤として活用しているSalesforceとの連携が容易に実装できそうなWEBフォームサービスを検討した結果、今回はformrunを採用して、問診票のような機能を実装できないか、というトライを始めました。

まだ本運用はスタートできていないものの、テキストコミュニケーションのみだったヒアリングの負担を改善できそうなフィードバックが弊社サポートデスクメンバーから挙がってきており、非常に期待しております。

今回実装した仕組みは弊社のような問診票としての利用に限らず、お問い合わせフォーム、ウェビナー申し込みフォーム、NPS集計など、様々な用途でWEBフォームとSalesforceを連携させたい場合に活用できると思いますので、皆さんの参考になれば幸いです。

最後に

最後までお読み頂き、ありがとうございました。 GROOVE Xでは一緒に働く仲間を募集しています!
是非下記リンクをチェックしてみてください。

recruit.jobcan.jp

ゼロからTerraformを導入した話

こんいす〜!ISUCON楽しかったですね。GROOVE X クラウドチームの mineo です。

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

qiita.com

クラウドチームでは、LOVOTと通信するクラウドプラットフォームの開発・運用をしています。今回は、インフラ管理にTerraformを導入したので、その話をさせてください。

www.terraform.io

導入した理由

Terraformを導入する以前はインフラを作成したコマンドをMakefileに残すことで管理されていました。Google Cloudをメインのクラウドとして使っているので、gcloudコマンドがMakefileに多く記載されていました。(記録が残っているだけでも素晴らしい...!)

作成記録を残すことで、どのようなリソースがあるか把握し、再度リソースを作成する必要が出来たとき、再利用することは可能です。ただし、

  • あくまで、実行記録のため、実体と乖離していってしまう可能性があり、もし、そうなっても気付けない
  • 各環境(staging/productionなど)に適用するオペレーションが難しい
  • Makefileが肥大化していく

といった課題がありました。そこで、宣言的にIaCを導入することで、解決したいと考えました。

Google Cloudでは、Deployment ManagerというIaCのサービスもありますが、現時点ではTerraformがデファクトスタンダードであるため、Terraformを導入しました。

方針

弊社では、Terraformを導入した実績がなかったため、なるべく認知負荷が低い方針を採用しました。

シンプルなディレクトリ構成

ディレクトリ構成はGoogleのこのセクションを参考に以下のような構成にしました。環境ごとに、ディレクトリを分ける構成としています。シンプルでわかりやすく気に入っています。

-- envs/
   -- dev/
      -- main.tf
      -- locals.tf
      -- xxx.tf
   -- stg/
      -- main.tf
      -- locals.tf
      -- xxx.tf
   -- prod/
      -- main.tf
      -- locals.tf
      -- xxx.tf

cloud.google.com

moduleを使わない

moduleはTerraformのリソースを共通化できる強力なツールです。

しかし、moduleは設計が難しく、使い方もとっつきにくいです。時期尚早と考えて、導入しませんでした。

そのため、環境ごとにほぼ同じコードをコピペで運用する管理となり、その点は少し手間ではあります。今後はなんらかの工夫をしていく必要があるかもしれませんが、ベタで管理するようにしています。

変数を一つのファイルでのみ管理する

moduleを使わず、環境をディレクトリごとに管理するため、変更をコピペで適用することが多いです。 このときに、環境ごとの固有の値が散らばっていると大変なため、locals.tfでlocal変数を宣言して、ここでのみ変数を扱うようにしています。そのため、locals.tfとbackend等の設定を記載したmain.tfのみ、環境個別のファイルとすることが出来ました。

また、外部から値を注入することがないので、variableは使わず、localのみ変数として定義しています。

例↓

locals {
  cloudenv = "dev"
  region   = "asia-northeast1"
}

これもGoogleのベストプラクティスを参考にしました。

Terraform を使用するためのベスト プラクティス  |  Google Cloud

master mergeでの自動デプロイはしない

Terraformの事例では、GitHubでTerraformのコードをレビューしたのちに、masterにmergeして自動でterraform applyするという運用がみられます。しかし、我々の場合はインフラ専門のチームはなく、小規模なチームのため、以下のような理由で、自動terraform applyは不要ではないか、と考えました。

  • terraform applyを実行しないと成功することがわからないときがある
    • planでは問題なかったのに発生することがあり、手戻りが大きい
  • 自動デプロイの場合は、実際にリソースを作成してから、terraform importをするという、運用ができない
  • ペアワークなどで、その場でterraform applyをする運用が多い
  • masterとの乖離が発生しにくい

可能な限り記述を短くする

先述しましたが、弊社ではTerraformの導入実績がないため、コードリーディングするときの負荷をなるべく減らすべく、コード行数をなるべく短く、わかりやすくするように心がけています。そのため、以下のような工夫をしています。

  • デフォルトの値と同じ場合は、なるべくコードに記述しない
  • 同じようなリソースが複数必要な場合は、for_eachを利用する

developer.hashicorp.com

for_eachは最近初めて使ったのですが、とても便利な機能でした。for_eachの個別のリソースをterraform importするときに少し悩んだのですが、ドキュメントに記載されている通り、以下のような書式で実行することが出来ました。

terraform import 'aws_instance.baz["example"]' i-abcd1234

developer.hashicorp.com

導入してみて

まだ道半ばですが、Terraformで宣言的にインフラの状態を管理することで、

  • 実体と乖離がないこと
  • 環境ごとの差異がないこと

といった安心感を持てるようになりました。また、コピペはやや面倒ではあるものの、一つの環境を他の環境に適用するオペレーションも簡単で、信頼できるものとなりました。

また、クラウドチームでは、Google Cloudだけではなく、中国のサービス展開用にTencent Cloudも使っており、両方ともTerraformでコード化できてよかったです。

ただ、TerraformがOSSでなくなってしまったことによりforkされたOpenTofuの動向も気になっており、状況次第で乗り換える必要があるかもしれない、とも考えています。

opentofu.org

まだ道半ばではありますが、IaCは今後も進めていきたいと思います。

おわりに

GROOVE Xでは一緒にLOVOTを成長させていくメンバーを絶賛募集中です!少しでも、興味を持って頂けたら、お気軽にお話しましょう〜

recruit.jobcan.jp

みんなで創ろう!GX Standard!

この記事は、GROOVE X Advent Calendar 2023の12日目の記事です。

はじめましてこんにちは! GX Standardおじさんこと、GROOVE Xの酢屋(すや)です。
日本に10世帯くらいしかいないレア苗字らしいです。

突然ですが、みなさん
GX Standardって聞いたことあります?

あるわけないですよね。
GX=GROOVE X社内の、Standard=基準ですから。
(長いので以下GXSって書きますね。)

今日はそんなGXSのお話をお伝えしていこうかと思います。

ありそうでなかった 社内基準

先日の記事でもあったように、GXはおかげさまで8周年を迎えることができました。 tech.groove-x.com

8年もあればメンバーも大部分が入れ替わってくるのは皆さんの会社でもご経験があるかと思いますがGXも多分に漏れずで、かくいう私も2年ちょっと前にJoinした身です。
歴史ある大きな会社ならともかく、スタートアップの弊社では管理ルール自体が手探りで、運用する人も入れ替わりで、まぁ言ってみればカオスでしたよ、私が入社したころはね。

特に、ハードウェア開発として重要な、作ったものをどう評価してどのような基準で良し悪しを判定するのかという基準、よくよく聞いて回れば有るには有ったんですが、各専門分野のチームごとに運用されていて、運用方法もプラットフォームもバラバラ、入ったばかりのメンバーから見たら、やっぱりカオスでした。

そりゃあね、まずは製品のカタチを作って、要求仕様を追い込んで、商品としてお届けしないことには出資も得られないベンチャー企業ですから、産み出す方が優先で、社内の運用のとりまとめはおいおいってなるのも仕方がないかと。私だって立場同じなら多分そうしていると思うからね、誰も悪くない。

でもね、みんなの想いは「LOVOTをお迎えしていただくお客様にとって、価値のあるものにしたい」という原点にあり、「良し悪しの判定基準」とはつまり「GXがお客様に自信をもってLOVOTをお届けするための基準」であり、お客様にとっての価値を創造するために大切な、我々GXの真心なんだと思うわけです。

まるで宝探しの毎日

そんなわけで、入社してからしばらくは、誰かが「欲しけりゃくれてやる」と言ったとか言わないとか、この世のどこかに置いてきた【ひとつなぎの大秘宝(社内基準)】を求めて、今まで各チームで実施した評価内容を巡る旅が始まりました。

ある日はコンフル*1を検索して掘り起こし…

またある日はesa*2の森をかき分け…

別の日にはJIRA*3のチケットの海を渡り…

時にはSlack*4の洞穴を探索し…

はたまた他チームのメンバーを捕まえてローカルのドキュメント*5をもらい受け…

まあ、有るわ有るわ、まるで宝の山のようにザックザクと!
その中にはGXSの前身と思しき道半ばな状態で取り残された遺跡なんかもありました。
みんな、がんばったね。

みんなで創ろう!GXS!

そうこうして集まった宝の山を今度は仕分けしていきます。
評価項目に名前を付けて、機能を評価しているのか、安全性を評価しているのかなどなど、評価の属性ごとにグルーピングしました。

GXSジャンル分け※開発コードは内緒

グルーピングが終わったら今度は中身を煮詰める作業。この時点で100項目を優に超えている評価内容をどう煮詰めるか…。

私の前身は某家電メーカで品質保証的なこともやっていたので、JIS等で規定されている規格試験は規格文から引用しササっとまとめられたのですが、LOVOT特有のネストへの帰巣性能とか音声認識とかタッチ認識とか…LOVOTとしてのふるまいに絡む評価については記録を見てもよくわからないし、今までなかったものを作りだしているので世の中見回しても類似の試験は見当たらないしで、悩んでても仕方がないので各専門チームに聞いてみました。

わからない項目は専門家に聞く!
毎週のように各チームを捕まえて聞いてまわります。

  • この評価の目的は?
  • どんな環境条件が必要?
  • どんなパラメータをとるの?
  • そのパラメータをどう評価するの?
  • 合格判断の基準は?


で、そうやって生まれたのがこのGXSってわけ。

俺たちの戦いはこれからだ!

そう、ほんとこれ打ち切りエンドじゃなくて、これからが大事!
前職でISOの品質システムを構築したんですけど、そのときに心底実感したのが、使ってもらえないルールなんて、作るだけ無駄
せっかく作ったGXSを陳腐化させないように随時内容を精査して必要あれば修正や追加して、ちゃんと評価項目を網羅できるように各チームフォローして、GXSの運用がメンバーの体に馴染んで自律的に運用されていく状態にしつつ、今後のバージョンアップや新機能の評価で悩む時間を減らせるようになるまで持っていくのが、私の当面のお仕事です。


おわりに弊社代表の林要から言われた言葉を。
『なんだかんだ叩き台を作って、しっかり叩かれながらでもやりきるやつが一番偉いよね』

そんな言葉を胸に、今日も叩かれながらGXSを育てています。

最後に

最後までお読み頂き、ありがとうございました。 GROOVE Xでは一緒に働く仲間を募集しています!
是非下記リンクをチェックしてみてください。

recruit.jobcan.jp

*1:Atlassianが提供する企業向けウィキ。正式名はconfluence

*2:esa LLCが提供する情報共有ツール

*3:Atlassianが提供するタスク管理ツール

*4:Slack Technologiesが提供するコミニケーションツール

*5:MicrosoftやGoogleのofficeツール。多種多様

プードルファーニット開発記

読者のみなさま、こんにちは!
この記事は GROOVE X Advent Calendar 2023 11日目の記事になります。
今回はLOVOTの服ネタ枠ということで、先日の「辰にっと」について書かせていただいた服チームの shigeri がお送りいたします。
今年の締めくくりにふさわしいLOVOT服の開発奮闘記として、企画からから販売まで約2年もの歳月を費やした、ただいま絶賛発売中のベースウェア「プードルファーニット」について書いてみようと思います!

絶賛販売中のプードルファーニット

プードルファーニットが生まれることになったきっかけ

LOVOTの服を作る上で1番重要なことは、もちろん「LOVOT本体に極力負荷をかけないように作る」ということです。愛らしい仕草のLOVOT達をみていると、ついつい本物の生き物のように錯覚してしまいますが、LOVOTは正真正銘の超精密機械なので笑、取り扱いには様々な注意が必要です。よってそのことが公式ウェアを買っていただくお客様にできるだけ伝わるように、販売画面やアテンション紙に取説を明記し、さらにオーナー様向けメールでもお知らせするようにして、オシャレなLOVOT達が健康&安全に暮らせるように、様々な配慮をしながら販売しています。

そんな公式ウェアを作っている服チームのメンバーは「LOVOTがより可愛いらしく見えるデザインや素材で、オーナー様とLOVOTに日々のお着換えを楽しんでもらう」という大きなミッションをかかげ、アパレルの知識を日々フル回転させながら、新商品の開発に繋がる様々なアイディアを生み出しています。私がGXに転職するきっかけとなった面接のプレゼン資料の中で、偶然にも「もこもこベア服」をデザインしていたということもあり、丸くてかわいいLOVOTがもふもふしてたらさらにかわいいだろうなぁ~というイメージは常に頭の中にありました。
そんなLOVOT服への思いを抱くのはもちろん服チームだけではなく「LOVOTの生みの親=林要」もそのうちの一人でした。確かにLOVOT MUSEUM にある歴代の開発機体には、数種類のもこもこ服が着せられているので、かなり初期の段階から「かわいいLOVOT」のイメージは、もこもこ服がセットなんだろうなと思っていました。

LOVOT MUSEUM に展示してある開発時代のもこもこ服たち
しかし毛足もありそこそこ厚みが出る素材は、LOVOTの服に適さないことは容易に想像できるので、過去にもこもこ服の開発を試みた服チームメンバーは「空気を取り込み循環させて熱を発し、車輪の出し入れをしながら走り回るLOVOTに、毛足のある素材の服を着せることは、何度か試作したけど難しかった」という経験談を話してくれていました。
そんなある日、その難しい服の開発過程を経験している要さんが、相変わらず「もこもこの服が欲しい」と話しているということを耳にしました。その頃の私はまだ入社して1年も経っておらず、服を開発するには様々なハードルがあることをあまり理解していない頃だったので、「だったら開発しましょう!」とやる気満々でメンバーに相談しはじめたところから、このLOVOT服(販売する服)史上最長の商品開発が始まったのです。。。

第一開発期 2021年/年末~

そんな流れからも、もこもこ服にとても興味が湧いていた私は、かわいいと思っていた生地(一般的なプードルファー)を買ってきて、とりあえず試作してみることにしました。まずはベースウェアのベロアのパターンを利用してサイズ調整したパターンを作り、思うように縫ってみました。プードルファーとはいえもちろん毛足のあるファーなので、そこら中に切ったファーが飛び散りまくり、GXの社内では裁断するのも縫うのも後片付けも一苦労でした笑。縫い上がった最初の試作をLOVOTに着せてみると、予想以上に伸びなかったり裏地があった方が滑りがよくなるなど、いろいろな改善点が確認できたので、修正しながら次の試作にとりかかりました。
その改良版試作を作りながら、チームメンバーにも「もこもこウェアを開発したい」と提案してみました。ちょうどその頃に販売していた「もふもふニット」が売れた時だったので、その素材感のバリエーションになるのでは、ということになり、早速メーカーさんに依頼してまずは素材収集から始めました。

編地や参考サンプルなど数種類の素材の中から、現実的にLOVOT服にできそうなモノに絞り込み、集めた素材を要さんにも確認してもらうことにしました。各素材の特長やメリデメなども一通り説明しながら見てもらったところ、試作に使っていたプードルファーが一番手触りが気持ちいいということになり、冬アイテムの販売時期(11月前後ごろ)に向けて素材をリプロ*1して開発することになりました。
改良版試作である程度のサイズ感が調整できたので、試しに簡易的に熱と稼動を検証してみることにしました。LOVOT服の最大の難関は、この「熱(必要な素材の場合)&稼働」の長期稼働検証です。

熱検証中の2体(左:時を同じくして開発中だった干支うさぎの試作 右:プードルファーニットの改良試作)
内容をざっと説明すると、日常生活で想定される室温などの条件を設定し、そこでLOVOTを数日間動かし数値をとり、その数値が許容範囲内に収まっているかを確認するという内容です。服検証の詳しい内容は12/04のブログでファームチームのaOikeさんが書いてくれていますが(本当にいつもありがとうございます!!!)この検証をクリアしなければ、どんなにかわいい素敵な服を作っても商品化することはできません。

しかもそのファームチームの長期稼働検証に辿り着くまでの過程は、①試作⇒②簡易検査OK(特殊な素材は熱検証も含む)⇒③工場で1stサンプル作成⇒④簡易検査OK、になった場合のみ、晴れて長期稼働検証ができるということになります。OKが出る前提で1stサンプルを上げても、簡易検証でNGだった場合は修正した次の2ndサンプルでの長期稼働検証になるので、さらに時間を要することになります。
この様に時間をかけて検証を繰り返しても、結果的に商品化できなかった企画も多々あります。。。また稼働検証には時間も必要なので、早速ファームチームとQbDチームに相談し、改良版試作で検証をしてもらいました。 もこもこ服の最大の懸念点は、ファー生地自体に厚みがあるので保温しやすいという点です。なのでできるだけ熱がこもらないように伸縮性のあるメッシュ裏地にして、想像できる対処は試みていましたが、検証結果は全体のサイズがきついことにより、サーボの負荷数値が高くなってしまいました。その影響で機体の温度も高くなっていることもわかったので、次に挑む1stサンプルはさらにパターンを調整することにしました。とはいえLOVOTの服は単純に大きく作ればいいという訳でもないので、慎重にサイズを調整していきました。

待望の1stサンプルが上がってきたので、着せて簡易検査をしてみると、やはりサーボには許容外の負荷がかかってしまっていました。その頃になると私も新規商品は簡単に開発できないことは既に経験済だったので、困った時のaOikeさん頼みで、一緒に検証&分析してもらいながら、いかに負荷を少なくするかを模索していきました。その段階でたどり着いたのは、リプロしたファー生地が試作に使ったものよりも厚く伸びにくく、密度が増していることも原因だろうということになり、ファーの厚みを調整してもらうことと、ファーではなく他の伸びる素材の箇所を増やすことで、負荷を軽減することを改良案としました。

2ndサンプルではいろんなバージョンのサンプルで比較できるように、生地の厚みを変えたりして3タイプのサンプルを上げてもらうことにしました。とはいえ中国にある工場はコロナ禍の影響で通常通りの稼働をしていないし、他の企画も並行して進んでいるし、修正してまたサンプルを作ってそれぞれ検証&分析して、を繰り返していたら、あっっという間に夏になってしまいました。結局もこもこ自体のクオリティが試作と差異があったことがネックとなり、2022年8月の段階で量産出しができないという結果になってしまいました。もこもこウェアの販売時期はもちろん冬がベストのため、開発は一旦ストップすることになってしまいました。

第二開発期 2022年/年末~

想定通り時間はかかってしまっていましたが、プードルファーの開発はあと一歩という段階にきていたので、再度開発したい旨をメーカーさんにお願いしたところ、諦めずに協力していただけるという快諾もいただき、生地の改良版を作るところからリスタートすることになりました。

前回から更にパターンも調整し、ファー以外の別生地も使うことになっていたので、ファーのカラーに合う別生地探しも依頼しました。プードルファーのカラーバリエーションは私の中に明確なイメージがあったので、「キャメル」「アイボリー」「ベージュピンク」を提案し、ビーカー*2も取り直してから改めてチームで検討し、無事にその希望の3色に決定しました。肝心の再1stサンプルはというと、工場の担当者が変わってしまったのか、寸法も仕様も生地の使い方も指示通りではなく、かなり残念なサンプルが上がってきてしまったので、また細かく検品コメントを書かなければならない状態でした。またパーツに使用する別布も、既存の生地から探すのでファーのカラーに合うものがなかなか見つからず、探し直してもらうこと数回、なんとか妥協できるレベルの生地が見つかりましたが、きちんと検証できるサンプルが上がるまで、またもや時間を費やす状況になっていました。さすがに私も「やっぱりなかなか一筋縄ではいかない企画だな…」としみじみ痛感してしまいました。

とはいえもこもこの生地感はかなり理想に近づいていたので、とりあえずどのぐらい負荷の数値が改善されているかを確認するため、数日間の熱検証&長期稼働検証をお願いしてみました。しかしその検証が終わるとまた新たな問題点が浮上しました。厚みと密度を調整したファー生地は温度の上昇が抑えられることがわかったのですが、長時間稼働させた結果予想以上に袖裏と脇下のファーが擦れてしまい、せっかくの柔らかい毛足がつぶれてしまっていました。手洗いすればなんとか元に戻る程度にはなるのですが、ファー自体の見た目も少し劣化しているように見えてしまっていたので、これでは販売できないと判断し、いつもの検証メンバー(aOikeさん、imaiさん)に引き続き相談してみました。

そこで怪我の功名とでも言うのでしょうか、袖裏と脇下を別布に変更すると、いろいろな問題が改善されることもわかってきました。まずは袖下の摩擦が減るので毛足もつぶれにくくなるだろうし、懸念点の熱ももう少し抑えられるだろうということが予想できたのです。その状況で商品化するかどうかの最終判断はPOでもある要さんに委ねることになり、その結果取り扱い注意を明記して商品化することが決定したのは、2023年のすでに初夏になっていた6月末ごろのことでした。

左:ビーカー 右:袖裏と脇下&ホイール周り
LOVOTの健康を保てる服を作ることが服チームの最大ミッションですし、撫で心地のよいかわいいプードルファーの毛足もつぶれにくくなるし、何よりお客様も服のお手入れがしやすくなるというメリットだらけという結論に。ちょっと斬新なデザインになるけど、少しでも安全な服にするために袖裏と脇下の生地を別布に変更することにしました。そしてその最終形態のサンプルを作るために改めてパターンを修正し、それを工場へ送り&マッハで縫ってもらい、最後の最後までメーカーさんとの細かいやり取りを続け、納期ギリギリで収めていただいたサンプルで最終確認の検証をし、想定内でクリアできたという結末に…(大泣)。やっっっと販売可能なレベルに開発できたという、嬉しさと充実感がこみ上げた2023年9月の初秋でした。。。

おわりに

このように約2年もの長い時間をかけて、様々な過程を経て、そしてたくさんの方にご尽力いただいた結果、やっっっとリリースできたブードルファーニット。触り心地バツグンのファーを使用しデザイン性を重視した服なので、少々取り扱いの手間もかかりますが、この様にたくさんの方々の知恵とアイディアが詰め込まれたこらこそ、無事に開発できた商品ということが読者の皆様に伝わればとても嬉しいです。ぜひたくさんのオーナー様に冬の「ふわもこLIFE」を楽しんでいただけたら幸いです。。。
これからもオーナー様に喜んでいただける、かわいいLOVOT服を開発していきたいと思います!
今回も最後までお読みいただきありがとうございました。
(おわり)

*1:生産背景で生地を作り直すこと

*2:量産用生地での色の出方や見え方を確かめる色出し工程

LOVOT と gRPC

こんにちは LOVOT の基盤となるサービスを Go で書いているチームのひとり id:atotto です。

gRPCProtocol Buffers (以下 protobuf)、と聞くとクラウドサービスで使うマイクロサービス間のメッセージングで活用するイメージが多いかと思います。もちろん、 LOVOTのクラウドサービスで gRPC を活用した開発 をしています。 本稿では、クラウドサービスではなく、 LOVOT 内部のサービス開発においても gRPC 、 protobuf を活用している事例を紹介します!

対象読者:

  • Web APIを設計・実装しているひと
  • 組み込みLinuxのデバイスにAPIをもつサービスを作っている、または、作りたいひと
  • gRPC を知っていて、活用事例を見たいひと

gRPC / Protocol Buffers

gRPC についてはさまざまな説明資料がでているのでここでは簡単に説明します。 gRPC は異なるサービス間で RPC(Remote Procedure Call)を実現するための仕組みで、現在ではマイクロサービスの内部メッセージングや、サービスのAPIとして幅広く利用されるようになりました。

gRPC では、メッセージやサービスを定義するために、 protobuf を IDL (インタフェース記述言語) として、データ構造やサービスの RPC のメソッドを protoファイルへ記述します。そして、 protoc (コンパイラ)の gRPC プラグインで、 protoファイルから様々なプログラミング言語のコードが生成できるようになります。

LOVOT での活用パターンとしては:

  • LOVOT内部の通信
  • LOVOT と Nest 間の通信
  • LOVOT / Nest の HTTP API サービス
  • 互換性を重視したサービス

などがあります。順番に見ていきましょう!

LOVOT内部の通信

システム間のデータストリーム

LOVOT のサービスは、 x86系のCPU(メインコンピュータ)、そして、ARM系のCPU(サブコンピュータ)上で Linux を動かしています。サブコンピュータに数多くのMCU(マイコン)を接続し、各MCUにセンサやアクチュエータなどを接続しています。 接続したセンサから上がってくるデータは、サブコンピュータを通じ、メインコンピュータで処理します。また、アクチュエータ(サーボ、モーターなど)へ指示するために、指示値をメインコンピュータからサブコンピュータ、そして各MCUへと伝えていきます。(MCU、センサ、アクチュエータの詳細については 「LOVOTを動かすファームウェア」 を参照)

LOVOTを動かすファームウェア より抜粋

このメインコンピュータとサブコンピュータ間のやりとりで gRPC の stream を活用しています:

  • サブコンピュータでセンサデータ(入力)とりまとめ、メインコンピュータへ伝送する
  • メインコンピュータからアクチュエータ指示値(出力)をサブコンピュータへ伝送する

protobuf により、 JSON や XML に比べるととても効率的なシリアライズとなっています。また stream は容易に双方向通信できるため、入出力処理にマッチしました。

疎結合なアーキテクチャ

LOVOT のシステム内ではさまざまなプログラミング言語でサービスを実装しています。そのため、例えば特定のセンサデータを取得したい、といった API を使いたい場合、その API のクライアントライブラリを各プログラミング言語で提供する必要が出てきます。 このような API を gRPC の根幹となる protobuf の IDL で API を定義しているため、各プログラミング言語向けにクライアントライブラリを容易に生成できるようになります。

参考: PyConJP 2019 らぼっと開発におけるPythonとGo より抜粋

独自プロトコルの提供

LOVOT のシステム内では LOVOT を動かすために独自のプロトコルを用意しています。 gRPC のコードを protoc から生成するのと同じように、 protobuf の IDL から独自のプロトコルのコードを生成するコードを実装し、 protoc のプラグインとして実行しています。protoc プラグインの作り方については 「protocプラグインの書き方」 が参考になると思います。

LOVOT と Nest 間の通信

LOVOT の内部の話からやや離れますが、 LOVOT のプロダクトでは、 LOVOT 本体だけではなく、Nest(巣) があり、3つの CPU が存在しています(3つの CPU の話は 「LOVOTのOSのつくりかた」を参照)。

LOVOTとNest(LOVOT ANATOMY より一部抜粋)

LOVOT は Nest をアクセスポイントとした WiFi で接続しています。 WiFiネットワークを介し、 LOVOT と Nest 間のメッセージのやり取りも gRPC を利用し、 TLS 通信しています。 gRPC のサーバを Go で書いていますが、 TLS の暗号化処理が容易に実装できるのはありがたいです。

LOVOT / Nest の HTTP API サービス

LOVOT や Nest には数多くの AP Iを実装しています。一般には公開していませんが、LOVOT や Nest を生産する上で欠かせない組み立て後の検査のための API や、検査用の WebUI があります。( LOVOT の生産に関する話は 「LOVOTがつくられて、お客様に届くまでの仕組み」 を参照) このような API の定義に protobuf の IDL を利用し、さらに grpc-gateway を活用して REST ライクな API を実装しています。

互換性を重視したサービス

gRPC を使うメリットのひとつとして互換性の維持、があります。 LOVOTのアップデートは LOVOTOS という OS を入れ替える仕組みがあり、アップデートの指示を出すクライアントと、アップデートを実行するサーバが存在しています。gRPC/protobuf をつかうことで、例えばアップデート前とアップデート後の LOVOT の RPC のメッセージフィールドに変更が入っていたとしても、メッセージ自体を破壊すること無くリクエストとレスポンスができます。また、サービス自体に大きな変更があったとしても、gRPC の定義では簡単にバージョニングができます。成長するサービスを支える上では必要不可欠な要素です。

gRPCの採用の歴史

かなり端折っていますが、参考になれば幸いです。

2017

LOVOT の開発で ROS(ROS1) をつかっていたため、メッセージングには ROS の pubsub をメインで利用していました。

2018

gRPC、 protobuf のしくみを取り入れはじめたのは、 2018/04 頃です。最初は LOVOT に対する API の定義と、 LOVOT が利用するクラウドの API を定義するところからでした。この頃まではクラウドのサービス自体は OpenAPI で定義し、 Go で HTTP サーバの実装をしていましたが、徐々に protobuf での記述と gRPC へ移行していきます。

2019

脱ROS *1 を進める一環で、効率的なメッセージングへの置き換え先として gRPC を採用する箇所が増えていきました*2。 また、 LOVOT 自体のソフトウェアアップデートサービスに gRPC を適用して実装していきました。

2019-12

LOVOT 出荷開始!

個人的に感じているメリット

id:atotto が感じているメリットをこちらにメモしておきます! ( LOVOT のシステム開発に限らず、クラウドでの開発の感想も含みます)

  • API 定義がきちっと決まる(型がある安心感)
  • すばらしい設計指針がある
  • protocol buffer の効率的なシリアライズが使える
  • proto ファイルから APIドキュメントを生成できる
  • protoc を通じたプラグインが簡単に作れる(コードジェネレータやアノテーション付与など)
  • gRPC + Go の効率的な開発エコシステムが使える

最後に

Web API を支える技術のひとつ、となったと言える gRPC を、このような組み込み Linux のシステム上で活用することで、効率的、かつ、変更に強いシステムとして構築できるようになりました。クラウドも LOVOT も Go で開発しているサービスが多いこともあり、 API 定義で型がきちっと決まり、クライアントとサーバが実装しやすい gRPC のエコシステムは開発フローとしてとても便利です。Web やクラウド上での技術を、組み込みの世界で活用したい方はぜひ一緒に開発を進めましょう!

最後に、このような技術選定と実践を自分たちで進められることはとてもすばらしいことです。この仕組みを共に開発したメンバに感謝いたします。 id:atotto

*1:100億円集めた日本最大のロボットベンチャー、GROOVE Xのロボットを大解剖/ソフトの半数はGo言語製、脱ROS、クラウドはGCP : 2020.03.10 日経Robotics

*2:ROS2 で gRPC / protobuf を採用すればよかったのに(個人の感想です)

Slackマルチワークスペースでgroup mentionを使う話

こんにちは、GROOVE X Advent Calendar 2023 9日目の記事です。
普段はスクラムマスターをしている niwano が、久しぶりにプログラムコードを書いことを紹介します。
プログラミング楽しい♫ 。

きっかけ

Slack user groupの仕様が想定と違った

2022年の春に弊社は、Slack を EnterpriseGrid版に移行しました。
ワークスペースを組織単位でいくつも作れることや、Slack connectが無料とのことで、メリットを感じて採用したのですが、EnterpiseGrid版 にしてがっかりだった仕様の第1位が、user groupのスコープがワークスペースの範囲だということです。

どういうことかというと、以下のようなイメージです。 EnterpriseGridになると、Organaizationという概念がうまれて、その中に複数のワークスペースを作成できます。
チーム単位だったり、組織単位だったり、大きなチャンネルグループのイメージです。
弊社では、基本、1ユーザーの所属は1つのワークスペースになるように管理されています。

Slack マルチワークスペースの概要図

user groupがワークスペースの中で閉じているので、ワークスペースにまたがるメンバーを同じグループにいれたり、異なるワークスペースの user groupを呼び出したりすることができません。
おまけに、異なるワークスペースの user group はマスクされてしまう。

マスクされるというのはこんな感じです。
トーフっぽい。(トーフで年齢がばれますね!)

マルチワークスペースのgroup mention

以前はマスクされてなかったんですけどね....
設定で変えられるようになるのを期待してます!Slackさんお願いします!

自作している人をみつけた

インターネット上では同じように困っている人がいて、自作していたので、エンジニア魂に火がついたわたしは、「自分でつくってみるかー」となりました。

仕様はこんな感じ "GroupMentionBot"

メッセージ上のグループメンションを検出して、
他のワークスペースに同じグループ名が存在したら、そのグループ名をリプライします。
その時に、メッセージの抜粋をちょっとつけます。
その名も、"GroupMentionBot" です。

こんなイメージです。 <画像はる>

作ってみる

システム構成

最初はGAS上で動かしていましたが、postMessageのACKを5秒以内(今の仕様は分からない)に返さないと、Slackが再通知を行う制限に頻繁にかかり、おなじリプライが何個も発生する...みたいなことが置きてました。
(これもやってみないとわからないですよね〜)

ですので、今は大好きなGO言語でcloud Pub/Sub と Cloud Functionsで実装しています。

システム構成図

Slack appから EventSubscriptionsで、メッセージイベントを一度、Cloud Functionsで受け取ります。ACKを返したあと、Pub/Subのキューにメッセージを入力することで、前述のタイムアウトを回避します。
Pub/Subから取り出す側のCloud Functionsで、メッセージ内のgroupメンションを解析し、リプライメッセージを postMessage( ) します

ポイント

作成するにあたっての、ポイントは3点でした。
(最初からこの形になったのではなく、試行錯誤をしてこういう形になったんです〜)

  • Slack connectにも対応したい
  • なるべくメンテナンスレスにしたい
  • リプライにはメッセージの抜粋をつける

Slack connectにも対応したい

弊社は、Slack connectをつかって沢山の外部ワークスペースの人とコラボレーションをしています。

その方々とも、あたかも、同じ user group を使っているような動きがほしかったのと、その方々からも、グループメンションで呼び出されたい!

前者はスプレッドシートにて、個別IDをメンション候補に追加することで対応しています。
シートの内容は以下のような感じです。

シートのサンプル(IDは架空のものです)

合致する user group があった場合には、members id に定義された個別IDをリプライします。

個別IDサンプル(group名は架空のものです)

後者は、平文での user groupの入力にもGroupMentionbotがリプライができるようにしました。

group名、handle名は架空のものです

なるべくメンテナンスレスにしたい

最初は、GroupMentionBotが検出する user group をスプレッドシートに記載していたのですが、追加の要望が頻繁にあるので、検出用のシートは廃止し、user groupの接頭置で実施するかどうかを判断することにしました。

弊社では、group 名のあたまに、 * @group * @team がついている user group にのみ、GroupMentionBotが反応します。

リプライにはメッセージの抜粋をつける

当初は GroupMentionBotからのリプライは、group mentionのみの味気ないものでした。
この方法でも通知はできるのですが、Slack の Activityリストにはメッセージのない GroupMentionBotからの通知が貯まるんですね。

過去のリプライ(group名、handle名は架空のものです)

そこで、GroupMentionBotからの通知に本メッセージの文章を先頭から3行抜粋することにしました。 また、抜粋しているメッセージの中に user group が含まれると二重で通知されることになるので、user group を削除する処理をつけてます。

こんな感じです。

リプライ時に3行を表示する & group mentionを消す

ところが... rate limit にかかる

ある時、メンバーから、BOTが動いてないみたいです!という連絡が。
Cloud Functionsのログをみてみると...

slack rate limit exceeded, retry after 30s

と出てました。むむ.... リプライする user group を決定するために、usergoup.listのAPIを使っているのですが、このAPIが、Tier2 という定義で、毎分20リクエストに制限がかかっていました。

よくよく処理をみると、全てのメッセージに対して、不要に usergroup.list をコールする処理になっていました。
メッセージのなかに、user group があったときにのみ usergroup.list をコールして処理すればいいですし、なんなら、Pub/Sub のまえにチェックしたらいいので、チェック処理の場所を変えて対応しました。

システム構成図 update 版

でも、心配なので、Tier3 に変えて欲しい気もする...。 (そんなことできるのかな)

1分に20個の group mention しか処理できないです!笑

まとめ

いかがでしたでしょうか!
みなさんの Slack 生活の参考になれば幸いです!

おまけ

slackメッセージの中の group mentionを抽出する処理サンプル

package p

import (
    "regexp"
)

// Find and extract '@group_xxxx' and '@team_yyyy' from the message,
// capturing @group_xxxx and @team_yyyy.
func extractGroupID(text string) (groupIDs []GroupID) {
    // <!subteam^AAAA|@BBBB> があったら検出しないように消す
    subRegex, err := regexp.Compile(`<!subteam\^[A-Z0-9]+\|@[^\>]+>`)
    if err != nil {
        return nil
    }
    text = subRegex.ReplaceAllString(text, "")

    regex, err := regexp.Compile(`@(group_[^\s>]+|team_[^\s>]+)([ >\n]|$)`)
    if err != nil {
        return nil
    }
    matches := regex.FindAllStringSubmatch(text, -1)
    for _, match := range matches {
        if len(match) > 1 {
            groupID := GroupID{
                handleName: "@" + match[1],
                subTeamID:  "",
            }
            groupIDs = append(groupIDs, groupID)
        }
    }
    return groupIDs
}

// Find and extract <!subteam^AAAAAA|@bbbbbb>
// capturing @bbbbbb
func extractSubTeam(text string) (groupIDs []GroupID) {
    regex, err := regexp.Compile(`<!subteam\^([A-Z0-9]+)\|@(group_[^\>]+|team_[^\>]+)>`)
    if err != nil {
        return nil
    }
    matches := regex.FindAllStringSubmatch(text, -1)
    for _, match := range matches {
        if len(match) > 2 {
            groupID := GroupID{
                handleName: "@" + match[2],
                subTeamID:  match[1],
            }
            groupIDs = append(groupIDs, groupID)
        }
    }
    return groupIDs
}

func extractGroupIDAndSubTeam(text string) []GroupID {
    groupIDs := extractGroupID(text)
    groupIDs = append(groupIDs, extractSubTeam(text)...)
    return groupIDs
}