# ロボティクス 声の単語帳を分野ごとに ― 記事65本から140枚足した

- 記録: LOG445（ひとり研究所の裏側）
- 公開: 2026/11/5
- 記事のページ: https://pixelstomotors.com/record/robotics-review-cards-by-category
- サイト: Pixels to Motors（ADAPT DESIGN / 島津 栄一） https://pixelstomotors.com/
- 要約: 声に出して覚えるロボティクスの復習ページに、記事65本から作ったカード140枚を足し、10の分野ごとに練習できる形にした。読み取りは AI 5つで同時に進め、別の AI 2つが全件を記事と照らして18枚を直した。

> AI が読むための記事の全文です（コード・実行ログを含む）。引用するときは上の「記事のページ」の住所を使ってください。

---

**声に出して覚えるロボティクスの復習ページ、ロボティクス 声の単語帳に、LOG443 までの記事65本から作ったカード140枚を足して408枚にし、10の分野を選んでその中だけで練習できる形に作り直した。** 記事の読み取りは Claude の作業を分けて受け持つ AI 5つで同時に進め、別の AI 2つが新しいカードを全部、元の記事と照らし合わせて18枚を直した。頼んでから公開まで約15分だった。

このページは [ロボティクス 声の単語帳](https://pixelstomotors.github.io/robotics-review/) です（ロボティクスの見直し という名前を、この作り直しで変えました）。カードの文を見て声に出し、押している間だけブラウザが声を文字にして、カードとの一致を出します。作り直して変わったことは3つです。

- 研究の新しい中身を、自分の口で言える範囲に入れられた。 Physical Intelligence の π0.5 を自分のアームで学ばせた記録、配膳ロボットの導入シミュレーション33本、考察記事の判断が、カードになりました。これまでのカードは10月11日に作った268枚で、DAY326 までの記事しか入っていませんでした
- 分野ごとに練習し、どこが手薄かが一覧で見える。 開くと10の分野が並び、それぞれに言えた数と苦手の数が出ます。分野を選ぶと、その中だけで 1 単語 → 2 説明 → 3 判断の型 と進みます
- 時間： 頼んだのが8時13分、カードを足して画面を作り直して公開したのが8時28分。そのあと見た目と名前を7回直し、9時2分に今の形になりました。前の268枚を作ったときの時間は測っていないので、何分浮いたかは比べられません

ロボティクスの言葉は、記事に書いて分かったつもりでも、人に説明しようとすると口から出てきません。このページは、その差を埋めるための自分用の練習の道具です。困っていたのは、研究が進むほどカードが古くなることでした。10月11日から3週間で記事は65本増え、π0.5 も LoRA も配膳ロボットの待ち時間も、カードには1枚もありませんでした。もうひとつ、分野を切り替える仕組みが画面の上の小さな選択肢に埋もれていて、今日はこの分野だけ、という練習がしにくい作りでした。

| やったこと | なぜ | 結果 |
|---|---|---|
| 新しい記事65本を5つに分け、AI 5つで同時にカードにした | 1本ずつ読むと、記事が大きくて時間がかかる | 156枚。重なった用語16枚をまとめて140枚 |
| 別の AI 2つで、新しいカードを全部元の記事と照らした | カードを作った AI が自分で確かめると、同じ見落としをする | 数字の取り違えや合否の語のずれなど18枚を直した |
| 分野に 配膳ロボットの導入 を足した | 導入シミュレーションの話の行き先がなかった | 分野は10になった |
| 最初の画面を分野の一覧にした | 分野ごとの練習が、上の選択肢に埋もれていた | 分野を押すと、その中だけで3つの段階を練習できる |
| 一覧を2列・サイトと同じカードの見た目にした | 1列だと縦に長く、押せるのか分かりにくかった | PC では2列、マウスを乗せると浮き上がる |
| 名前を ロボティクス 声の単語帳 にし、見出しをパンくずにした | 他の人も見る前提で、何のページかとどこにいるかを分かるようにした | 分野の中では ロボティクス 声の単語帳 › 分野 と出て、右にサイトのロゴが出る |

![作り直したロボティクス 声の単語帳の最初の画面。10の分野がカードで2列に並び、単語と判断の型の枚数、言えた数、進み具合の棒が出ている](https://pixelstomotors.com/media/LOG445_01_category_list_pc_v2.png)


## 作業ログ ― 作り直す前のロボティクスの見直しページを確かめた

[ファイル一式: https://pixelstomotors.com/files/LOG445/index.json](https://pixelstomotors.com/files/LOG445/index.json)


### ロボティクスの見直し（今の ロボティクス 声の単語帳）は、声で覚えるカードのページ

ロボティクスの見直しは、ブラウザの音声認識（[Web Speech API](https://developer.mozilla.org/ja/docs/Web/API/Web_Speech_API)。話した声をブラウザが文字にする仕組み）を使う、自分用の練習ページです。仕事で使っている台本の読み合わせのページと同じ作りで、10月11日の記録 [DAY328](https://pixelstomotors.com/record/speak-to-relearn-robotics) で作りました。

| 言葉 | このページでの意味 |
|---|---|
| 1 単語 | 用語と短い意味を、一続きで言う（例 LoRA（ローラ）は、モデル本体は固定し、足した小さな部品だけを学習する方法） |
| 2 説明 | 用語の詳しい説明を言う |
| 3 判断の型 | 困った場面の問いを見て、教訓を自分の言葉で言う。決めた語が言えていれば合格。実際に起きた例は読むだけ |
| 見て言う・見ないで言う | カードの文を読むか、用語や問いだけを見て答えるか |
| 苦手だけ | 前回の一致が80%に届かなかったカードだけを出す |

公開は [GitHub Pages](https://pages.github.com/)（GitHub に置いたファイルを、そのまま Web ページとして出すサービス）の PixelstoMotors/robotics-review です。声を文字にするには https のページが要るため、iCloud のファイルを直接開かず、公開した URL で開いて使います。

データの元は iCloud の ロボティクス復習 フォルダにある robotics_review.json で、_tools/build_review.py がページの形（_tools/template.html）にデータを埋め込んで HTML を書き出します。書き出した HTML を、robotics-review の index.html として置くと公開されます。

### 前のカードは268枚・9分野・DAY326 まで

作り直す前のデータを数えると、次のとおりでした。

| 分野 | 単語 | 判断の型 |
|---|---|---|
| 運動学・座標系 | 18 | 12 |
| 実機・サーボ制御 | 17 | 12 |
| シミュレーション・デジタルツイン | 19 | 10 |
| 画像認識・カメラ | 19 | 12 |
| 視覚サーボ・Pick&Place・受け渡し | 18 | 12 |
| ロボット学習 | 18 | 12 |
| 評価・データ管理 | 20 | 11 |
| 設計原則・進め方 | 18 | 12 |
| 社会実装・現場 | 17 | 11 |
| 計 | 164 | 104 |

練習の記録は、練習した端末のブラウザと、iCloud の 練習記録 フォルダにたまっています。集計（練習記録_集計.json）には161件（カード×段階）の前回の一致が入っていて、これはページを書き出すときに埋め込みます。作り直しても、この記録をそのまま引き継ぐことを先に決めました。カードの id は変えず、新しいカードには続きの番号（単語は w165 から、判断の型は k105 から）を振ります。

分野の切り替えは、画面の上の段階の切り替えの下にある、黒い選択肢1つでした。全部・まず覚える・9分野がその中に並んでいて、分野ごとの言えた数はどこにも出ていませんでした。


## 作業ログ ― 新しい記事65本を、AI 5つに分けてカードにした

対象は、pixelstomotors.com の記事のうち、10月11日のカードに入っていない DAY336〜LOG443 です。サイトの外側の作業（サイト・動画・SNS の仕組みづくり）の記事は外し、ロボットの研究にかかわる3つの流れに絞りました。

### 同時に動かした AI 5つは、Claude のサブエージェント

ここで使ったのは、Claude の[サブエージェント](https://docs.claude.com/en/docs/claude-code/sub-agents)です。Claude が作業の一部を、別の AI に任せて同時に動かす仕組みで、任された AI は渡された依頼文だけを手がかりに作業し、終わると結果を返します。

| 言葉 | ここでの意味 |
|---|---|
| サブエージェント | Claude が仕事を分けて任せる、別の AI。1つずつ自分の作業の場を持つ |
| 依頼文（BRIEF.md） | 5つ全部に同じものを渡した、カードの形と書き方の決まり |
| 重なり | 別の担当が同じ用語のカードを作ること（π0.5・LoRA など） |

記事は1本が大きく、全部のコードとログを載せているので、20万字を超えるものもあります。1つの AI が65本を順に読むと時間がかかり、途中で前に読んだ記事の中身も薄れます。そこで、話の流れで5つに分けました。

| 担当 | 記事 | 本数 |
|---|---|---|
| A | フィジカルAI研究室 前半（DAY337・LOG341〜LOG363） | 10 |
| B | フィジカルAI研究室 後半と道具選び（LOG368・LOG396〜LOG436） | 9 |
| C | サービスロボット導入シミュレーション Log#1〜#16（LOG351〜LOG384） | 16 |
| D | サービスロボット導入シミュレーション Log#17〜#33（LOG391〜LOG440） | 17 |
| E | 考察記事（DAY336・LOG375〜LOG443） | 13 |

5つに渡した依頼文は次のとおりです。既存の268枚の用語と意味の一覧（existing.json）も一緒に渡し、同じ用語を作らないようにしました。

#### 依頼文 BRIEF.md

```markdown
# ロボティクス見直しページに足す項目を、記事から作る

## 何のためか
エイイチ（ロボティクス研究を個人で進めている、29年のデザイナー経験を持つ人）が、声に出して覚える復習カードのページを使っている。
カードは2種類。いまある268枚（DAY102〜DAY326 の記事から作成済み）に、それ以降の新しい記事から作ったカードを足す。

既存カードの一覧（重複させないために必ず読む）：/home/claude/work/existing.json
記事の本文：/home/claude/p2m-site/src/content/records/{記事番号}.md （1本が大きいものがある。frontmatter・冒頭・見出し・まとめ・表を中心に読み、必要なところを grep で拾う。実行ログの全文は読み飛ばしてよい）

## カードの形（JSON の1件）

### word（単語）— 一般的なロボティクス・AI の用語
{"category": "...", "type": "word", "front": "用語（別名や読み）", "back": "短い意味（声に出す一言。35字前後まで）", "detail": "詳しい説明（2〜3文。一般的な定義を先に、記事でどう出てきたかを1文。数値は記事にある値だけ）", "key": true/false, "src": "LOG401"}
- front は「用語（読み・別名）」の形。カッコの前後どちらで言っても採点される。例「LoRA（ローラ）」「π0.5（パイレイテンゴ）」— π0.5 の読みは必ず「パイレイテンゴ」
- back は「〜こと」「〜方法」「〜仕組み」のように、用語と続けて一息で言える短い意味
- key=true は「まず覚える」。現場で頻出の基本語だけ（各バッチで3〜5枚まで）
- 記事にしか出てこない社内用語・ファイル名・スクリプト名・変数名は単語にしない。一般に通じる用語だけ
- 既存カードと同じ用語・ほぼ同じ意味のものは作らない（existing.json の front を必ず確認）

### lesson（判断の型）— 記事の実話から得た教訓
{"category": "...", "type": "lesson", "front": "場面（問い）。困った状況を1〜2文で書き「どうする？」「何を疑う？」で終える", "back": "教訓の一言（声に出す。25字前後まで）", "detail": "実例（2〜4文。記事で実際に起きたこと・数値・結果。読むだけ）", "kw": [["語A","言い換え1","ひらがな"],["語B","言い換え"],["語C",...]], "key": true/false, "src": "LOG401,LOG403"}
- 事実の丸暗記ではなく、別の場面でも使える判断にする（場面→教訓）
- kw は2〜3組。教訓の中核の語を組にし、各組に言い換え・ひらがな・漢字違いを並べる（音声認識の聞き違いも見越す）。各組から1語言えれば合格
- key=true は「核心」。特に大事なものだけ（各バッチで2〜3枚）
- detail の数値・固有名詞は記事に書いてある通りに。推測で足さない

## category（このどれか）
kinematics 運動学・座標系 / hardware 実機・サーボ制御 / sim シミュレーション・デジタルツイン / vision 画像認識・カメラ / servo 視覚サーボ・Pick&Place・受け渡し / learning ロボット学習 / eval 評価・データ管理 / principles 設計原則・進め方 / society 社会実装・現場 / service 配膳ロボット・導入シミュレーション（新設。配膳・サービスロボットの導入の試算や Unity の店のシミュレーションの話）

## 文体（厳守）
- 日本語だけ。英語の文を混ぜない（用語の英字表記は可）
- 鉤括弧「」は使わない。説明くさい言い回しを避け、短く言い切る
- 撮影機種などの具体的な機種名は出さない（スマホで撮影 など）。費用は円で書く
- 公開済み記事の誤りを指摘したり直したりはしない。記事に書いてある内容をそのまま使う

## 出すもの
指定のファイルに JSON 配列で書く（items のみ。id は付けない）。
最後の返答は、作った枚数（word / lesson）と、記事どうしで結論が食い違っていて新しい方を採った所があればその一言だけ。
```

#### 5つの担当に出した指示（担当ごとの部分）

```text
【担当 A】
/home/claude/work/BRIEF.md を読んで、その通りに作業してください。
担当の記事：DAY337, LOG341, LOG345, LOG348, LOG349, LOG352, LOG355, LOG358, LOG361, LOG363（フィジカルAI研究室シリーズ前半。π0.5・Runpod・LoRA・お手本収集など）
目安：word 12〜16枚、lesson 10〜14枚。
出力ファイル：/home/claude/work/out_A.json

【担当 B】
/home/claude/work/BRIEF.md を読んで、その通りに作業してください。
担当の記事：LOG368, LOG396, LOG401, LOG403, LOG407, LOG417, LOG419, LOG422, LOG436（フィジカルAI研究室シリーズ後半・GPU選び・道具選び。回数での評価、失敗の巻き戻し、人の修正、ゲームパッドでの補正など）
目安：word 12〜16枚、lesson 10〜14枚。
出力ファイル：/home/claude/work/out_B.json

【担当 C】
/home/claude/work/BRIEF.md を読んで、その通りに作業してください。
担当の記事：LOG351, LOG353, LOG356, LOG357, LOG359, LOG360, LOG362, LOG364, LOG366, LOG367, LOG370, LOG374, LOG379, LOG382, LOG383, LOG384（サービスロボット導入シミュレーション Log#1〜#16。配膳ロボットの試算と Unity の店）
目安：word 12〜16枚、lesson 12〜16枚。category は主に service（一般的なシミュレーション用語は sim、評価の考え方は eval でもよい）。
出力ファイル：/home/claude/work/out_C.json

【担当 D】
/home/claude/work/BRIEF.md を読んで、その通りに作業してください。
担当の記事：LOG391, LOG393, LOG397, LOG398, LOG400, LOG402, LOG404, LOG405, LOG409, LOG414, LOG415, LOG418, LOG423, LOG426, LOG434, LOG435, LOG440（サービスロボット導入シミュレーション Log#17〜#33。現場で測った数・画像認識で止まりを見つける話など）
目安：word 12〜16枚、lesson 12〜16枚。category は主に service（画像認識の一般用語は vision なども可）。
※ 同じシリーズの前半（Log#1〜#16）は別の担当が作っている。前半の記事に出てくる基本語（配膳ロボット・待ち時間など）は前半側に任せ、後半で新しく出てくる語と教訓を中心にすること。
出力ファイル：/home/claude/work/out_D.json

【担当 E】
/home/claude/work/BRIEF.md を読んで、その通りに作業してください。
担当の記事：DAY336, LOG375, LOG389, LOG392, LOG406, LOG412, LOG424, LOG433, LOG438, LOG439, LOG441, LOG442, LOG443（考察記事。報酬だけで学ぶ組立、協働ロボットの法律・資格、現場で扱えるか、自分で直す学び方、追加学習、照明、脳の分業、世界モデル、配膳ロボットが止まる理由、遠隔の人による修正、フィジカルAIの定義、LoRA と YOLO など）
目安：word 14〜18枚、lesson 10〜14枚。
出力ファイル：/home/claude/work/out_E.json
```

5つは3分から4分で返ってきました（A 203秒・B 169秒・C 217秒・D 182秒・E 226秒）。作った枚数と、記事どうしで結論が違っていて新しい方を採った所は次のとおりです。

| 担当 | 単語 | 判断の型 | 新しい方を採った所 |
|---|---|---|---|
| A | 16 | 14 | アームの手首が止まった原因は、発熱の推測（DAY337・LOG341）ではなく、爪がブロックに乗っていた（LOG345）。ずれを直す手は、学習を増やす（LOG345）ではなく、位置の違うお手本（LOG348） |
| B | 16 | 14 | ゲームパッドの1押しの刻みは、0.5°（LOG419）ではなく1°（LOG436） |
| C | 16 | 16 | 試算と Unity のどちらを正とするかは、項目ごとに分ける（LOG362）。ロボットの道は、地図の上で自分で引く（LOG374）。すれ違いは、直したあと2晩比べて標準にした方（LOG384） |
| D | 16 | 16 | 試算と3Dの店で詰まる場所が違った原因は、会計の店員を呼ぶ順番の違い（LOG405）。揺れ続けるロボットの拾い方は、ロボット自身に進み具合を見張らせる形（LOG440） |
| E | 18 | 14 | なし |
| 計 | 82 | 74 | |

### 重なった16枚をまとめて、140枚にした

5つは別々に作っているので、同じ用語が何枚も出ました。π0.5 は A と B、LoRA は A と B と E が作っていました。用語の名前（カッコの前）が同じものと、名前は違っても同じ中身のもの（乱数シードと乱数の種、稼働率と利用率）を1枚にし、残す方に、外した方の出典の記事番号を足しました。既存の268枚と同じ用語はありませんでした。

#### 重なりをまとめて番号を振ったスクリプト

```python
import json,re
new=[]
for b in 'ABCDE':
    for x in json.load(open(f'out_{b}.json')): x['_b']=b; new.append(x)
# (batch, front) to drop -> merge its src into the kept one
drop={('A','π0.5（パイレイテンゴ）'):('B','π0.5（パイレイテンゴ）'),
 ('B','LoRA（ローラ）'):('A','LoRA（ローラ）'),('E','LoRA（ローラ・低ランク適応）'):('A','LoRA（ローラ）'),
 ('B','アクションチャンク（チャンク）'):('A','アクションチャンク（チャンク）'),
 ('A','損失（ロス・loss）'):('B','損失（ロス・loss）'),
 ('A','チェックポイント'):('B','チェックポイント'),
 ('B','リーダーアーム（リーダーとフォロワー）'):('A','リーダーアーム（リーダーとフォロワー）'),
 ('A','クラウドGPU（GPUの時間借り）'):('B','クラウドGPU（GPUの時間貸し）'),
 ('C','デッドロック（行き詰まり）'):('D','デッドロック（立ち往生・お見合い）'),
 ('D','下げ膳（バッシング）'):('C','下げ膳（下膳）'),
 ('D','中央値（メディアン）'):('C','中央値（メディアン）'),
 ('D','乱数の種（シード）'):('C','乱数シード（種）'),
 ('D','利用率（稼働率）'):('C','稼働率（利用率）'),
 ('B','世界モデル（ワールドモデル）'):('E','世界モデル（ワールドモデル）'),
 ('B','DAgger（ダガー）'):('E','DAgger（ダガー）'),
 ('C','SLAM（スラム）'):('E','SLAM（スラム）')}
idx={(x['_b'],x['front']):x for x in new}
for d,k in drop.items():
    a,b=idx[d],idx[k]
    s=[p.strip() for p in (b['src']+','+a['src']).split(',') if p.strip()]
    b['src']=','.join(sorted(set(s),key=lambda t:int(re.sub(r'\D','',t))))
keep=[x for x in new if (x['_b'],x['front']) not in drop]
w=[x for x in keep if x['type']=='word'];l=[x for x in keep if x['type']=='lesson']
for i,x in enumerate(w): x['id']=f'w{165+i:03d}'
for i,x in enumerate(l): x['id']=f'k{105+i:03d}'
out=[]
for x in w+l:
    x.pop('_b'); out.append({'id':x['id'],'category':x['category'],'sub':'','type':x['type'],'front':x['front'],'back':x['back'],'detail':x['detail'],**({'kw':x['kw']} if x['type']=='lesson' else {}),'key':bool(x.get('key')),'src':x['src']})
json.dump(out,open('new_items.json','w'),ensure_ascii=False,indent=1)
print(len(w),len(l),sum(x['key'] for x in w),sum(x['key'] for x in l))
print([x['detail'] for x in out if x['front'].startswith('π0.5')])
```

```text
66 74 13 15
['米国の Physical Intelligence 社が重みを公開している視覚言語行動モデル。自分の機体のお手本で追加学習できる。記事では、上から見た画像と6つの関節角を受け取り、次の1秒ぶんの関節角を返すモデルとして使った。']
```

結果は単語66枚・判断の型74枚の140枚で、まず覚える（単語の印）は13枚、核心（判断の型の印）は15枚です。π0.5 の読みは、記事での書き方にそろえて パイレイテンゴ にしました。


## 作業ログ ― 別の AI 2つで、新しいカード140枚を元の記事と照らした

10月11日に前のカードを作ったとき、判断の型104枚を出典と照らすと40枚に直す所がありました。数字の取り違えや、計画を実施済みと書いていたものです。カードは声に出して何度も覚えるものなので、まちがいを覚えると直すのに手間がかかります。今回も、作った AI とは別の AI 2つに、70枚ずつ照らし合わせを任せました。作った側が自分で見直すと、同じ所を見落とすためです。

- ねらい：記事と違う数字や、記事にない推し量りを、覚える前に取り除く
- 変わったこと：140枚のうち18枚を直した（前半70枚で5枚、後半70枚で13枚）
- 見込める効果：前回の判断の型の照らし合わせでは104枚中40枚に直す所があり、今回は140枚中18枚だった。作る段階で、既存の一覧と書き方の決まりを渡したことが効いた見込み。まちがいが残っていないかは、練習で引っかかった所を記録から見て確かめる

#### 照らし合わせの指示（前半と後半で同じ形）

```text
/home/claude/work/new_items.json の配列のうち、先頭から70件（0〜69番目）を、出典の記事と照合してください。記事は /home/claude/p2m-site/src/content/records/{src の記事番号}.md（src はカンマ区切りで複数ある）。書き方の決まりは /home/claude/work/BRIEF.md。

見ること：
- detail・back の数値・固有名詞・起きたことが記事と一致しているか（取り違え、計画を実施済みと書いている、記事にない推測の足し込み）
- word の一般的な定義が正しいか（用語として誤った説明になっていないか）
- lesson の back が、detail の実例から無理なく言える教訓か。kw が back の中核語と合っていて、言い換えが足りているか
- src が実際にその話が出てくる記事か（違えば直す）
- 文体：日本語だけ、鉤括弧なし、機種名なし

直すときは意味を変えずに最小限で。id と category と type は変えない。
結果を /home/claude/work/verified_1.json に、担当の70件だけを同じ形の JSON 配列で書く。
最後の返答は、直した件数と、直した所を1行ずつ（id：何を直したか）。

（後半の担当には、範囲を「70番目から最後まで（70〜139番目の70件）」、書き出し先を /home/claude/work/verified_2.json にした同じ指示を出した）
```

#### 照らし合わせで直した18枚

| id | 直したこと |
|---|---|
| w170 | 最後の文の、0.5秒分を送るか全部送るかで持ち上げの高さが変わった、は記事になかった。LOG345 に書いてある、1秒分を全部送る形に変えると手首が指令に届かなくなった、に直した |
| w174 | 出典から LOG419 を外した（リーダーアームが出てこない）。LOG348・LOG403 にした |
| w195 | 80cmの通路で床が途切れた、と通路の狭さが原因のように読めた。LOG362 では、前に計算した古い床が消えずに新しい床と重なって残ったのが原因 |
| w200 | 70回が約1時間で終わる、は数字が違った。LOG383 の表では4本同時で約85分の見込み |
| k105 | 合否の語の1組目に、言い換えの つなぐ を足した |
| k111 | 合否の語の3組目（実機）を、教訓の中心の 分からない・判断できない の組に替えた |
| k112 | 4回続けて箱に入れた理由に、着いたかを確かめる幅を広げたことを足した。合否の語の3組目を、お手本・学習データの組に替えた |
| k115 | 実例の英語の FAIL を、不合格に直した |
| k117 | 合否の語の3組目（目標）を、不足・足りない の組に替えた |
| k118 | 電源を入れたままのたたんだ姿勢、を記事どおり、電源を入れたときのたたんだ姿勢に直した |
| k123 | 合否の語の2組目（記録）を、残らない・反映されない の組に替えた |
| k124 | 合否の語の1組目に、教訓の語の 決まった を足した |
| k151 | 店ごとに配置を動かしても消えなかった、と実施済みのように書いていた。記事どおり、消えない詰まりだったという見立ての形に直した |
| k152 | 合否の語の3組目（現場の実感）を、教訓の 仕事の量 の組に替えた |
| k165 | 98.2% が実機の結果と読めないよう、シミュレーションの中での結果と足した |
| k167 | 合否の語の3組目に、教訓の語の 次の動き を足した |
| k168 | 合否の語の3組目（凍結）を、教訓の 0% の組に替えた |
| k177 | 合否の語の2組目（関わる領域）を、教訓の 線を引く・線引き の組に替えた |

**直した18枚のうち10枚は、判断の型の合否の語（kw）でした。**合否の語は、声に出した教訓の中にその語があるかで合格を決めます。教訓の文に入っていない語が合否の語になっていると、教訓を正しく言っても不合格になります。数字の取り違えより見つけにくく、練習してから気づくと、自分が言えていないのか、カードがおかしいのか分からなくなるところでした。

### 分野 配膳ロボットの導入 を足し、データを408枚にした

照らし合わせたカードを既存の268枚の後ろに足し、分野に 配膳ロボットの導入 を足しました。最初は 配膳ロボット・導入シミュレーション という名前にしましたが、スマホの画面で見出しが2行に折れたので短くしました。音声認識の聞き違いの補正（homophones）には、新しい用語の読みを足しました。

```python
import json
d=json.load(open('src/robotics_review.json'))
v=json.load(open('verified_1.json'))+json.load(open('verified_2.json'))
ids=[x['id'] for x in v]; assert len(ids)==140==len(set(ids)), len(ids)
old={x['id'] for x in d['items']}; assert not old & set(ids)
d['categories'].append({'key':'service','label':'配膳ロボット・導入シミュレーション'})
cats={c['key'] for c in d['categories']}
assert all(x['category'] in cats for x in v)
d['items']+=v
d['updated']='2026-11-05'
d['note']='単語は一般的なロボティクス用語、判断の型は Pixels to Motors 開発ログ（DAY102〜LOG443）の実話から作成。'
d['homophones']+= [['パイレイテンゴ','パイレイ点5','パイレイテン5','パイ0.5','π0.5'],['ローラ','ローラー','lora'],['ダガー','dagger'],['ヨロ','yolo'],['スラム','slam'],['ルロボット','lerobot','ルーロボット'],['パッチコア','patchcore']]
json.dump(d,open('robotics_review.json','w'),ensure_ascii=False,indent=1)
from collections import Counter
print(len(d['items']),Counter(x['category'] for x in d['items']))

# スマホで見出しが2行に折れたので、分野の名前を短くした（このあと書き出し直し）
d=json.load(open('robotics_review.json'))
for c in d['categories']:
    if c['key']=='service': c['label']='配膳ロボットの導入'
json.dump(d,open('robotics_review.json','w'),ensure_ascii=False,indent=1)
```

```text
408 Counter({'learning': 66, 'sim': 51, 'eval': 50, 'principles': 41, 'vision': 40, 'hardware': 39, 'society': 34, 'servo': 31, 'kinematics': 30, 'service': 26})
```

分野ごとの枚数は次のようになりました。

| 分野 | 単語（前→後） | 判断の型（前→後） | 足した枚数 |
|---|---|---|---|
| 運動学・座標系 | 18 → 18 | 12 → 12 | 0 |
| 実機・サーボ制御 | 17 → 22 | 12 → 17 | 10 |
| シミュレーション・デジタルツイン | 19 → 29 | 10 → 22 | 22 |
| 画像認識・カメラ | 19 → 25 | 12 → 15 | 9 |
| 視覚サーボ・Pick&Place・受け渡し | 18 → 18 | 12 → 13 | 1 |
| ロボット学習 | 18 → 41 | 12 → 25 | 36 |
| 評価・データ管理 | 20 → 27 | 11 → 23 | 19 |
| 設計原則・進め方 | 18 → 19 | 12 → 22 | 11 |
| 社会実装・現場 | 17 → 20 | 11 → 14 | 6 |
| 配膳ロボットの導入 | 0 → 11 | 0 → 15 | 26 |
| 計 | 164 → 230 | 104 → 178 | 140 |

ロボット学習が66枚でいちばん多くなりました。フィジカルAI研究室の π0.5 の記録で、学習の言葉（損失・エポック・学習率・チェックポイントなど）が一度に増えたためです。運動学・座標系は、新しい記事に新しい言葉がなく、増えていません。

[図解: LOG445_cards_by_field.html](https://pixelstomotors.com/embeds/LOG445_cards_by_field.html)

#### 足した140枚のデータ（id・分野・種類・表・意味・説明・合否の語・印・出典）

```json
[
 {
  "id": "w165",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "LoRA（ローラ）",
  "back": "モデル本体は固定し、足した小さな部品だけを学習する方法",
  "detail": "大きなモデルの重みを凍結したまま、低ランクの小さな行列を差し込んでそこだけを学習するファインチューニングの手法。必要なGPUメモリと保存容量を大きく減らせる。記事では、π0.5を手元のアームのお手本19本で追加学習するときにこの方法を使い、lossは0.1155から0.0054へ下がった。",
  "key": true,
  "src": "LOG341,LOG417,LOG443"
 },
 {
  "id": "w166",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "バッチサイズ（バッチ）",
  "back": "学習で、重みを1回直すのに使うデータの数",
  "detail": "1ステップごとに、データの中からバッチサイズ分をまとめて見て、平均のずれから重みを直す。大きいほど1ステップのGPUメモリと時間が増える。記事ではバッチ32で、1ステップごとにお手本から32コマを見て、1ステップ約7秒かかった。",
  "key": false,
  "src": "LOG341"
 },
 {
  "id": "w167",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "エポック（周）",
  "back": "学習データ全体を、ひととおり見終える回数の単位",
  "detail": "ステップ数とバッチサイズの積をデータ数で割ると、何周したかが分かる。周回が多すぎると、データを丸暗記する過学習が起きやすくなる。記事では、2,000ステップ×バッチ32で約6万4千コマとなり、5,635コマのお手本を約11周した計算だった。",
  "key": false,
  "src": "LOG345,LOG348"
 },
 {
  "id": "w168",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "学習率スケジュール（ウォームアップとコサイン減衰）",
  "back": "学習の進み具合に合わせて、重みを直す幅を変える決め方",
  "detail": "最初は小さい幅から上げるウォームアップで不安定さを避け、その後コサインの形でゆっくり下げて仕上げる組み合わせがよく使われる。記事では、ウォームアップ300ステップ、最大5e-5から5,000ステップかけて5e-6まで下げる予定を前回と同じにし、違うのはお手本だけになるようにした。",
  "key": false,
  "src": "LOG355"
 },
 {
  "id": "w169",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "正規化（ノーマライゼーション）",
  "back": "値の大きさと散らばりを、学習しやすい範囲にそろえる処理",
  "detail": "関節角などの平均と広がりを全データから計算し、その統計で値を変換してから学習に使う。推論のときも同じ統計を使わないと、入力と出力の意味がずれる。記事では、新しいお手本の統計を前回と別の名前で書き出し、前回の学習の統計を上書きしないようにした。",
  "key": false,
  "src": "LOG341,LOG355"
 },
 {
  "id": "w170",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "アクションチャンク（チャンク）",
  "back": "1回の推論で、少し先までの動きをまとめて出す単位",
  "detail": "模倣学習の方策が、次の1手ではなく数ステップ先までの動きの並びを一度に返す方式。その並びのどこまでを実行して次の推論に移るかで、動きが変わる。記事では、π0.5が0.1秒刻みで1秒分（10ステップ）を返し、そのうち0.5秒分を送る形から1秒分を全部送る形に変えると、手首が指令に届かなくなった。",
  "key": true,
  "src": "LOG345,LOG358,LOG401,LOG417"
 },
 {
  "id": "w171",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "LeRobot（ルロボット）",
  "back": "ロボット学習のデータとコードをそろえた、公開ライブラリ",
  "detail": "Hugging Faceが公開しているロボット学習用のライブラリで、画像・関節角・行動を時刻つきで保存する共通のデータ形式を持つ。形式がそろうと、別のモデルの学習にもそのまま渡せる。記事では、自前の記録をこの形式に変換してから、π0.5の学習に読み込ませた。",
  "key": false,
  "src": "LOG341,LOG355"
 },
 {
  "id": "w172",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "推論サーバー（ポリシーサーバー）",
  "back": "学習したモデルを動かし、問い合わせに動きの予測を返すプログラム",
  "detail": "GPUのある計算機でモデルを常駐させ、ロボット側のクライアントが画像と関節角を送ると予測を返す。最初の1回はコンパイルなどの準備で遅くなることが多い。記事では、最初の推論が約55秒、2回目からは0.6〜1.6秒で返った。",
  "key": false,
  "src": "LOG341,LOG345"
 },
 {
  "id": "w173",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "ダイレクトティーチング（直接教示）",
  "back": "関節の力を抜いたロボットを、人が手で動かして姿勢を覚えさせる方法",
  "detail": "人が腕を持って動かし、その間の関節角を記録する教え方。専用の操作装置がいらない一方、手で支えている間と自力で保持している間で姿勢が変わることがある。記事では、手で教えた要所の姿勢をアームに再生させ、ブロックを箱に入れる動きを4回続けて成功させた。",
  "key": false,
  "src": "LOG349"
 },
 {
  "id": "w174",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "リーダーアーム（リーダーとフォロワー）",
  "back": "人が動かす操作用の腕の動きを、もう1台の腕にまねさせる仕組み",
  "detail": "操作用のリーダーと同じ形のフォロワーを組にし、人が見ながら動かした通りにフォロワーが動く遠隔操作の方式。人の手がカメラに映らず、位置合わせも人がその場でできるので、お手本集めに向く。記事では、リーダーアームのないアームでお手本の位置を広げる方法として比べられた。",
  "key": false,
  "src": "LOG348,LOG403"
 },
 {
  "id": "w175",
  "category": "eval",
  "sub": "",
  "type": "word",
  "front": "オフライン評価（お手本との答え合わせ）",
  "back": "実機を動かさず、記録したデータでモデルの予測を確かめること",
  "detail": "学習に使ったお手本や別に取ったデータの入力をモデルに渡し、返る予測を正解と比べる。実機の時間と危険を使わずに、明らかな不具合を先に見つけられる。記事では、お手本の画像と角度で166回問い合わせ、お手本は動くのに予測が止まる場面が0回であることを確かめてから実機に進んだ。",
  "key": false,
  "src": "LOG358"
 },
 {
  "id": "w176",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "複合誤差（コンパウンディングエラー）",
  "back": "1回ごとの小さなずれが、次の判断に持ち越されて積み重なること",
  "detail": "模倣学習の方策は自分の動いた結果を次の入力にするため、小さなずれが重なると、お手本にない状態へ外れていく。一歩ずつは見えないほど小さくても、合計では大きくなる。記事では、ブロックを持って肩を上げる指令が毎回約2°足りず、0.5秒ずつ動かした回は合計41.2°の指令に対し11.1°しか上がらなかった。",
  "key": false,
  "src": "LOG361"
 },
 {
  "id": "w177",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "π0.5（パイレイテンゴ）",
  "back": "画像と言葉の指示からロボットの動きを出す公開モデル",
  "detail": "米国の Physical Intelligence 社が重みを公開している視覚言語行動モデル。自分の機体のお手本で追加学習できる。記事では、上から見た画像と6つの関節角を受け取り、次の1秒ぶんの関節角を返すモデルとして使った。",
  "key": false,
  "src": "DAY337,LOG341,LOG396,LOG401"
 },
 {
  "id": "w178",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "ACT（アクト）",
  "back": "動きをまとめて予測する小さな模倣学習モデル",
  "detail": "Action Chunking with Transformers の略で、人のお手本から行動のまとまりを予測する軽い模倣学習の手法。学習が安く、比べる基準にしやすい。記事では、公開モデルの実機比較で人が安全のために止めた回が一度もなかったため、π0.5 と並べる物差しの候補にした。",
  "key": false,
  "src": "LOG396"
 },
 {
  "id": "w179",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "損失（ロス・loss）",
  "back": "正解とモデルの答えのずれを数字にしたもの",
  "detail": "学習ではこの値が小さくなるように重みを直していく。小さいほどお手本に近いが、下がっても実機で良くなるとは限らない。記事では、続きからの学習で 0.0075 から 0.0028 に下がったが、実機では人の直しが残った。",
  "key": true,
  "src": "LOG341,LOG348,LOG417,LOG422"
 },
 {
  "id": "w180",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "学習率（ラーニングレート）",
  "back": "1回の学習でモデルをどれだけ大きく直すかの強さ",
  "detail": "大きすぎると学習が暴れ、小さすぎると進まない。最初に少しずつ上げる立ち上がりと、終わりに下げる仕組みを組み合わせることが多い。記事では、続きからの学習なので最大を 2.5e-5 に弱め、立ち上がり100ステップ、1,500ステップで下げきる形にした。",
  "key": false,
  "src": "LOG417"
 },
 {
  "id": "w181",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "チェックポイント",
  "back": "学習の途中や終わりに保存したモデルの重み",
  "detail": "ここから推論したり、続きから学習を再開したりできる。どの時点の重みかを名前で残すと、結果をたどり直せる。記事では、4,000ステップ目の重みを出発点に、人の直しを足して続きから学習した。",
  "key": false,
  "src": "LOG345,LOG348,LOG417"
 },
 {
  "id": "w182",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "オープンウェイト（重み公開モデル）",
  "back": "学習済みの重みが公開され手元で動かせるモデル",
  "detail": "重みを手元に固定できるので、同じ条件で他の人が結果を再現しやすい。クラウドのAPIだけのモデルは裏で更新されることがある。記事では、重みが公開されていて追加学習できることを、π0.5 を選ぶ理由に挙げた。",
  "key": false,
  "src": "LOG396"
 },
 {
  "id": "w183",
  "category": "hardware",
  "sub": "",
  "type": "word",
  "front": "GPUメモリ（VRAM）",
  "back": "GPUがモデルやデータを載せておく作業場所",
  "detail": "足りなければ学習は遅くなるのではなく始まらない。速さや値段を比べる前に、まず必要な量に届くかで候補が決まる。記事では、LoRA学習の目安が22.5GB超で、最初に計画した16GBのGPUは届かなかった。",
  "key": true,
  "src": "LOG368"
 },
 {
  "id": "w184",
  "category": "hardware",
  "sub": "",
  "type": "word",
  "front": "クラウドGPU（GPUの時間貸し）",
  "back": "GPU付きの計算機を時間単位で借りる仕組み",
  "detail": "買わずに必要なときだけ大きなGPUを使える。置いておくだけの時間も課金され、使い終わったら削除までしないと料金が続く。記事では、約75万円のPCを買わずに借りて、7回の学習と実機テストのGPU代は記録のある分で4,677円だった。",
  "key": false,
  "src": "LOG341,LOG345,LOG355,LOG368,LOG401"
 },
 {
  "id": "w185",
  "category": "society",
  "sub": "",
  "type": "word",
  "front": "損益分岐点（ブレークイーブン）",
  "back": "買うと借りるの費用が逆転する使用量",
  "detail": "これより多く使うなら買う方が、少ないなら借りる方が安くなる境目。使う時間や置いておく割合など、前提を変えると動く。記事の計算機の初期値では、買う方が安くなるのは月約178時間からだった。",
  "key": false,
  "src": "LOG368"
 },
 {
  "id": "w186",
  "category": "hardware",
  "sub": "",
  "type": "word",
  "front": "ジョグ運転（jog）",
  "back": "押している間だけ一定の速さで動き続ける命令",
  "detail": "目標角度を何度も送り直す代わりに、動き始めと止めの命令だけで連続して動かす方式。止める命令や通信切れで止める見張りが要る。記事では、送り直し方式ではなめらかにならず、ジョグ命令に応えるかを測る試しを用意した。",
  "key": false,
  "src": "LOG436"
 },
 {
  "id": "w187",
  "category": "principles",
  "sub": "",
  "type": "word",
  "front": "敵対的検証（レッドチーミング）",
  "back": "粗を探す前提で別の目に読ませ、誤りを洗い出すこと",
  "detail": "同じ書き手が確かめると同じ所を見落とすため、別の人やAIにあえて弱点を突かせる。記事では、判断用のページを別のAIに読ませ、誤り2件・誤解を招く12件・改善4件の合計18件を直した。",
  "key": false,
  "src": "LOG368"
 },
 {
  "id": "w188",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "離散イベントシミュレーション（DES）",
  "back": "出来事が起きた瞬間だけを追って時間を進める計算",
  "detail": "客が来る、料理ができる、誰かが運ぶといった出来事を時刻順に並べ、数に限りのある人や設備の取り合いで生まれる待ちを数える方法。物の位置や動きは扱わない。記事では飲食店のピーク2時間を再現し、店・人数・台数・使い方の7,665条件を回した。",
  "key": true,
  "src": "LOG351"
 },
 {
  "id": "w189",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "SimPy（シムパイ）",
  "back": "Pythonで離散イベントシミュレーションを組む道具",
  "detail": "時計にあたる環境、1組の客の流れにあたるプロセス、卓やスタッフのように数に限りのあるリソースの3つの部品で組む。記事ではピークの2時間を1回あたり1秒もかからずに進め、配膳のほか病院搬送や夜間清掃など5つの現場の試算にも使った。",
  "key": false,
  "src": "LOG351,LOG356"
 },
 {
  "id": "w190",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "待ち行列（キュー）",
  "back": "手が空くまで順番を待つ列と、その長さや待ち時間の考え方",
  "detail": "窓口の数、来る間隔、さばく時間で、待ちがどれだけたまるかが決まる。記事の5現場の試算は、ロボットの仕事と、ロボットに呼ばれる人の順番待ちの2段で組んだ。",
  "key": false,
  "src": "LOG356"
 },
 {
  "id": "w191",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "ポアソン到着（ランダムな来客）",
  "back": "来る間隔がばらばらで、平均の頻度だけが決まった来方",
  "detail": "来る間隔が指数分布に従うと、一定時間に来る数はポアソン分布になる。客や注文の来方を表す定番の置き方。記事の試算では、来客の間隔を指数分布で決め、1日の件数もポアソンでばらつかせた。",
  "key": false,
  "src": "LOG356,LOG359"
 },
 {
  "id": "w192",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "稼働率（利用率）",
  "back": "人や設備が、時間のうち手がふさがっている割合",
  "detail": "稼働率が100%に近づくと、小さな揺れでも待ちが急に伸びる。記事では、試算のピークでスタッフの手が95%ふさがっていて、1組にかかる時間の1割に満たない差でも人手が足りなくなった。ロボット2台を入れると82%から76%に下がる例もあった。",
  "key": true,
  "src": "LOG366,LOG379,LOG397"
 },
 {
  "id": "w193",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "ウォームアップ期間（慣らし期間）",
  "back": "空の状態から始めた直後を、数えずに捨てる時間",
  "detail": "シミュレーションは空の店や空の工場から始まるので、ふだんの状態に落ち着くまでの最初の時間を集計から外す。記事では最初の60分を数えずに回すと、多くの条件で待ちが伸び続け、慣らしを120分にしただけで待ちが倍近くになった。",
  "key": false,
  "src": "LOG359"
 },
 {
  "id": "w194",
  "category": "eval",
  "sub": "",
  "type": "word",
  "front": "感度分析",
  "back": "仮の値を1つずつ動かし、結論がどれだけ変わるかを見ること",
  "detail": "どの前提が結果を大きく動かすかを知り、測る優先順位を決めるために使う。記事では仮の値4つを振り、止まる割合は0%から90%まで動かしても7秒しか変わらず、止まる長さ・動けなくなる割合・走る速さの3つが効くと分かった。",
  "key": true,
  "src": "LOG357"
 },
 {
  "id": "w195",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "NavMesh（ナビメッシュ）",
  "back": "床の形から自動で作る、歩ける範囲の地図",
  "detail": "ゲームエンジンが人やキャラクターの道探しに使う、通れる床を多角形で表した地図。記事では80cmに詰めた通路の店で、前に計算した古い床が消えずに新しい床と重なって残り、人が21卓に入れなかった。",
  "key": false,
  "src": "LOG357,LOG362,LOG370"
 },
 {
  "id": "w196",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "占有格子地図（グリッドマップ）",
  "back": "床を細かいマス目に分け、通れるかどうかを塗った地図",
  "detail": "移動ロボットの道探しで広く使う地図の形。マス目の粗さで、狭い通路が通れないように見えることがある。記事では10cmのマス目を2cmにしただけで届く卓が32卓から49卓全部に変わり、のちに2.5cm刻みの地図をロボットに渡した。",
  "key": false,
  "src": "LOG360,LOG374"
 },
 {
  "id": "w197",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "経路探索（パスファインディング）",
  "back": "地図の上で、いる所から行き先までの道を自分で引くこと",
  "detail": "A*（エースター）などの方法で、障害物を避けた道をその都度計算する。ふさがれたら道を引き直す。記事では、決まった線をたどる作りから地図の上で道を引き3秒で回り込む作りに変え、行き止まりも直すと、60秒以上の止まりが57回から20回に減った。",
  "key": false,
  "src": "LOG370,LOG374"
 },
 {
  "id": "w198",
  "category": "eval",
  "sub": "",
  "type": "word",
  "front": "乱数シード（種）",
  "back": "乱数の出方を決める最初の数。同じなら同じ乱数が出る",
  "detail": "シードを固定すると同じ条件を出し直せ、変えるとばらつきを見られる。記事では、同じ条件でもシードによって入店待ちが最大11倍違い、1条件のシードを3つから10個に増やした。",
  "key": false,
  "src": "LOG370,LOG383,LOG393,LOG423"
 },
 {
  "id": "w199",
  "category": "eval",
  "sub": "",
  "type": "word",
  "front": "中央値（メディアン）",
  "back": "値を小さい順に並べたとき、真ん中にくる値",
  "detail": "少数の極端な値に引っぱられにくく、ばらつきの大きい結果の代表値に向く。記事では、同じシードでも結果が少し揺れるため、シード10個の中央値で比べることにした。",
  "key": false,
  "src": "LOG383,LOG384,LOG393,LOG397"
 },
 {
  "id": "w200",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "ヘッドレス実行（画面なし実行）",
  "back": "画面を出さずに、シミュレーションを裏で回すこと",
  "detail": "描画を省くので速く、何本も同時に回したり夜のあいだ自動で回したりできる。記事では画面なしの実行ファイルを作り、4本同時なら70回が約85分で終わる見込みになった。",
  "key": false,
  "src": "LOG383,LOG384"
 },
 {
  "id": "w201",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "下げ膳（下膳）",
  "back": "食べ終わった皿を卓から下げ、洗い場へ運ぶ仕事",
  "detail": "飲食店では片付けと卓拭きが終わるまで次の客を通せないので、入店待ちに直結する。記事で公開事例を調べると、配膳ロボットの使い方は料理を運ぶだけ、下げ膳が中心、客が皿を載せる、ロボットが自分で下げるの4つの型に分かれ、どの型でも皿をロボットに載せるのは人だった。",
  "key": false,
  "src": "LOG351,LOG391,LOG440"
 },
 {
  "id": "w202",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "デッドロック（立ち往生・お見合い）",
  "back": "互いに相手がどくのを待ち、どちらも動けなくなること",
  "detail": "複数の主体がそれぞれ相手の持つ場所や資源を待ち続け、全体が止まる状態。実店舗の動画では、1台分の幅の通路で2台が向かい合って止まり、店員が来るまで41秒以上かかった。3Dの店では同じ向かい合いが9分続いた。",
  "key": true,
  "src": "LOG364,LOG370,LOG391,LOG434"
 },
 {
  "id": "w203",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "ライブロック（揺れ続け）",
  "back": "動き続けているのに、少しも前へ進まない状態",
  "detail": "止まってはいないが、よけ合いや迷いを繰り返して仕事が進まない状態。デッドロックと違い、止まりとして数えにくい。3Dの店では、ロボットが1.4cmの幅を行ったり来たりし続け、止まりと数えられず助けも呼ばない詰まりが残った。",
  "key": false,
  "src": "LOG435,LOG440"
 },
 {
  "id": "w204",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "スタック検知（進み具合の見張り）",
  "back": "決めた時間に決めた距離を進めたかで、行き詰まりを見つける仕組み",
  "detail": "移動ロボットが目的地へ向かって本当に進めているかを、ロボット自身が見張る仕組み。ROS 2 の Nav2 の標準部品は、既定で10秒に0.5m動けなければ進めていないと判断する。3Dの店にも10秒で0.5mの見張りを入れ、揺れ続けは180回で0回になった。",
  "key": false,
  "src": "LOG440"
 },
 {
  "id": "w205",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "フリート管理（群管理・管制）",
  "back": "複数台のロボットの通る道や順番をまとめて決める仕組み",
  "detail": "倉庫や店で何台ものロボットを動かすとき、行き先の割り当てや、狭い所を通る順番を上から調整する仕組み。配膳ロボットのどちらが譲るかの決まりは、どのメーカーも公式には公開していなかった。日本の管制の仕組みと規格では、狭い区間を予約制にし、一度に一方向だけ通す形だった。",
  "key": false,
  "src": "LOG434"
 },
 {
  "id": "w206",
  "category": "sim",
  "sub": "",
  "type": "word",
  "front": "決定論的シミュレーション（再現できる実行）",
  "back": "同じ条件と同じ種なら、毎回まったく同じ結果が出るシミュレーション",
  "detail": "実行の順番や時間の刻みに左右されず、入力が同じなら出力も同じになるように作ったシミュレーション。前は同じ種で2回回しても、動けなくなったロボットが12回と0回に分かれた。同じ結果にする作りにすると、12組とも記録が1行も違わず、1回の計算も3〜4分から1.2〜1.5分に縮んだ。",
  "key": false,
  "src": "LOG435"
 },
 {
  "id": "w207",
  "category": "eval",
  "sub": "",
  "type": "word",
  "front": "共通乱数法（対応のある比較）",
  "back": "比べる条件どうしで同じ乱数を使い、差だけを取る比べ方",
  "detail": "シミュレーションで2つの条件を比べるとき、同じ種を使って偶然の部分をそろえ、差のぶれを小さくする方法。3Dの店では、同じ種でのずれは種を変えたときの差の約10分の1だった。同じ種どうしで差を取ると、はっきり言える組が76組中3組から37組に増えた。",
  "key": false,
  "src": "LOG423"
 },
 {
  "id": "w208",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "タイムスタディ（時間観測）",
  "back": "作業を区切ってストップウォッチで時間を測る調べ方",
  "detail": "現場の作業を小さな単位に分け、1回ずつの所要時間を測って標準の時間を決める方法。配膳ロボットの試算は会計1回90秒・片付け40秒などの仮の値で動いている。店の人がスマホで作業ごとの時間を測る観察シートを作り、前提を確かめられるようにした。",
  "key": false,
  "src": "LOG414,LOG415"
 },
 {
  "id": "w209",
  "category": "eval",
  "sub": "",
  "type": "word",
  "front": "回帰テスト（リグレッションテスト）",
  "back": "直したあと、前と同じ結果が変わっていないかを確かめるテスト",
  "detail": "変更で、前は正しく動いていた所が壊れていないかを確かめる試験。試算の前提109個をプログラムの外の表に移したとき、数字は変えずに600条件を回し直し、前の版と全部の数字が同じになることを確かめた。",
  "key": false,
  "src": "LOG415"
 },
 {
  "id": "w210",
  "category": "vision",
  "sub": "",
  "type": "word",
  "front": "異常検知（アノマリー検知）",
  "back": "普段のデータだけを覚え、いつもと違うものを見つける方法",
  "detail": "失敗の例が少なく形もばらばらなときに、正常な例だけで学習して外れを見つける手法。工場の検査で良品だけを覚えさせて傷を見つけるのに使われる。3Dの店の録画で、配膳ロボットの30秒以上の止まりを見つける試しに使った。",
  "key": true,
  "src": "LOG418"
 },
 {
  "id": "w211",
  "category": "vision",
  "sub": "",
  "type": "word",
  "front": "PatchCore（パッチコア）",
  "back": "画像を小区画に分け、覚えた普段の区画との距離で異常を測る方法",
  "detail": "学習済みの画像モデルで区画ごとの特徴を取り出し、普段の画像の特徴の代表を覚えておき、新しい区画が一番近い代表からどれだけ遠いかを点数にする。新しく学習させる部分がないので、クラウドの CPU 2コアでも1通り約15分で回った。予備実験では誤報5%で長い止まりの82%を拾えた。",
  "key": false,
  "src": "LOG418"
 },
 {
  "id": "w212",
  "category": "vision",
  "sub": "",
  "type": "word",
  "front": "再現率（リコール・検出率）",
  "back": "見つけるべきもののうち、実際に見つけられた割合",
  "detail": "取りこぼしの少なさを表す指標で、誤報の割合と組で見る。配膳ロボットの止まりを画像で拾う試しでは、画面の道の線を消すと再現率は72%から28%に落ちた。",
  "key": false,
  "src": "LOG418"
 },
 {
  "id": "w213",
  "category": "vision",
  "sub": "",
  "type": "word",
  "front": "ショートカット学習（近道学習）",
  "back": "本来の手がかりでなく、たまたま効く別の手がかりで当ててしまうこと",
  "detail": "モデルが課題の本質ではなく、データにだけある目印や背景で答えを出してしまう現象。本番では目印がないので性能が落ちる。3Dの店の録画では、モデルが画面に描かれたロボットの道の線の交わる所を手がかりにしていて、線を消すとロボットどうしの詰まりは47件中4件しか拾えなかった。",
  "key": false,
  "src": "LOG418"
 },
 {
  "id": "w214",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "世界モデル（ワールドモデル）",
  "back": "ロボットが動いた後の場面を、動かす前に予測するAI",
  "detail": "今の場面とロボットの動きを入れると、その動きをしたあとの場面を予測する。方策が次に何をするかを出すのに対し、世界モデルはそれをしたらどうなるかを出す。物理シミュレータのように人が形や摩擦を作り込まず、映像とロボットのデータから学ぶ。記事では、奥行きつきのカメラ5台が前提の手法を調べ、今の手元の環境では得が小さいと判断した。",
  "key": true,
  "src": "LOG396,LOG438"
 },
 {
  "id": "w215",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "DAgger（ダガー）",
  "back": "ロボットにやらせ、間違えた所を人が直してお手本に足す学習",
  "detail": "お手本だけで学んだロボットは、お手本にないずれた場面から戻れない。実際に動かして間違えた所で人が手を出し、その直しを学習データに足していくことで、その穴を埋める。記事の動画では、枠の改良とデータ追加と DAgger で計260本にし、もう片方のタスクの物に手を出す割合が73%から0%になった。",
  "key": false,
  "src": "LOG403,LOG417,LOG443"
 },
 {
  "id": "w216",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "残差強化学習",
  "back": "元のモデルの動きに、小さな部品が補正を足して学ぶ方法",
  "detail": "大きなモデルは凍結し、その出した動きに小さなネットワークが補正を足す。学ぶのは補正の部分だけなので軽く、元のモデルの範囲の外にも出られる。記事の調査では、報酬は成功1・失敗0だけで、人型ロボットの成功率が約15分の練習で14%から64%に上がった例があった。",
  "key": false,
  "src": "LOG392"
 },
 {
  "id": "w217",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "報酬整形（リワードシェーピング）",
  "back": "最後の成否に加え、途中の進み具合にも点を与える工夫",
  "detail": "成否だけの報酬では学習が進みにくいときに、近づいた・向きが合ったなどの途中の進み具合にも点を与えて学習を導く。ただし点の付け方を誤ると、点を稼ぐ動きに引きずられる。記事の組立の研究では、進捗の報酬を足すと共通の1本のポリシーの成功率が98.2%から17.8%まで落ちた。",
  "key": false,
  "src": "DAY336"
 },
 {
  "id": "w218",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "蒸留（ディスティレーション）",
  "back": "大きなAIの答え方を小さなAIに学ばせ、速く安く動かす方法",
  "detail": "賢さを少し落とす代わりに、反応を速くし、計算の費用を下げる。速くなるのは大きなモデルそのものではなく、小さくした版のほうになる。記事では、ロボット用の学習した動きのモデルの1回の判断が約0.05秒、汎用AIは11〜19秒で、そのままでは動く物に使えないと整理した。",
  "key": false,
  "src": "LOG424"
 },
 {
  "id": "w219",
  "category": "learning",
  "sub": "",
  "type": "word",
  "front": "エンドツーエンド（端から端まで）",
  "back": "途中の手順を人が決めず、1つのモデルが入力から操作まで出す作り方",
  "detail": "認識・判断・操作を別々の部品に分けず、カメラ映像などの入力から直接動きを出す。途中の決まりを人が書かずに済む代わりに、なぜその動きになったかは見えにくい。記事では、カメラだけで走る車が8個のカメラの映像から直接運転操作まで出している例が出てきた。",
  "key": false,
  "src": "LOG412"
 },
 {
  "id": "w220",
  "category": "vision",
  "sub": "",
  "type": "word",
  "front": "YOLO（ヨロ）",
  "back": "画像の何がどこにあるかを四角い枠で返す、速い物体検出モデル",
  "detail": "1回の処理で画像全体から物の種類と位置の枠を出すので速く、小さな GPU でも動く。記事の動画では、一番小さい版で目標に枠を描き、その画像を π0.5 に渡すと、関係ない物に手を出す問題がほぼ解決した。物を見分ける役は検出器、言葉と物を結ぶ役は LoRA と分けて使っていた。",
  "key": false,
  "src": "LOG443"
 },
 {
  "id": "w221",
  "category": "vision",
  "sub": "",
  "type": "word",
  "front": "センサーフュージョン",
  "back": "得意の違う複数のセンサーの値を合わせ、1つより確かな値にすること",
  "detail": "レーダーは距離に強く、カメラは横の位置に強いように、センサーには得意と不得意がある。重ねるとあいまいさが交わった部分まで縮み、片方だけの範囲ではあいまいなまま残る。記事の講義では、測定と予測の差を見張ることで、センサーの故障の予兆が取れると学んだ。",
  "key": false,
  "src": "LOG439"
 },
 {
  "id": "w222",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "自己位置推定（ローカライゼーション）",
  "back": "ロボットが地図の上で自分がどこにいるかを割り出すこと",
  "detail": "動くと位置の不確かさが広がり、センサーで周りを測ると締まる、を繰り返して位置を絞る。地図に頼る方法はどんな環境にも使える主流だが、中の認識がずれると自信満々に外す。記事では、配膳ロボットが店で自分の位置を見失う迷子を、この段で崩れた止まり方として分けた。",
  "key": true,
  "src": "LOG439"
 },
 {
  "id": "w223",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "SLAM（スラム）",
  "back": "地図を作りながら、同時にその地図の上で自分の位置を割り出すこと",
  "detail": "知らない場所でも、走りながら周りを測って地図を広げ、その地図で自分の位置を推定する。地図作りと位置推定を同時に解くのが難しさの中心になる。記事では、配膳・清掃ロボットが今まさに使っている古典的な仕組みとして、スタンフォードの講義の中で出てきた。",
  "key": false,
  "src": "LOG370,LOG439"
 },
 {
  "id": "w224",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "差動二輪",
  "back": "左右の車輪を別々の速さで回して曲がる車輪の付け方",
  "detail": "後ろの2輪を左右別々に回し、前はキャスターで支える形が多い。逆に回せばその場で回れるが、真横には動けない。記事では、配膳ロボットが前をふさがれると、その場で向きを変えても道が開かず止まる理由として整理した。",
  "key": false,
  "src": "LOG439"
 },
 {
  "id": "w225",
  "category": "service",
  "sub": "",
  "type": "word",
  "front": "カルマンフィルタ",
  "back": "予測と測定を合わせ、位置の候補を山1つの形で絞る方法",
  "detail": "動いたら候補を広げ、測ったら合わない候補を下げる、というベイズフィルタの考え方を、候補を山1つのガウス分布で持つことで計算できるようにしたもの。候補を多数の点で持つのがパーティクルフィルタ。記事では、位置のずれは動きで崩れたか、測って直し損ねたかの2択で切り分けられると学んだ。",
  "key": false,
  "src": "LOG439"
 },
 {
  "id": "w226",
  "category": "eval",
  "sub": "",
  "type": "word",
  "front": "動作設計領域（ODD）",
  "back": "システムが動いてよい条件の範囲を先に決め、外では動かさない考え方",
  "detail": "自動運転で使われる考え方で、場所・天候・時間帯・明るさなどの許容範囲を決め、範囲の外を検知したら止めるか人に渡す。記事では、少量のお手本で学んだピッキングが部屋を変えて崩れた件を、この範囲を決めずに会場を変えたことが問題だと整理した。",
  "key": true,
  "src": "LOG412"
 },
 {
  "id": "w227",
  "category": "hardware",
  "sub": "",
  "type": "word",
  "front": "バックドライバブル（外から押すと動く関節）",
  "back": "手で押すと関節が回り、ぶつかると力が逃げる作り",
  "detail": "減速機の減速比を小さくして、外から押すと関節が回るようにする。ぶつかっても力が逃げるので人のそばで使いやすく、モーターの電流から当たった力も推定できる。記事では、精度より壊れにくさと安さを取る研究用の安いアームの方向として出てきた。",
  "key": false,
  "src": "LOG412"
 },
 {
  "id": "w228",
  "category": "hardware",
  "sub": "",
  "type": "word",
  "front": "視触覚センサー（カメラ式の触覚センサー）",
  "back": "指先のゴムの膜を裏からカメラで撮り、触った様子を測るセンサー",
  "detail": "膜のへこみ方から、押し付けた形や滑り始めが画像として取れるので、学習モデルにもそのまま入れられる。記事では、手首カメラは接触の瞬間を見て確かめるだけで、力の強さや滑りまでは見えず、そこを補う研究として紹介された。",
  "key": false,
  "src": "LOG412"
 },
 {
  "id": "w229",
  "category": "society",
  "sub": "",
  "type": "word",
  "front": "産業用ロボットの特別教育",
  "back": "ロボットに動きを教えたり動かしたまま点検する人に、事業者が受けさせる教育",
  "detail": "動く範囲の中で動きを教える作業や、動かしたままの点検・調整をする従業員に、使う事業者が受けさせる決まり。記事では、対象はモーターの出力が80Wを超えるロボットで、受講は学科2日と実技1日の3日間、費用は3万〜5万円と整理した。納入する側は、従業員に操作させるならこの教育が要ると伝える。",
  "key": true,
  "src": "LOG375"
 },
 {
  "id": "w230",
  "category": "society",
  "sub": "",
  "type": "word",
  "front": "製造物責任法（PL法）",
  "back": "製品の欠陥で被害が出たとき、作った者が負う責任を定めた法律",
  "detail": "業として作った人、輸入した人、自社製と表示した人が負う。記事では、ソフト単体は対象外でも、機械に組み込まれたソフトの不具合は機械の欠陥になりうること、責任は引き渡しから10年であることを確かめた。メーカーの製品の中身を書き換えると、改造した人が作り手の側に回る。",
  "key": false,
  "src": "LOG375,LOG406"
 },
 {
  "id": "k105",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "プログラムが動いているかの確認スクリプトはOKと出るのに、実際につなぐと拒否される。何を疑う？",
  "back": "生存確認は、実際につないで確かめる",
  "detail": "プロセスを名前で探す確認が、検索を実行したシェル自身のコマンド文字列に一致し、デーモンが止まっていても毎回OKと返していた。確認するたびに番号が変わっていたのもこのためだった。後日には、古いプロセスを止めてから起動する1行のコマンドが、起動部分を実行する前に自分自身を止めていた。",
  "kw": [
   [
    "実際につなぐ",
    "つなぐ",
    "接続",
    "つないで",
    "せつぞく"
   ],
   [
    "生存確認",
    "確認",
    "かくにん",
    "チェック"
   ]
  ],
  "key": false,
  "src": "DAY337,LOG341"
 },
 {
  "id": "k106",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "成功のたびに少しずつ自分で直す補正を入れたら、成功を重ねるほど動きがおかしくなり、ついに安全チェックで止まった。何を疑う？",
  "back": "補正が一方向に動き続けたら、元の測定を疑う",
  "detail": "置く高さの補正が成功のたびに5mmずつ上がり、29mmになったところで手首の角度が安全範囲の外に出た。箱は固定してあるのに、置いたあとの画像で箱が約20cm動いたと測り違え、箱に当たったと受け取っていた。お手本を集めている間は補正を読むだけにして、書き換えないようにした。",
  "kw": [
   [
    "補正",
    "ほせい"
   ],
   [
    "一方向",
    "いっぽうこう",
    "上がり続ける",
    "動き続ける"
   ],
   [
    "測定",
    "そくてい",
    "測り違い",
    "計測"
   ]
  ],
  "key": false,
  "src": "LOG341"
 },
 {
  "id": "k107",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "アームが途中で止まった。関節角の記録を見ると、熱か力不足のように見える。数字だけで原因を決めてよい？",
  "back": "止まった原因は、数字だけでなく映像で確かめる",
  "detail": "手首が3〜4°動いて止まった記録から、まず手首の発熱、次に1回の指令が小さすぎる力負けと見立てたが、どちらも外れた。横から撮った動画を見ると、手前の爪がブロックの上面に乗って押していた。当たって止まったのか力が足りずに止まったのかは、角度の記録の上では同じに見える。",
  "kw": [
   [
    "映像",
    "動画",
    "どうが",
    "えいぞう"
   ],
   [
    "数字",
    "記録",
    "すうじ",
    "ログ"
   ],
   [
    "原因",
    "げんいん"
   ]
  ],
  "key": false,
  "src": "LOG345"
 },
 {
  "id": "k108",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "大きなモデルのファイル群を別の計算機へ送った。フォルダ全体の大きさが期待どおりになったので、送り終わったとみなしてよい？",
  "back": "送り終わりは、ファイルごとの大きさや中身で照合する",
  "detail": "合計の大きさが6.0G前後になったのを見て移動とサーバー起動に進んだところ、2つのファイルがまだ送信中で、829MBあるはずのファイルが700MBしかなく起動に失敗した。以後は全ファイルの名前とバイト数の一覧を送り元と送り先で行ごとに比べ、29ファイルすべての一致を確かめてから使った。",
  "kw": [
   [
    "ファイルごと",
    "1つずつ",
    "ひとつずつ",
    "個別"
   ],
   [
    "照合",
    "しょうごう",
    "比べる",
    "突き合わせ"
   ],
   [
    "大きさ",
    "サイズ",
    "おおきさ"
   ]
  ],
  "key": false,
  "src": "LOG345,LOG355,LOG358"
 },
 {
  "id": "k109",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "時間単位で借りるGPUで、朝にモデルを用意し、実機は夕方に試すことにした。借り方として何がまずい？",
  "back": "GPUは使う直前に借り、使い終えたらすぐ消す",
  "detail": "朝に推論サーバーを起動してから実機を動かすまでの約8時間、何もせずに約790円を払った。前日も学習が終わったあと朝まで置き、約585円かかっていた。そこで、学習が終わったらその計算機のまま推論サーバーを起動して実機で試し、モデルを持ち帰ってから削除する段取りに変えた。",
  "kw": [
   [
    "直前",
    "ちょくぜん",
    "使う時",
    "必要な時"
   ],
   [
    "消す",
    "削除",
    "さくじょ",
    "けす"
   ],
   [
    "借りる",
    "かりる",
    "時間借り",
    "レンタル"
   ]
  ],
  "key": false,
  "src": "LOG341,LOG345,LOG358"
 },
 {
  "id": "k110",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "模倣学習したアームが、物の位置をずらしても毎回ほぼ同じ所へ手を伸ばす。lossはまだ下がっているので、学習を倍に増やせば直る？",
  "back": "足りないのは学習の回数でなく、条件の違うお手本",
  "detail": "学習を2,000ステップから4,000ステップに倍にし、lossは0.0030まで下がったが、つかむ瞬間の土台と肩の角度はほぼ同じで、どちらもお手本の範囲の外だった。お手本19本はブロックをほぼ同じ場所に置いて集めたもので、画像を見なくてもいつもの場所へ手を伸ばすだけで正解になっていた。置き場所を5か所に広げてお手本を録り直した。",
  "kw": [
   [
    "お手本",
    "手本",
    "おてほん",
    "デモ"
   ],
   [
    "条件の違う",
    "位置の違う",
    "ばらつき",
    "違う"
   ],
   [
    "回数",
    "かいすう",
    "学習量",
    "ステップ"
   ]
  ],
  "key": true,
  "src": "LOG348,LOG355"
 },
 {
  "id": "k111",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "お手本を変えて学習し直したら、lossの下がり方は前回とほとんど同じだった。モデルが画像を見て動きを変えるようになったかを、lossで判断できる？",
  "back": "lossでは、画像を見ているかは分からない",
  "detail": "置き場所を5か所に広げた19本と、ほぼ同じ場所の19本で、loss は区間ごとの平均でもほとんど差がなかった。今の関節角も入力に渡しているので、動き出したあとは画像を見なくても続きをほぼ当てられ、画像が効くのは最初の数秒だけと見立てた。手を伸ばす先が置き場所で変わるかは、実機で見ることにした。",
  "kw": [
   [
    "loss",
    "ロス",
    "損失",
    "そんしつ"
   ],
   [
    "画像",
    "がぞう",
    "カメラ"
   ],
   [
    "分からない",
    "わからない",
    "判断できない"
   ]
  ],
  "key": false,
  "src": "LOG355,LOG348"
 },
 {
  "id": "k112",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "手で支えて教えた姿勢をロボットに再生させると、教えたときより低く下りて床に当たる。お手本として記録する角度はどちらにする？",
  "back": "お手本には、再生で実際に届いた角度を残す",
  "detail": "床から浮かせて教えたつかむ姿勢へ再生すると、アームは床まで下り、指令との差が5°前後のまま止まった。つかむ位置をもう一段高めに教えると高さは合い、着いたかを確かめる幅を広げると4回続けて箱に入れられたが、角度の上では約4.4°届いていなかった。画像と合うのは届いた角度なので、学習に入れるのは再生中の実測と決めた。",
  "kw": [
   [
    "届いた角度",
    "実測",
    "じっそく",
    "実際の角度"
   ],
   [
    "再生",
    "さいせい",
    "リプレイ"
   ],
   [
    "お手本",
    "手本",
    "おてほん",
    "学習データ"
   ]
  ],
  "key": false,
  "src": "LOG348,LOG349"
 },
 {
  "id": "k113",
  "category": "hardware",
  "sub": "",
  "type": "lesson",
  "front": "アームが目標へ動いている最中にグリッパーを閉じたら、その後の指令を受け付けなくなった。何を足す？",
  "back": "爪を動かす前に、腕が止まったことを確かめる",
  "detail": "着いたとみなす幅が8°と広く、14.76°動く区間を送ってから0.383秒で着いたと判断し、残り6.06°の途中で爪を閉じていた。同じ常駐プログラムで647回動いている本線は、幅2〜3°で止まってから爪を動かしていた。幅の中に一度入るだけでは通過中でも成り立つので、3回読んだ角度の差が1°以内かも確かめるようにした。",
  "kw": [
   [
    "止まった",
    "静止",
    "せいし",
    "とまった"
   ],
   [
    "爪",
    "グリッパー",
    "つめ"
   ],
   [
    "確かめる",
    "確認",
    "かくにん"
   ]
  ],
  "key": false,
  "src": "LOG352"
 },
 {
  "id": "k114",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "お手本どおり動くはずのモデルが、物の真上に来たところで止まり、何度聞いてもほとんど動かない答えを返す。お手本の何を疑う？",
  "back": "お手本の待ち時間も、AIは動きとしてまねる",
  "detail": "手で教えた姿勢を再生して録ったお手本は、着いたかの確認やグリッパーの待ちで何度も止まっていた。止まっているステップは3,949のうち1,594で全体の40%、ブロックの真上では毎回1.5〜2秒動いていなかった。途中の停止を詰めて学習し直すと、夕方の8回は1度も止まらなかった。",
  "kw": [
   [
    "待ち時間",
    "停止",
    "ていし",
    "まちじかん"
   ],
   [
    "お手本",
    "手本",
    "おてほん",
    "デモ"
   ],
   [
    "まねる",
    "模倣",
    "もほう",
    "真似"
   ]
  ],
  "key": true,
  "src": "LOG358"
 },
 {
  "id": "k115",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "学習に8時間かけて実機で試すたび、お手本側の問題が1つずつ見つかり、1つ直すのにほぼ1日かかる。どう回し方を変える？",
  "back": "学習の前にお手本を点検し、実機の前に答え合わせをする",
  "detail": "爪のずれ、置き場所の偏り、お手本の停止と、実機を止めた原因は毎回お手本の側にあり、停止は数えれば1分で分かるものだった。学習前の点検スクリプトでは、元のお手本が19本すべてで停止が1秒を超えるとして不合格になった。学習後には、お手本の画像と角度をサーバーに送る答え合わせを挟んでから実機に進んだ。",
  "kw": [
   [
    "学習の前",
    "学習前",
    "がくしゅうまえ",
    "事前"
   ],
   [
    "点検",
    "てんけん",
    "チェック"
   ],
   [
    "答え合わせ",
    "オフライン評価",
    "こたえあわせ"
   ]
  ],
  "key": false,
  "src": "LOG358"
 },
 {
  "id": "k116",
  "category": "vision",
  "sub": "",
  "type": "lesson",
  "front": "カメラ画像を入力にするモデルを、昨日と同じ配置で動かしているのに、日によって動きが変わる。動かす前に何を確かめる？",
  "back": "開始前に、カメラの写り方をお手本の画像と重ねて比べる",
  "detail": "上カメラは普段使いのスマホを毎回付け外ししていて、実機を動かした回だけ画像が37.5ピクセル下にずれ、高さ216ピクセルの2割近くになっていた。別の日には、顔に合わせて画角を自動で寄せる機能が入っていて、カメラを開いて数秒後に写り方が変わった。開始時の1枚目をお手本の最初のコマと比べ、ずれが大きければ何も送らずに止める確認を入れた。",
  "kw": [
   [
    "写り方",
    "画角",
    "うつりかた",
    "カメラの位置"
   ],
   [
    "お手本の画像",
    "お手本",
    "おてほん",
    "基準画像"
   ],
   [
    "開始前",
    "動かす前",
    "かいしまえ",
    "事前"
   ]
  ],
  "key": false,
  "src": "LOG358,LOG361"
 },
 {
  "id": "k117",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "学習したモデルで動かすと、腕を上げる動作が毎回少しずつ足りず、だんだん低くなる。お手本をそのまま再生すると問題ない。何を見直す？",
  "back": "小さな不足が積み重なるなら、モデルに渡す状態を見直す",
  "detail": "ブロックを持って肩を上げる指令43回は、すべて目標より0.77〜2.80°低く止まっていた。モデルは低いまま測った角度を見て、そこからまた上げる量を答えるので、不足が持ち越されていた。決まった角度を送るお手本の再生では4回とも箱に入った。モデルに渡す角度を直前に送った目標に変えると、学習し直さずに初めて箱に入れられた。",
  "kw": [
   [
    "渡す状態",
    "入力",
    "にゅうりょく",
    "状態"
   ],
   [
    "積み重なる",
    "蓄積",
    "ちくせき",
    "持ち越し"
   ],
   [
    "不足",
    "足りない",
    "ふそく"
   ]
  ],
  "key": true,
  "src": "LOG361,LOG363"
 },
 {
  "id": "k118",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "学習したモデルの予測を確かめたいが、アームはまだたたんだ姿勢や、手首だけ戻りきらない姿勢にある。このまま試してよい？",
  "back": "お手本と同じ開始姿勢にそろえてから試す",
  "detail": "お手本はどれも待機姿勢から始まっていて、電源を入れたときのたたんだ姿勢でのドライランは、予測がほとんど0で判断の材料にならなかった。手首だけが+56°に残ったまま始めた回は、ブロックの上に来ても手首が約30°ずれたままで、小さな動きを繰り返すだけになった。待機姿勢に戻してから試すと、最初の1秒の予測はお手本の平均と2.2°以内で重なった。",
  "kw": [
   [
    "開始姿勢",
    "初期姿勢",
    "かいししせい",
    "スタート"
   ],
   [
    "お手本と同じ",
    "同じ",
    "そろえる"
   ],
   [
    "待機姿勢",
    "ホーム",
    "HOME"
   ]
  ],
  "key": false,
  "src": "LOG341,LOG345,LOG358"
 },
 {
  "id": "k119",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "ある置き場所で1回うまくいった。この置き場所なら成功すると考えて先へ進んでよい？",
  "back": "条件ごとに回数で数え、割合で見る",
  "detail": "3か所に5回ずつブロックを置いて数えると、合わせて15回中9回で、位置Cは5回中5回、Eは5回中3回、腕を伸ばしきるDは5回中1回だった。お手本の本数はDがいちばん多い5本で、それでもいちばん入らなかった。",
  "kw": [
   [
    "回数",
    "試行回数",
    "かいすう"
   ],
   [
    "条件ごと",
    "置き場所ごと",
    "位置ごと"
   ],
   [
    "割合",
    "成功率",
    "わりあい"
   ]
  ],
  "key": true,
  "src": "LOG401"
 },
 {
  "id": "k120",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "クラウドの計算機を借りるたびに、大きなモデルを手元から送る待ちで時間の半分が消える。どうする？",
  "back": "重い物は一度置き場に上げ、借りた側から取りに行く",
  "detail": "6.3GBのモデルを借りるたびに家の回線で送ると約30分かかり、借りた時間の約半分がその待ちだった。モデル配布サービスの非公開の置き場に一度だけ上げると6分5秒で済み、借りた計算機から取ってくるのは13.19秒になった。自動で作られた置き場は公開設定になっていて、約9分公開のままだった。",
  "kw": [
   [
    "置き場",
    "リポジトリ",
    "置き場所"
   ],
   [
    "取りに行く",
    "ダウンロード",
    "取ってくる"
   ]
  ],
  "key": false,
  "src": "LOG401"
 },
 {
  "id": "k121",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "失敗した回の関節角の記録を、正しい値に書き換えた。これをそのまま学習データに足してよい？",
  "back": "画像と行動の組を、実機で直しながら撮る",
  "detail": "学習データは写真と関節角の組で、写真には直す前の動きが写っている。数字だけ直しても組が合わない。そこで、カメラに手が写らない所のキーで実機を直し、直している間と再開後を1秒10コマで撮る形にした。",
  "kw": [
   [
    "画像",
    "写真",
    "カメラ"
   ],
   [
    "組",
    "ペア",
    "セット"
   ],
   [
    "実機",
    "じっき",
    "本物"
   ]
  ],
  "key": true,
  "src": "LOG403"
 },
 {
  "id": "k122",
  "category": "servo",
  "sub": "",
  "type": "lesson",
  "front": "AIに動かさせていたら、つかむ位置が少しずれ始めた。止めて最初からやり直すか、人が全部操作して運ぶか迷う。どうする？",
  "back": "ずれた関節だけ直し、続きはAIに返す",
  "detail": "巻き戻しのキーで始めの姿勢に戻し、根元の向きを21回直してAIに戻すと、AIはその姿勢から続きを組み立ててブロックをつかんだ。別の回では、つかんだあと人が肩で腕を持ち上げてからAIに戻し、箱に入れて成功で終えた。持ち上げを数分続けた回では、爪がゆるんでブロックが落ちた。",
  "kw": [
   [
    "一部",
    "ずれた関節",
    "部分"
   ],
   [
    "返す",
    "戻す",
    "任せる"
   ]
  ],
  "key": false,
  "src": "LOG407"
 },
 {
  "id": "k123",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "人が途中で関節を直すとうまくいった。次の回はAIが同じ直しを覚えていると考えてよい？",
  "back": "その場の補正は残らない。学習させて初めて覚える",
  "detail": "キーでの補正は学習した重みを変えず、その回の続きと記録にだけ効く。1回目も2回目も、つかむ前に根元を約3°直していて、1回目の直しを覚えていたわけではなかった。直した記録を学習データにして学び直す依頼を出した。",
  "kw": [
   [
    "学習",
    "学び直し",
    "再学習"
   ],
   [
    "残らない",
    "のこらない",
    "反映されない"
   ]
  ],
  "key": false,
  "src": "LOG417"
 },
 {
  "id": "k124",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "成功した回を並べると、人が毎回同じ関節を同じ向きに同じくらい直していた。これは何を意味する？",
  "back": "決まったずれは、学習で直せる手がかり",
  "detail": "成功5回はどれも、つかむ前に根元を +2〜4°、つかんだあとにひじを 15〜31° 持ち上げる向きに直していた。直したあとの根元の角度は 12.8〜13.7° にそろい、お手本でつかんだ瞬間の 13.2° とほぼ同じだった。",
  "kw": [
   [
    "決まった",
    "同じ向き",
    "一定",
    "系統"
   ],
   [
    "ずれ",
    "偏り",
    "くせ"
   ],
   [
    "学習",
    "学び直し"
   ]
  ],
  "key": false,
  "src": "LOG417"
 },
 {
  "id": "k125",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "実機の記録を学習データに変えたら、ロボットが1コマで大きく飛ぶ不自然な動きになった。何を疑う？",
  "back": "記録の抜けを確かめ、指令の道すじで補う",
  "detail": "アームの常駐プログラムは指令を送ると約1.6秒ほかの読み取りを受け付けず、動いている間の関節角が1.5〜3.6秒ずつ抜けていた。そのまま使うと1コマで最大28°飛ぶ。送った指令の道すじから角度を作り直し、止まっている時間を詰めてから学習させた。",
  "kw": [
   [
    "抜け",
    "欠け",
    "欠損"
   ],
   [
    "指令",
    "コマンド",
    "しれい"
   ]
  ],
  "key": false,
  "src": "LOG417"
 },
 {
  "id": "k126",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "少しの直しデータを足して学び直し、損失も下がった。本当に人の直しが減ったかをどう確かめる？",
  "back": "補正なしで任せる回を入れ、数字で比べる",
  "detail": "直した5本をお手本19本に足して続きから1,500ステップ学習したが、成功6回はすべて補正つきだった。補正なしの1回は、爪を閉じたときの根元が 11.5° で空振りし、人が直して成功した回の 12.9〜14.4° に届かなかった。足した本数が少なく、答えを動かしきれなかったと見ている。",
  "kw": [
   [
    "補正なし",
    "任せる",
    "自律"
   ],
   [
    "比べる",
    "比較",
    "くらべる"
   ]
  ],
  "key": false,
  "src": "LOG422"
 },
 {
  "id": "k127",
  "category": "hardware",
  "sub": "",
  "type": "lesson",
  "front": "指令と実測の差を見ると、関節が数十度もずれていて故障に見える。すぐ壊れたと判断してよい？",
  "back": "動いている途中と止まったあとを分けて見る",
  "detail": "手首の指令と実測の差は、動いている途中も含めると 1.8〜59.5° だった。止まったあとのずれだけを出すと 1.2〜4.0° で、推論の間は指令どおりに着いていた。外れたのは最後に始めの姿勢へ戻す動きの中だった。",
  "kw": [
   [
    "止まったあと",
    "停止後",
    "静止"
   ],
   [
    "動いている途中",
    "動作中",
    "移動中"
   ]
  ],
  "key": false,
  "src": "LOG422"
 },
 {
  "id": "k128",
  "category": "hardware",
  "sub": "",
  "type": "lesson",
  "front": "プログラムは何も変えていないのに、ある日から1回の指令に長く待たされるようになった。まず何を疑う？",
  "back": "実際に読み込んでいるライブラリの版を疑う",
  "detail": "アームの受け口のプログラムが、使うはずの 2.9.6 ではなく 4.0.4 の制御ライブラリを読んでいた。4.0.4 は送るたびに返事を0.5秒ずつ3回待ち、1回の指令に約1.6秒かかっていた。起動スクリプトの1行を直して 2.9.6 に戻すと、送信は約0.02秒になった。",
  "kw": [
   [
    "版",
    "バージョン",
    "ばん"
   ],
   [
    "ライブラリ",
    "依存",
    "らいぶらり"
   ]
  ],
  "key": true,
  "src": "LOG436"
 },
 {
  "id": "k129",
  "category": "hardware",
  "sub": "",
  "type": "lesson",
  "front": "手で直すときの刻みを細かくしたら、押しても関節が動かないことがある。どうする？",
  "back": "反応する最小の刻みを測り、無視は送り直す",
  "detail": "0.5°の送信で実際に動いたのは、7回中3回と18回中8回で、2°の送信は毎回動いた。1押しを1°にした。指令がまるごと無視されて8秒止まる場面もあったので、0.5秒たっても動いていなければ同じ目標を3回まで送り直す形にした。",
  "kw": [
   [
    "最小",
    "刻み",
    "きざみ"
   ],
   [
    "送り直す",
    "再送",
    "リトライ"
   ]
  ],
  "key": false,
  "src": "LOG436,LOG419"
 },
 {
  "id": "k130",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "人がロボットの動きをその場で直す操作を作る。便利な自動の動きやモードを足したくなる。どう設計する？",
  "back": "ボタンと動きを1対1にし、自動を減らす",
  "detail": "最初の案にあった握り直しの自動ボタンやモードの切り替えをやめ、押した分だけそのまま動く1つの形にした。十字キーの左右はいつも根元、停止は誤って押しにくい左右2つの同時押しにした。失敗しかけた数秒で迷わず直すためで、直した操作は関節と角度の形でそのまま記録に残る。",
  "kw": [
   [
    "1対1",
    "いったいいち",
    "そのまま"
   ],
   [
    "自動",
    "オート",
    "じどう"
   ]
  ],
  "key": false,
  "src": "LOG419"
 },
 {
  "id": "k131",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "ロボット学習の道具を選ぶとき、新しく話題の強いモデルやサービスに乗り換えたくなる。何を基準にする？",
  "back": "強さより、結果を固定して再現できるか",
  "detail": "世界モデルや他社のロボット用モデル、学習を代わりに回すサービスと比べたが、裏でモデルが更新されるものは同じ手順で同じ結果になる保証がない。重みが公開されたモデル、型番と費用を残せる時間貸しのGPU、手順を書いて回すAIの組み合わせを替えず、比べる相手として小さな模倣学習モデルを足すことにした。",
  "kw": [
   [
    "再現",
    "再現性",
    "さいげん"
   ],
   [
    "固定",
    "こてい",
    "変わらない"
   ]
  ],
  "key": false,
  "src": "LOG396"
 },
 {
  "id": "k132",
  "category": "hardware",
  "sub": "",
  "type": "lesson",
  "front": "学習用のGPUを買うか借りるか決めたい。まず値段を比べ始めてよい？",
  "back": "先にメモリが足りるか、次に使う時間を見る",
  "detail": "メモリが足りなければ学習は始まらないので、値段より先に候補が決まる。LoRA学習の目安22.5GB超に対し、最初に計画した16GBのGPUは届かなかった。そのうえで計算機の初期値では、借りると月16,793円、買うと月23,540円で、買う方が安くなるのは月約178時間からだった。",
  "kw": [
   [
    "メモリ",
    "VRAM",
    "めもり"
   ],
   [
    "使う時間",
    "使用時間",
    "稼働時間"
   ]
  ],
  "key": false,
  "src": "LOG368"
 },
 {
  "id": "k133",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "配膳ロボットで料理は早く届くようになったのに、スタッフを1人減らすと入口の待ちが延びた。何を疑う？",
  "back": "ロボットが外す仕事でなく、人に残る仕事を数える",
  "detail": "35卓のファミレスで、3人を2人とロボット2台に置き換えると、料理が届くまでは58秒から40秒に縮んだが、入店待ちは7.7分から14.0分に延びた。ロボットが運ぶのは料理だけで、案内・会計・下げ膳は人に残る。皿がたまる焼肉の食べ放題では、同じ置き換えで8.2分から4.5分に縮んだ。",
  "kw": [
   [
    "人に残る仕事",
    "残る仕事",
    "のこるしごと"
   ],
   [
    "数える",
    "かぞえる",
    "洗い出す"
   ],
   [
    "下げ膳",
    "片付け",
    "さげぜん"
   ]
  ],
  "key": false,
  "src": "LOG351"
 },
 {
  "id": "k134",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "ロボットが止まる確率を10%と置いたら、2時間でスタッフが何度も呼ばれる結果になり、現場の感覚と合わない。何を疑う？",
  "back": "性質の違う出来事を、1つの数字に混ぜていないか",
  "detail": "止まる確率10%は、10往復に1回止まり、その半分はスタッフが動かしに行くという意味で、標準ファミレスのピーク中の救出は5.5回になっていた。数秒で自然に解ける一時停止と、人が行かないと解けない立ち往生に分け、立ち往生は1往復あたり1%以下の見当で置き直した。",
  "kw": [
   [
    "混ぜる",
    "まぜる",
    "一緒にする"
   ],
   [
    "分ける",
    "わける",
    "切り分け"
   ],
   [
    "性質",
    "種類",
    "せいしつ"
   ]
  ],
  "key": false,
  "src": "LOG351"
 },
 {
  "id": "k135",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "試算に現場データのない仮の値が4つある。どれも測るには手間がかかる。何から測る？",
  "back": "1つずつ振り、結論を動かす値から測る",
  "detail": "仮の値を1つずつ動かすと、止まる割合は0%から90%まで変えても料理が届く時間は7秒しか変わらなかった。走る速さを0.5から0.3 m/秒にすると40秒から64秒になり、止まる長さと動けなくなる割合も効いた。そこで3Dのシミュレーションでは、止まる回数ではなくこの3つを測ることにした。",
  "kw": [
   [
    "振る",
    "動かす",
    "ふる",
    "感度"
   ],
   [
    "結論",
    "けつろん",
    "結果"
   ],
   [
    "優先",
    "先に",
    "ゆうせん"
   ]
  ],
  "key": true,
  "src": "LOG357"
 },
 {
  "id": "k136",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "混雑の条件を選ぶと、人やロボットを何人・何台足しても店が回らないの判定になる。判定のどこを疑う？",
  "back": "待ちを原因で分け、手を打てる待ちだけで判定する",
  "detail": "入店待ちを、満席で待った・席はあるのに待った・案内の3つに分けた。標準ファミレスの普通の日に3人・ロボットなしだと、9.6分のうち7.4分が席はあるのに待った時間で、ロボット1台で4.3分に減った。席はあるのに待ったが平均10分以上の条件を、店が回らないとして公開した。",
  "kw": [
   [
    "原因",
    "げんいん",
    "内訳",
    "うちわけ"
   ],
   [
    "分ける",
    "わける",
    "分解"
   ],
   [
    "手を打てる",
    "変えられる",
    "てをうてる"
   ]
  ],
  "key": true,
  "src": "LOG359"
 },
 {
  "id": "k137",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "計算の試算と3Dのシミュレーションで数字がずれる。どちらに合わせるかが毎回ぶれる。どうする？",
  "back": "正は項目ごとに決め、測る値は測る側を正にする",
  "detail": "最初はサイトの試算を正とし、3D側は必ず合わせると決めた。ところが試算の速さや止まる割合は、3D側で測って戻すための仮の値だった。そこで店の形・条件・数え方はサイト、通路で起きることは3D側が測る、と何について正かを表に分けた。",
  "kw": [
   [
    "正",
    "せい",
    "基準",
    "正本"
   ],
   [
    "項目ごと",
    "何について",
    "こうもく"
   ],
   [
    "測る側",
    "はかるがわ",
    "測る値"
   ]
  ],
  "key": false,
  "src": "LOG359,LOG362"
 },
 {
  "id": "k138",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "シミュレーションの詰まりを1つ直すと、別の場所に詰まりが移る。直す作業が終わらない。どうする？",
  "back": "終わりの条件を先に決め、残るズレは調べる対象にする",
  "detail": "作る・回して台帳にまとめる・ズレを調べるの3段階に分け、それぞれの終わりを先に決めた。直すたびに詰まりが移るので、決めた区切りで直すのをやめた。その時点で、時間切れの往復は31%（51回のうち16）から7%（92回のうち6）に減っていた。",
  "kw": [
   [
    "終わり",
    "区切り",
    "おわり",
    "ゴール"
   ],
   [
    "先に決める",
    "さきにきめる",
    "事前に"
   ],
   [
    "調べる対象",
    "しらべる",
    "記録する"
   ]
  ],
  "key": true,
  "src": "LOG362,LOG364,LOG367"
 },
 {
  "id": "k139",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "狭い通路の店で、ロボットが届かない卓がたくさん出た。店を広げる前に何を疑う？",
  "back": "計算のマス目を細かくして、結果が変わるかを見る",
  "detail": "障害物をマス目に塗ってからロボットの半径だけ太らせる作りで、10cmのマス目では半径35cmのつもりが約40cm太っていた。マス目を2cmにするだけで、届く卓が49卓のうち32卓から全部に変わった。以後は2.5cmと1.25cmの2回計算し、結果が分かれたら知らせる形にした。",
  "kw": [
   [
    "マス目",
    "ますめ",
    "解像度",
    "格子"
   ],
   [
    "細かく",
    "こまかく",
    "刻み"
   ],
   [
    "計算",
    "けいさん",
    "判定"
   ]
  ],
  "key": false,
  "src": "LOG360"
 },
 {
  "id": "k140",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "3Dの店が36分で止まった。動けないロボットをスタッフ3人が囲んだまま、入口に客が並んでいる。何を疑う？",
  "back": "2つの決まりが、同時に満たせない形になっていないか",
  "detail": "スタッフにはロボットから0.75m離れる決まりがあり、助けに行く仕事には0.35mまで近づいたら着いたという決まりがあった。食い違いで助けに行く仕事が終わらず、案内できる人がいなくなり客34組が並んだ。その台だけはよけない形に直した。",
  "kw": [
   [
    "決まり",
    "ルール",
    "きまり"
   ],
   [
    "食い違い",
    "矛盾",
    "くいちがい",
    "むじゅん"
   ],
   [
    "同時に",
    "両立",
    "どうじに"
   ]
  ],
  "key": false,
  "src": "LOG364"
 },
 {
  "id": "k141",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "2台のロボットが通路で向かい合って止まり続ける。店の形を変えようかと考えている。その前に何を疑う？",
  "back": "現実の機械にない制約を、作りに入れていないか",
  "detail": "ロボットは渡された1本の線の上しか走れない作りで、行きと帰りが同じ線を通っていた。実際の配膳ロボットは地図の上で自分で道を引く。地図の上で道を引き、ふさがれたら3秒で回り込む形に変え、行き止まりのドリンクバーも動かすと、60秒以上の止まりは57回から20回に、料理の待ちは2.90分から1.17分になった。",
  "kw": [
   [
    "現実にない",
    "実機にない",
    "げんじつ"
   ],
   [
    "制約",
    "縛り",
    "せいやく",
    "しばり"
   ],
   [
    "作り",
    "設計",
    "つくり"
   ]
  ],
  "key": false,
  "src": "LOG370,LOG374"
 },
 {
  "id": "k142",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "ロボットを入れた条件でだけ、料理の待ちが試算より大きくずれる。動きを直す前に何を疑う？",
  "back": "同じ瞬間を、同じ名前で数えているかを疑う",
  "detail": "ロボットが運んだ皿は、往復を終えて受け渡し口に戻るまで届いたになっていなかった。これだけで料理の待ちが1皿あたり1.0〜1.3分長く出ていた。卓に着いた時点で届いたにすると、ロボットが運ぶ時間は2.90分から1.09分になった。",
  "kw": [
   [
    "数え方",
    "定義",
    "かぞえかた"
   ],
   [
    "同じ瞬間",
    "時点",
    "じてん"
   ],
   [
    "そろえる",
    "合わせる",
    "揃える"
   ]
  ],
  "key": false,
  "src": "LOG370"
 },
 {
  "id": "k143",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "ロボットを増やしても、入店待ちがまったく変わらない。ロボットの性能を疑う前に、何を探す？",
  "back": "ロボットより手前で、1つずつしかさばけない所を探す",
  "detail": "3Dの店はレジに店員が1人で、会計は1組ずつしかできなかった。レジ1つ・会計90秒だと1時間に40組までで、普通の混み具合の42組に届かないため、ロボットの前にレジが詰まる。試算にもレジ1つにつき1組ずつの決まりを入れ、レジの数を選べるようにした。",
  "kw": [
   [
    "1つずつ",
    "ひとつずつ",
    "窓口"
   ],
   [
    "手前",
    "てまえ",
    "先に詰まる"
   ],
   [
    "さばける",
    "処理できる",
    "能力"
   ]
  ],
  "key": false,
  "src": "LOG379"
 },
 {
  "id": "k144",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "試算で、店を回すのに要るスタッフが1人減った。月の人件費が1人分浮くと伝えたい。何を確かめる？",
  "back": "減るのは要る人数。給料が減るかはシフトで決まる",
  "detail": "ロボット2台を月額で借りると、99条件のうち47条件では要る人数が減らなかった。減っても混む2時間ずつだけ浮くなら月14.1万円で、ロボット2台の月14.4万円と差し引き−0.3万円。4時間のシフト1回分減らせれば月28.2万円になる。人件費以外に、歩く距離や待ち時間が縮むことも並べて出した。",
  "kw": [
   [
    "シフト",
    "しふと",
    "勤務"
   ],
   [
    "要る人数",
    "必要人数",
    "ひつようにんずう"
   ],
   [
    "給料",
    "人件費",
    "じんけんひ"
   ]
  ],
  "key": false,
  "src": "LOG366,LOG382"
 },
 {
  "id": "k145",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "夜に回した結果を自動で振り分けたい。どこから問題とみなすかの線をどう決める？",
  "back": "線は勘で決めず、手元の実データに当てて決める",
  "detail": "たたき台の、試算と2倍以上ずれたら印という線を18回の実データに当てると、入店待ちはすべての条件で1.9〜5.7倍ずれていて、毎晩すべての回に印が付くと分かった。乱数の種で入店待ちが最大11倍違うことも分かり、種を1条件10個に増やした。",
  "kw": [
   [
    "実データ",
    "実測",
    "じつデータ"
   ],
   [
    "当てる",
    "あてる",
    "試す"
   ],
   [
    "線",
    "しきい値",
    "閾値",
    "せん"
   ]
  ],
  "key": false,
  "src": "LOG383"
 },
 {
  "id": "k146",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "同じ条件なのに、比べる相手の基準とずれたり、同時に何本回すかで結果が変わったりする。何を疑う？",
  "back": "数字が、計算機の状態や忙しさに引きずられていないか",
  "detail": "長く使い続けたエディタで作った基準では入店待ち6.16分、開き直して回すと4.54分だった。1本だけで回すと動けなくなった回数は中央値1回、4本同時では7回。基準を作り直し、夜は4本同時に固定して、比べる相手も同じ回し方でそろえた。",
  "kw": [
   [
    "計算機",
    "パソコン",
    "けいさんき",
    "環境"
   ],
   [
    "状態",
    "忙しさ",
    "じょうたい",
    "負荷"
   ],
   [
    "そろえる",
    "固定",
    "こてい"
   ]
  ],
  "key": false,
  "src": "LOG383,LOG384"
 },
 {
  "id": "k147",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "実物どおりにロボットをすれ違わせたら、すれ違いは増えたのに、店全体の入店待ちが3倍になった。何を見落とした？",
  "back": "部分の動きを直したら、まわりへの影響を全体で測る",
  "detail": "幅1.2mの通路で2台が寄ると、体が通路の大半を占めて人が通れなくなっていた。人の通る分を見て要る幅を1.5mにし、足りなければ元の決まりに戻す形に直した。そのうえで2晩分を比べると、向かい合って動けない時間の中央値が422秒から64秒に縮み、客の待ちは悪くならなかったので標準にした。",
  "kw": [
   [
    "全体",
    "ぜんたい",
    "店全体"
   ],
   [
    "影響",
    "まわり",
    "えいきょう",
    "周り"
   ],
   [
    "部分",
    "ぶぶん",
    "一部"
   ]
  ],
  "key": false,
  "src": "LOG383,LOG384"
 },
 {
  "id": "k148",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "導入事例の効果を調べると、何分減ったという結果ばかりで、途中で何回止まり人が呼ばれたかは見つからない。どうする？",
  "back": "過程の値は仮に置いて動かせる形にし、現場で数えに行く",
  "detail": "病院搬送や夜間清掃など5つの現場を調べると、公開された効果の数字はほぼすべて企業の発表で、ロボットが人を呼ぶ回数はどこも出ていなかった。そこで呼ぶ回数を仮に置いて切り替えられる試算にし、配膳では止まった場所と原因をスマホで押して数える観察シートを作った。",
  "kw": [
   [
    "過程",
    "途中",
    "かてい"
   ],
   [
    "仮に置く",
    "仮置き",
    "かりおき"
   ],
   [
    "数えに行く",
    "観察",
    "かんさつ",
    "現場"
   ]
  ],
  "key": false,
  "src": "LOG353,LOG356"
 },
 {
  "id": "k149",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "店で止まった配膳ロボットの復旧が遅い。直す作業を簡単にすれば、止まっている時間は縮む？",
  "back": "直す時間より、気づくまでの時間を縮める",
  "detail": "実店舗の動画5本では、店員が直す作業は1〜10秒、店員が来るまでは18秒から45秒以上だった。ロボットの声かけや赤い明かりはお客さん向けで、店員の手元に知らせが届く様子は映っていなかった。店員が早く来たのは、店員がよく通るドリンクバーの前で止まったときだけだった。",
  "kw": [
   [
    "気づく",
    "気付く",
    "きづく"
   ],
   [
    "知らせ",
    "通知",
    "つうち"
   ],
   [
    "来るまで",
    "駆けつけ",
    "くるまで"
   ]
  ],
  "key": false,
  "src": "LOG391,LOG400"
 },
 {
  "id": "k150",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "シミュレーションでロボットを2台に増やしたら、1台より待ちが延びた。この結果をそのまま結論にしてよい？",
  "back": "逆の結果は、まず作りの誤りを疑い1つずつ外す",
  "detail": "28卓の店で2台にすると入店待ちは29.5分で、試算は3.2分だった。席をずらす、ドリンクバーを動かす、と原因を1つずつ外すうちに、往復を終えたロボットが待機場所へ戻っていない作りの誤りが見つかった。直すと受け渡し口の前のロボットどうしの止まりは278回から110回に減った。",
  "kw": [
   [
    "作りの誤り",
    "バグ",
    "実装の誤り"
   ],
   [
    "1つずつ",
    "ひとつずつ",
    "順に"
   ],
   [
    "外す",
    "切り分け",
    "きりわけ"
   ]
  ],
  "key": false,
  "src": "LOG393"
 },
 {
  "id": "k151",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "店の図面を直すたびに、次の店でもまた同じ場所で詰まる。店ごとに配置を直し続けるべき？",
  "back": "どの店でも同じ所なら、作る決まりを直す",
  "detail": "6店すべてで、ロボット2台のときの止まりが待機場所と受け渡し口のまわりに集まった（25〜57%）。6店とも、待機場所を受け渡し口のすぐ横に置くという同じ決め方で作っていたため、店ごとにドリンクバーやレジや卓を動かしても消えない詰まりだった。",
  "kw": [
   [
    "決まり",
    "ルール",
    "きまり"
   ],
   [
    "同じ所",
    "共通",
    "おなじところ"
   ],
   [
    "作り方",
    "生成",
    "つくりかた"
   ]
  ],
  "key": true,
  "src": "LOG398,LOG402"
 },
 {
  "id": "k152",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "計算の試算と3Dのシミュレーションで、店が詰まる場所が違う。仕事の量はほぼ同じなのに差が消えない。何を疑う？",
  "back": "仕事の量より、仕事を取る順番の違いを疑う",
  "detail": "試算はレジで、3Dの店は片付けと案内で詰まっていた。仕事の量の差は5〜10%だけで、違っていたのは会計のスタッフを呼ぶタイミングだった。ファミレスのフロント経験からレジと案内を優先する形に試算を直すと、普通の混み具合で両者はほぼ一致し、ページに出していた店が回る人数は3人から2人ではなく、3人から3人に変わった。",
  "kw": [
   [
    "順番",
    "優先順位",
    "ゆうせんじゅんい"
   ],
   [
    "取り方",
    "割り当て",
    "わりあて"
   ],
   [
    "仕事の量",
    "量",
    "しごとのりょう"
   ]
  ],
  "key": true,
  "src": "LOG397,LOG398,LOG405"
 },
 {
  "id": "k153",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "シミュレーションを直したら数字がよくなった。だが同じ条件で回し直すと結果が大きく変わる。この改善を信じてよい？",
  "back": "先に、同じ条件で同じ結果が出る作りにする",
  "detail": "前は同じ種で2回回しても、動けなくなったロボットが12回と0回に分かれていた。同じ種なら同じ結果になる作りにすると、12組とも記録が1行も違わず、直す前と後を同じ条件で比べられるようになった。",
  "kw": [
   [
    "同じ結果",
    "再現",
    "さいげん"
   ],
   [
    "決定論",
    "決まった結果",
    "けっていろん"
   ],
   [
    "比べる",
    "比較",
    "ひかく"
   ]
  ],
  "key": false,
  "src": "LOG435"
 },
 {
  "id": "k154",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "シミュレーションで詰まったお客さんやロボットが、一瞬で別の場所へ置き直されて流れていた。数字はきれい。そのままでよい？",
  "back": "現実にない動きで、詰まりを消さない",
  "detail": "③⑦の店でロボット2台のとき、1回の営業で66回の瞬間移動があった。いちばん大事な詰まりの場面を、現実にない動きで消していたことになる。瞬間移動を0回にし、スタッフが歩いて行ってロボットを押す形に変えると、解けない詰まりは21回から2回まで減った。",
  "kw": [
   [
    "瞬間移動",
    "ワープ",
    "テレポート"
   ],
   [
    "現実にない",
    "非現実",
    "げんじつにない"
   ],
   [
    "詰まり",
    "渋滞",
    "つまり"
   ]
  ],
  "key": false,
  "src": "LOG426,LOG435"
 },
 {
  "id": "k155",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "シミュレーションで2台のロボットが9分にらみ合って動かない。ロボットどうしの譲り合いの決まりを作り込むべき？",
  "back": "まず、現場で実際に誰が解いているかを写す",
  "detail": "実物の動画4本では、どれも店員が来て手で片方をどかし、作業は5秒前後だった。譲る側の決まりはどのメーカーも公式に公開していなかった。そこで30秒動けなければ、手の空いた近いスタッフが助けに来る作りに変えた。",
  "kw": [
   [
    "現場",
    "実店舗",
    "げんば"
   ],
   [
    "人の助け",
    "店員",
    "てんいん"
   ],
   [
    "写す",
    "まねる",
    "再現"
   ]
  ],
  "key": false,
  "src": "LOG434"
 },
 {
  "id": "k156",
  "category": "sim",
  "sub": "",
  "type": "lesson",
  "front": "ロボットが同じ所で細かく揺れ続け、止まりとして数えられない。外から測る判定を足したら、列に並んでいるだけの台まで拾ってしまった。どうする？",
  "back": "外から数えず、ロボット自身に進み具合を見張らせる",
  "detail": "30秒で0.3m進めなければ止まりと扱う測り方では、並んでいるだけのロボットまで拾い、助けが増えた。実物の移動ロボットの標準部品にならい、走ろうとしている間だけ10秒で0.5m動けなければ止まる見張りをロボットの中に入れると、揺れ続けは180回で0回になった。",
  "kw": [
   [
    "自身",
    "ロボットの中",
    "じしん"
   ],
   [
    "見張り",
    "監視",
    "かんし"
   ],
   [
    "進み具合",
    "進捗",
    "しんちょく"
   ]
  ],
  "key": false,
  "src": "LOG440"
 },
 {
  "id": "k157",
  "category": "vision",
  "sub": "",
  "type": "lesson",
  "front": "シミュレーションの録画で、画像のAIが失敗の場面をよく見つけた。この結果を実際の店のカメラにも期待してよい？",
  "back": "実カメラに映らない表示を消して試し直す",
  "detail": "予備実験では長い止まりの8割前後を拾えたが、怪しいと見た所が画面に描かれた道の線の交わる所にも光っていた。線を消して撮り直すと、拾えた割合は72%から28%に落ちた。お客さんとすれ違った止まりは線なしでも17件中13件拾えたが、ロボットどうしの詰まりはほとんど拾えなかった。",
  "kw": [
   [
    "実カメラ",
    "本番の映像",
    "じっかめら"
   ],
   [
    "表示",
    "目印",
    "道の線"
   ],
   [
    "消す",
    "外す",
    "けす"
   ]
  ],
  "key": true,
  "src": "LOG418"
 },
 {
  "id": "k158",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "手描きの店の見取り図から、シミュレーション用の店の形を自動で起こしたら、通路が狭すぎてロボットがほとんど卓に届かない。何を足す？",
  "back": "寸法を1か所だけでも教えてもらう",
  "detail": "手描きでは家具が部屋に対して大きめに描かれ、家具の大きさから縮尺を決めると店が半分ほどの広さに読まれた。ふだんの道で届くのは18卓中3卓だった。店の幅を1か所教えるだけで、決まりの違反は0件になり、18卓すべてに届いた。",
  "kw": [
   [
    "寸法",
    "大きさ",
    "すんぽう"
   ],
   [
    "縮尺",
    "スケール",
    "しゅくしゃく"
   ]
  ],
  "key": false,
  "src": "LOG418"
 },
 {
  "id": "k159",
  "category": "eval",
  "sub": "",
  "type": "lesson",
  "front": "試算の前提の数字がプログラムの中に直接書いてあり、どれが実感でどれが仮置きか分からない。店の数字が届いたらどう直す？",
  "back": "前提を根拠つきの1つの表に出して管理する",
  "detail": "前提109個を1つの表に移し、値ごとに根拠の種類・出典・現場で測った回数・更新日を持たせた。内訳は仮置き79・決まり17・設定11・実感1・公開事例1。移すときに数字は変えず、600条件を回し直して前の版と全部の数字が同じことを確かめた。",
  "kw": [
   [
    "前提",
    "仮定",
    "ぜんてい"
   ],
   [
    "根拠",
    "出典",
    "こんきょ"
   ],
   [
    "表",
    "台帳",
    "ひょう"
   ]
  ],
  "key": false,
  "src": "LOG415"
 },
 {
  "id": "k160",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "忙しい店のスタッフに、スマホで作業時間を測ってもらいたい。測る項目や説明は細かく丁寧に並べるべき？",
  "back": "1画面に1つだけ出し、押し間違いはすぐ戻せる形にする",
  "detail": "前の観察シートは、止まった場所と原因を細かく選ぶ形で、1画面目から説明と設定がぎっしり並んでいた。使うのはロボットに詳しくないホールの人なので、目的を選ぶ、お店のこと、作業を選ぶ、スタート・ストップの1画面1つの流れに作り直した。",
  "kw": [
   [
    "1画面1つ",
    "ひとつずつ",
    "いちがめん"
   ],
   [
    "戻せる",
    "取り消し",
    "もどせる"
   ],
   [
    "使う人",
    "現場の人",
    "つかうひと"
   ]
  ],
  "key": false,
  "src": "LOG414"
 },
 {
  "id": "k161",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "卓で料理を受け渡す時間を、シミュレーションに1つの固定値で置いている。実物の動画を見て、どう直す？",
  "back": "客で変わる時間は、固定値でなく幅で置く",
  "detail": "実店舗の動画では、客が皿を取るだけなら10秒前後、客がロボットの耳で遊ぶと40秒近くまで延びた。卓で止まる時間を幅で置くこと、立ち往生を止まった時間でなく前に進んでいない時間で数えることを、ずれとして記録した。",
  "kw": [
   [
    "幅",
    "ばらつき",
    "はば"
   ],
   [
    "固定値",
    "一定",
    "こていち"
   ],
   [
    "客",
    "お客さん",
    "きゃく"
   ]
  ],
  "key": false,
  "src": "LOG400"
 },
 {
  "id": "k162",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "シミュレーションの結果をデモ動画にして見せたい。ロボットが店の中を走る映像を載せれば伝わる？",
  "back": "同じ客・同じ時刻で、直す前と後を並べる",
  "detail": "走るだけの映像はゲームの画面のように見え、判断の材料にならないと見立てた。丸テーブルの間を1.1mから2.1mに広げる前と後を、同じ種・同じ時刻・同じ画角で撮って左右に並べると、前は2台が約8分動けず、後は通り抜けた。違いは店の形のせいだと言い切れる。",
  "kw": [
   [
    "前と後",
    "ビフォーアフター",
    "まえとあと"
   ],
   [
    "並べる",
    "比較",
    "ならべる"
   ],
   [
    "同じ条件",
    "同じ種",
    "おなじじょうけん"
   ]
  ],
  "key": false,
  "src": "LOG404,LOG409"
 },
 {
  "id": "k163",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "通路の狭い店で、配膳ロボットが1台では足りない気がする。卓の数と混み具合から2台目を決めてよい？",
  "back": "台数は、店の1本道の通路で決める",
  "detail": "28卓・スタッフ2人の店で2台にすると、入店待ちは13.8分から25.1分に延び、ロボットを動かしに行く回数は13回から54回に増えた。1本道は1台ずつ入る決まりにすると不利は半分ほどに縮んだが、それでも1台には届かなかった。一方通行は混むと逆に悪くなった。",
  "kw": [
   [
    "1本道",
    "一本道",
    "いっぽんみち"
   ],
   [
    "通路",
    "道幅",
    "つうろ"
   ],
   [
    "台数",
    "何台",
    "だいすう"
   ]
  ],
  "key": false,
  "src": "LOG409,LOG423"
 },
 {
  "id": "k164",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "ロボットに仕事を増やしたら、スタッフの歩く距離が全条件で減った。これで店にとって良い変更と言える？",
  "back": "1つの指標の改善で、別の所が悪くなっていないか見る",
  "detail": "下げ膳にもロボットを使うと、歩く距離は12条件すべてで減った。ただ通路の細い店でロボット2台のときは、下げ膳のロボットが卓でスタッフを待つあいだに通路をふさぎ、卓が使えるまでの時間が③で4.1分から6.1分、⑦で4.5分から8.1分に延びた。",
  "kw": [
   [
    "別の所",
    "副作用",
    "べつのところ"
   ],
   [
    "指標",
    "数字",
    "しひょう"
   ],
   [
    "両方",
    "全体",
    "りょうほう"
   ]
  ],
  "key": false,
  "src": "LOG440"
 },
 {
  "id": "k165",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "強化学習で組立を学ばせたい。成否だけでは学びにくそうなので、近づいた・向きが合ったなど途中の進み具合にも点を付けたくなる。どうする？",
  "back": "成功の定義を決め、途中の点は安易に足さない",
  "detail": "AlphaNIST は、誤差1mm未満で組めたら1回だけ+1、それに毎ステップ動きの大きさ・関節速度・トルクの罰則を引くだけの報酬で、シミュレーションの中で、20種類の組立を1本のポリシーで98.2%成功させた。進捗の報酬を足すと17.8%まで落ちた。滑らかさを生んでいたのは、動きの大きさへの罰則と、置いた・掴んだ・途中まで組んだ状態から始めさせる練習の組み立て方だった。",
  "kw": [
   [
    "成功の定義",
    "成否",
    "せいひ",
    "成功"
   ],
   [
    "途中の点",
    "進捗",
    "しんちょく",
    "途中"
   ],
   [
    "罰則",
    "ばっそく",
    "ペナルティ"
   ]
  ],
  "key": false,
  "src": "DAY336"
 },
 {
  "id": "k166",
  "category": "society",
  "sub": "",
  "type": "lesson",
  "front": "協働ロボットなら柵なしで人の隣に置けると聞いた。製品が協働ロボットなら、それで安全と言える？",
  "back": "安全は製品名でなく、使い方ごとに評価する",
  "detail": "2013年の通達では、柵なしで使えるのは、使う事業者が危険の評価をして記録を残すか、ISO 10218 に合った設計と設置で技術資料と適合宣言書がある場合に限られる。ISO 10218 の2025年改訂も、ロボット単体ではなく使い方の単位で評価し、手先を変えたら評価し直すとしている。学習したモデルを更新したときに誰が確かめ直すかも、契約で決めておく論点になった。",
  "kw": [
   [
    "使い方",
    "つかいかた",
    "用途"
   ],
   [
    "評価",
    "ひょうか",
    "リスクアセスメント",
    "アセスメント"
   ],
   [
    "評価し直す",
    "再評価",
    "さいひょうか",
    "確かめ直す"
   ]
  ],
  "key": true,
  "src": "LOG375"
 },
 {
  "id": "k167",
  "category": "society",
  "sub": "",
  "type": "lesson",
  "front": "成功率の高いロボットを入れたのに、現場では面倒な機械として使われなくなった。導入の前に、現場で扱えるかを何で見極める？",
  "back": "止まった理由・戻し方・次の動きが分かるかを見る",
  "detail": "見極めの問いは3つ。止まったとき現場の人が理由を判断できるか、エンジニアを呼ばずにその場の人が再開できるか、次の動きが周りに読めて怖くないか。配膳ロボットは、ボタンでテーブル番号を押すだけ、止まったらどかせばいい形で導入後の手間を消して広がった。アイリスオーヤマが自動の充電や交換しやすい紙パックにこだわったのも、小さなことでロボットが使われなくなるのを防ぐためだった。",
  "kw": [
   [
    "止まった理由",
    "理由",
    "りゆう"
   ],
   [
    "戻し方",
    "元に戻す",
    "もどしかた",
    "再開"
   ],
   [
    "次の動き",
    "動きが読める",
    "予測",
    "よそく",
    "読みやすさ"
   ]
  ],
  "key": false,
  "src": "LOG389"
 },
 {
  "id": "k168",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "公開されている大きなロボット用モデルを、手元の実機で強化学習して伸ばしたい。練習を始める前に何を確かめる？",
  "back": "今の成功率を測る。0%に近ければどの手も効かない",
  "detail": "大きなモデルは凍結し、小さな部品だけを実機で学ぶ形がそろってきている。例では、入口の乱数を選ぶ方法で、トースターのスイッチが80回の練習で5/20から18/20に上がった。ただし小さな部品は元のモデルの近くを探すだけなので、成功率0%の作業は直せない。実験の元の成功率は20〜75%で、決めた条件で20回以上測ってから始めるよう整理した。",
  "kw": [
   [
    "成功率",
    "せいこうりつ"
   ],
   [
    "測る",
    "はかる",
    "計測",
    "測定"
   ],
   [
    "0%",
    "ゼロ",
    "れいパーセント"
   ]
  ],
  "key": false,
  "src": "LOG392"
 },
 {
  "id": "k169",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "配膳ロボットの動きを店に合わせて良くしたい。メーカーの製品に、どこまで手を入れてよい？",
  "back": "メーカーが開けた範囲で直し、中身は書き換えない",
  "detail": "触り方は5段階に分かれる。設定・公式の外部接続・外付けまではメーカーの想定の内側で、得が大きく損は小さい。ソフトの書き換えや機体の改造は外側で、改造した人が作り手としての責任を負い、法律・保証・保守もこの線で分かれる。長い目で見ると改造の主役は、自分の店のお手本で動きを学ばせ直す追加学習になると考えた。",
  "kw": [
   [
    "開けた範囲",
    "公式",
    "こうしき",
    "想定の内側"
   ],
   [
    "書き換えない",
    "改造しない",
    "かいぞう"
   ],
   [
    "責任",
    "せきにん",
    "作り手"
   ]
  ],
  "key": false,
  "src": "LOG406"
 },
 {
  "id": "k170",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "少量のお手本で学んだピッキングが、会場を変えたとたんに崩れた。データを大量に足す前に何を決める？",
  "back": "動かしてよい条件の範囲を先に決める",
  "detail": "少量のお手本で学んだモデルは、物そのものではなくその部屋での見え方を覚えるので、影や反射が変わるだけで別の環境になる。進め方は、条件の範囲を決める、センサー側で見え方の差を減らす、認識は学習済みモデルに任せる、条件違いのデータを撮る、現場の失敗を集めて広げる、の順。最初から全環境に強いモデルを狙うと、データ集めが終わらず現場に出られない。",
  "kw": [
   [
    "条件の範囲",
    "動作範囲",
    "じょうけん",
    "ODD"
   ],
   [
    "先に決める",
    "さきにきめる",
    "決める"
   ]
  ],
  "key": false,
  "src": "LOG412"
 },
 {
  "id": "k171",
  "category": "vision",
  "sub": "",
  "type": "lesson",
  "front": "奥行きの分からない上からのカメラ1台でも、学習したモデルがブロックを掴めている。この構成のまま別の物にも広げてよい？",
  "back": "成功を支えている前提が何かを先に確かめる",
  "detail": "上から見て苦手なのは高さの推定だが、ブロックは机の上で高さがほぼ一定なので、机の上の位置と向きが分かれば足りていた。条件がたまたま噛み合っていただけで、高さの違う物では同じようにいかない可能性がある。手首カメラがどこから要るようになるかは、研究室で実際に試して確かめることにした。",
  "kw": [
   [
    "前提",
    "ぜんてい",
    "条件"
   ],
   [
    "高さ",
    "奥行き",
    "おくゆき",
    "たかさ"
   ]
  ],
  "key": false,
  "src": "LOG412"
 },
 {
  "id": "k172",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "失敗をうまく手直しできた記録を学習に入れたい。でもそのままだと、失敗した動きまで覚えてしまいそう。どう記録する？",
  "back": "失敗・手直し開始・復帰の3点で区切って残す",
  "detail": "成功の手本だけでは、ずれた所から戻る動きを教えられない。手直しのデータには、戻り始めてからの部分で戻り方を覚えさせる使い方と、手直しが何度も起きる場所からお手本の弱い所を見つけて足す使い方がある。そのために、失敗した瞬間・手直しを始めた瞬間・正しい道に戻った瞬間の3つを1回ごとに残すと整理した。",
  "kw": [
   [
    "失敗",
    "しっぱい"
   ],
   [
    "手直し",
    "修正",
    "しゅうせい",
    "てなおし"
   ],
   [
    "戻った",
    "復帰",
    "ふっき",
    "もどった"
   ]
  ],
  "key": true,
  "src": "LOG424"
 },
 {
  "id": "k173",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "学習も推論もクラウドのGPUに頼っている。外のサービスが止まったときに備えたい。上位の機材を買い足すべき？",
  "back": "買う前に、手元だけで推論まで回せるかを確かめる",
  "detail": "研究の段階なら、実機の制御・Unity・データ集め・記事づくりは手元で回るので、クラウドが止まっても影響は小さい。止まるのは学習したモデルで機体を動かすところ。将来への不安は上位の機材を買う理由にしないと決めているので、ローカルの強化は買い物ではなく作りの話として持ち、データと学習済みモデルの原本は手元に置いておく。",
  "kw": [
   [
    "手元",
    "ローカル",
    "てもと"
   ],
   [
    "推論",
    "すいろん"
   ],
   [
    "確かめる",
    "たしかめる",
    "試す"
   ]
  ],
  "key": false,
  "src": "LOG433"
 },
 {
  "id": "k174",
  "category": "principles",
  "sub": "",
  "type": "lesson",
  "front": "話題の新しい手法を、自分のロボットでも試してみたい。試すかどうかを何で決める？",
  "back": "今の困りごとに効き、増える手間より得が大きいか",
  "detail": "3D の世界モデルは、奥行きつきのカメラ5台の点群が前提だった。手元のアームはスマホ2台で奥行きが取れず、今の困りごとである届かない・落とす・待ちが長いにも効かない。入れる基準は、カメラを増やさずに今より動きが良くなるかどうかとし、動向を押さえて、試すきっかけが来たら借りた GPU で試すことにした。",
  "kw": [
   [
    "困りごと",
    "課題",
    "かだい",
    "こまりごと"
   ],
   [
    "手間",
    "てま",
    "コスト"
   ]
  ],
  "key": false,
  "src": "LOG438"
 },
 {
  "id": "k175",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "配膳ロボットが店でたびたび止まる。もっと賢い機種に替える前に、止まり方をどう見る？",
  "back": "見る・位置を知る・決める・動くのどこで崩れたか分ける",
  "detail": "観察してきた止まり方を4段に当てはめると、狭い通路で向かい合うのは決める、前をふさがれて回っても抜けられないのは動く、迷子は位置を知る、下げた皿を載せたままなのは4段の外の運用だった。決めるで止まったなら手で寄せて約5秒、位置を知るなら決まった場所まで押して戻す、運用なら人の配置と、段ごとに直し方が違う。直す作業は5秒前後なのに、人が来るまでに18〜53秒以上かかっていた。",
  "kw": [
   [
    "4段",
    "よんだん",
    "段"
   ],
   [
    "どこで崩れた",
    "崩れた段",
    "くずれた"
   ],
   [
    "直し方",
    "なおしかた",
    "対処"
   ]
  ],
  "key": true,
  "src": "LOG439"
 },
 {
  "id": "k176",
  "category": "service",
  "sub": "",
  "type": "lesson",
  "front": "同じモデルで動くロボットを、遠隔の1人で何台も見ている。見慣れない荷物が入ると、呼び出しが一斉に重なる。どうする？",
  "back": "1台目を合図に他を待たせ、1回直して全台に返す",
  "detail": "同じモデル・同じデータで動く群れは、苦手な場面も全台同じなので、同じ所で一斉に詰まる。裏を返せば、1回直せば全台に効く。呼び出しの重なりは事前の予測が難しいので、最初は1人あたりの台数を少なく始めて呼び出しの回数と対応時間を測り、重なっても困らない作りにしてから台数を増やす。",
  "kw": [
   [
    "一斉",
    "いっせい",
    "同時"
   ],
   [
    "全台",
    "ぜんだい",
    "群れ",
    "フリート"
   ],
   [
    "少なく始めて測る",
    "測りながら広げる",
    "はかる"
   ]
  ],
  "key": false,
  "src": "LOG441"
 },
 {
  "id": "k177",
  "category": "society",
  "sub": "",
  "type": "lesson",
  "front": "フィジカルAIという言葉を、人によってばらばらの意味で使っている。自分の発信でこの言葉を使うとき、どう線を引く？",
  "back": "何に働きかけるかでなく、どう作るかで線を引く",
  "detail": "尾形哲也教授は、生成AIと同じやり方で、人が見せた動きのデータからロボットの動きを直接作ることと定義した。この線だと、画像認識を足しただけの産業用ロボットは昔からある側に入る。AIが現実に働きかける分野全体は関わる領域として分けて呼び、配膳ロボットは今は境目にいると整理した。定義に入るかと、現場で役に立つかは別の問いとして扱う。",
  "kw": [
   [
    "作り方",
    "どう作るか",
    "つくりかた"
   ],
   [
    "線を引く",
    "線引き",
    "せんをひく",
    "定義"
   ]
  ],
  "key": false,
  "src": "LOG442"
 },
 {
  "id": "k178",
  "category": "learning",
  "sub": "",
  "type": "lesson",
  "front": "学習したロボットが、指示と関係ない物にまで手を伸ばす。モデルを大きくしたり手法を替えたりする前に何をする？",
  "back": "学習データを場面ごとに数え、足りない場面を探す",
  "detail": "動画の作者は、ゴミ用の100本のうち、靴下が見えていて手を出さない場面が7本しかなく、枠の付いた物をつかむ場面が70本あることを数えて突き止めた。ロボットは、何かあれば取りに行くと覚えていた。データをそろえ、枠で目標を指し、足りない場面を埋めたうえで LoRA を当て、最後は50回ほぼ全部成功し、別の部屋でも動いた。",
  "kw": [
   [
    "数える",
    "かぞえる",
    "棚卸し"
   ],
   [
    "学習データ",
    "データ",
    "お手本"
   ],
   [
    "足りない場面",
    "偏り",
    "かたより"
   ]
  ],
  "key": false,
  "src": "LOG443"
 }
]
```


## 作業ログ ― ロボティクス 声の単語帳の最初の画面を、分野の一覧にした

### 分野を選んでから、その中で3つの段階を練習する形に変えた

前の作りでは、段階（1 単語・2 説明・3 判断の型）を選び、その下の黒い選択肢で分野を選んでいました。分野は選べても、どの分野がどれだけ言えているかは見えませんでした。そこで順番を入れ替え、まず分野を選び、分野の中で段階を選ぶ形にしました。

- ねらい：今日はこの分野だけ、という練習を1回の操作で始められるようにする。手薄な分野を、練習の前に見て選べるようにする
- 変わったこと：開くと10の分野がカードで並ぶ。カードには、単語と判断の型の枚数、言えた数（最新の一致が80%以上のカード×段階の数）、苦手の数、進み具合の棒が出る。分野を押すと、見出しがその分野の名前になり、その中だけで3つの段階を練習する。一覧の下に、分野をまたぐ まず覚える・核心 と 全部 を置いた
- 見込める効果：練習の記録に、分野ごとの回数が残るようになる。分野がかたよっていないか、言えた数が増えたかは、次に練習記録を集計するときに分野ごとに数えて確かめる。今はまだ見込み

言えた数は、単語は 1 単語 と 2 説明 の2回分、判断の型は 3 判断の型 の1回分で数えます。たとえば運動学・座標系は単語18・判断の型12なので、満点は 18×2＋12＝48 です。引き継いだ記録で、運動学・座標系は48のうち16が言えた状態から始まります。新しい分野の 配膳ロボットの導入 は、まだ0です。

分野を開くと、URL の後ろに #service のように分野の名前が付きます。ブラウザの戻る（iPhone では右へのスワイプ）で一覧に戻れて、読み込み直しても同じ分野が開きます。分野の中のカードからは、分野の名前の表示を外しました。見出しに出ているので、カードごとに同じ名前を出す必要がないためです。

![スマホで見た最初の画面。10の分野のカードが1列に並ぶ](https://pixelstomotors.com/media/LOG445_03_category_list_phone_v2.png)

![スマホで分野 配膳ロボットの導入 を開き、3 判断の型 を選んだところ。見出しが分野の名前になり、配膳ロボットで料理は早く届くのにスタッフを1人減らすと入口の待ちが延びた場面の問いと、教訓、35卓のファミレスでの実例が出ている](https://pixelstomotors.com/media/LOG445_04_service_lesson_phone_v2.png)

![同じ分野の 1 単語。稼働率（利用率）のカードに、まず覚える の印が付いている](https://pixelstomotors.com/media/LOG445_05_service_word_phone_v2.png)

### スマホ幅の画面で、分野の切り替えと戻るを確かめた

公開の前に、クラウドの作業環境のブラウザ（Playwright の Chromium）で、iPhone の画面の大きさにして動きを確かめました。分野を押す → 3 判断の型 → 戻る → まず覚える・核心 → ブラウザの戻る、の順に操作し、画面に出る枚数と URL、エラーが出ないかを見ました。

```javascript
const {chromium,devices}=require('playwright');
(async()=>{const b=await chromium.launch();const c=await b.newContext({...devices['iPhone 13']});const p=await c.newPage();
const errs=[];p.on('pageerror',e=>errs.push(e.message));p.on('console',m=>{if(m.type()==='error')errs.push(m.text())});
await p.goto('file:///home/claude/work/build/ロボティクス復習.html');await p.waitForTimeout(500);
const S='/tmp/claude-0/-home-claude/9c16c0ef-d68a-5917-bf07-87e3620da361/scratchpad/';
await p.screenshot({path:S+'home.png',fullPage:true});
await p.click('.cat[data-k="service"]');await p.waitForTimeout(300);
await p.screenshot({path:S+'cat1.png'});
await p.click('#s3');await p.waitForTimeout(300);await p.screenshot({path:S+'cat3.png'});
console.log('info',await p.textContent('#info'),'hash',await p.evaluate(()=>location.hash));
await p.click('#back');await p.waitForTimeout(300);
console.log('home visible',await p.isVisible('#home'),'study',await p.isVisible('#study'));
await p.click('.cat[data-k="key"]');await p.click('#s1');console.log('key',await p.textContent('#info'));
await p.goBack();await p.waitForTimeout(200);console.log('after back home',await p.isVisible('#home'));
console.log('errs',errs);await b.close();})();
```

```text
info 15枚（核心 2）・場面を見て、教訓を自分の言葉で hash #service
home visible true study false
key 66枚・用語から続けて言う
after back home true
errs []
```

最初に撮った画面では、分野を開いたときにページの見出し ロボティクスの見直し と、その下の小さな行の ロボティクスの見直し、さらに分野の名前が大きく並び、カードとマイクのボタンが画面の下へ押し出されていました。分野の名前をページの見出しに入れ、本文の中の分野の見出しはやめました。その分だけカードとマイクのボタンが上に上がりました。


## 作業ログ ― 分野のカードを、2列・サイトと同じ見た目にした

公開したあと、PC で開いた画面を見ながら5回直しました。

![公開した直後の PC の画面。分野のカードが横に長い1列で縦に並び、1画面に6つしか入らない](https://pixelstomotors.com/media/LOG445_06_one_column_first.png)

> カード1カラムで縦並びは見づらいね。2カラムに変更

PC の幅では2列にしました。スマホの幅（560px 未満）では、2列にすると分野の名前が3行に折れるので1列のままです。2列にすると、シミュレーション・デジタルツイン のような長い名前のカードだけ高さが伸び、進み具合の棒の位置が段ごとにずれました。カードの中を、枚数の行と、言えた数の行の2行に分け、棒をカードの下の端にそろえました。PC では一覧の幅を広げ（720px から 960px）、分野の名前が1行に収まるようにしました。

![2列にした直後の画面。カードどうしの間隔が10pxで詰まって見える](https://pixelstomotors.com/media/LOG445_07_two_columns_first.png)

> カード間のギャップが狭いのでもう少し広げて

カードの間隔を、縦横とも10pxから20pxにしました。

> カードをマウスオーバーしてもハンドが出ないけど？

カードは押せるボタンなのに、マウスの形が矢印のままでした。前の作りは iPhone で指で押す前提だったので、マウスの形を決めていませんでした。カードだけでなく、ページの中の押せるもの全部（段階・言い方・出す範囲の切り替え、戻る、マイク、保存）を、乗せると指の形にしました。

> カードのUIデザインを合わせてほしい（カードにはドリップシャドウをつけて、枠線はなし）

pixelstomotors.com のシリーズのカードの作り（p2m-site の RecordCard.astro）を見ると、枠線はなく、影は 0 8px 16px rgba(0,0,0,.08)、角は 8px の丸みでした。分野のカードを同じ値にしました。ただ、黒い背景のダークモードでは、この薄い影はほとんど見えません。ダークモードのときだけ、影の濃さを .45 にしました。

> もう少しマウスオーバーがわかりやすいように改良してください

マウスを乗せたときに、カードが4px 浮き、影が大きく濃くなり（0 16px 32px）、右の矢印が少し右へ動くようにしました。ダークモードでは影だけだと変化が小さかったので、カードの地も少し明るくしました（#1c1d22 → #23242a）。最初は進み具合の棒の地も濃くしましたが、棒の値が変わったように見えたので外しました。押した瞬間は、浮いたカードが少し沈みます。

- ねらい：PC で、どれが押せるかと、どれを押そうとしているかを、見ただけで分かるようにする
- 変わったこと：枠線をなくし、サイトのカードと同じ影と角の丸みにした。乗せると浮いて影が濃くなり、矢印が動く。指の形のマウスになる
- 見込める効果：一覧の見た目が pixelstomotors.com と同じになり、サイトから来たときに別のものに見えない。押しまちがいが減るかは測っていない

![ダークモードの PC の画面で、実機・サーボ制御 のカードにマウスを乗せたところ。そのカードだけ少し上に浮き、地が明るく、影が濃い](https://pixelstomotors.com/media/LOG445_02_category_hover_dark_v2.png)

5回の直しはどれも、ページの形（_tools/template.html）の見た目の決まり（CSS）だけを変え、データは変えていません。直すたびに書き出し、スマホの幅の動きの確かめ（上のスクリプト）を通してから公開しました。

| 時刻 | 変えたこと | robotics-review の記録 |
|---|---|---|
| 8:28 | カードを足し、最初の画面を分野の一覧にした | b4a2ac7 |
| 8:31 | PC の幅で2列、カードの中を2行に | a8bb768 |
| 8:34 | カードの間隔を20pxに | 0e5e21a |
| 8:36 | 押せるもの全部を指の形のマウスに | 78e8f2b |
| 8:38 | 枠線をなくし、サイトのカードの影と角の丸みに | 77fcb2b |
| 8:40 | 乗せると浮く・矢印が動く・ダークモードで地が明るくなる | e30ce25 |


## 作業ログ ― ページの名前を ロボティクス 声の単語帳 にし、見出しをパンくずにした

公開した後、他の人も見る前提で名前を考え直しました。

> ここをパンクズにした方が使いやすいかな
> それとヘッダーの右にPTMのロゴを追加してほしい

分野を開いたときのヘッダーは、大きな分野の名前の下に、小さくページの名前が出ていました。どこにいて、どこへ戻れるかが、ひと目では分かりません。上の行をパンくず（今いる場所までの道筋を、上の階層から並べた表示）にし、ページの名前 › の形にしました。ページの名前を押すと分野の一覧に戻ります。本文の中にあった戻るのボタンは、パンくずと役目が同じなので外しました。

ヘッダーの右には、pixelstomotors.com のフッターと同じ白いロゴ（p2m-site の public/site/pixelstomotors-logo-white.svg）を、ページの中に埋め込んで置きました。押すと pixelstomotors.com が新しいタブで開きます。スマホの幅（420px 以下）では、ロゴを少し小さくしています。

名前は、他の人も見る前提で5つの候補を出し、エイイチが ロボティクス 声の単語帳 を選びました。

| 候補 | 伝わること |
|---|---|
| 声で覚えるロボティクス | 声に出すことと分野。いちばん短い |
| ロボティクスを声で覚えるカード | 1つ目に、カードの形を足した |
| 実装ログから学ぶロボティクス | 実際の開発の記録から作ったこと。声で練習することは伝わらない |
| ロボティクス 声の単語帳（選んだ名前） | だれでも知っている単語帳の形 |
| Robotics Speak Drill（声で覚えるロボティクス） | 英語と日本語を並べる、サイトの見出しの形 |

見出し・パンくず・ブラウザのタブを新しい名前にし、分野を開いたときは、タブに 運動学・座標系｜ロボティクス 声の単語帳 のように分野の名前も出るようにしました。URL（pixelstomotors.github.io/robotics-review/）と iCloud のフォルダの名前（ロボティクス復習）は変えていません。前に共有したリンクが切れないようにするためです。

- ねらい：初めて開いた人が、何のページで、今どこにいて、どこへ戻れるかを、ヘッダーだけで分かるようにする
- 変わったこと：名前が ロボティクス 声の単語帳 になった。分野の中では ロボティクス 声の単語帳 › 分野の名前 と出る。右に Pixels to Motors のロゴが出る
- 見込める効果：ロゴから pixelstomotors.com へ移れるので、このページだけを開いた人が、元の研究の記録へたどれる。ロゴが押されたかは、まだ測る仕組みがない

![PC で分野 運動学・座標系 を開いたところ。ヘッダーの上の行に ロボティクス 声の単語帳 › のパンくず、その下に大きく分野の名前、右に白い Pixels to Motors のロゴが出ている](https://pixelstomotors.com/media/LOG445_08_breadcrumb_logo_pc.png)

| 時刻 | 変えたこと | robotics-review の記録 |
|---|---|---|
| 9:00 | 見出しをパンくずに、右にロゴ | a8e7b3b |
| 9:02 | ページの名前を ロボティクス 声の単語帳 に | 8be363f |


## 作業ログ ― 作り直したロボティクス 声の単語帳のコードと公開

### ページの形 _tools/template.html（全文）

データ（/*DATA*/null）と練習記録の集計（/*PROGRESS*/{}）は、書き出すときに build_review.py が埋め込みます。

```html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="robots" content="noindex, nofollow">
<title>ロボティクス 声の単語帳</title>
<style>
/* 仕事で使っている「台本読み合わせ」ページと同じ見た目：横スワイプのカード＋その下に「押している間だけ聞く」 */
:root{
  --bg:#f2f3f5; --fg:#1d2129; --muted:#6b7280; --panel:#ffffff; --line:#e3e5e9;
  --card:#1a1b1f; --card-fg:#ffffff; --card-sub:#a9adb6; --gold:#f2c14e; --time:#c98a12;
  --miss:#fde2e1; --miss-fg:#b42318; --extra:#dbeafe; --extra-fg:#1e40af; --ok:#15803d;
  --btn:#1a1b1f; --btn-fg:#ffffff; --rec:#b42318;
  --sh:0 8px 16px rgba(0,0,0,.08); --sh-hover:0 16px 32px rgba(0,0,0,.18); --panel-hover:#ffffff;
  --font:-apple-system,"Hiragino Sans","Hiragino Kaku Gothic ProN",sans-serif;
}
@media (prefers-color-scheme: dark){:root:not([data-theme="light"]){
  --bg:#111215; --fg:#e8eaee; --muted:#9aa0aa; --panel:#1c1d22; --line:#2c2e35;
  --card:#000000; --miss:#4a1714; --miss-fg:#ffb4ab; --extra:#13284d; --extra-fg:#bcd3ff; --ok:#4ade80;
  --btn:#e8eaee; --btn-fg:#111215; --sh:0 8px 16px rgba(0,0,0,.45); --sh-hover:0 16px 32px rgba(0,0,0,.8); --panel-hover:#23242a; color-scheme:dark}}
:root[data-theme="dark"]{
  --bg:#111215; --fg:#e8eaee; --muted:#9aa0aa; --panel:#1c1d22; --line:#2c2e35;
  --card:#000000; --miss:#4a1714; --miss-fg:#ffb4ab; --extra:#13284d; --extra-fg:#bcd3ff; --ok:#4ade80;
  --btn:#e8eaee; --btn-fg:#111215; --sh:0 8px 16px rgba(0,0,0,.45); --sh-hover:0 16px 32px rgba(0,0,0,.8); --panel-hover:#23242a; color-scheme:dark}
*{box-sizing:border-box}
[hidden]{display:none!important}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--bg);color:var(--fg);font:18px/1.7 var(--font);padding:0 0 calc(24px + env(safe-area-inset-bottom))}
header{background:#1a1b1f;color:#fff;padding:calc(12px + env(safe-area-inset-top)) 16px 12px}
header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
header .hl{min-width:0}
header h1{margin:0;font-size:21px}
.crumb{display:flex;align-items:center;gap:8px;font-size:14px;margin:0 0 2px}
.crumb a{color:rgba(255,255,255,.75);text-decoration:underline;text-underline-offset:3px;text-decoration-color:rgba(255,255,255,.35)}
.crumb a:hover{color:#fff;text-decoration-color:#fff}
.crumb span{color:rgba(255,255,255,.5)}
.logo{flex:0 0 auto;display:block;margin-top:6px;opacity:.9}
.logo:hover{opacity:1}
.logo .ptm{display:block;height:17px;width:auto}
@media (max-width:420px){.logo .ptm{height:13px}}
header p{margin:2px 0 0;font-size:14px;color:rgba(255,255,255,.75)}
main{padding:12px 16px 0;max-width:720px;margin:0 auto}
.bar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin:0 0 10px}
.selwrap{position:relative;display:block;width:100%}
select{-webkit-appearance:none;appearance:none;display:block;width:100%;font:inherit;font-size:16px;font-weight:600;background:var(--btn);color:var(--btn-fg);border:0;border-radius:10px;padding:10px 44px 10px 14px;margin:0}
.selwrap::after{content:"";position:absolute;right:19px;top:50%;width:9px;height:9px;border-right:2.75px solid var(--btn-fg);border-bottom:2.75px solid var(--btn-fg);border-radius:1px;transform:translateY(-70%) rotate(45deg);pointer-events:none}
.seg{display:inline-flex;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--panel)}
.seg button{border:0;background:transparent;color:var(--fg);font:inherit;font-size:15px;padding:8px 12px}
.seg button[aria-pressed="true"]{background:var(--btn);color:var(--btn-fg)}
.seg.step{display:flex;margin:0 0 8px}.seg.step button{flex:1;font-size:16px;font-weight:600;padding:10px 6px}
.card .w{font-size:27px;line-height:1.5;font-weight:700;margin:8px 0 2px}
.card .w.small{font-size:20px;margin-bottom:0}
.info{font-size:14px;color:var(--muted);margin:0 2px 8px}
.track{display:flex;align-items:flex-start;overflow-x:auto;overflow-y:hidden;transition:height .15s;scroll-snap-type:x mandatory;gap:10px;padding:4px 2px 10px;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.track::-webkit-scrollbar{display:none}
.card{flex:0 0 88%;scroll-snap-align:center;background:var(--card);color:var(--card-fg);border-radius:14px;padding:16px 16px 12px;min-height:250px;display:flex;flex-direction:column}
.card .sec{font-size:13.5px;color:var(--card-sub)}
.card .lb{font-size:13.5px;font-weight:700;color:var(--gold);margin-top:2px}
.card .q{font-size:15px;color:var(--card-sub);margin-top:6px}
.card .t{font-size:25px;line-height:1.75;font-weight:600;flex:1;margin:8px 0}
.card .t.long{font-size:20px}
.card .t.mid{font-size:22px}
.card .t.hide{color:var(--card-sub)}
.card .b{font-size:14.5px;line-height:1.7;color:var(--card-sub);margin:0 0 8px}
.card .m{font-size:13.5px;color:var(--time);display:flex;justify-content:space-between;gap:8px}
.card .m .done{color:#9be3b2}.card .m .done.low{color:#ffb4ab}.card .m .done small{color:#a9adb6;font-size:12.5px;margin-left:4px}
.pos{font-size:14px;color:var(--muted);text-align:center;margin:0 0 8px;font-variant-numeric:tabular-nums}
.mic{display:block;width:100%;border:0;border-radius:16px;padding:18px 12px;font:inherit;font-size:20px;font-weight:700;background:var(--btn);color:var(--btn-fg);-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:manipulation}
.mic.on{background:var(--rec);color:#fff}
.mic:disabled{opacity:.45}
.mic small{display:block;font-size:13.5px;font-weight:400;opacity:.8}
.res{background:var(--panel);border-radius:14px;padding:12px 14px;margin:12px 0 0;border:1px solid var(--line)}
.res h2{font-size:15px;margin:0 0 6px;color:var(--muted);font-weight:600}
.res .said{font-size:19px;line-height:1.8;min-height:1.8em;word-break:break-all}
.res .cmp{font-size:19px;line-height:1.8;word-break:break-all}
.res .miss{background:var(--miss);color:var(--miss-fg);border-radius:3px}
.res .kw{display:inline-block;border-radius:6px;padding:0 8px;margin:2px 6px 2px 0;background:var(--extra);color:var(--extra-fg)}
.res .kw.miss{background:var(--miss);color:var(--miss-fg)}
.res .lesson{margin-top:6px}
.res .extra{background:var(--extra);color:var(--extra-fg);border-radius:3px}
.stats{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:15px;margin-top:8px;font-variant-numeric:tabular-nums}
.stats b{font-size:20px}
.stats .good{color:var(--ok)}.stats .bad{color:var(--miss-fg)}
.row{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
.row #save{background:var(--btn);color:var(--btn-fg);border-color:var(--btn);font-weight:700}
.row button{border:1px solid var(--line);background:var(--panel);color:var(--fg);font:inherit;font-size:15px;border-radius:10px;padding:8px 12px}
.log{margin:18px 0 0}
.log h2{font-size:18px;margin:0 0 8px;padding-left:10px;border-left:4px solid var(--fg)}
.log table{width:100%;border-collapse:collapse;font-size:15px;background:var(--panel);border-radius:12px;overflow:hidden;font-variant-numeric:tabular-nums}
.log th,.log td{padding:7px 6px;border-bottom:1px solid var(--line);text-align:right}
.log th:first-child,.log td:first-child{text-align:left}
.log th{font-size:13px;color:var(--muted)}
.note{font-size:13px;color:var(--muted);margin:14px 2px 0}
.warn{background:var(--miss);color:var(--miss-fg);border-radius:10px;padding:10px 12px;font-size:15px;margin:0 0 10px}
button:focus-visible,select:focus-visible{outline:3px solid var(--time);outline-offset:2px}
/* カテゴリの一覧（最初の画面） */
.cats{display:flex;flex-direction:column;gap:10px;margin:4px 0 0}
.cat{display:block;width:100%;text-align:left;border:0;background:var(--panel);color:var(--fg);font:inherit;border-radius:8px;box-shadow:var(--sh);padding:14px 44px 14px 16px;position:relative}
.cat::after{content:"";position:absolute;right:20px;top:50%;width:9px;height:9px;border-right:2.75px solid var(--fg);border-top:2.75px solid var(--fg);border-radius:1px;transform:translateY(-50%) rotate(45deg)}
button,select{cursor:pointer}button:disabled{cursor:default}
@media (hover:hover){.cat{transition:box-shadow .18s,transform .18s,background-color .18s}.cat::after{transition:right .18s}.cat:hover{box-shadow:var(--sh-hover);transform:translateY(-4px);background:var(--panel-hover)}.cat:hover::after{right:14px}.cat:active{transform:translateY(-1px)}}
@media (prefers-reduced-motion:reduce){.cat,.cat::after{transition:none}}
.cat .nm{display:block;font-size:18px;font-weight:700;line-height:1.5}
.cat .ct{display:block;font-size:14px;color:var(--muted);margin-top:2px;font-variant-numeric:tabular-nums}
.cat .ct b{color:var(--fg);font-size:16px}
.cat .ct .weak{color:var(--miss-fg)}
.cat .meter{display:block;height:6px;border-radius:3px;background:var(--line);margin-top:10px;overflow:hidden}
.cat .meter i{display:block;height:100%;background:var(--fg);border-radius:3px}
.cat.dark{background:var(--btn);color:var(--btn-fg);border-color:var(--btn)}
.cat.dark::after{border-color:var(--btn-fg)}
.cat.dark .ct{color:inherit;opacity:.75}.cat.dark .ct b{color:inherit}
.homeh{font-size:15px;color:var(--muted);font-weight:600;margin:18px 2px 8px}
@media (min-width:560px){.cats{display:grid;grid-template-columns:1fr 1fr;gap:20px}.homeh{grid-column:1/-1;margin:12px 2px -6px}.cat{display:flex;flex-direction:column}.cat .meter{margin-top:auto}.cat .ct+.ct{margin-bottom:10px}main:has(#home:not([hidden])){max-width:960px}}
.ctitle{font-size:22px;font-weight:700;margin:0 0 10px;line-height:1.4}
</style>
</head>
<body>
<header>
  <div class="hl">
    <nav class="crumb" id="crumb" aria-label="パンくず" hidden><a href="./" id="back">ロボティクス 声の単語帳</a><span aria-hidden="true">›</span></nav>
    <h1 id="h1">ロボティクス 声の単語帳</h1>
    <p id="sub"></p>
  </div>
  <a class="logo" href="https://pixelstomotors.com/" target="_blank" rel="noopener" aria-label="Pixels to Motors（pixelstomotors.com を開く）"><svg class="ptm" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2146 227" role="img" aria-label="Pixels to Motors"><path fill="#ffffff" fill-rule="evenodd" d="M112.78 209.75L112.5 196.5L99.22 196.22L85.94 195.94L86.22 183.22L86.5 170.5L99.78 170.22L113.05 169.94L112.78 155.22L112.5 140.5L99.5 140.5L86.5 140.5L86.5 113L86.5 85.5L99.5 85L112.5 84.5L112.78 71.28L113.06 58.06L125.78 57.78L138.5 57.5L138.5 44.5L138.5 31.5L125.75 31.22L113 30.94L113 18.08C113 7.74 113.29 5.1 114.46 4.66C116.77 3.77 138.58 5.84 145.34 7.58C153.76 9.76 167.03 15.35 175.48 20.28C183.61 25.02 201.04 41.49 207.44 50.48C219.85 67.9 227.92 93.14 227.98 114.76C228.03 131.31 221.93 153.84 213.74 167.34C208.59 175.84 199.91 187.14 194.59 192.27C189.66 197.02 177 206.42 170.88 209.88C157.95 217.17 135.84 223 121.12 223L113.06 223L112.78 209.75ZM60 210L60 198L71 198L82 198L82 210L82 222L71 222L60 222L60 210ZM672 210.35C648.29 207.18 630.41 194.35 622.75 175C618.26 163.66 617.62 160.03 617.56 145.5C617.49 128.97 618.93 122.39 625.73 108.08C634.59 89.44 650.56 77.75 671.87 74.3C711.12 67.95 735.25 91.06 735.45 135.16C735.5 146.17 735.36 146.98 733.08 149.41L730.65 152L689.4 152C642.65 152 644.38 151.71 646.27 159.17C647.94 165.76 653.14 174.12 657.74 177.63C664.51 182.79 672.14 185.2 683.5 185.74C694.69 186.27 701.55 184.93 713.44 179.88C717.25 178.26 720.89 177.13 721.51 177.37C723.28 178.04 731.25 194.45 730.85 196.59C730.34 199.36 717.26 205.53 706.5 208.06C697.69 210.14 679.47 211.35 672 210.35ZM857.83 209.98C847.56 208.24 832.58 202.74 828.78 199.3C827.8 198.42 827 196.69 827 195.46C827 192.99 834.28 177.84 836.25 176.21C837.15 175.47 840.21 176.51 847.18 179.94C858.28 185.39 864.99 187.01 876.42 186.98C888.96 186.94 896.6 182.4 897.68 174.35C898.68 166.86 893.85 163.22 870.19 153.64C852.9 146.63 845.54 142.49 839.52 136.36C828.3 124.95 826.87 105.4 836.24 91.38C840.39 85.17 849.94 78.71 859.34 75.75C869.07 72.7 893.7 72.5 904.5 75.39C913.38 77.76 920.68 81.41 921.46 83.86C922.58 87.39 913.84 108.61 911.67 107.62C896 100.52 883.16 97.21 876.19 98.46C868.83 99.79 865.02 101.35 862.04 104.26C859.59 106.66 859 108.07 859 111.52C859 115.16 859.52 116.25 862.53 118.89C866.99 122.81 870.65 124.65 885.41 130.41C908.5 139.42 917.74 145.94 922.83 156.81C925.17 161.79 925.5 163.74 925.49 172.5C925.48 181.41 925.17 183.12 922.68 188.21C917.66 198.48 906.01 206.41 891.41 209.5C884.08 211.05 865.76 211.31 857.83 209.98ZM1156.16 209.56C1137.52 206.01 1120.64 193.17 1113.11 176.84C1108.09 165.95 1106.54 158.05 1106.66 144C1106.99 103.16 1130.66 76.51 1169.31 73.44C1194.76 71.42 1217.33 81.81 1229.19 101C1244.66 126.01 1243.13 160.84 1225.49 185.57C1220.66 192.33 1212.31 199.28 1203.84 203.58C1191.74 209.72 1170.77 212.35 1156.16 209.56ZM1600.2 209.5C1592.4 208.08 1580.34 202.87 1574.29 198.3C1555.95 184.44 1547.48 161.03 1550.93 133.68C1554.72 103.55 1571.65 83.11 1598.86 75.81C1609.18 73.04 1628.71 72.82 1637.91 75.37C1659.65 81.38 1674.11 95.75 1680.61 117.83C1682.74 125.08 1683.17 128.55 1683.18 139C1683.21 165.01 1674.8 184.47 1658.11 197.02C1642.72 208.59 1621.01 213.27 1600.2 209.5ZM1849.5 209.52C1819.86 204.03 1801.05 181.34 1799.32 149C1797.38 112.66 1816.12 84.33 1847.7 75.85C1858.11 73.05 1877.54 72.83 1887 75.4C1912.45 82.32 1928.16 101.62 1932.05 130.75C1934.17 146.62 1930.26 166.31 1922.28 179.96C1908.72 203.13 1879.36 215.06 1849.5 209.52ZM2070 209.05C2054.77 205.8 2043 200.04 2043 195.83C2043 193.43 2050.57 177.6 2052.47 176.02C2053.47 175.2 2055.37 175.79 2060.21 178.45C2070.47 184.09 2077.39 186.1 2088.73 186.72C2100.85 187.39 2106.14 185.89 2110.95 180.4C2116.32 174.28 2114.52 167.88 2105.9 162.44C2103.78 161.1 2096.3 157.72 2089.28 154.92C2059.7 143.13 2049.02 134.18 2046.5 119.04C2043.04 98.33 2054.16 82.15 2076.5 75.39C2085.19 72.76 2110.68 72.76 2120.5 75.39C2128.86 77.62 2136.86 81.47 2137.7 83.66C2138.86 86.67 2129.73 108.55 2127.7 107.63C2118.17 103.33 2108.27 99.89 2102.74 98.96C2088.17 96.5 2075 102.21 2075 110.98C2075 118.25 2080.03 122 2100.56 130.06C2124.9 139.62 2133.64 145.72 2138.82 156.78C2141.17 161.79 2141.5 163.74 2141.5 172.5C2141.5 181.43 2141.2 183.11 2138.69 188.21C2133.65 198.49 2122.01 206.41 2107.41 209.5C2097.97 211.5 2080.52 211.29 2070 209.05ZM283.62 208.71C281.86 207.42 281.79 203.64 281.94 116.43C282.07 38.35 282.3 25.29 283.55 24.03C284.79 22.79 290.71 22.61 323.25 22.86C362.91 23.16 368.98 23.69 379.64 27.8C394.35 33.46 403 43.37 407.75 60C410.2 68.59 409.91 88.54 407.19 97.89C400.23 121.89 384.68 135.68 359 140.62C354.88 141.41 343.02 142.35 332.66 142.69C318.44 143.17 313.47 143.67 312.41 144.73C311.26 145.89 311 151.79 311 177.1C311 201 310.71 208.15 309.75 208.45C309.06 208.66 303.32 209.11 296.98 209.45C288.14 209.91 285.03 209.74 283.62 208.71ZM444.53 209.21C438.91 209.02 435.28 208.47 434.8 207.71C433.66 205.92 433.84 77.81 434.98 76C435.72 74.83 438.38 74.47 447.18 74.36C453.37 74.28 459.46 74.6 460.72 75.08L463 75.95L463 141.35C463 192.48 462.73 206.97 461.75 207.74C460 209.12 454.96 209.55 444.53 209.21ZM491.25 209.26C486.48 209.06 483 208.49 483 207.89C483 206.64 485.69 202.66 501.84 180C508.71 170.38 516.88 158.9 520.02 154.5C527.25 144.35 529.17 140.6 528.29 138.31C527.91 137.32 521.74 128.18 514.59 118C507.44 107.83 498.31 94.83 494.29 89.13C490.28 83.42 487 77.94 487 76.95C487 74.56 494.67 73.57 508.19 74.22C520.26 74.8 518.71 73.49 533.86 96C546.64 114.98 547.39 116 548.66 116C550.67 116 556.13 108.87 566.42 92.83C572.31 83.67 577.88 75.87 578.81 75.51C579.74 75.15 586.12 74.61 593 74.32C603.02 73.9 605.88 74.09 607.42 75.26C609.28 76.68 609.25 76.84 606.36 81.11C602.55 86.73 597.47 93.98 588.57 106.5C580.32 118.1 579.95 118.63 572 130.35C568.7 135.22 566 140.07 566 141.12C566 143.72 568.35 147.32 590.05 178.02C600.37 192.61 608.96 205.43 609.15 206.52C609.47 208.35 608.71 208.56 599 209.26C593.23 209.68 586.2 209.77 583.38 209.46C577.72 208.83 575.34 206.9 570.46 199C565.46 190.9 551.75 171.57 548.64 168.25C546.97 166.46 545.08 165 544.43 165C543.32 165 539.2 170.85 523.67 194.5C520.06 200 516.43 205.34 515.6 206.38C514.01 208.35 507.54 210.02 502.5 209.75C500.85 209.66 495.79 209.44 491.25 209.26ZM785.38 208.63C775.57 206.17 768.62 200.76 765.33 193C761.24 183.35 761 178.06 761 96.11C761 36.22 761.29 17.11 762.2 16.2C763.91 14.49 787.58 14.53 788.69 16.25C789.14 16.94 789.5 52.6 789.5 95.5C789.5 172.12 789.54 173.56 791.53 177C794.31 181.78 799.1 184 806.67 184C816.25 184 816 183.69 816 195.42C816 209.54 816.09 209.45 801.83 209.71C795.22 209.83 788.37 209.38 785.38 208.63ZM1051 208.41C1043.62 206.46 1035.24 200.58 1031.73 194.89C1026.38 186.22 1025.87 181.93 1025.3 140.5C1024.93 113.71 1024.44 102.28 1023.64 101.77C1023.01 101.36 1019.35 101.02 1015.5 101C1003.4 100.95 1004 101.63 1004 87.99C1004 74.43 1003.56 75.01 1013.83 74.99C1025.88 74.96 1025 76.4 1025 56.58C1025 44.55 1025.36 39.04 1026.2 38.2C1027.46 36.94 1048.4 36.45 1051.42 37.61C1052.78 38.13 1053 40.66 1053 55.55C1053 68.32 1053.33 73.09 1054.25 73.68C1054.94 74.12 1062.7 74.63 1071.5 74.82C1084.77 75.1 1087.71 75.43 1088.75 76.77C1090.65 79.23 1090.42 95.95 1088.44 98.78C1086.95 100.91 1086.25 101 1071.69 101.02C1063.34 101.02 1055.83 101.46 1055 101.98C1053.71 102.79 1053.5 107.88 1053.5 137.71C1053.5 170.84 1053.6 172.67 1055.53 176C1058.9 181.83 1063.65 183.5 1076.79 183.5C1085.73 183.5 1088.27 183.81 1089.02 185C1089.54 185.82 1089.98 190.34 1089.98 195.03C1090 204.96 1088.82 207.4 1083.22 208.96C1077.78 210.47 1057.47 210.13 1051 208.41ZM1744.23 208.45C1729.79 204.56 1722.49 196.28 1719.43 180.31C1718.36 174.69 1718 164.01 1718 137.52C1718 97.26 1719.03 101 1707.98 101C1704.21 101 1699.98 100.56 1698.57 100.02C1696.06 99.07 1696 98.82 1696 89.59C1696 84.39 1696.43 78.99 1696.96 77.6C1697.89 75.16 1698.28 75.06 1707.71 74.79L1717.5 74.5L1717.77 57.07C1717.94 46.29 1718.47 39.14 1719.15 38.32C1720.62 36.55 1743.04 36.44 1744.8 38.2C1745.64 39.04 1746 44.47 1746 56.22L1746 73.05L1748.57 74.02C1749.98 74.56 1757.82 75 1766.01 75C1774.19 75 1781.14 75.42 1781.46 75.94C1781.78 76.45 1782.14 81.66 1782.27 87.51C1782.57 101.45 1783.19 101 1763.88 101C1751.59 101 1748.87 101.27 1747.51 102.63C1746.06 104.09 1745.92 107.97 1746.19 139.06C1746.49 172.36 1746.59 173.98 1748.56 176.61C1752.58 181.99 1757.26 183.5 1769.93 183.5C1783.07 183.5 1782.49 183.01 1782.85 194.5C1783.29 208.12 1780.71 210.03 1762 209.93C1754.56 209.89 1747.37 209.29 1744.23 208.45ZM1333.35 207.48C1333.03 206.64 1333.26 190.11 1333.86 170.73C1334.47 151.35 1335.24 110.98 1335.59 81C1336.13 33.86 1336.43 26.26 1337.76 24.75C1339.77 22.48 1354.64 22.24 1358.83 24.41C1361.72 25.9 1362.61 26.97 1382.74 53.01C1399.5 74.69 1418.82 98.95 1424.33 105.25C1425.65 106.76 1427.42 108 1428.27 108C1429.11 108 1432.02 105.41 1434.73 102.25C1439.86 96.28 1470.04 58.01 1485.42 38C1490.28 31.68 1495.21 25.72 1496.38 24.77C1499.11 22.55 1517.08 22.19 1518.85 24.32C1519.59 25.21 1520.13 39.38 1520.5 67.57C1520.8 90.63 1521.54 131.22 1522.14 157.76C1523 195.91 1522.97 206.33 1521.99 207.51C1520.35 209.49 1495.8 209.7 1494.26 207.75C1493.65 206.98 1493.03 183.37 1492.64 146.34C1492.29 113.25 1491.73 85.46 1491.4 84.59C1491.06 83.72 1490 83 1489.04 83C1486.98 83 1482.67 87.88 1461.21 114.5C1427.53 156.29 1429.4 154.35 1425.08 152.04C1423.39 151.14 1410.95 136.35 1400.73 123.1C1399.2 121.12 1397.4 118.83 1396.72 118C1396.04 117.17 1390.09 109.75 1383.51 101.5C1368.67 82.9 1367.16 81.41 1365.17 83.4C1363.93 84.64 1363.62 94.21 1363.1 145.97C1362.58 198.75 1362.3 207.2 1361 208.02C1360.17 208.54 1353.75 208.98 1346.72 208.98C1336.19 209 1333.83 208.73 1333.35 207.48ZM1961.26 207.75C1960.63 206.96 1960.27 182.83 1960.28 141.75C1960.29 86.55 1960.5 76.83 1961.73 75.81C1963.57 74.28 1982.57 74.17 1984.07 75.67C1984.66 76.26 1985.47 79.17 1985.87 82.14C1986.64 87.82 1987.52 90 1989.02 90C1989.52 90 1993.47 87.58 1997.81 84.63C2006.81 78.5 2014.06 75.51 2023.58 73.99C2033.05 72.47 2033 72.39 2033 89.15L2033 103.05L2030.43 104.02C2029.02 104.56 2025.18 105 2021.9 105C2011.76 105 1998.02 110.8 1992.06 117.59L1989 121.07L1989 163.83C1989 195.76 1988.7 206.9 1987.8 207.8C1986.09 209.51 1962.61 209.46 1961.26 207.75ZM33.67 191.33C33.3 190.97 33 186.69 33 181.83L33 173L42.5 173L52 173L52 182.5L52 192L43.17 192C38.31 192 34.03 191.7 33.67 191.33ZM1186.07 183.07C1198.11 178.56 1206.39 167.49 1209.67 151.5C1216.03 120.5 1192.45 95.16 1164 102.41C1151.8 105.52 1143.19 113.54 1138.68 126C1130.87 147.58 1137.9 172.79 1154.03 181.01C1164.37 186.29 1175.56 187.01 1186.07 183.07ZM1629 183.52C1639.77 179.73 1648.38 170.06 1652.12 157.55C1653.82 151.88 1654.14 148.57 1653.85 139.53C1653.53 129.37 1653.23 127.9 1650.16 121.68C1646.09 113.4 1640.3 107.49 1633.01 104.17C1628.43 102.08 1625.9 101.65 1618 101.58C1607.75 101.49 1604.68 102.32 1596.68 107.3C1584.22 115.05 1577.65 132.77 1580.07 152.08C1582.28 169.73 1591.72 180.87 1607.98 185.03C1612.72 186.24 1623.48 185.47 1629 183.52ZM1878.18 183.32C1888.78 179.16 1896.04 171.44 1900.59 159.5C1902.11 155.51 1902.5 151.87 1902.5 141.5C1902.5 128.91 1902.4 128.29 1899.31 122C1895.32 113.87 1889.12 107.48 1882 104.16C1877.28 101.96 1875.08 101.59 1866.48 101.55C1857.12 101.5 1856.02 101.72 1849.77 104.79C1835.99 111.57 1828.59 125.46 1828.59 144.5C1828.59 161.97 1835.29 175.45 1846.99 181.5C1856.04 186.18 1868.99 186.93 1878.18 183.32ZM60 155.12L60 143.18L68.25 142.5C72.79 142.13 78.08 141.97 80 142.16L83.5 142.5L83.5 154.5L83.5 166.5L71.75 166.78L60 167.06L60 155.12ZM4.95 164.62C4.7 164.37 4.64 159.86 4.82 154.59L5.14 145L14.57 145L24 145L24 154.5L24 164L16.08 164C11.73 164 7.54 164.24 6.78 164.53C6.02 164.82 5.19 164.86 4.95 164.62ZM32 127.5L32 115.93L42.72 116.22L53.45 116.5L54.2 120.92C54.61 123.35 54.63 128.3 54.23 131.92L53.5 138.5L42.75 138.78L32 139.07L32 127.5ZM139 126.97L139 114.94L125.75 115.22L112.5 115.5L112.22 127.25L111.94 139L125.47 139L139 139L139 126.97ZM706.89 127.09C710.02 125.11 706.15 112.13 700.66 106.15C692.84 97.64 674.17 96.82 662.09 104.44C655.1 108.85 647.52 119.68 648.19 124.29C648.38 125.56 649.5 126.69 651 127.11C654.71 128.13 705.27 128.11 706.89 127.09ZM358.45 114.02C365.14 111.42 373.22 104.49 375.94 99.02C380.02 90.83 380.87 78.6 378.03 68.88C375.53 60.29 368.56 54.09 358.54 51.52C350.91 49.57 314.09 49.31 312.2 51.2C311.32 52.08 311 60.68 311 83.68L311 114.96L313.75 115.57C315.26 115.9 316.95 116.35 317.5 116.57C318.05 116.78 326.15 116.81 335.5 116.64C350.42 116.37 353.23 116.04 358.45 114.02ZM5 100L5 90L14.5 90L24 90L24 100L24 110L14.5 110L5 110L5 100ZM61 100L61 90L71 90L81 90L81 100L81 110L71 110L61 110L61 100ZM59.43 82.69C59.16 81.96 59.06 76.45 59.21 70.44L59.5 59.5L71.25 59.22L83 58.94L83 71.47L83 84L71.47 84C62.87 84 59.81 83.67 59.43 82.69ZM33.21 72.25L33.5 62.5L42.5 62.5L51.5 62.5L51.79 72.25L52.07 82L42.5 82L32.93 82L33.21 72.25ZM97.22 57.26L85.94 56.93L86.22 44.22L86.5 31.5L99.28 31.41L112.06 31.33L111.78 44.4C111.55 55.2 111.24 57.49 110 57.53C109.17 57.56 103.42 57.44 97.22 57.26ZM441.78 56.85C436.56 55.4 431.32 49.33 430.49 43.76C428.65 31.52 437.66 21.08 450.1 21.02C455.48 21 460.25 23.76 463.73 28.92C467.2 34.08 466.94 43.13 463.13 49.13C458.79 55.98 450.05 59.14 441.78 56.85ZM13.16 53.21C5.22 52.97 3.9 52.69 4.41 51.38C4.73 50.53 5 46.27 5 41.92L5 34L14.5 34L24 34L24 43.44C24 48.63 23.66 53.02 23.25 53.19C22.84 53.36 18.3 53.37 13.16 53.21ZM60 17L60 5L71.5 5L83 5L83 17L83 29L71.5 29L60 29L60 17Z"/></svg></a>
</header>
<main>
  <div id="nojs" class="warn" style="font-size:17px;line-height:1.8">このファイルは「ファイル」アプリのプレビューでは動きません（声の聞き取りとカードはブラウザで動きます）。<br><a href="https://pixelstomotors.github.io/robotics-review/" style="color:inherit;font-weight:700">Safari で開く → pixelstomotors.github.io/robotics-review</a></div>
  <section id="home">
    <div class="cats" id="cats"></div>
  </section>
  <section id="study" hidden>
  <div class="seg step" role="group" aria-label="段階">
    <button id="s1" type="button" aria-pressed="true">1 単語</button>
    <button id="s2" type="button" aria-pressed="false">2 説明</button>
    <button id="s3" type="button" aria-pressed="false">3 判断の型</button>
  </div>
  <div class="bar">
    <div class="seg" role="group" aria-label="言い方">
      <button id="mSee" type="button" aria-pressed="true">見て言う</button>
      <button id="mHide" type="button" aria-pressed="false">見ないで言う</button>
    </div>
    <div class="seg" role="group" aria-label="出す範囲">
      <button id="fAll" type="button" aria-pressed="true">全部</button>
      <button id="fWeak" type="button" aria-pressed="false">苦手だけ</button>
    </div>
  </div>
  <div id="warn" class="warn" hidden></div>
  <p class="info" id="info"></p>
  <div class="track" id="track" aria-live="off"></div>
  <p class="pos" id="pos"></p>
  <button id="mic" class="mic" type="button">押している間だけ聞く<small>カードを見て話して、指を離す</small></button>
  <section class="res" id="res" aria-live="polite">
    <h2>話した言葉</h2>
    <div class="said" id="said">ここに出ます</div>
    <h2 style="margin-top:10px" id="cmpH">違い（赤＝言わなかった・青＝余分）</h2>
    <div class="cmp" id="cmp">—</div>
    <div class="stats" id="stats"></div>
  </section>
  </section>
  <section class="log" id="logSec" hidden>
    <h2>今回の練習</h2>
    <table><thead><tr><th>文</th><th>一致</th><th>前回</th></tr></thead><tbody id="log"><tr><td colspan="3">まだありません</td></tr></tbody></table>
    <div class="row"><button id="save" type="button">結果を保存</button><button id="copy" type="button">結果をコピー</button><button id="clear" type="button">今回の結果を消す</button></div>
    <div class="row" id="dirRow" hidden><button id="redir" type="button">保存先を選び直す</button><span id="dirName" class="note" style="margin:0;align-self:center"></span></div>
    <p class="note" id="saveNote" style="margin-top:6px"></p>
  </section>
  <p class="note" id="ver"></p>
</main>
<script>
(() => {
  document.getElementById('nojs').remove(); // ブラウザで開けている（プレビューではない）
  const DATA = /*DATA*/null;
  const LABEL = Object.fromEntries(DATA.categories.map(c => [c.key, c.label]));
  const GOOD = 80; // 一致がこれ未満なら「苦手」

  const $ = id => document.getElementById(id);
  const track = $('track'), micBtn = $('mic');
  let step = 1, cat = 'all', mode = 'see', filter = 'all', cards = [], cur = 0, results = {}, tries = {};
  const rk = x => `${x.id}:${step === 3 ? 3 : step}`; // 結果は段階ごとに分けて覚える

  // 前回の一致（この端末のブラウザの中だけに覚える）
  const STORE = 'robotics-review-v2';
  let saved = {};
  try { saved = JSON.parse(localStorage.getItem(STORE) || '{}') || {}; } catch (_) { saved = {}; }
  const persist = () => { try { localStorage.setItem(STORE, JSON.stringify(saved)); } catch (_) {} };
  // 保存した練習記録の集計（iCloud の 練習記録 → _tools/practice_review.py → 書き出し時に埋め込む）。端末の記録より新しければこちらを使う
  const PROGRESS = /*PROGRESS*/{};
  Object.entries(PROGRESS).forEach(([k, v]) => { if (!saved[k] || !saved[k].at || saved[k].at < v.at) saved[k] = v; });
  let attempts = []; // 今回の練習の全部の回（記録ファイルに書く）

  // 声に出す文：要点はその文、用語・問いは答え
  const sayText = x => x.type === 'word' ? (step === 2 ? x.detail : x.back) : x.type === 'point' ? x.front : x.back;
  const termForms = w => { const m = w.match(/^(.*?)（(.*)）$/); if (!m) return [w]; const sub = m[2].split(/・|、/).map(t => t.trim()).filter(Boolean); return [m[1], ...sub, m[1] + m[2]]; };
  const targets = x => x.type === 'word' && step === 1 ? termForms(x.front).flatMap(t => [`${t}は、${x.back}`, `${t}、${x.back}`]) : [sayText(x)];
  const pool = () => DATA.items.filter(x => step === 3 ? x.type === 'lesson' : x.type === 'word');
  const esc = s => String(s).replace(/[&<>"]/g, c => ({'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;'}[c]));

  // ===== カテゴリの一覧（最初の画面）。カテゴリを選ぶと、その中で 1 単語 → 2 説明 → 3 判断の型 を練習する =====
  const inCat = (x, k) => k === 'all' ? true : k === 'key' ? x.key : x.category === k;
  const catName = k => k === 'all' ? '全部' : k === 'key' ? 'まず覚える・核心' : LABEL[k];
  function progress(k) { // 言えた＝最新の一致が80%以上。単語は1と2の2回、判断の型は3で数える
    const its = DATA.items.filter(x => inCat(x, k));
    const keys = its.flatMap(x => x.type === 'word' ? [`${x.id}:1`, `${x.id}:2`] : [`${x.id}:3`]);
    const ok = keys.filter(q => saved[q] && saved[q].s >= GOOD).length, weak = keys.filter(q => saved[q] && saved[q].s < GOOD).length;
    return {w: its.filter(x => x.type === 'word').length, l: its.filter(x => x.type === 'lesson').length, ok, weak, total: keys.length};
  }
  function catRow(k, dark) {
    const p = progress(k);
    return `<button class="cat${dark ? ' dark' : ''}" type="button" data-k="${k}"><span class="nm">${esc(catName(k))}</span>`
      + `<span class="ct">単語 ${p.w}・判断の型 ${p.l}</span><span class="ct">言えた <b>${p.ok}</b> / ${p.total}${p.weak ? `<span class="weak">・苦手 ${p.weak}</span>` : ''}</span>`
      + `<span class="meter"><i style="width:${p.total ? Math.round(p.ok / p.total * 100) : 0}%"></i></span></button>`;
  }
  function renderHome() {
    $('cats').innerHTML = DATA.categories.map(c => catRow(c.key)).join('')
      + `<div class="homeh">まとめて</div>` + catRow('key') + catRow('all');
  }
  $('cats').addEventListener('click', e => { const b = e.target.closest('.cat'); if (b) openCat(b.dataset.k, true); });
  function showView(k) {
    const home = !k;
    $('home').hidden = !home; $('study').hidden = home;
    if (home) { if (listening) stop(); $('h1').textContent = 'ロボティクス 声の単語帳'; document.title = 'ロボティクス 声の単語帳'; $('sub').hidden = false; $('crumb').hidden = true; renderHome(); $('sub').textContent = `${DATA.categories.length}のカテゴリ・単語 ${DATA.items.filter(x => x.type === 'word').length}・判断の型 ${DATA.items.filter(x => x.type === 'lesson').length}`; window.scrollTo(0, 0); return; }
    cat = k; $('h1').textContent = catName(k); document.title = catName(k) + '｜ロボティクス 声の単語帳'; $('sub').hidden = true; $('crumb').hidden = false;
    setStep(step); window.scrollTo(0, 0);
  }
  function openCat(k, push) { if (push) try { history.pushState({k}, '', '#' + k); } catch (_) {} showView(k); }
  $('back').addEventListener('click', e => { e.preventDefault(); if (history.state && history.state.k) history.back(); else { try { history.replaceState(null, '', location.pathname); } catch (_) {} showView(null); } });
  window.addEventListener('popstate', () => { const k = (location.hash || '').slice(1); showView(validCat(k) ? k : null); });
  const validCat = k => k === 'all' || k === 'key' || !!LABEL[k];
  $('ver').textContent = `データ：${DATA.updated} 更新${Object.keys(PROGRESS).length ? `・記録の集計 ${Object.keys(PROGRESS).length}件を反映` : ''}`;

  function render() { // 出すカードを選び直して描く
    cards = pool().filter(x => cat === 'key' ? x.key : cat === 'all' ? true : x.category === cat)
      .filter(x => filter === 'all' || (saved[rk(x)] && saved[rk(x)].s < GOOD));
    draw();
    track.scrollLeft = 0; cur = 0; updatePos(); showResult();
  }
  function draw() { // 今のカードのまま描き直す（言い終えたあと）
    track.innerHTML = cards.map(x => {
      const say = sayText(x);
      const r = results[rk(x)], p = saved[rk(x)];
      const done = r ? `<span class="done${r.score < GOOD ? ' low' : ''}">${r.n > 1 ? `${r.n}回目 ` : ''}一致 ${r.score}%</span>`
        : p ? `<span class="done${p.s < GOOD ? ' low' : ''}">前回 ${p.s}%</span>` : '';
      const lb = x.key ? `<div class="lb">${x.type === 'word' ? 'まず覚える' : '核心'}</div>` : '';
      const sec = x.type === 'word' || x.type === 'lesson' ? (cat === 'all' || cat === 'key' ? LABEL[x.category] : '') : `${cat === 'key' || cat === 'all' ? LABEL[x.category] + '｜' : ''}${x.sub}`;
      let q = '', t, cls = say.length > 60 ? ' long' : '', b = '', w = '';
      if (x.type === 'word') {
        const hidden = mode === 'hide' && !r;
        if (step === 1) { w = `<div class="w">${esc(x.front)}</div>`; t = hidden ? 'どういう意味？' : say; cls = hidden ? ' mid hide' : ' mid'; }
        else { w = `<div class="w small">${esc(x.front)}</div><div class="q">${esc(x.back)}</div>`; t = hidden ? 'もう少し詳しく言うと？' : say; cls = hidden ? ' mid hide' : ' long'; }
      } else if (x.type === 'lesson') { // 場面を見て、教訓を自分の言葉で言う。実例は読むだけ
        const hidden = mode === 'hide' && !r;
        if (hidden) { t = x.front; cls = ' mid'; }
        else { q = `<div class="q">${esc(x.front)}</div>`; t = x.back; cls = ''; b = `<div class="b">${esc(x.detail)}</div>`; }
      } else if (x.type === 'point') {
        if (mode === 'hide' && !r) { t = say.slice(0, 10) + '…'; cls = ' hide'; } else t = say;
        if (mode === 'see' && x.back) b = `<div class="b">${esc(x.back)}</div>`;
      } else {
        const ask = x.type === 'term' ? `「${x.front}」とは？` : x.front;
        if (mode === 'hide' && !r) { q = ''; t = ask; cls = x.front.length > 40 ? ' long' : ''; }
        else { q = `<div class="q">${esc(ask)}</div>`; t = say; }
      }
      const kind = x.type === 'word' ? (x.src && step === 2 ? x.src : '') : x.type === 'lesson' ? x.src : `${x.type === 'point' ? '要点' : x.type === 'term' ? '用語' : '問い'}・${x.src}`;
      return `<div class="card">${sec ? `<div class="sec">${esc(sec)}</div>` : ''}${lb}${w}${q}<div class="t${cls}">${esc(t)}</div>${b}<div class="m"><span>${esc(kind)}</span>${done}</div></div>`;
    }).join('') || '<div class="card"><div class="t">ここに出す項目がありません</div></div>';
    const keys = cards.filter(x => x.key).length;
    $('info').textContent = `${cards.length}枚${keys && cat !== 'key' ? `（${step === 3 ? '核心' : 'まず覚える'} ${keys}）` : ''}${step === 1 ? '・用語から続けて言う' : step === 3 ? '・場面を見て、教訓を自分の言葉で' : ''}`;
  }

  function updatePos() {
    const el = track.children[0];
    if (!el || !cards.length) { $('pos').textContent = ''; micBtn.disabled = true; return; }
    const w = el.getBoundingClientRect().width + 10;
    cur = Math.min(cards.length - 1, Math.max(0, Math.round(track.scrollLeft / w)));
    $('pos').textContent = `${cur + 1} / ${cards.length}`;
    const cel = track.children[cur]; if (cel) track.style.height = (cel.offsetHeight + 14) + 'px'; // 今のカードの高さに合わせる（長いカードの分の空白を作らない）
    micBtn.disabled = false;
  }
  let posTimer;
  track.addEventListener('scroll', () => { clearTimeout(posTimer); posTimer = setTimeout(() => { const before = cur; updatePos(); if (before !== cur) showResult(); }, 80); }, {passive: true});

  // 文字の比較（台本読み合わせページと同じ。句読点と空白は無視し、記号は読み方にそろえる）
  const SYM = [['°', '度'], ['mm', 'ミリ'], ['cm', 'センチ'], ['px', 'ピクセル'], ['%', 'パーセント'], ['％', 'パーセント'], ['±', 'プラスマイナス'], ['×', 'かける'], ['＝', 'は'], ['=', 'は'], ['→', ''], ['＋', 'プラス'], ['+', 'プラス'], ['&', 'アンド'], ['＆', 'アンド']];
  const sym = s => SYM.reduce((t, [a, b]) => t.split(a).join(b), s);
  const PUNCT = /[\s、。，．,.!！?？「」『』（）()・…〜~：:；;"'“”‘’\-―｜|／/①②③④⑤⑥]/;
  const kata2hira = s => s.replace(/[\u30A1-\u30F6]/g, ch => String.fromCharCode(ch.charCodeAt(0) - 0x60));
  const half = ch => ch.replace(/[Ａ-Ｚａ-ｚ０-９]/g, c => String.fromCharCode(c.charCodeAt(0) - 0xFEE0));
  // 1文字ずつ「表示する字」と「比べる字」を持つ（カタカナ・ひらがな、全角・半角、大文字・小文字は同じとみなす）
  const norm = s => prep(s).map(c => c.k).join('');
  const prep = s => [...sym(s)].filter(ch => !PUNCT.test(ch)).map(ch => ({o: ch, k: kata2hira(half(ch).toLowerCase())}));
  // 音声認識の書き分け・聞き違い（同じ読み）は、カードの書き方にそろえてから比べる。一覧はデータの homophones（練習記録を見て増やす）
  const VARIANTS = DATA.homophones || [];
  const align = (script, said) => {
    script = script.toLowerCase(); said = said.replace(/\s+/g, '').toLowerCase();
    said = VARIANTS.reduce((t, g) => { const k = g.find(v => script.includes(v)); return k ? g.filter(v => v !== k).reduce((u, v) => u.split(v).join(k), t) : t; }, said);
    return said.replace(/([ァ-ヶ]+)ー/g, (m, w) => script.includes(w + 'ー') || !script.includes(w) ? m : w); // シミュレーター→シミュレータ（カードに長音がないとき）
  };
  function diff(a, b) { // a=カード, b=話した（prep の配列）
    const n = a.length, m = b.length, dp = Array.from({length: n + 1}, () => new Uint16Array(m + 1));
    for (let i = n - 1; i >= 0; i--) for (let j = m - 1; j >= 0; j--) dp[i][j] = a[i].k === b[j].k ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
    const ops = []; let i = 0, j = 0;
    while (i < n && j < m) { if (a[i].k === b[j].k) { ops.push(['=', a[i].o]); i++; j++; } else if (dp[i + 1][j] >= dp[i][j + 1]) { ops.push(['-', a[i].o]); i++; } else { ops.push(['+', b[j].o]); j++; } }
    while (i < n) ops.push(['-', a[i++].o]); while (j < m) ops.push(['+', b[j++].o]);
    return {ops, same: dp[0][0]};
  }
  function opsHtml(ops) {
    let h = '', run = '', type = null;
    const flush = () => { if (!run) return; const t = esc(run); h += type === '=' ? t : `<span class="${type === '-' ? 'miss' : 'extra'}">${t}</span>`; run = ''; };
    ops.forEach(([o, ch]) => { if (o !== type) { flush(); type = o; } run += ch; });
    flush(); return h;
  }

  function showResult() {
    const x = cards[cur];
    const r = x ? results[rk(x)] : null;
    if (!r) { $('cmpH').textContent = x && x.type === 'lesson' ? 'キーワード（赤＝言えなかった）' : '違い（赤＝言わなかった・青＝余分）'; $('said').textContent = 'ここに出ます'; $('cmp').textContent = '—'; $('stats').innerHTML = ''; return; }
    $('said').textContent = r.raw || '（聞き取れませんでした）';
    $('cmpH').textContent = r.kws ? 'キーワード（赤＝言えなかった）' : '違い（赤＝言わなかった・青＝余分）';
    $('cmp').innerHTML = r.kws ? r.kws.map(k => `<span class="kw${k.hit ? '' : ' miss'}">${esc(k.w)}</span>`).join('') + `<div class="lesson">${esc(x.back)}</div>` : opsHtml(r.ops);
    const p = saved[rk(x)] && saved[rk(x)].prev;
    $('stats').innerHTML = `<span>${r.n}回目</span><span>一致 <b class="${r.score >= GOOD ? 'good' : 'bad'}">${r.score}%</b>${p != null ? `（前回 ${p}%）` : ''}</span>`;
  }

  function renderLog() {
    $('logSec').hidden = !Object.keys(results).length && !attempts.length;
    const done = Object.entries(results);
    const STEP = {1: '単語', 2: '説明', 3: '型'};
    $('log').innerHTML = done.length ? done.map(([k, r]) => { const p = saved[k] && saved[k].prev; const s = r.x.type === 'lesson' ? r.x.back : r.x.front;
      const sv = r.ai < savedIdx ? ' <small style="color:var(--ok)">保存済</small>' : '';
      return `<tr><td>${STEP[r.st]}｜${esc(s.slice(0, 14))}${s.length > 14 ? '…' : ''}${sv}</td><td>${r.score}%${r.n > 1 ? `<br><small>${r.n}回目</small>` : ''}</td><td>${p != null ? p + '%' : '—'}</td></tr>`; }).join('')
      : '<tr><td colspan="3">まだありません</td></tr>';
  }


  // 音声認識（押している間だけ）
  const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
  const IOS = /iPhone|iPad|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
  let rec = null, listening = false, finalText = '', interim = '', t0 = 0, target = null, restarts = 0;
  if (!SR) { micBtn.disabled = true; showWarn('このブラウザでは声を文字にできません。iPhoneはSafari、PCはChromeかSafariで開いてください。'); }
  function makeRec() {
    const r = new SR(); r.lang = 'ja-JP'; r.continuous = true; r.interimResults = true;
    r.onresult = e => { if (r !== rec) return; interim = ''; for (let i = e.resultIndex; i < e.results.length; i++) { const tx = e.results[i][0].transcript; if (e.results[i].isFinal) finalText += tx; else interim += tx; } $('said').textContent = (finalText + interim) || '…'; };
    r.onerror = e => { if (e.error === 'not-allowed' || e.error === 'service-not-allowed') showWarn('マイクが許可されていません。ブラウザの設定でマイクを許可してください。'); };
    r.onend = () => {
      if (r !== rec) return;
      if (listening && restarts < 3) { restarts++; finalText += interim; interim = ''; rec = makeRec(); try { rec.start(); } catch (_) {} }
      else finish();
    };
    return r;
  }
  // PC：iPhoneと同じ「始まり」「終わり」の音を鳴らす
  let actx = null;
  function beep(up) {
    if (IOS) return;
    try {
      actx = actx || new (window.AudioContext || window.webkitAudioContext)();
      if (actx.state === 'suspended') actx.resume();
      const t = actx.currentTime + 0.01;
      (up ? [660, 990] : [990, 660]).forEach((f, k) => {
        const o = actx.createOscillator(), g = actx.createGain();
        o.type = 'sine'; o.frequency.value = f;
        g.gain.setValueAtTime(0.0001, t + k * 0.09); g.gain.exponentialRampToValueAtTime(0.25, t + k * 0.09 + 0.012); g.gain.exponentialRampToValueAtTime(0.0001, t + k * 0.09 + 0.08);
        o.connect(g).connect(actx.destination); o.start(t + k * 0.09); o.stop(t + k * 0.09 + 0.09);
      });
    } catch (_) {}
  }
  const PRE = IOS ? 0 : 200;
  function start(ev) {
    if (micBtn.disabled || listening || !SR || $('study').hidden) return;
    ev.preventDefault();
    updatePos(); // スワイプ・←→の直後でも、今見えているカードを対象にする
    target = cards[cur]; if (!target) return;
    finalText = ''; interim = ''; listening = true; t0 = performance.now(); restarts = 0;
    micBtn.classList.add('on'); micBtn.firstChild.textContent = '聞いています…';
    $('said').textContent = '…'; $('cmp').textContent = '—'; $('stats').innerHTML = '';
    beep(true);
    try { if (rec) rec.abort(); } catch (_) {}
    rec = makeRec();
    const go = () => { if (!listening) return; try { rec.start(); } catch (_) {} };
    PRE ? setTimeout(go, PRE) : go();
  }
  function stop(ev) {
    if (!listening) return;
    if (ev) ev.preventDefault();
    listening = false; target.sec = (performance.now() - t0) / 1000;
    micBtn.classList.remove('on'); micBtn.firstChild.textContent = '押している間だけ聞く';
    setTimeout(() => { try { rec.stop(); } catch (_) { finish(); } beep(false); }, 250); // 言い終わりを取りこぼさない
  }
  function finish() {
    if (!target) return;
    const x = target; target = null;
    const raw = (finalText + interim).trim();
    if (!raw || x.sec < 0.6) { $('said').textContent = !raw ? '聞き取れませんでした。押したまま話して、言い終わってから離してください' : '押す時間が短すぎました'; return; }
    // 1 単語は「用語＋意味」を一続きで言う。用語はカッコの前後どちらで言ってもよく、「は」は無くてもよい（いちばん合う言い方で比べる）
    let best = null;
    if (x.type === 'lesson') { // 教訓はキーワードが言えたかで見る（言い方は自由）
      const s = norm(raw);
      const kws = x.kw.map(g => ({w: g[0], hit: g.some(v => s.includes(norm(v)))}));
      best = {say: x.back, kws, ops: null, score: Math.round(kws.filter(k => k.hit).length / kws.length * 100)};
    } else targets(x).forEach(t => { const a = prep(t), b = prep(align(t, raw)); const d = diff(a, b); const sc = a.length ? Math.round(d.same / Math.max(a.length, b.length) * 100) : 0; if (!best || sc > best.score) best = {say: t, ops: d.ops, score: sc}; });
    const {say, ops, score, kws} = best;
    const key = rk(x);
    (tries[key] = tries[key] || []).push(score);
    const at = stamp();
    results[key] = {raw, ops, kws, score, n: tries[key].length, x, st: step, ai: attempts.length}; // ai：この回が attempts の何番目か（保存済みの表示用）
    if (tries[key].length === 1) saved[key] = {s: score, prev: saved[key] ? saved[key].s : null, at};
    else saved[key] = {s: score, prev: saved[key] ? saved[key].prev : null, at};
    persist();
    attempts.push({id: x.id, step, word: x.type === 'lesson' ? x.back : x.front, card: say, said: raw, score, sec: Math.round(x.sec * 10) / 10, at, ...(kws ? {miss: kws.filter(k => !k.hit).map(k => k.w)} : {})});
    unsaved++; saveLabel();
    const keep = track.scrollLeft; draw(); track.scrollLeft = keep; updatePos(); showResult(); renderLog();
  }
  micBtn.addEventListener('touchstart', start, {passive: false});
  micBtn.addEventListener('touchend', stop, {passive: false});
  micBtn.addEventListener('touchcancel', stop, {passive: false});
  micBtn.addEventListener('mousedown', start);
  window.addEventListener('mouseup', () => stop());
  micBtn.addEventListener('contextmenu', e => e.preventDefault());
  // PC：スペースを押している間だけ聞く／←→でカードを移動
  const typing = e => /INPUT|TEXTAREA|SELECT/.test((e.target || {}).tagName || '');
  window.addEventListener('keydown', e => {
    if (typing(e)) return;
    if (e.code === 'Space') { e.preventDefault(); if (!e.repeat) start(e); }
    else if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
      e.preventDefault(); const el = track.children[0]; if (!el || listening) return;
      const w = el.getBoundingClientRect().width + 10;
      track.scrollTo({left: Math.round(track.scrollLeft / w + (e.key === 'ArrowRight' ? 1 : -1)) * w, behavior: 'smooth'});
    }
  });
  window.addEventListener('keyup', e => { if (e.code === 'Space' && !typing(e)) { e.preventDefault(); stop(e); if (document.activeElement && document.activeElement.blur) document.activeElement.blur(); } });
  window.addEventListener('blur', () => stop());
  if (!IOS) micBtn.querySelector('small').textContent = 'スペースキーでも同じ。←→でカードを移動';

  function showWarn(t) { const w = $('warn'); w.textContent = t; w.hidden = false; }

  const press = (on, off, set) => { $(on).setAttribute('aria-pressed', true); $(off).setAttribute('aria-pressed', false); set(); render(); };
  $('mSee').addEventListener('click', () => { press('mSee', 'mHide', () => mode = 'see'); keepView(); });
  $('mHide').addEventListener('click', () => { press('mHide', 'mSee', () => mode = 'hide'); keepView(); });
  $('fAll').addEventListener('click', () => press('fAll', 'fWeak', () => filter = 'all'));
  $('fWeak').addEventListener('click', () => press('fWeak', 'fAll', () => filter = 'weak'));
  const PREF = 'robotics-review-view';
  const keepView = () => { try { localStorage.setItem(PREF, JSON.stringify({step, cat, mode})); } catch (_) {} };
  const setStep = n => { step = n; [1, 2, 3].forEach(k => $('s' + k).setAttribute('aria-pressed', k === n)); render(); keepView(); };
  [1, 2, 3].forEach(k => $('s' + k).addEventListener('click', () => setStep(k)));
  $('clear').addEventListener('click', () => { results = {}; tries = {}; attempts = []; unsaved = 0; savedIdx = 0; saveLabel(); draw(); showResult(); renderLog(); });

  // ===== 記録の保存（台本読み合わせページと同じ流れ。保存先は iCloud の ロボティクス復習/練習記録） =====
  function stamp() { const d = new Date(), z = n => String(n).padStart(2, '0'); const o = -d.getTimezoneOffset(); return `${d.getFullYear()}-${z(d.getMonth() + 1)}-${z(d.getDate())}T${z(d.getHours())}:${z(d.getMinutes())}:${z(d.getSeconds())}${o >= 0 ? '+' : '-'}${z(Math.floor(Math.abs(o) / 60))}:${z(Math.abs(o) % 60)}`; }
  let unsaved = 0, savedIdx = 0; // savedIdx：保存済みの回の数（次の保存はそれより後の回だけ送る）
  const saveLabel = () => { $('save').textContent = unsaved ? `結果を保存（未保存 ${unsaved}回）` : attempts.length ? `保存済み（${attempts.length}回）` : '結果を保存'; };
  const STEPNAME = {1: '1 単語', 2: '2 説明', 3: '3 判断の型'};
  function report(all) {
    const list = all ? attempts : attempts.slice(savedIdx);
    const d = new Date(); const ts = `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
    let s = `# ロボティクス見直しの記録（${ts}）\n\n${list.length}回・データ ${DATA.updated}\n\n`;
    list.forEach(a => { s += `## ${STEPNAME[a.step]}｜${a.word}\n- カード：${a.card}\n- 話した：${a.said}\n- 一致 ${a.score}%／${a.sec}秒\n\n`; });
    s += '<!-- 集計用（_tools/practice_review.py が読む） -->\n```json\n' + JSON.stringify({page: 'robotics-review', data: DATA.updated, at: stamp(), tries: list}) + '\n```\n';
    return s;
  }
  const fileName = () => { const d = new Date(), z = n => String(n).padStart(2, '0'); return `ロボ見直し_${d.getFullYear()}${z(d.getMonth() + 1)}${z(d.getDate())}_${z(d.getHours())}${z(d.getMinutes())}${z(d.getSeconds())}.md`; };
  const doneSaving = msg => { unsaved = 0; savedIdx = attempts.length; renderLog(); $('save').textContent = msg; setTimeout(saveLabel, 2500); };
  function download() {
    const b = new Blob([report()], {type: 'application/octet-stream'}); const a = document.createElement('a');
    a.href = URL.createObjectURL(b); a.download = fileName(); document.body.appendChild(a); a.click();
    setTimeout(() => { URL.revokeObjectURL(a.href); a.remove(); }, 1500);
    doneSaving('保存しました（ダウンロード）');
  }
  // PC（Chrome・Edge）：一度選んだ「練習記録」フォルダへ直接書き込む（選んだフォルダはこのブラウザの中だけに覚える）
  const CAN_DIR = !IOS && 'showDirectoryPicker' in window;
  const idb = (mode, fn) => new Promise((ok, ng) => { try { const q = indexedDB.open('robotics-review', 1); q.onupgradeneeded = () => q.result.createObjectStore('kv'); q.onerror = () => ng(q.error);
    q.onsuccess = () => { try { const t = q.result.transaction('kv', mode); const r = fn(t.objectStore('kv')); t.oncomplete = () => ok(r && r.result); t.onerror = () => ng(t.error); } catch (e) { ng(e); } }; } catch (e) { ng(e); } });
  const getDir = () => idb('readonly', st => st.get('dir')).catch(() => null);
  const putDir = h => idb('readwrite', st => st.put(h, 'dir')).catch(() => null);
  const showDirName = h => { $('dirName').textContent = h ? `保存先：${h.name}` : '保存先：まだ選んでいません'; };
  async function pickDir() {
    const h = await window.showDirectoryPicker({id: 'robo-renshu', mode: 'readwrite'});
    if (h.name !== '練習記録') showWarn(`選んだのは「${h.name}」です。iCloud Drive → PixelstoMotors → ロボティクス復習 → 練習記録 を選んでください。`); else $('warn').hidden = true;
    await putDir(h); showDirName(h); return h;
  }
  // iPhone：共有の画面から「"ファイル"に保存」→ ロボティクス復習 → 練習記録（2回目からは同じ場所が出る）
  const SHORTCUT = 'ロボ見直しを保存';
  async function shareSave() {
    const f = new File([report()], fileName(), {type: 'text/markdown'});
    if (navigator.canShare && navigator.canShare({files: [f]})) { await navigator.share({files: [f]}); doneSaving('保存しました'); return true; }
    return false;
  }
  $('save').addEventListener('click', async () => {
    if (attempts.length <= savedIdx) { $('save').textContent = attempts.length ? '保存済みです' : 'まだ記録がありません'; setTimeout(saveLabel, 1800); return; }
    if (IOS) { // iPhone：記録をコピーして、ショートカット「ロボ見直しを保存」に渡す（練習記録フォルダに直接入る）
      try { await navigator.clipboard.writeText(report()); doneSaving('ショートカットで保存中'); location.href = 'shortcuts://run-shortcut?name=' + encodeURIComponent(SHORTCUT) + '&input=clipboard'; return; } catch (_) {}
      try { if (await shareSave()) return; } catch (e) { if (e && e.name === 'AbortError') return; } return download();
    }
    if (!CAN_DIR) return download();
    try {
      let h = await getDir();
      if (h && (await h.queryPermission({mode: 'readwrite'})) !== 'granted' && (await h.requestPermission({mode: 'readwrite'})) !== 'granted') h = null;
      if (!h) { showWarn('保存先のフォルダを選んでください：iCloud Drive → PixelstoMotors → ロボティクス復習 → 練習記録（最初の1回だけ）'); h = await pickDir(); }
      const w = await (await h.getFileHandle(fileName(), {create: true})).createWritable(); await w.write(report()); await w.close();
      doneSaving(`${h.name}に保存しました`);
    } catch (e) {
      if (e && e.name === 'AbortError') { $('warn').hidden = true; return; }
      showWarn('練習記録に書き込めなかったので、ダウンロードに保存しました。'); download();
    }
  });
  $('copy').addEventListener('click', async () => { try { await navigator.clipboard.writeText(report(true)); $('copy').textContent = 'コピーしました'; } catch (_) { $('copy').textContent = 'コピーできませんでした'; } setTimeout(() => $('copy').textContent = '結果をコピー', 1800); });
  if (CAN_DIR) { $('dirRow').hidden = false; $('redir').addEventListener('click', () => pickDir().catch(() => {})); getDir().then(showDirName); }
  if (IOS) { const b = document.createElement('button'); b.type = 'button'; b.id = 'share'; b.textContent = '共有から保存';
    b.addEventListener('click', async () => { if (attempts.length <= savedIdx) return; try { if (!(await shareSave())) download(); } catch (e) { if (!(e && e.name === 'AbortError')) download(); } });
    $('clear').before(b); }
  $('saveNote').textContent = IOS ? '' : CAN_DIR ? '' : '保存はダウンロードに入ります。練習記録に直接入れたいときはChromeで開いてください';
  window.addEventListener('beforeunload', e => { if (unsaved) { e.preventDefault(); e.returnValue = ''; } });
  // 前に開いていた段階・カテゴリ・言い方から始める（この端末の中だけ）
  // 最初はカテゴリの一覧。URL に #カテゴリ があればそこから（読み込み直しても同じ場所に戻る）
  try { const v = JSON.parse(localStorage.getItem(PREF) || 'null'); if (v) { step = v.step || 1; if (v.mode === 'hide') { mode = 'hide'; $('mHide').setAttribute('aria-pressed', true); $('mSee').setAttribute('aria-pressed', false); } } } catch (_) {}
  const h0 = (location.hash || '').slice(1);
  showView(validCat(h0) ? h0 : null);
})();
</script>
</body>
</html>
```

### 書き出すスクリプト _tools/build_review.py（全文・変えていない）

```python
#!/usr/bin/env python3
"""robotics_review.json（データ）から ロボティクス復習.html（声に出して見直すページ）を作る。

使い方（このファイルがある _tools の1つ上のフォルダで）:
    python3 _tools/practice_review.py   # 練習記録があれば先に集計
    python3 _tools/build_review.py

- ページの形は _tools/template.html（仕事で使っている「台本読み合わせ」ページと同じ作り）。データはページの中に埋め込む
- 書き出した ロボティクス復習.html は、GitHub の PixelstoMotors/robotics-review に index.html として置く
  （声を文字にするには https が要るため。公開URL https://pixelstomotors.github.io/robotics-review/）

データの形（items の1件）:
    {"id": "r001", "category": "kinematics", "sub": "小見出し",
     "type": "word" | "lesson",
     "front": "用語 / 場面（問い）", "back": "短い意味 / 教訓（声に出す一言）",
     "detail": "詳しい説明 / 実例（読むだけ）", "kw": [["座標系","座標"],["そろえる","揃える"]]（lesson だけ）,
     "key": true/false, "src": "DAY163"}
    word は「1 単語」（用語＋back を一続きで言う）と「2 説明」（detail を言う）に出る。key は「まず覚える」。
    lesson は「3 判断の型」に出る。場面を見て教訓を自分の言葉で言い、kw の各組から1語ずつ言えていれば合格。key は「核心」。
    id は一度付けたら変えない（ページが「前回の一致」をこの id で覚えている）。新しい項目は末尾の番号の次を付ける。
"""
import json
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
DATA = ROOT / "robotics_review.json"
TEMPLATE = Path(__file__).resolve().parent / "template.html"
OUT = ROOT / "ロボティクス復習.html"
PROGRESS = ROOT / "練習記録_集計.json"  # _tools/practice_review.py が作る（無ければ空）


def src_label(s):
    return "・".join(p.strip() for p in (s or "").split(",") if p.strip())


def build():
    data = json.loads(DATA.read_text(encoding="utf-8"))
    cats = {c["key"] for c in data["categories"]}
    ids = [x.get("id") for x in data["items"]]
    bad = [x.get("id") for x in data["items"]
           if x.get("category") not in cats or x.get("type") not in ("word", "lesson", "point", "term", "qa")
           or not x.get("front") or (x.get("type") != "point" and not x.get("back"))
           or (x.get("type") in ("word", "lesson") and not x.get("detail"))
           or (x.get("type") == "lesson" and not x.get("kw"))]
    if bad:
        sys.exit(f"category / type / front / back が不正な項目: {bad}")
    dup = {i for i in ids if ids.count(i) > 1}
    if dup or None in ids:
        sys.exit(f"id が重複または空: {sorted(i for i in dup if i)}")

    page = {
        "updated": data.get("updated", ""),
        "categories": data["categories"],
        "homophones": data.get("homophones", []),
        "items": [{"id": x["id"], "category": x["category"], "sub": x.get("sub", ""), "type": x["type"],
                   "front": x["front"], "back": x.get("back", ""), "detail": x.get("detail", ""), "key": bool(x.get("key")),
                   "src": src_label(x.get("src")), **({"kw": x["kw"]} if x.get("kw") else {})} for x in data["items"]],
    }
    js = json.dumps(page, ensure_ascii=False, separators=(",", ":")).replace("</", "<\\/")
    html = TEMPLATE.read_text(encoding="utf-8")
    if "/*DATA*/null" not in html:
        sys.exit("template.html に /*DATA*/null が見つかりません")
    prog = json.loads(PROGRESS.read_text(encoding="utf-8")) if PROGRESS.exists() else {}
    pj = json.dumps(prog, ensure_ascii=False, separators=(",", ":")).replace("</", "<\\/")
    if "/*PROGRESS*/{}" not in html:
        sys.exit("template.html に /*PROGRESS*/{} が見つかりません")
    OUT.write_text(html.replace("/*DATA*/null", js).replace("/*PROGRESS*/{}", pj), encoding="utf-8")
    print(f"{OUT.name} を書き出しました（{len(page['items'])}項目・練習記録の集計 {len(prog)}件）")


if __name__ == "__main__":
    build()
```

```text
ロボティクス復習.html を書き出しました（408項目・練習記録の集計 161件）
```

### 公開と、iCloud の控え

書き出した HTML を robotics-review の index.html として push すると、約1分で公開されます。公開した URL を読み込み、データの更新日が新しくなったことを確かめました。

```bash
cd /home/claude/robotics-review
cp /home/claude/work/build/ロボティクス復習.html index.html
git add index.html
git commit -q -m "カテゴリごとに学べる形に（最初にカテゴリの一覧と進み具合）・LOG443 までの記事から単語66・判断の型74を追加・配膳ロボットの導入のカテゴリを新設"
git push -q origin HEAD
git log --oneline -1
# 約1分後
curl -s https://pixelstomotors.github.io/robotics-review/ | grep -o '"updated":"[0-9-]*"' | head -1
```

```text
b4a2ac7 カテゴリごとに学べる形に（最初にカテゴリの一覧と進み具合）・LOG443 までの記事から単語66・判断の型74を追加・配膳ロボットの導入のカテゴリを新設
"updated":"2026-11-05"

（このあとの見た目の直しの記録）
e30ce25 カテゴリのカードのマウスオーバーを分かりやすく（浮き上がる・影が濃くなる・矢印が右へ動く）
77fcb2b カテゴリのカードをサイトのカードにそろえる（枠線なし・影・角丸8px、乗せると影が濃くなる）
78e8f2b 押せるもの全部にマウスのハンドを出す・カテゴリのカードは乗せると枠が濃くなる
0e5e21a カテゴリの一覧のカードの間隔を広げる
a8bb768 カテゴリの一覧を2列に（PC・タブレットの幅）・件数と言えた数を2行に分けて高さをそろえる
b4a2ac7 カテゴリごとに学べる形に（最初にカテゴリの一覧と進み具合）・LOG443 までの記事から単語66・判断の型74を追加・配膳ロボットの導入のカテゴリを新設
```

iCloud の ロボティクス復習 フォルダには、robotics_review.json・_tools/template.html・ロボティクス復習.html の3つを書き戻し、README.md の作り方の記録と改善の記録に、この作り直しを1行ずつ足しました。

```text
README.md に足した行（作り方の記録・改善の記録）

- 最初の画面はカテゴリの一覧（言えた数と苦手の数つき）。カテゴリを選ぶと、その中で3つの段階を練習する。まとめて練習するときは一覧の下の「まず覚える・核心」「全部」
- 2026-11-05 LOG443 までの記事（フィジカルAI研究室・導入シミュレーション・考察記事）から単語66・判断の型74を追加（計408枚）。全件を出典と照合し18件を修正。カテゴリ「配膳ロボットの導入」を新設。最初の画面をカテゴリの一覧にし、カテゴリごとに練習する形に変えた（URL の #カテゴリ で直接開ける・戻るで一覧へ）

あわせて、開発ログの範囲を DAY102〜DAY326 から DAY102〜LOG443 に、category の一覧に service を足した。
```

次は、新しい140枚を練習して練習記録を集計し、分野ごとの言えた数と、新しいカードで聞き違いが多い語を見ます。ロボット学習の66枚が一度に練習するには多ければ、分野の中を π0.5 と LoRA のまとまり、評価と人の直しのまとまりのように分けます。
