---
title: "pixelstomotors.com を AI で読む入口 ― Gemini のアプリは HTML しか読まない"
slug: top-ai-reading-guide-gemini-html-only
date: 2026-11-03
day: 437
meta_description: "Gemini のアプリは URL を渡しても .txt や .md を読まず、HTML のページだけを読む。pixelstomotors.com の TOP に記録を AI に読ませる入口を作り、4つの AI で確かめた。"
tags: [AI活用, Gemini, ChatGPT, Claude, Perplexity, llms.txt, AIで読む, サイト改修, Cloudflare Workers]
ai_consult: true
series: "ひとり研究所の裏側"
---

**Gemini のアプリ（gemini.google.com）は、URL を渡しても .txt や .md のテキストのファイルは読まず、ふつうの Web ページ（HTML）だけを読む。** pixelstomotors.com の TOP に、記録を AI に読ませて聞く入口を作り、ChatGPT・Claude・Gemini で実際に押して確かめて分かったことです。

この入口で変わったことは3つです。

- **何度も来てくれる人が、長いログを読まずに記録を楽しめるようになった。** TOP の帯を開いて質問を1つ選び、ふだん使っている AI のボタンを押すだけで、AI がサイトの記録を読んで答えます。質問は、前に来たときから何が進んだか・失敗した回・自分で始めるなら最初の1本・ハコブくんの説明、の4つです。ページを移らずに TOP の中で開きます。
- **Gemini で押した人にも、答えが返るようになった。** 記事ページの AIで読む の Gemini は、置いた11月1日から、押した人に読めませんでしたと返していたはずです。サイトの記録には、この日の未明に Gemini 経由で記事の全文ファイル（.md）を読みに来た跡が2件残っています。今は Gemini にだけ HTML のページを読ませています。
- **時間：** 相談を始めてから TOP に帯を出すまでが約17分（9時3分に相談、9時20分に公開）。Gemini を含む4つの AI で答えが返るまでが約50分（9時51分）でした。前に同じことをした記録はないので、何分浮いたかは比べられません。

pixelstomotors.com は、ロボットとフィジカルAI（現実の物を動かす AI）の研究を、ひとりで記録して公開しているサイトです。記録はコードと実行ログを全部載せたログなので、人が頭から読む読み物にはなっていません。前に測ったときは、人の何十倍も AI に読まれていました（LOG430）。それでも、何度も来てくれる人が少しはいます。その人たちに、このサイトの楽しみ方を伝えたい、というのが始まりです。

楽しみ方の中身は、AI に読ませて聞くことにしました。ところが4つの AI で試すと、Gemini だけが、サイトの案内（/llms.txt）にアクセスできませんでしたと答えました。サイトの記録を見ると、Gemini は読みに来ていて、サイトは正常に返していました。届いているのに読めない、という状態です。

| やったこと | なぜ | 結果 |
|---|---|---|
| TOP に、閉じると1行の帯を置いた | 取説を読むために、ほかのページへ移らせたくなかった | 開くと、質問4つと AI のボタンが出る |
| 4つの AI で質問を押した | 質問が答えとして成り立つかを見たかった | Claude と ChatGPT は成り立った。Gemini のアプリは読めなかった |
| サイトの記録と Gemini の API で切り分けた | Gemini だけ読めない理由を知りたかった | アプリはテキストのファイルを読まず、HTML だけ読む |
| 案内の HTML 版（/llms）を作った | Gemini にも同じ一覧を読ませたかった | Gemini のアプリでも、本文まで読んだ答えが返った |
| 記事の AIで読む の Gemini を、記事ページに向けた | 全文ファイル（.md）を Gemini が読めないため | Gemini の API で、記事ページを読めることを確かめた |


## 作業ログ ― TOP に「このサイトの楽しみ方」の帯を置いた

最初は、TOP には入口だけを置き、取説の本体は別のページにする案を出しました。今の TOP には動画・1年・導入前ガイド・4コマ・いまの考え・シリーズ・記録・事業所の8つのセクションが並んでいて、長い取説をそのまま入れるとほかの入口が下に沈むからです。

エイイチの答えは、次のとおりでした。

> うちのサイトはそもそも、人がそのまま読むのはかなりきつい（ログなので文章量が多くて読み物として作られていない）でもサイトに何度も来訪している人は少ないけどいるようなので、その人たちにうちのサイトの活用法と楽しみ方（こっちが主かも）を伝えたい。あとそれを読むことによって他のページに遷移してしまうことは避けたい。

これで形が決まりました。別のページには分けず、TOP の中で開く帯にします。


### 帯はヒーローのすぐ下に置き、閉じると1行にした

帯は、最初の画面（ヒーロー）のすぐ下、解説動画の前に置きました。閉じているときは、このサイトの楽しみ方と一文だけが見える白い帯です。記事ページの AIで読む と同じ開き方（押すとその場で開く）にそろえました。

- ねらい：何度も来ている人が、ページを移らずに、AI に聞く楽しみ方へ入れるようにする
- 変わったこと：TOP の最初の画面の下に、閉じると1行の帯が出る。開くと、①聞きたいことを選ぶ ②ふだん使っている AI で開く、の2段になる
- 見込める効果：TOP の下のセクションを押し下げずに、AI に読ませる使い方を伝えられる。押した数は、サイトの計測（/api/ev-stats）の TOPの楽しみ方 で数える。効果はまだ見込みで、押された数はこれから見る

【画像】LOG437_01_top_band_closed.png TOP の最初の画面。ヒーローの下に、閉じた「このサイトの楽しみ方」の帯が1行で出ている

【画像】LOG437_02_top_band_open_pc.png PC で帯を開いたところ。質問4つのカードと、ChatGPT・Claude・Perplexity・Gemini・質問文をコピーのボタンが並ぶ

【画像】LOG437_03_top_band_open_sp.png スマホで帯を開いたところ。質問のカードは縦に1列に並ぶ


### 閉じた帯は目立たなかったので、14秒に1回だけ細い黒い帯を通した

公開して TOP を見たエイイチから、次の指示がありました。

> これだと全然目立たないので（と言ってめっちゃ目立たせたわけではない）、ごくたまにふぁーっと黒に左右にスライドして一瞬色が切り替わるみたいなことはできますか？

閉じた帯の上に白い膜を重ね、重ね方を difference（下の色との差を出す重ね方）にしました。白い地は黒に、黒い字は白に反転します。この膜を、左から右へ広げてから右へ抜くように動かします。ページを開いて2.5秒後に1回、そのあとは14秒に1回、入ってから抜けるまで約1.4秒です。帯を開いているあいだと、端末で動きを減らす設定にしている人には出しません。

【画像】LOG437_08_band_wipe_frames.png 帯に黒い膜が通るときの3コマ。左から入ってくる途中、帯全体が黒地に白い字になったところ、右へ抜けていく途中

帯全体が黒に変わる形は、見たエイイチから、もっとさりげない感じがいい、と言われました。膜を帯の幅の2割ほどの細い帯にし、両端をぼかして、約1.1秒ですっと通り抜ける形に変えました。重なった所だけが反転し、帯全体が黒くなることはありません。出る間隔（開いて2.5秒後に1回、そのあと14秒に1回）は同じです。

【画像】LOG437_09_band_sweep_subtle.png さりげない形に変えたあとの3コマ。両端のぼけた細い黒い帯が、左から右へ通り抜けていく


### 質問は4つにし、前に来た日を見た人のブラウザに覚えた

質問は、何度も来ている人向けに4つ用意しました。

| 質問 | AI に頼むこと |
|---|---|
| 前に来たときから、何が進んだ？ | 前に来た日より後の記録を、シリーズごとにまとめる。面白い1本は全文を読んで理由も添える |
| 失敗した回だけ集めて | うまくいかなかった回を探し、どこでつまずき、どう直したかを3つ |
| 自分で始めるなら、最初の1本は？ | 最初に読む記録を3本、順番と理由、要る機材と費用（円） |
| ハコブくんに説明してもらう | 4コマのハコブくんの口調で、最近の研究をユーモアを入れて |

最後の1つは遊びです。1つ入っていると、ただの取説ではなく楽しみ方として伝わると考えました。

前に来たときからの質問は、見た人のブラウザに、前に TOP を開いた日を覚えておきます（localStorage の p2m-site-top-visit）。覚えていれば、カードの小さい字が前に来た10月28日より後の記録をまとめる、のように、その人の日付に変わります。覚えていなければ、この2週間の記録になります。


### llms.txt に、記録ごとの公開日を足した

AI に読ませるのは、サイトの案内の /llms.txt です。llms.txt は、AI がサイトを読むときの案内を、サイトの一番上に置いておく決まった形のテキストファイルです（[llmstxt.org](https://llmstxt.org)）。このサイトでは、シリーズ・導入前ガイド・記録の一覧と、記録ごとの全文ファイルの住所を書いています。

前に来た日より後の記録を AI に拾わせるには、一覧に日付が要ります。そこで、記録の行に公開日（日本時間）を足しました。

```text
- [LOG436 フィジカルAI研究室を作る Log#20 | ゲームパッドで動いた mechArm 270、遅さは pymycobot の版](https://pixelstomotors.com/record/gamepad-moves-mecharm-270-pymycobot-version): フィジカルAI研究室・2026-11-03 公開（全文の Markdown: https://pixelstomotors.com/record/gamepad-moves-mecharm-270-pymycobot-version.md）
```


### 帯の中で押したものは、サイトの計測に残る

帯の中で押したものは、記事ページの AIで読む と同じ仕組み（サイトに組み込んだ自前の計測。LOG430）で記録しています。部品の data-ev に付けた名前で、何を押したかが /api/ev-stats の押したリンク・ボタンの表に出ます。記録するのは押したものの名前と、ページのどの部分か（ai）だけで、入力した内容やコピーした文は残しません。

| 押したもの | 記録に出る名前 |
|---|---|
| 帯を開いた | btn:TOPの楽しみ方:開く |
| 質問を選んだ | btn:TOPの楽しみ方:質問 前に来たときから、何が進んだ？ など |
| AI のボタン | TOPの楽しみ方:since chatgpt.com のように、質問と行き先の AI が組で出る |
| 質問文をコピー | btn:TOPの楽しみ方:since:コピー など |

押したあとで、その AI が実際に案内（/llms.txt・/llms）を読みに来たかは、別の記録（/api/cr-stats の AIで読む経由）で見ます。押した数と、AI が実際に読みに来た数の両方を並べて見られます。

気をつけることが2つあります。帯を閉じたときも、開いたときと同じ名前で数えるので、開いた数は少し多めに出ます。1回のページ表示で記録する押したものは8つまでです。エイイチ自身の閲覧は数えない設定（?p2m-notrack=1）にしているので、この表に出るのはほかの人が押した分だけです。

記録が取れているかを、公開した日の10時15分ごろに確かめました。鍵（集計の鍵）は KIOXIA のファイルから読むだけで、表に出していません。

```text
$ K=$(tr -d '\n\r ' < $HOME/mnt/KIOXIA/p2m_stats_key.txt); curl -s -m 90 -H "Authorization: Bearer $K" "https://pixelstomotors.com/api/ev-stats?since=2026-11-03&format=json" -o $HOME/ev.json; python3 -c "
import json;d=json.load(open('$HOME/ev.json'));print(list(d.keys()))
s=json.dumps(d,ensure_ascii=False)
import re
for k,v in d.items():
  if isinstance(v,list):
    for r in v:
      if 'TOPの楽しみ方' in json.dumps(r,ensure_ascii=False): print(k, json.dumps(r,ensure_ascii=False)[:200])
"
['total', 'counted', 'truncated', 'since', 'week', 'who', 'byWeek', 'byPage', 'moves', 'referrers', 'utmSources', 'clicks', 'areas', 'deadClicks']

$ python3 -c "
import json;d=json.load(open('$HOME/ev.json'))
print('total',d['total'],'counted',d['counted'])
print(json.dumps(d['clicks'][:8],ensure_ascii=False)[:900])
print(json.dumps([a for a in d['areas'] if 'ai' in json.dumps(a)],ensure_ascii=False)[:400])
p=[r for r in d['byPage'] if r.get('path') in ('/','/index') or r.get('page')=='/']
print(json.dumps(p,ensure_ascii=False)[:300])
"
total 106 counted 106
[]
[]
[{"path": "/", "views": 12, "medianSeconds": 18, "read25Pct": 42, "read50Pct": 42, "read75Pct": 42, "read100Pct": 42, "deadClicks": 0, "rageClicks": 0, "copies": 0, "videoPlays": 0, "outClicks": 0}]

$ K=$(tr -d '\n\r ' < $HOME/mnt/KIOXIA/p2m_stats_key.txt); curl -s -m 150 -H "Authorization: Bearer $K" "https://pixelstomotors.com/api/ev-stats?since=2026-10-30&format=json" -o $HOME/ev2.json; python3 -c "
import json;d=json.load(open('$HOME/ev2.json'))
print('who',d.get('who'),'total',d['total'],'week',d.get('week'))
print(json.dumps(d['byWeek'],ensure_ascii=False)[:500])
print(json.dumps(d['clicks'][:10],ensure_ascii=False)[:1200])
print([c for c in d['clicks'] if 'AI' in c['target'] or '楽しみ' in c['target']])
"
who None total 1213 week None
[{"week": "2026-11-02", "label": "2026-11-02 〜 11-08", "views": 392, "medianSeconds": 6, "read75Pct": 84, "spPct": 5, "backViews": 0, "outClicks": 0, "deadClicks": 4, "rageClicks": 0, "messageViews": 2, "formStarts": 0, "formSubmits": 0, "formSubmitsValid": 0, "messagesReceived": 0, "humanViews": 35, "botViews": 346, "unknownViews": 11, "unmarkedViews": 0}, {"week": "2026-10-26", "label": "2026-10-26 〜 11-01", "views": 821, "medianSeconds": 6, "read75Pct": 89, "spPct": 6, "backViews": 1, "outCli
[{"target": "/｜ヘッダー", "count": 4}, {"target": "btn:次のコマ｜ページの中身", "count": 3}, {"target": "btn:メニュー｜ヘッダー", "count": 2}, {"target": "iframe:/embeds/LOG408_tate_4koma.html｜本文", "count": 1}, {"target": "/manga/ep133｜その他", "count": 1}, {"target": "/manga/ep132｜前後の記事", "count": 1}, {"target": "/record/lighting-breaks-picking｜ページの中身", "count": 1}, {"target": "/record/gemini-api-4koma｜ページの中身", "count": 1}, {"target": "/record/check-serving-robot-sim-with-phone｜その他", "count": 1}, {"target": "iframe:/embeds/LOG414_sheet_to_assumptions.html｜本文", "count": 1}]
[{'target': 'btn:AIで読む:開く｜ai', 'count': 1}]
```

10月30日からの記録に、記事ページの AIで読む を開いた1回（btn:AIで読む:開く）が残っていて、仕組みが動いていることは確かめられました。TOP の帯の分は、公開した日の10時台にはまだ0件でした。エイイチがこの日に何度も押したのは、数えない設定のブラウザからだったためです。


### TopAiGuide.astro（TOP の帯の部品・最後の版）

帯の部品の全文です。このあと Gemini の件と黒い帯の動きで直したので、直したあとの最後の版を載せます。

```astro
---
// TOP の「このサイトの楽しみ方」（2026-11-03）
// 記録は長いログで、人がそのまま読むのはきつい。何度も来ている人に、AI に読ませて聞く楽しみ方を伝える帯。
// 決めたこと（エイイチ 2026-11-03）：活用＝AI に読ませて使う方法。楽しみ方を主にする。取説を読むためにほかのページへ移らせない（TOP の中で開く）。
// 形：ヒーローの下に、閉じると1行の帯。開くと、①質問を選ぶ ②ふだん使っている AI で開く。AI にはサイトの案内（/llms.txt）を読ませる。
// AI の開き方・前回の AI の覚え方（p2m-site-ai）は記事ページの「AIで読む」（src/pages/record/[slug].astro）と同じ。
// 「前に来たときから」は、TOP を前に開いた日を見た人のブラウザに覚えておき（p2m-site-top-visit）、その日より後の記録を AI に拾わせる。覚えていなければ「この2週間」。
// 押した数は /api/ev-stats（場所 ai。「TOPの楽しみ方:…」）、AI が案内を読みに来た数は /api/cr-stats の「AIで読む経由」（/llms.txt?via=…）
import { SITE } from '../lib/seo';

const QUESTIONS = [
  { id: 'since', label: '前に来たときから、何が進んだ？', sub: 'この2週間の記録をまとめる',
    body: '案内の記録の一覧から、{SINCE}に公開された記録を拾い、何が進んだのかをシリーズごとに短くまとめてください。とくに面白い1本があれば、その全文を読んで、どこが面白いのかも添えてください。' },
  { id: 'fail', label: '失敗した回だけ集めて', sub: 'どこでつまずき、どう直したか',
    body: '記録の一覧と全文を読んで、うまくいかなかった回・失敗した回を探してください。どこでつまずき、次にどう直したのかを、新しいものから3つ教えてください。' },
  { id: 'start', label: '自分で始めるなら、最初の1本は？', sub: '読む順番と、要る機材・費用',
    body: '私も同じようにロボットの学習を自分で始めてみたいです。記録の一覧から最初に読むべき記録を3本選び、読む順番と理由を教えてください。要る機材と費用（円）も、分かる範囲で添えてください。' },
  { id: 'hakobu', label: 'ハコブくんに説明してもらう', sub: '最近の研究を、ゆるく',
    body: 'このサイトのマンガに出てくるハコブくん（病院で薬を運ぶお手伝いロボット。明るくて親しみのある口調）になりきって、最近の記録から、エイイチがいま何に取り組んでいるのかを話してください。少しユーモアを入れて、3分で読める長さでお願いします。' },
];
const HEAD = 'Pixels to Motors（フィジカルAI・ロボットの研究記録のサイト）について質問です。\nサイトの案内（記録の一覧と、記録ごとの全文の住所）: {LLMS}\n\n';
const SINCE_DEFAULT = 'この2週間';
// Gemini はテキストのファイルを読めないので、同じ案内の HTML 版を読ませる。住所は拡張子なしの /llms（/llms.html は配信の設定で /llms へ転送されるため）（2026-11-03）
const guideFile = (via) => (via === 'gemini' ? 'llms' : 'llms.txt');
const promptFor = (q, via, since = SINCE_DEFAULT) =>
  (HEAD + q.body).replace('{LLMS}', `${SITE}/${guideFile(via)}?via=${via}`).replace('{SINCE}', since);
const qOf = (q, via) => encodeURIComponent(promptFor(q, via));
const AIS = [
  { id: 'chatgpt', name: 'ChatGPT', url: 'https://chatgpt.com/?q=' },
  { id: 'claude', name: 'Claude', url: 'https://claude.ai/new?q=' },
  { id: 'perplexity', name: 'Perplexity', url: 'https://www.perplexity.ai/search?q=' },
  // Gemini：質問文を入れて開けないので、コピーしてから開く。読ませるのは /llms.html（アプリはテキストのファイルを読まないため。2026-11-03）
  { id: 'gemini', name: 'Gemini', url: 'https://gemini.google.com/app', copy: true },
];
const q0 = QUESTIONS[0];
---
<details class="tag-guide" data-ev-area="ai">
  <summary data-ev="TOPの楽しみ方:開く">
    <span class="tg-k">このサイトの楽しみ方</span>
    <span class="tg-t">記録は長いログです。読むより、AIに聞くほうが早くて楽しい。</span>
    <i class="ms" aria-hidden="true">expand_more</i>
  </summary>
  <div class="tg-body">
    <p class="tg-step"><span class="tg-n">1</span>聞きたいことを選ぶ</p>
    <div class="tg-qs" role="radiogroup" aria-label="AIに聞くこと">
      {QUESTIONS.map((q, i) => (
        <button type="button" class="tg-q" role="radio" aria-checked={i === 0 ? 'true' : 'false'} data-q={q.id} data-ev={`TOPの楽しみ方:質問 ${q.label}`}>
          <span class="tg-ql">{q.label}</span><span class="tg-qs-sub">{q.sub}</span>
        </button>
      ))}
    </div>
    <p class="tg-step"><span class="tg-n">2</span>ふだん使っている AI で開く</p>
    <ul class="tg-ais">
      {AIS.map((a) => (
        <li data-ai={a.id}>
          <a href={a.copy ? a.url : a.url + qOf(q0, a.id)} target="_blank" rel="noopener" data-ev={`TOPの楽しみ方:${q0.id}`} data-copy={a.copy ? promptFor(q0, a.id) : undefined}>
            {a.name}{a.copy && <span class="tg-an">コピーして開く</span>}<i class="ms" aria-hidden="true">open_in_new</i>
          </a>
        </li>
      ))}
      <li data-ai="copy">
        <button type="button" data-ev={`TOPの楽しみ方:${q0.id}:コピー`} data-copy={promptFor(q0, 'copy')}>質問文をコピー<i class="ms" aria-hidden="true">content_copy</i></button>
      </li>
    </ul>
    <p class="tg-msg" role="status" aria-live="polite"></p>
    <textarea class="tg-prompt" hidden readonly></textarea>
    <p class="tg-note">AI がこのサイトの記録を読んで答えます。Gemini は質問を入れた状態で開けないため、質問文をコピーしてから開きます。開いたら入力欄に貼り付けて送ってください。記録のページの AIで読む からは、その1本だけを読ませて聞けます。</p>
  </div>
</details>

<script is:inline define:vars={{ QUESTIONS, HEAD, SINCE_DEFAULT, AIS, SITE }}>
  (() => {
    const box = document.querySelector('.tag-guide');
    if (!box) return;
    const AI_KEY = 'p2m-site-ai', VISIT_KEY = 'p2m-site-top-visit';
    const today = new Date(Date.now() + 9 * 3600e3).toISOString().slice(0, 10);
    // 前に TOP を開いた日（今日より前の日）。今日のうちに何度開いても「前の日」は変えない
    let prev = '';
    try {
      const v = JSON.parse(localStorage.getItem(VISIT_KEY) || 'null') || {};
      if (v.last && v.last !== today) { prev = v.last; localStorage.setItem(VISIT_KEY, JSON.stringify({ prev, last: today })); }
      else { prev = v.prev || ''; localStorage.setItem(VISIT_KEY, JSON.stringify({ prev, last: today })); }
    } catch { /* 覚えられない環境では「この2週間」 */ }
    const md = (d) => { const [, m, dd] = d.split('-').map(Number); return `${m}月${dd}日`; };
    const since = prev ? `${prev}より後` : SINCE_DEFAULT;
    if (prev) {
      const sub = box.querySelector('[data-q="since"] .tg-qs-sub');
      if (sub) sub.textContent = `前に来た${md(prev)}より後の記録をまとめる`;
    }
    let cur = QUESTIONS[0];
    const promptFor = (q, via) => (HEAD + q.body).replace('{LLMS}', `${SITE}/${via === 'gemini' ? 'llms' : 'llms.txt'}?via=${via}`).replace('{SINCE}', since);
    const render = () => {
      for (const a of AIS) {
        const el = box.querySelector(`li[data-ai="${a.id}"] a`);
        if (!el) continue;
        el.dataset.ev = `TOPの楽しみ方:${cur.id}`;
        if (a.copy) el.dataset.copy = promptFor(cur, a.id);
        else el.href = a.url + encodeURIComponent(promptFor(cur, a.id));
      }
      const c = box.querySelector('li[data-ai="copy"] button');
      if (c) { c.dataset.copy = promptFor(cur, 'copy'); c.dataset.ev = `TOPの楽しみ方:${cur.id}:コピー`; }
    };
    render();
    const list = box.querySelector('.tg-ais'), msg = box.querySelector('.tg-msg');
    try {
      const last = localStorage.getItem(AI_KEY);
      const li = last && list.querySelector(`li[data-ai="${last}"]`);
      if (li && li !== list.firstElementChild) list.prepend(li);
      if (li) li.classList.add('tg-last');
    } catch { /* いつもの並び */ }
    box.querySelector('.tg-qs').addEventListener('click', (e) => {
      const b = e.target.closest('.tg-q');
      if (!b) return;
      box.querySelectorAll('.tg-q').forEach((x) => x.setAttribute('aria-checked', x === b ? 'true' : 'false'));
      cur = QUESTIONS.find((q) => q.id === b.dataset.q) || QUESTIONS[0];
      msg.textContent = '';
      box.querySelector('.tg-prompt').hidden = true;
      render();
    });
    list.addEventListener('click', async (e) => {
      const li = e.target.closest('li[data-ai]');
      if (!li) return;
      try { localStorage.setItem(AI_KEY, li.dataset.ai); } catch { /* 覚えられなくても開く */ }
      const a = e.target.closest('[data-copy]');
      if (!a) return;
      let ok = false;
      try { await navigator.clipboard.writeText(a.dataset.copy); ok = true; } catch { ok = false; }
      msg.textContent = ok ? '質問文をコピーしました。AI の入力欄に貼り付けてください。' : 'コピーできませんでした。下の質問文を選んでコピーしてください。';
      if (!ok) { const t = box.querySelector('.tg-prompt'); t.value = a.dataset.copy; t.hidden = false; t.select(); }
    });
  })();
</script>

<style>
  .tag-guide { max-width: 964px; margin: 0 auto 8px; border-radius: 8px; background: #fff; color: var(--ink); position: relative; overflow: hidden; isolation: isolate; }
  /* 閉じているあいだだけ、ごくたまに細い黒い帯が左から右へすっと通る（2026-11-03 エイイチの指示「ごくたまにふぁーっと黒に左右にスライドして一瞬色が切り替わる」→「もっとさりげない感じがいい」）。
     白い膜を difference で重ねると、重なった所だけ白黒が反転する。膜は帯の幅の2割ほどで、両端をぼかした細い帯にし、約1.1秒で通り抜ける。
     ページを開いて2.5秒後に1回、そのあとは14秒に1回。動きを減らす設定の人には出さない */
  .tag-guide:not([open])::after { content: ""; position: absolute; top: 0; bottom: 0; left: -24%; width: 20%; background: linear-gradient(90deg, rgba(255,255,255,0), #fff 45%, #fff 55%, rgba(255,255,255,0)); mix-blend-mode: difference; pointer-events: none; animation: tg-wipe 14s cubic-bezier(.45,0,.25,1) 2.5s infinite; }
  @keyframes tg-wipe {
    0% { left: -24%; }
    8% { left: 104%; }
    100% { left: 104%; }
  }
  @media (prefers-reduced-motion: reduce) { .tag-guide:not([open])::after { animation: none; display: none; } }
  .tag-guide summary { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 4px 16px; padding: 16px 20px; cursor: pointer; list-style: none; border-radius: 8px; }
  .tag-guide summary::-webkit-details-marker { display: none; }
  .tag-guide summary:hover { background: #fafafa; }
  .tg-k { font: 700 16px/24px var(--sans); }
  .tg-t { font: 400 14px/22px var(--sans); color: var(--ink-2); }
  .tag-guide summary .ms { font-size: 22px; transition: transform .2s ease; }
  .tag-guide[open] summary .ms { transform: rotate(180deg); }
  .tg-body { padding: 4px 20px 20px; }
  .tg-step { display: flex; align-items: center; gap: 8px; margin: 16px 0 10px; font: 600 14px/22px var(--sans); }
  .tg-step:first-child { margin-top: 4px; }
  .tg-n { display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: var(--ink); color: #fff; font: 600 12px/1 var(--sans); }
  .tg-qs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .tg-q { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 14px 16px; border: 0; border-radius: 4px; background: #f0f0f0; color: var(--ink); text-align: left; cursor: pointer; }
  .tg-q:hover { background: #e6e6e6; }
  .tg-q[aria-checked="true"] { background: var(--ink); color: #fff; }
  .tg-ql { font: 600 16px/24px var(--sans); }
  .tg-qs-sub { font: 400 12px/18px var(--sans); opacity: .75; }
  .tg-ais { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
  .tg-ais a, .tg-ais button { display: inline-flex; align-items: center; gap: 8px; padding: 10px 16px; border: 1px solid var(--ink); border-radius: 4px; background: #fff; color: var(--ink); font: 600 14px/22px var(--sans); text-decoration: none; cursor: pointer; }
  .tg-ais a:hover, .tg-ais button:hover { background: var(--ink); color: #fff; }
  .tg-ais .ms { font-size: 18px; }
  .tg-an { font: 400 12px/18px var(--sans); opacity: .7; }
  .tg-last a::before { content: "前回"; padding: 0 6px; border: 1px solid currentColor; border-radius: 4px; font: 400 11px/18px var(--sans); }
  .tg-msg { margin: 8px 0 0; font: 400 13px/20px var(--sans); color: var(--ink-2); }
  .tg-msg:empty { display: none; }
  .tg-prompt { display: block; width: 100%; margin-top: 8px; min-height: 120px; font: 400 13px/20px var(--sans); box-sizing: border-box; }
  .tg-prompt[hidden] { display: none; }
  .tg-note { margin: 16px 0 0; font: 400 12px/18px var(--sans); color: var(--ink-3); }
  @media (max-width: 1023px) { .tag-guide { margin: 0 24px 8px; } }
  @media (max-width: 599px) {
    .tag-guide { margin: 0 12px 8px; }
    .tag-guide summary { grid-template-columns: minmax(0, 1fr) auto; padding: 14px 16px; }
    .tg-k { grid-column: 1; }
    .tg-t { grid-column: 1; font-size: 13px; line-height: 20px; }
    .tag-guide summary .ms { grid-column: 2; grid-row: 1 / span 2; }
    .tg-body { padding: 4px 16px 16px; }
    .tg-qs { grid-template-columns: 1fr; }
  }
</style>
```


## 作業ログ ― 4つの AI で質問を押して、答えを確かめた

公開したあと、エイイチが Claude・ChatGPT・Gemini で質問を押し、返ってきた答えを貼ってくれました。答えは全文を残します。


### Claude：ハコブくんに説明してもらう

LOG436・LOG434・LOG430 の中身を拾い、ハコブくんの口調で話しています。最後の出典の注記に、一覧は長いため前半のみ読みました、とありました。

```text
ハコブくんとして話すために、まずサイトの案内から最近の記録を確認します。

こんにちは、ハコブだよ！ふだんは病院で薬を運んでるけど、今日はエイイチの「最近なにしてるの？」を運んできたよ。ここ数日の記録を読んだら、エイイチはだいたい3つの現場を行ったり来たりしてた。

**① 机の上のアームに、ゲームパッドで手を貸す**

フィジカルAI研究室では、小さなロボットアーム mechArm 270 に、AI（π0.5）を使ってブロックを箱に入れる動きを覚えさせてる。でも、人が直した動きで学び直しても、まだ途中で人の手直しが要るんだって。だから今は「AIが迷ったら人がゲームパッドで直し、その直しも記録して学習に使う」仕組みを作ってるところ。

11月3日の朝、初めて実物のアームをゲームパッドで動かしたら、1回押すたびに約1.6秒待たされた。原因を追ったら、アームの中の Raspberry Pi で動く受け口のプログラムが、想定と違う版のライブラリ（pymycobot 4.0.4）を読んでいた。指令を送るたびにアームの返事を0.5秒ずつ3回待って、同じ指令を3回書いてたんだ。返事をしない相手に「聞こえてる？」を3回ずつ。律儀すぎるよね。ボクも呼ばれたらすぐ返事しようっと。

起動スクリプトを1行直したら、送信は約0.02秒になって、長押しで毎秒約8°動くようになった。気づいてから直るまで約6分、費用は0円。ただ、動きはまだなめらかとは言えない。次は、押している間だけ一定の速さで動き続ける jog という命令で、このアームがどこまでなめらかに動けるか、限界を測るんだって。

**② 配膳ロボットの「にらみ合い」は店員さんが解く**

もう一つはサービスロボット導入シミュレーション。お店に配膳ロボットを入れる前に、待ち時間や必要な人数、ロボットが止まる場所を試算するシリーズだよ。Unity で再現したファミレスで、ロボット2台が狭い通路で向かい合ったら、9分間にらみ合ってた。どっちも相手がどくのを待ってたんだ。わかるなあ。ボクも昔、廊下で看護師さんとお見合いしないように設定してもらったから。

実物の動画4本を見直すと、本物のお店でもロボットどうしでは解けず、店員さんが来るまで18〜53秒以上、手で直す作業は5秒前後だった。どっちが譲るかの決まりは、どのメーカーも公開してない。そこで「30秒動けなかったら店員さんが助けに来る」作りに変えて、店員さんがどれだけ手を取られるかを数字で出せるようにした。試算のページでは、店ごとの Unity の動画も見られるようになったよ。

**③ サイトは人より AI に読まれてる**

研究の外側では、サイトの読まれ方を測ってた。1週間で、AIや検索エンジンのクローラーの訪問が約1万1千回、人のページ表示は163回。ざっと70倍！ ChatGPT が利用者のために記事を読んだのは60回で、そこから実際に来た人は1人だった。AIに読まれることと、人に来てもらうことは別の話だって、数字ではっきりしたんだ。

ちなみに、このお話を作るためにいまAIがサイトを読んだから、カウンターがまた1つ増えてるかも。読んでくれたのが人なら、うれしいな。

ほかにも、考察記事を YouTube の掛け合い動画にしたり、AIとの作業の「イラッ」を14本のショートにしたり、ボクの4コマの英語版を描き直さずに作ったりしてた。ボク、海外デビューも近いかも。

**まとめると**

3つに共通してるのは、ロボットやAIだけでは最後の一歩が届かないところで、人がどう手を貸すかを、ちゃんと測って数字にしてること。アームには人のゲームパッド、配膳ロボットには店員さん、AIに読まれたサイトには人を連れてくる道。エイイチは、きれいなデモよりも、そのすき間のほうをいつも見てるんだ。

次は jog の限界測定と、店員さんの助けを入れたシミュレーション180回の結果待ち。どうなったか、また運んでくるね！

---

Sources:
- [サイトの案内（記録の一覧）](https://pixelstomotors.com/llms.txt?via=claude)（一覧は長いため前半のみ読みました。最近の記録はすべてその範囲に入っています）
- [LOG436 ゲームパッドで動いた mechArm 270、遅さは pymycobot の版](https://pixelstomotors.com/record/gamepad-moves-mecharm-270-pymycobot-version)
- [LOG434 配膳ロボットのにらみ合い、解くのは店員だった](https://pixelstomotors.com/record/serving-robot-faceoff-staff-resolves)
- [LOG430 pixelstomotors.com は人の数十倍 AI に読まれていた](https://pixelstomotors.com/record/ai-reads-more-than-people)
```


### Claude：前に来たときから、何が進んだ？（この2週間）

前に来た日を覚えていないブラウザなので、この2週間の記録になりました。シリーズごとの流れを押さえたうえで、面白い1本に LOG434 を選んでいます。全文ファイル（.md）の住所は、こちらの取得ツールが受け付けず、記事ページ（HTML）のほうで読んだ、と書いています。

```text
サイトの案内から直近2週間分の記録を拾い、シリーズごとにまとめます。

直近2週間（10/20〜11/3）に公開された記録は LOG355〜LOG436 で、動きがあったのは4シリーズでした。ハコブくん・R2R2R・UV-C・画像認識の4シリーズは、この期間の公開はありません。

## シリーズごとの進み

**フィジカルAI研究室（π0.5 × mechArm 270）**
- 前半は成功率が上がった時期です。置き場所を変えたお手本で学習し直し、π0.5 が初めてブロックを箱に入れ、続いてお手本の3か所すべてで成功しました。
- 後半は「人が直すループ」に移っています。回数で測って苦手な置き場所を見つけ、失敗を巻き戻して直し、直した5回で学び直しました。それでも人の直しは必要でした。
- 直近は、直す道具の整備です。ゲームパッドで直す準備ができ、実際に動かすと遅く、原因は pymycobot の版でした。

**サービスロボット導入シミュレーション（10/19開始、2週間で約30本）**
- SimPy の試算から始まり、Unity にファミレスを建て、平面図6店・費用と補助金・夜間の自動実行まで一気に揃いました。
- 分かったことの軸は「詰まる場所」です。大きい店でも詰まるのは片付けで、小さい店では2台目のロボットが待ちを延ばし、狭いファミレスでは配膳ロボットは1台までという結論になっています。
- 実店舗の検証に向けた準備も進みました。店のスマホで測って試算を確かめる仕組みを作り、測った数字が届けばすぐ直せる状態です。

**考察記事（8本）**
- ロボットを広げるのは性能より現場で扱えるかという論点が軸になっています。
- 学習の話では、お手本の先にある「ロボットが自分で直す学び方」と、小脳を育てるのは失敗の手直しだという整理が出ていて、研究室シリーズの実装と噛み合っています。

**ひとり研究所の裏側（いちばん本数が多い）**
- AIに読まれるサイトへの改修が中心です。サイトは人の数十倍 AI に読まれていた一方、AI から来た人はまだ1人で、全記事に「AIで読む」ボタンを置きました。
- 動画は方針を変えています。再生数を追うのをやめ、考察記事を掛け合い動画にする型を3本まで作りました。4コマは縦長化・Gemini API化・英語版まで進んでいます。
- セキュリティでは、ダウンロードフォルダの指示書で Claude Code が乗っ取られるかを自分の Mac で確かめた記録があります。

## いちばん面白い1本：LOG434「配膳ロボットのにらみ合い、解くのは店員だった」

**シミュレーションの不具合が、現実の運用の答えになっている点が面白いです。**

1. **Unity で起きた詰まりが、現実と同じ形だった。** Unity の店では2台が9分にらみ合い、料理の取り消しでやっと動いた。原因は2つありました。どちらが譲るかが決まらないことと、よけた場所が相手の道の上だったことです。

2. **メーカーも答えを出していない。** どちらが譲るかの決まりはどのメーカーも公式には公開しておらず、手がかりは特許だけでした。そこでロボットではなく店員を作りに入れ、30秒動けなければ店員が来て片方を動かす形にしています。

3. **時間を食っているのは修理ではなく「人が来るまで」。** 実物の動画4本の数字が効いています。店員が来るまでは18〜53秒以上と長く、手で直す作業は5秒前後と短い。導入の評価軸が「ロボットの性能」から「詰まりに誰がどれだけ早く気づくか」に移る根拠になります。LOG389の「性能より現場で扱えるか」を、数字で裏づけた形です。

4. **学習と決まりの使い分けがはっきりした。** ロボットどうしの向かい合いは学習がほとんど要らず決まりで解ける。学習が効くのは人の動きが入る所なので、二段構えにした。研究室シリーズの π0.5 の話とつながる整理です。

5. **自分の不利な点も書いている。** Unity の人はロボットのそばでだけ遠慮しすぎていて、店員の助けの回数は多めに出る可能性があると明記しています。読み手の信頼に効く一文です。

結果（依頼76の180回）はまだ見込みの段階なので、追記で数字が出れば、この記事の価値はさらに上がります。

なお、案内の `.md` の住所はこちらの取得ツールが受け付けず、記事ページ（HTML）のほうで全文を読みました。サイトを AI に読ませる上で、`.md` だけでなく HTML でも読める今の形は保険として効いています。案内は末尾の約2%（LOG14以前の古い記録）を読んでいませんが、今回の集計には影響しません。
```


### Claude：自分で始めるなら、最初の1本は？

ロボットがなくても始められる順に組み直し、費用を円でまとめています。LOG277 を外した理由として、計画していた自作 PC があとで借りる方針に変わったことまで書いています。

```text
pixelstomotors.com のフィジカルAI研究室シリーズから、**ロボットがなくても始められる順**に3本選びました。

## 読む順番と理由

**1. LOG326 フィジカルAI研究室を作る Log#2｜π0.5 を借りたGPUで動かした**
最初に読むべき記録です。ロボットもGPUも買わずに、Mac と Runpod で借りたGPU（A40）だけで π0.5 を動かしています。約35分・0.34ドルで済みました。公式手順どおりだと止まる2か所の直し方を含めた再現手順が最後にまとまっていて、そのまま真似できます。
https://pixelstomotors.com/record/openpi-remote-inference-first-run

**2. LOG337 Log#4｜π0.5 を mechArm 270 で追加学習**
自分のアームで学習させる工程の全体がわかる回です。お手本の記録、学習用データへの変換、追加学習（LoRA）、サーバーで起動、Mac で予測を受け取る、までを一度通しています。お手本は「画像・関節角・0.1秒後の目標」を10Hzで並べたデータが要ること、過去の静止画だけの記録は使えなかったことなど、データの集め方の注意点が具体的です。
https://pixelstomotors.com/record/pi05-mecharm-demo-to-inference

**3. LOG361 Log#12｜π0.5 が初めてブロックを箱に入れた**
実機で詰まるのはAIそのものより「AIの答えをアームに届ける側」だ、とわかる回です。肩が毎回約2°届かず、その不足が積み重なっていました。AIに渡す角度を「直前に送った目標」に変えたら成功しています。期待値を現実に合わせるために読んでおくとよい記録です。
https://pixelstomotors.com/record/pi05-first-block-in-box

LOG277（Log#1）は外しました。シリーズの宣言回で実装がなく、そこで計画した75万円のPC自作は、後の記録で「借りる」方針に変わっています。

## 要る機材と費用

| 段階 | 機材・サービス | 費用 |
|---|---|---|
| ①まず触る（LOG326） | 手持ちのPC（Macでも可。ただし計算はクラウド側） | 0円 |
| | Runpod の入金（最低10ドル） | 約1,600円。実際の使用は約54円 |
| ②自分のアームで学習 | mechArm 270-Pi（Switch Science、2026年3月の価格） | 154,220円 |
| | アダプティブグリッパー | 23,100円 |
| | 上カメラ（記録では iPhone を連係カメラで使用） | 手持ちがあれば0円 |
| | GPU代（記録が残る15回の合計） | 5,743円 |

**合計の目安：アームありで約18.5万円。** アームなしの①だけなら、2,000円かからずに始められます。

補足が3点あります。

- **GPUは買わなくてよい。** π0.5 の追加学習にはGPUメモリが22.5GB超要るため、16GBのRTX 5080では足りません。RTX 5090搭載PC（約124万円）が借りるより安くなるのは、月180時間以上使う場合です。判断材料は導入ガイドにまとまっています：https://pixelstomotors.com/gpu-buy-or-rent
- **価格に入っていないもの。** ACアダプタはアームに付属していません（LOG113）。初期設定には有線キーボードとマウスが要ります（LOG122）。どちらも費用は確認していません。
- **為替。** 円は1ドル＝158円前後で換算した、記事の時点の値です。

サイトの案内は、記録一覧を LOG14 あたりまで読んだところで切れています。それより古い記録は確認していません。

Sources:
- [Pixels to Motors llms.txt](https://pixelstomotors.com/llms.txt?via=claude)
- [LOG326](https://pixelstomotors.com/record/openpi-remote-inference-first-run)
- [LOG337](https://pixelstomotors.com/record/pi05-mecharm-demo-to-inference)
- [LOG361](https://pixelstomotors.com/record/pi05-first-block-in-box)
- [LOG103 購入リスト](https://pixelstomotors.com/record/day103)
- [GPU 買うか借りるか](https://pixelstomotors.com/gpu-buy-or-rent)
```


### Claude：失敗した回だけ集めて

質問文が新しいものから3つと頼んでいるので、3つとも11月2日と3日の回になりました。

```text
llms.txtから記録の一覧を読み、失敗した回を探します。

新しい順に、うまくいかなかった3回です。

### 1. LOG436（11/3）ゲームパッドで mechArm 270 を動かす回

- **つまずき**：ゲームパッドで動かすと1回の指令に1.6秒かかっていた。原因は Pi の daemon が pymycobot 2.9.6 ではなく 4.0.4 を読み込んでいたこと。4.0.4 はアームの返事を0.5秒ずつ3回待ち、同じ指令を3回書いてから −1 を返していた。10月30日に daemon を systemd で自動起動にしてから、PYTHONPATH が入らず 4.0.4 が読まれていた。
- **直し方**：起動スクリプトに PYTHONPATH の1行を足して 2.9.6 に戻すと、送信は約0.02秒、長押しで毎秒約8°動くようになった。指令がアームに無視されて8秒止まる件は、0.5秒たっても動いていなければ同じ目標を送り直す形にした。
- **残った課題**：「3°先を0.1秒おきに送る」形でもなめらかにはならず、角度の読み取りが続けて失敗して13秒止まる場面もあった。今の形はタグで保存し、jog 命令や fresh_mode を測る試しを用意して次の回で実機で測る。

### 2. LOG434（11/3）配膳ロボット2台のにらみ合い

- **つまずき**：Unity の店で2台が受け渡し口付近で約9分止まっていた。1号機が入ったよける場所が2号機の道の上にあり、2台とも相手を待ち続けていた。解けたのは料理が600秒で打ち切られたとき。原因は、2台とも料理あり／2台とも空のとき、どちらが譲るかが決まっていないこと。
- **直し方**：30秒動けなければ、手の空いた近いスタッフが行き、15秒で片方（空の方、同じなら行き先が遠い方）を動かす作りにした。実物の動画4本でも、ロボットだけでは解けず店員が手で解いていたので、それに合わせた形です。
- **ついでに直した失敗**：動画の書き出しが4K録画を毎回頭から読んでいて1店約96秒かかっていたのを、場面へ飛んでから切る形にして約27秒にした。動画を見たまま店を切り替えると再生されない不具合も直した。
- **残った課題**：効果の数字は11/3夜の取り直し待ちで、まだ見込みです。

### 3. LOG423（11/2）狭いファミレスに配膳ロボット2台

- **つまずき**：28卓・スタッフ2人の店で2台にすると、入店待ちが13.8分→25.1分に延び、ロボットを動かしに行く回数が2時間で13回→54回に増えた。原因は1本道でロボットどうしが向かい合うこと。
- **直し方（3案を夜に80回比較）**：
  - 一方通行は遠回りで通路にいる時間が延び、混むと悪化した
  - 「1本道は1台ずつ」がいちばん効き、世話が3〜4割減った
  - 途中の失敗として、最初の版は待ちの権利を手放さないコードの誤りなどで571秒の待ちっぱなしが出ていた。
- **結論**：どの案でも2台は1台に届かなかった。狭い店は1台から始める。この「1本道は1台ずつ」が1台に届かなかったことが、次の LOG434 で店員が助ける案を選ぶ理由になった。

4つ目の候補は LOG422（学び直した π0.5 でも人の補正が必要で、最後に手首が戻らなくなった回）です。この手首の問題は、LOG436 の冒頭の点検につながっています。

なお、記録の一覧は新しい側から LOG14 付近まで読み、それより古い末尾は読んでいません。LOG436 も本文と解説は全部読み、末尾の生ログの一部は読んでいません。

Sources:
- [LOG436 ゲームパッドで動いた mechArm 270、遅さは pymycobot の版](https://pixelstomotors.com/record/gamepad-moves-mecharm-270-pymycobot-version)
- [LOG434 配膳ロボットのにらみ合い、解くのは店員だった](https://pixelstomotors.com/record/serving-robot-faceoff-staff-resolves)
- [LOG423 狭いファミレスでは、配膳ロボットは1台まで](https://pixelstomotors.com/record/serving-robot-one-per-narrow-restaurant)
- [LOG422 学び直した π0.5 でも mechArm 270 には人の直しが要った](https://pixelstomotors.com/record/retrained-pi05-still-needs-human-corrections)
- [Pixels to Motors llms.txt](https://pixelstomotors.com/llms.txt?via=claude)
```


### ChatGPT：失敗した回だけ集めて

ChatGPT は、本文まで確かめられた失敗を優先して選び、そのことを最後に断っています。LOG418（画像認識の AI が、ロボットの詰まりではなく画面の道の線を見ていた回）を拾っているのが、Claude と違うところです。公開したばかりの LOG436 と LOG434 は、全文を取れなかったと書いています。ChatGPT が付けたリンクには ?utm_source=chatgpt.com が付いていて、ここから記事を開いた人は、サイトの計測で ChatGPT から来た人として数えられます。

```text
Pixels to Motors の `llms.txt` にある最新の記録から、実験の失敗・想定外の動作・技術的なつまずきを中心に確認しました。

特に注目したのは、単に「失敗した」で終わらず、失敗の原因をどう切り分け、次の実験で何を変えたかが記録されている回です。

まず、2026年11月1日の LOG422 には、かなり具体的な実機実験の失敗が残っています。

- π0.5 を追加学習しても、ロボットアームの把持位置のずれは解消しなかった。
- 補正なしではブロックをつかめず、空振りした。
- さらに実験後、手首のサーボが約54度ずれたまま応答しなくなった。
- 次の対応として、成功データの追加点検、再学習、サーボの診断スクリプト作成を進めているところです。

  [image](https://www.google.com/s2/favicons?domain=https://pixelstomotors.com\&sz=32)

  Pixels to Motors



## 1. LOG422（2026年11月1日）

学び直したAIでも、ロボットアームがブロックをつかめない

[LOG422：学び直した π0.5 でも mechArm 270 には人の直しが要った](https://pixelstomotors.com/record/retrained-pi05-still-needs-human-corrections?utm_source=chatgpt.com)

実機実験で失敗

どこでつまずいたか

19回のお手本で学習した π0.5 に、人間が修正した5回の動作を追加学習させ、実機で9回テストしました。

結果は成功6回、失敗3回。ただし成功した6回もすべて人間の補正が入っています。

具体的には、

- ブロックをつかむ前に、ロボットの根元の角度が約2度足りない。
- つかんだ後、箱の縁にぶつからない高さまで持ち上げられない。
- 補正なしでは爪がブロックの角を押すだけで空振りした。

さらに9回目の終了後、手首のサーボが54度までずれて、電源を入れ直しても戻らなくなりました。

次にどう直したか

- 人間が補正した成功データ6本を点検する。
- 使えるデータを追加して、π0.5を再学習する。
- 手首のサーボを個別に診断するスクリプトを作る。

面白いのは、AIの学習不足とハードウェアの故障を分けて考えていることです。AIが失敗したように見えても、実際には機械側の問題が混ざっている可能性があります。

[image](https://www.google.com/s2/favicons?domain=https://pixelstomotors.com\&sz=32)

Pixels to Motors

## 2. LOG418（2026年11月1日）

画像認識AIが、実際のロボットではなく画面上の道の線を見ていた

[LOG418：道の線を消すと、配膳ロボットの詰まりは3割しか拾えない](https://pixelstomotors.com/record/serving-robot-jam-detection-without-path-lines?utm_source=chatgpt.com)

検証結果が大幅悪化

どこでつまずいたか

配膳ロボットが30秒以上停止する場面を、画像の異常検知AI「PatchCore」で検出する実験です。

最初は、長い停止の約82%を検出できました。一見うまくいったように見えました。

ところが、AIが異常と判断した場所を画像で確認すると、ロボット同士の間だけでなく、Unityの画面に描かれた「ロボットの走行予定ルートの線」にも反応していました。

つまり、AIはロボットの詰まりを理解していたのではなく、シミュレーション画面特有の表示を手掛かりにしていた可能性があったわけです。

次にどう直したか

Unityの録画から走行ルートの線を消し、同じ条件で撮り直して比較しました。

結果は次のとおりです。

| 検証項目       | 線あり    | 線なし   |
| ---------- | ------ | ----- |
| 長い停止の検出率   | 72%    | 28%   |
| ロボット同士の詰まり | 23/43件 | 4/47件 |

一方、人とすれ違って止まった場面は、線がなくても17件中13件を検出できました。

ここから得た修正方針

シミュレーション画像をそのままAIに学習させるのではなく、実際の監視カメラに映るものだけを使う。

これは、シミュレーションで成功したAIを実環境に持ち込んだときに性能が落ちる、いわゆるドメインギャップの問題にもつながる重要な発見です。

[image](https://www.google.com/s2/favicons?domain=https://pixelstomotors.com\&sz=32)

Pixels to Motors



## 3. LOG403（2026年10月29日）

ロボットが失敗しても、AIに何を直せばよいか教えられなかった

[LOG403：mechArm 270 の失敗を巻き戻し、直して覚えさせる](https://pixelstomotors.com/record/rewind-and-correct-pi05?utm_source=chatgpt.com)

従来方式の限界

どこでつまずいたか

LOG401の実験では、赤いブロックを箱に入れる動作を15回試し、成功9回、失敗6回でした。

失敗の内容は、

- 爪がブロックの奥側に下りてしまう。
- つかんだ直後にブロックを落としてしまう。

ところが、従来の仕組みでは失敗したら最初からやり直すしかありませんでした。

AIに「どこで間違えたのか」「どう修正すればよかったのか」を伝える手段がなかったのです。

次にどう直したか

ここで、MicroFactoryというロボット企業の学習方式を参考にしています。

新たに実装したのは次の3つです。

1. `b` キー：失敗した区間まで動作を巻き戻す。
2. `c` キー：関節を選んで1度ずつ人間が補正する。
3. 補正後の動きを、写真と関節角度のセットで記録する。

ここで重要だったのが、最初の設計上の思い込みを修正したことです。

当初は関節角度の数値だけを書き換えれば学習データになると考えていました。しかし、写真には修正前の動作が写っているため、数値だけ変えても正しい学習データにはなりません。

そこで、実機を動かしながら撮影し直す方式に変更しました。また、人間の手がカメラに写らないよう、操作をカメラの外から行う設計にしています。

この回の到達点

- 巻き戻し・補正機能を実装。
- アームを使わないテスト37件に合格。
- 別のAIによるコードレビューも通過。

ただし、この時点では実機テストはまだ行っていません。実際にAIの成功率が改善したわけではなく、実機で試す準備ができた段階です。

[image](https://www.google.com/s2/favicons?domain=https://pixelstomotors.com\&sz=32)

Pixels to Motors



## 3つの失敗に共通すること

今回の記録を横断して見ると、研究の進め方に共通した特徴があります。

| 失敗の種類  | 最初の思い込み                 | 修正の方向               |
| ------ | ----------------------- | ------------------- |
| ロボット制御 | 追加学習すれば動作が改善する          | 人の補正データと機械の状態を分けて検証 |
| 画像認識   | シミュレーションで検出できれば実環境でも使える | 実際のカメラ映像に近い条件で再検証   |
| 学習データ  | 関節角度を直せばよい              | 写真と動作をセットで撮り直す      |

個人的に最も重要だと思ったのは、LOG403からLOG422への流れです。

LOG403では、人間が失敗を直して、その修正を学習データにする仕組みを考えました。

LOG422では、それを実際に使って追加学習したにもかかわらず、まだ人間の補正が必要でした。

つまり、

1. AIが失敗する。
2. 人間が失敗を修正する。
3. 修正内容を学習させる。
4. 再び実機で試す。
5. まだ失敗する部分を特定する。

という、実際のロボット開発に必要な改善サイクルが見えてきます。

なお、一覧には11月3日公開の LOG436（ゲームパッド操作）と LOG434（配膳ロボットの行き詰まり）もあります。今回は両記事の全文を取得できなかったため、上記3件は本文の実験内容と修正過程まで確認できた失敗記録から選んでいます。厳密な意味での最新3件とは区別しています。

[image](https://www.google.com/s2/favicons?domain=https://pixelstomotors.com\&sz=32)

Pixels to Motors



このサイトの面白さは、成功事例を並べることではなく、失敗を次の実験の材料に変えているところですね。 特にフィジカルAIの研究記録は、実機の失敗、数値による原因分析、コードの変更、再学習まで追えるので、研究の過程そのものに価値があると思います。
```


### 案内の /llms.txt は約15万字あり、Claude は1回目に前半だけ読んだ

/llms.txt は476行・14万8,496バイトあります。Claude は1回目に前半だけを読み、2回目以降は LOG14 あたりまで（一覧のおよそ98%）を読んでいました。読める量は回によって違います。頭の方に新しい記録が並んでいるので、前に来たときからの質問とハコブくんの質問は、前半だけでも成り立ちます。


## 作業ログ ― Gemini のアプリだけ、サイトの案内を読めなかった

Gemini は、ほかの3つと違い、住所の後ろに質問文を付けて入力欄に入れた状態で開く方法がありません。そこで、質問文をコピーしてから Gemini を開く形にしていました。エイイチが開いてみると、入力欄は空のままでした。

> geminiは直接開いてもチャットには何も入らないので註釈が必要ですね

TOP の側に出していた、コピーしましたの一文は、Gemini のタブに移ると見えません。Gemini のボタンの中に小さく コピーして開く と書き、帯の下の説明にも、開いたら入力欄に貼り付けて送ってください、と足しました。


### Gemini のアプリは、アクセスできませんでしたと答えた

貼り付けて送ると、Gemini は /llms.txt にアクセスできませんでしたと答えました。住所の後ろの ?via=gemini（どの AI から来たかを数える印）を消して送り直しても、同じでした。

【画像】LOG437_04_gemini_llms_txt_failed.png Gemini のアプリに、/llms.txt を読んで最初に読むべき記録を選ぶよう頼んだ画面。ウェブサイトにアクセスすることができませんでした、と返っている

ここでいったん、TOP の選択肢から Gemini を外しました。押しても答えが返ってこない道は、ないほうがいいと考えたからです。そのうえでエイイチから、詳しく調べてほしいと頼まれました。

> Geminiがなぜできないのか詳しく調査してみてください。


### サイトは止めていなかった（robots.txt と、名乗りを変えた取得）

まず、サイトの側で止めていないかを見ました。robots.txt（検索エンジンなどに、読んでよい範囲を知らせるファイル）は、すべての読み手に開けています。Google の名乗りで /llms.txt を取りに行っても、200（正常）が返ります。

```text
$ curl -s https://pixelstomotors.com/robots.txt | head -40
User-agent: *
Allow: /

Sitemap: https://pixelstomotors.com/sitemap-index.xml
$ curl -s -o /dev/null -w "%{http_code} %{content_type}\n" "https://pixelstomotors.com/llms.txt?via=gemini"
200 text/plain
$ curl -s -o /dev/null -w "%{http_code}\n" -A "Mozilla/5.0 (compatible; Google-Extended)" "https://pixelstomotors.com/llms.txt?via=gemini"
200
$ curl -s https://pixelstomotors.com/llms.txt | wc -lc
    476  148496
$ curl -s -o /dev/null -D - "https://pixelstomotors.com/record/day2.md?via=x" | grep -i -E "^HTTP|content-type|link|content-length|cf-mitigated"
HTTP/2 200 
content-type: text/markdown; charset=utf-8
content-length: 9265
link: <https://pixelstomotors.com/record/day2>; rel="canonical"
$ curl -s -o /dev/null -D - "https://pixelstomotors.com/llms.txt" | grep -i -E "^HTTP|content-type|content-length"
HTTP/2 200 
content-type: text/plain
content-length: 148496
```


### サイトの記録では、Gemini は読みに来て、200 を受け取っていた

サイトには、AI や検索エンジンの読み手がどのページを読んだかを残す記録があります（Cloudflare の R2 に1回ずつ置いている。LOG430）。この日の記録から、案内と、AI の印（via）の付いた訪問を時刻つきで出しました。

```text
$ T=$(tr -d '\n\r ' < $HOME/mnt/KIOXIA/p2m_cloudflare_token.txt); curl -s -m 60 -H "Authorization: Bearer $T" "https://api.cloudflare.com/client/v4/accounts/21d7aed1b193796b89287df10913f2dc/r2/buckets/p2m-messages/objects?prefix=cr/2026-11-03/&per_page=1000" -o $HOME/r2.json; python3 -c "
import json,datetime
d=json.load(open('$HOME/r2.json'))['result']
for o in sorted(d,key=lambda o:o['key']):
  m=o['custom_metadata']; t=int(o['key'].split('/')[2].split('-')[0])
  jst=datetime.datetime.utcfromtimestamp(t/1000)+datetime.timedelta(hours=9)
  p=m.get('p','')
  if p in ('/llms.txt','/llms','/llms.html') or (m.get('v') and m.get('b')!='Amazonbot') :
    print(jst.strftime('%H:%M:%S'), json.dumps(m,ensure_ascii=False))
"
02:22:59 {"b": "その他", "p": "/record/day3.md", "s": "200", "ok": "0", "v": "copy"}
03:06:14 {"b": "その他", "p": "/record/DesFKiCf.md", "s": "200", "ok": "1", "v": "chatgpt"}
03:13:47 {"b": "その他", "p": "/record/day2.md", "s": "200", "ok": "0", "v": "gemini"}
04:45:42 {"b": "その他", "p": "/record/issues-map-weekly-update.md", "s": "200", "ok": "0", "v": "copy"}
05:03:38 {"b": "その他", "p": "/record/service-robot-last-mile.md", "s": "200", "ok": "0", "v": "gemini"}
05:25:32 {"b": "その他", "p": "/record/issues-map-weekly-update.md", "s": "200", "ok": "0", "v": "copy"}
05:57:04 {"b": "Googlebot", "p": "/record/from-drawing-to-solid.md", "s": "200", "ok": "1", "v": "chatgpt"}
06:35:57 {"b": "Googlebot", "p": "/record/ai-chaos-control-systems-before-robots.md", "s": "200", "ok": "1", "v": "chatgpt"}
06:49:14 {"b": "Googlebot", "p": "/record/no-such-thing-as-free-ai-en.md", "s": "200", "ok": "1", "v": "chatgpt"}
07:25:57 {"b": "Googlebot", "p": "/record/toward-dual-arm-coffee-en.md", "s": "200", "ok": "1", "v": "chatgpt"}
09:26:19 {"b": "Claude-User", "p": "/llms.txt", "s": "200", "ok": "0", "v": "claude"}
09:32:37 {"b": "ChatGPT-User", "p": "/llms.txt", "s": "200", "ok": "1", "v": "chatgpt"}
09:35:13 {"b": "その他", "p": "/llms.txt", "s": "200", "ok": "0", "v": "gemini"}
09:35:44 {"b": "その他", "p": "/llms.txt", "s": "200", "ok": "0", "v": "gemini"}
09:35:45 {"b": "その他", "p": "/llms.txt", "s": "200", "ok": "0", "v": "gemini"}
09:40:02 {"b": "その他", "p": "/llms.txt", "s": "200", "ok": "0", "v": "gemini"}
09:40:12 {"b": "その他", "p": "/record/day2.md", "s": "200", "ok": "0", "v": "gemini"}
09:48:27 {"b": "その他", "p": "/llms", "s": "200", "ok": "0", "v": "gemini"}
09:50:23 {"b": "その他", "p": "/llms", "s": "200", "ok": "0", "v": "gemini"}
09:50:23 {"b": "Googlebot", "p": "/record/place-release-trial-log.md", "s": "200", "ok": "1", "v": "chatgpt"}
```

9時35分13秒の /llms.txt が、Gemini のアプリが読みに来た跡です。サイトは200を返しています。9時35分44秒と45秒の2件は、上の curl で確かめたときのものです。9時40分の2件は、このあと Gemini の API で試したときのものです。未明の3時13分と5時3分には、記事の AIで読む の Gemini から、全文ファイル（.md）を読みに来た跡が2件あります。

**Gemini はサイトまで届いていて、サイトは正常に返していました。届いたのに、読めませんでしたと答えています。**


### Gemini の API で、同じ読み方を試した

Gemini の API には、URL を渡すとその中身を読んで答えに使う、URL context という機能があります（[URL context の説明](https://ai.google.dev/gemini-api/docs/url-context)）。アプリの中の読み方と同じかは分かりませんが、Gemini の側で何が起きているかの手がかりになります。エイイチの Mac の上で、次のスクリプトを動かしました（鍵は KIOXIA のファイルから読むだけで、表に出していません）。

```python
import json, os, sys, urllib.request
key = open(os.path.expanduser('~/mnt/KIOXIA/gemini_api_key.txt')).read().strip()
def ask(url, model='gemini-3.8-flash'):
    body = {"contents":[{"parts":[{"text":f"次の住所の中身を読んで、最初の3行をそのまま書き写してください。読めなかったら理由を書いてください。\n{url}"}]}],
            "tools":[{"url_context":{}}]}
    req = urllib.request.Request(f"https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent",
        data=json.dumps(body).encode(), headers={"Content-Type":"application/json","x-goog-api-key":key})
    try:
        r = json.load(urllib.request.urlopen(req, timeout=120))
    except Exception as e:
        print(url, 'ERR', getattr(e,'read',lambda:b'')()[:300]); return
    c = r['candidates'][0]
    meta = c.get('urlContextMetadata') or c.get('url_context_metadata')
    txt = ''.join(p.get('text','') for p in c.get('content',{}).get('parts',[]))
    print('==', url); print('  meta:', json.dumps(meta, ensure_ascii=False)); print('  text:', txt[:200].replace('\n',' / '))
for u in sys.argv[1:]: ask(u)
```

```text
$ python3 $HOME/gem_test.py "https://pixelstomotors.com/llms.txt?via=gemini" "https://pixelstomotors.com/record/day2.md?via=gemini" "https://pixelstomotors.com/thinking"
（1回目：モデル名を gemini-2.5-flash にしていた）
https://pixelstomotors.com/llms.txt?via=gemini ERR b'{\n  "error": {\n    "code": 404,\n    "message": "This model models/gemini-2.5-flash is no longer available to new users. Please update your code to use models/gemini-3.8-flash for the latest features and improvements. We recommend you to use the Interactions API (https://ai.google.dev/gemini-api/docs'
https://pixelstomotors.com/record/day2.md?via=gemini ERR b'{\n  "error": {\n    "code": 404,\n    "message": "This model models/gemini-2.5-flash is no longer available to new users. Please update your code to use models/gemini-3.8-flash for the latest features and improvements. We recommend you to use the Interactions API (https://ai.google.dev/gemini-api/docs'
https://pixelstomotors.com/thinking ERR b'{\n  "error": {\n    "code": 404,\n    "message": "This model models/gemini-2.5-flash is no longer available to new users. Please update your code to use models/gemini-3.8-flash for the latest features and improvements. We recommend you to use the Interactions API (https://ai.google.dev/gemini-api/docs'

$ sed -i "s/model='gemini-2.5-flash'/model='gemini-3.8-flash'/" $HOME/gem_test.py; python3 $HOME/gem_test.py "https://pixelstomotors.com/llms.txt?via=gemini" "https://pixelstomotors.com/record/day2.md?via=gemini" "https://pixelstomotors.com/thinking"
== https://pixelstomotors.com/llms.txt?via=gemini
  meta: {"urlMetadata": [{"retrievedUrl": "https://pixelstomotors.com/llms.txt?via=gemini", "urlRetrievalStatus": "URL_RETRIEVAL_STATUS_SUCCESS"}]}
  text: 指定されたURLの中身を読み込みました。最初の3行は以下の通りです（2行目は空行です）。 /  / ``` / # Pixels to Motors /  / > ADAPT DESIGN（島津 栄一 / Eiichi Shimazu）による、フィジカルAI・ロボットの研究記録のサイト。机の上のロボットアームでお手本のデータを集め、AI に学習させ、実機で動かして確かめる環境を自宅で組み、誰でも同じ手順で始められる形
== https://pixelstomotors.com/record/day2.md?via=gemini
  meta: {"urlMetadata": [{"retrievedUrl": "https://pixelstomotors.com/record/day2.md?via=gemini", "urlRetrievalStatus": "URL_RETRIEVAL_STATUS_ERROR"}]}
  text: 指定されたURLにアクセスを試みましたが、ウェブサイトの内容を読み取ることができませんでした [1.1]。 /  / **理由：** / アクセス制限、認証・ログイン要求、または外部ツールからの自動アクセスがブロックされているなどの要因により、当方のブラウジング環境から該当ページの内容を取得できませんでした [1.1]。
== https://pixelstomotors.com/thinking
  meta: {"urlMetadata": [{"retrievedUrl": "https://pixelstomotors.com/thinking", "urlRetrievalStatus": "URL_RETRIEVAL_STATUS_SUCCESS"}]}
  text: 指定されたURLの内容を読み取ることができました。 /  / Webページのメインコンテンツ冒頭の最初の3行は以下の通りです。 /  / ```text / 二〇二六年十月までの考察記事から、いま考えていること / ロボットに、 / 何を任せればいいのか。 / ``` /  / （※ページタイトルおよびその直後のテキストを含めた冒頭3行の場合は以下の通りです） /  / ```text / ロボットに、何を任せればいいのか ― 考察記事から、いま考
```

API では、テキストの /llms.txt も HTML の /thinking も読めました。全文ファイル（.md）だけが URL_RETRIEVAL_STATUS_ERROR になりました。.md も、サイトは200を返しています（上の記録の9時40分12秒）。.md だけ、ページの種類の札（Content-Type）が text/markdown です。この札を受け付けていない見込みが一番高いと考えています。

API とアプリでは結果が違いました。API は /llms.txt を読めたのに、アプリは読めませんでした。


### アプリで、HTML のページと小さいテキストを読ませ比べた

アプリの側を切り分けるため、エイイチに2つ試してもらいました。HTML のページ（/thinking）と、数行しかないテキストのファイル（/robots.txt）です。

【画像】LOG437_05_gemini_thinking_read.png Gemini のアプリに /thinking を読んで3行で要約するよう頼んだ画面。中身に合った3行の要約が返っている

【画像】LOG437_06_gemini_robots_txt_failed.png Gemini のアプリに /robots.txt を書き写すよう頼んだ画面。アクセスすることができませんでした、と返っている

HTML の /thinking は読めて、数行の /robots.txt は読めませんでした。**Gemini のアプリは、大きさに関係なく、テキストのファイルを読まず、HTML のページだけを読みます。**

| 読み手 | テキスト（/llms.txt・/robots.txt） | Markdown（記事の .md） | HTML（/thinking・/llms・記事ページ） |
|---|---|---|---|
| Gemini のアプリ | 読めない | 試していない | 読める |
| Gemini の API（URL context） | 読める | 読めない | 読める |
| Claude | 読める | 受け付けなかった（答えの中の注記） | 読める |
| ChatGPT | 読める | 試していない | 読める |

【図解】LOG437_ai_reads_matrix.html


## 作業ログ ― Gemini には、案内の HTML 版 /llms を読ませた

直し方は2つ考えました。記事の AIで読む の Gemini を記事ページに向ける小さな直しと、TOP の帯のために案内の HTML 版を作る直しです。エイイチは HTML 版を選び、そのあと記事の側も進めることにしました。


### 案内の中身を llmsGuide.ts に切り出し、テキスト版と HTML 版で共通にした

同じ中身を2つの形で出すため、案内の中身を src/lib/llmsGuide.ts に切り出しました。/llms.txt の出力は1文字も変えていません（作り直す前の /llms.txt と diff で同じだと確かめた）。HTML 版では、記録のリンクを全文ファイル（.md）ではなく記事ページにしました。Gemini のアプリは .md も読めないからです。HTML 版は検索に出さない設定（noindex）にしています。

- ねらい：Gemini のアプリでも、ほかの AI と同じ記録の一覧を読ませる
- 変わったこと：テキストの /llms.txt と同じ中身の HTML のページ（/llms）ができ、Gemini の質問文だけがそちらを指す
- 見込める効果：Gemini を使う人も、TOP の4つの質問を使える。実際にアプリで答えが返ることは、このあと確かめた

#### src/lib/llmsGuide.ts

```ts
// AI がこのサイトを読むときの案内の中身（2026-11-03 に /llms.txt から切り出した）。
// 同じ中身を2つの形で出す：/llms.txt（テキスト。src/pages/llms.txt.ts）と /llms.html（HTML。src/pages/llms.html.ts）。
// HTML 版を作った理由：Gemini のアプリはテキストのファイル（.txt・.md）を読まず、ふつうの Web ページ（HTML）だけ読むため
// （2026-11-03 にアプリで確かめた。/thinking は読めて、/robots.txt・/llms.txt は「アクセスできませんでした」。API では .txt も読める）。
import { allCards, SERIES_PAGES } from './records';
import { SITE } from './seo';
import { fullTextPath } from './fulltext';

export type GuideItem = { title: string; url: string; note?: string; date?: string; full?: string };
export type GuideSection = { h: string; items: GuideItem[] };

export const GUIDE_TITLE = 'Pixels to Motors';
export const GUIDE_LEAD = 'ADAPT DESIGN（島津 栄一 / Eiichi Shimazu）による、フィジカルAI・ロボットの研究記録のサイト。机の上のロボットアームでお手本のデータを集め、AI に学習させ、実機で動かして確かめる環境を自宅で組み、誰でも同じ手順で始められる形で公開している。サービスロボット（配膳・搬送・清掃など）の導入前シミュレーションも行っている。';
export const GUIDE_NOTES = [
  '記録（LOG）は作業の過程をそのまま残したもので、コード・実行ログ・数値を全量で載せている。長いコードとログは別ファイルに分けている。どの記録にも、コード・ログを含む全文の Markdown（記事の住所の末尾に .md）がある',
  '試算の前提の数字は、公開事例と観察から置いた仮の値。記録が新しいほど前提が更新されている（公開済みの記録は直さない）',
  '言語は日本語',
];

const jstDate = (iso: string) => new Date(new Date(iso).getTime() + 9 * 3600e3).toISOString().slice(0, 10);

export async function guideSections(): Promise<GuideSection[]> {
  const cards = await allCards();
  const seriesName = (s: string) => Object.values(SERIES_PAGES).find((c) => c.series === s)?.title.split(' | ').pop() || s;
  return [
    { h: 'シリーズ', items: Object.entries(SERIES_PAGES).map(([slug, c]) => {
      const [en, ja] = c.title.split(' | ');
      return { title: `${ja || c.title}（${en}）`, url: `${SITE}/${slug}`, note: c.intro || undefined };
    }) },
    { h: '導入前ガイドと考え', items: [
      { title: '配膳ロボットの導入前シミュレーション', url: `${SITE}/serving-robot-one-less-staff`, note: '店舗の図面と人数から、何人で回るか・どこで止まるか・月いくらかかるかを出す' },
      { title: 'ロボットAIの学習用GPU、買うか借りるか', url: `${SITE}/gpu-buy-or-rent`, note: '実費と損益分岐の計算機で決める導入ガイド' },
      { title: 'フィジカルAIの導入を、一緒に手を動かして進めます', url: `${SITE}/consult`, note: 'ロボット学習の環境の立ち上げ・サービスロボットの導入前の確認・現場で回る運用の決め方を、週1回の打ち合わせと月1回の訪問で一緒に進めるコンサル。最初の30分の相談は無料（オンライン）' },
      { title: 'ロボットに、何を任せればいいのか', url: `${SITE}/thinking`, note: '考察記事から、いま考えていること' },
      { title: 'Pixels to Motors の1年', url: `${SITE}/one-year`, note: '画面の中の AI から、ロボットを動かす AI へ' },
      { title: '課題地図', url: `${SITE}/issues`, note: 'フィジカルAIが社会実装される前に解くべき課題（週1回更新）' },
    ] },
    // 公開日（日本時間）を添える。TOP の「このサイトの楽しみ方」で、AI に「前に来た日より後の記録」を拾わせるため（2026-11-03）
    { h: '記録（新しい順）', items: cards.map((c) => ({
      title: `${c.day} ${c.title}`, url: `${SITE}/record/${c.slug}`, note: seriesName(c.series), date: jstDate(c.published), full: `${SITE}${fullTextPath(c.slug)}`,
    })) },
    { h: 'Optional', items: [
      { title: '4コママンガ', url: `${SITE}/manga-archive`, note: '薬剤搬送ロボット「ハコブくん」の4コマ' },
      { title: 'メッセージ', url: `${SITE}/message`, note: '問い合わせフォーム' },
    ] },
  ];
}
```

#### src/pages/llms.txt.ts

```ts
// /llms.txt：AI がこのサイトを読むときの案内（2026-10-25）。https://llmstxt.org の形に合わせる。
// 主要な AI が公式に使うとは言っていないので効果ははっきりしないが、作るのは軽いので置く。
// ビルドのたびに、シリーズ（src/lib/records.ts の SERIES_PAGES）と記録の一覧から作り直す。
// すべての記録に、全文の Markdown（/record/{slug}.md。src/lib/fulltext.ts。2026-11-01）を添える
// 中身は src/lib/llmsGuide.ts（HTML 版の /llms.html と共通。2026-11-03）
import { GUIDE_TITLE, GUIDE_LEAD, GUIDE_NOTES, guideSections } from '../lib/llmsGuide';

export async function GET() {
  const sections = await guideSections();
  const line = (it) => it.full
    ? `- [${it.title}](${it.url}): ${it.note}・${it.date} 公開（全文の Markdown: ${it.full}）`
    : `- [${it.title}](${it.url})${it.note ? `: ${it.note}` : ''}`;
  const lines: string[] = [
    `# ${GUIDE_TITLE}`, '', `> ${GUIDE_LEAD}`, '',
    ...GUIDE_NOTES.map((n) => `- ${n}`), '',
    ...sections.flatMap((s) => [`## ${s.h}`, '', ...s.items.map(line), '']),
  ];
  return new Response(lines.join('\n'), { headers: { 'Content-Type': 'text/plain; charset=utf-8' } });
}
```

#### src/pages/llms.html.ts

```ts
// /llms.html：/llms.txt と同じ案内の HTML 版（2026-11-03）。中身は src/lib/llmsGuide.ts。
// Gemini のアプリはテキストのファイルを読まず HTML だけ読むので、TOP の「このサイトの楽しみ方」の Gemini はこちらを読ませる。
// 記録のリンクは記事ページ（HTML）にする（Gemini のアプリは .md の全文ページも読めないため）。全文の Markdown の住所は添えるだけ。
// 人が見るページではないので飾りは付けない。検索には出さない（同じ中身の /llms.txt があるため）
import { GUIDE_TITLE, GUIDE_LEAD, GUIDE_NOTES, guideSections } from '../lib/llmsGuide';
import { SITE } from '../lib/seo';

const esc = (s: string) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');

export async function GET() {
  const sections = await guideSections();
  const li = (it) => it.full
    ? `<li><a href="${esc(it.url)}">${esc(it.title)}</a>：${esc(it.note || '')}・<time datetime="${it.date}">${it.date}</time> 公開（全文の Markdown: ${esc(it.full)}）</li>`
    : `<li><a href="${esc(it.url)}">${esc(it.title)}</a>${it.note ? `：${esc(it.note)}` : ''}</li>`;
  const html = `<!doctype html>
<html lang="ja"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>${esc(GUIDE_TITLE)}：AI 向けのサイトの案内</title>
<meta name="description" content="${esc(GUIDE_LEAD)}">
<meta name="robots" content="noindex">
<link rel="alternate" type="text/plain" href="${SITE}/llms.txt">
<style>body{max-width:960px;margin:0 auto;padding:24px 16px;font:15px/1.7 sans-serif;color:#242424;background:#fff}li{margin:4px 0}</style>
</head><body>
<h1>${esc(GUIDE_TITLE)}</h1>
<p>${esc(GUIDE_LEAD)}</p>
<ul>${GUIDE_NOTES.map((n) => `<li>${esc(n)}</li>`).join('')}</ul>
${sections.map((s) => `<h2>${esc(s.h)}</h2>\n<ul>\n${s.items.map(li).join('\n')}\n</ul>`).join('\n')}
</body></html>
`;
  return new Response(html, { headers: { 'Content-Type': 'text/html; charset=utf-8' } });
}
```


### /llms.html は、配信の設定で /llms へ転送された

最初は住所を /llms.html にしましたが、公開すると /llms へ転送されました（Cloudflare の静的ファイルの配信が、.html を外した住所にそろえる設定のため）。Gemini が転送でつまずかないよう、質問文の住所は最初から /llms にしました。サイトの記録で数える住所にも /llms を足しています。

```text
$ curl -s -o /dev/null -D - "https://pixelstomotors.com/llms.html?via=test" | grep -i -E "^HTTP|^location|content-type"
HTTP/2 307 
location: /llms?via=test
$ curl -s -o /dev/null -D - "https://pixelstomotors.com/llms?via=test" | grep -i -E "^HTTP/2|^location|content-type|content-length"
HTTP/2 200 
content-type: text/html
```

```text
$ python3 $HOME/gem_test.py "https://pixelstomotors.com/llms?via=gemini"
== https://pixelstomotors.com/llms?via=gemini
  meta: {"urlMetadata": [{"retrievedUrl": "https://pixelstomotors.com/llms?via=gemini", "urlRetrievalStatus": "URL_RETRIEVAL_STATUS_SUCCESS"}]}
  text: 指定されたURL（https://pixelstomotors.com/llms?via=gemini）の内容を読み取りました。空行を除いた最初の3行は以下の通りです。 /  / ``` / Pixels to Motors：AI 向けのサイトの案内 / # Pixels to Motors / ADAPT DESIGN（島津 栄一 / Eiichi Shimazu）による、フィジカルAI・ロボットの研究記録のサイト

$ python3 $HOME/gem_test.py "https://pixelstomotors.com/record/gamepad-moves-mecharm-270-pymycobot-version?via=gemini"
== https://pixelstomotors.com/record/gamepad-moves-mecharm-270-pymycobot-version?via=gemini
  meta: {"urlMetadata": [{"retrievedUrl": "https://pixelstomotors.com/record/gamepad-moves-mecharm-270-pymycobot-version?via=gemini", "urlRetrievalStatus": "URL_RETRIEVAL_STATUS_SUCCESS"}]}
  text: 指定されたページ（URL）の内容を確認しました。 /  / Webページのタイトルおよび記事冒頭の最初の3行は以下のとおりです。 /  / **記事の冒頭（タイトル・メタ情報）** / ```text / LOG436 フィジカルAI研究室 2026/11/3 / フィジカルAI研究室を作る Log#20 | ゲームパッドで動いた mechArm 270、遅さは pymycobot
```


### Gemini のアプリで、/llms を読んだ答えが返った

TOP の帯の Gemini から、失敗した回の質問を貼り付けて送ってもらいました。サイトの記録には、9時50分23秒に /llms を読みに来た跡があります。答えは、一覧だけでなく記事の本文まで読んでいます。LOG436 で長押しの方式を変えたことや、LOG422 で手首の点検用スクリプトを作ったことは、本文を読まないと出てこない話です。

【画像】LOG437_07_gemini_llms_html_read.png Gemini のアプリに /llms を読ませて失敗した回を聞いた画面。LOG436 のつまずきと直し方を本文から答えている

```text
Pixels to Motorsの記録一覧および本文から、実験やシミュレーションでつまずき（失敗や不具合）が発生し、それを手直し・解決した記録を新しい順に3件まとめます。

---

### 1. LOG436：ゲームパッド操作でアームが1.6秒ごとに停止・遅延した（2026-11-03 公開）

* **どこでつまずいたか：**
小型ロボットアーム「mechArm 270」を市販のゲームパッドで操作した際、十字キーの1操作ごとに約1.6秒の待機時間が発生し、長押しをしても毎秒1.0°程度しか動かない極端な遅延に見舞われました。また、0.5°単位の微小な入力ではサーボが反応せず、半分以上が無視されて途中で8秒間止まる現象も発生しました。
原因を調べたところ、アーム内蔵のRaspberry Piで動く制御デーモンが、本来意図していたライブラリのバージョン「`pymycobot 2.9.6`」ではなく「`4.0.4`」を読み込んでいたことが判明しました。`4.0.4` は指令を送るたびにアームからの返答を0.5秒×3回待ち、同じ指令を3回書き込んでから `-1`（エラー）を返していたため、1回あたり約1.6秒のラグが生じていました。
* **次にどう直したのか：**
* 起動スクリプトを修正し、安定動作する「`pymycobot 2.9.6`」を明示的に読み込むよう変更しました。これにより1回の送信所要時間が約1.6秒から約0.02秒へ短縮され、長押し時の速度も毎秒約8°まで改善しました。
* 0.5°の空振りを防ぐため、1押しの最小単位を1.0°に拡大し、動いていなければ同じ指令を再送するロジックを導入しました。
* 長押し処理も「現在読み取った関節角度から3°先の目標値を0.1秒間隔で送り続ける」方式に改修し、途切れず連続して動くようにしました。



---

### 2. LOG434：配膳ロボット2台が通路で9分間にらみ合い膠着した（2026-11-03 公開）

* **どこでつまずいたか：**
Unityでファミレス店舗を再現した配膳ロボットシミュレーションにおいて、受け渡し口付近の狭い通路で2台のロボットが正面から向かい合った際、1号機が回避行動を取った位置が2号機の進路を塞いでしまい、互いに相手がどくのを待ち続けて9分間完全に膠着（タイムアウトの10分まで停止）しました。
* **次にどう直したのか：**
* 各メーカーの資料や実店舗の観察動画を調査した結果、実運用でもロボット同士の自律回避は難しく、18〜53秒ほどで店員が手動で位置をずらして解消している実態を把握しました。
* そこでシミュレーション側にも「ロボットが30秒間動けなくなった場合、店員が駆けつけて手で詰まりを解く」という実態に即した解消ロジックを実装し、スタックによるシミュレーション破綻を防止しました。
* あわせて、動画書き出しに1店あたり約96秒かかりMac連携の上限（3分）を圧迫していた問題に対し、4K録画の必要なタイムスタンプへ直接シークして切り出す方式へ改修し、書き出し時間を約27秒へ短縮しました。



---

### 3. LOG422：再学習モデルが把持を空振りし、手首関節が復帰不能になった（2026-11-01 公開）

* **どこでつまずいたか：**
人間が手動補正した動作データで再学習させたAIモデル「π0.5」を用いて実機（mechArm 270）を動かしたところ、人間の介入なしでAI単独に任せた試行において、ブロックの手前（根元角度11.5°）で爪を閉じてしまい、空振りして把持に失敗しました。
さらに9回目の動作後、初期姿勢（HOME）へ復帰させる段階で手首関節（j4）が +54° まで大きく垂れ下がり、以降の指令を一切受け付けなくなりました。アームの電源を入れ直しても手首だけが脱力したまま戻らず、実機を停止せざるを得なくなりました。
* **次にどう直したのか：**
* 人の補正を入れてブロック把持に成功した6回分のログを抽出・保存し、次回の再学習データ（計11本）としてストックしました。
* 復帰しなくなった手首サーボに対しては無理な通電操作を避け、サーボの負荷・角度・電圧・温度を動作させずに読み出す専用の点検スクリプトを作成・実行しました（この切り分けによりハードウェア破損の有無を確認し、後のLOG436におけるゲームパッド制御での実機検証へと繋げています）。
```


### 記事の AIで読む の Gemini も、記事ページを読ませる形にした

記事ページの AIで読む は、ChatGPT・Claude・Perplexity には全文ファイル（.md）を読ませています。Gemini の質問文だけ、.md ではなく記事ページ（?via=gemini 付き）を読ませる形に変えました。サイトの記録でも、記事ページへの ?via=gemini の訪問を数えるようにしました。直したあと、Gemini の API で LOG436 の記事ページを読めることを確かめています（上の2つ目の結果）。


### 変更の差分（worker・記事ページ・TOP・README）

```diff
diff --git a/README.md b/README.md
index b5312e8..da1206d 100644
--- a/README.md
+++ b/README.md
@@ -95,11 +95,12 @@ AI活用の記事の末尾には、相談の案内（`/message?topic=ai` への
 AI に引用される数を増やすため、すべての記事に、AI が丸ごと読める全文ページを足し、記事ページに「AIで読む」ボタンを置いた。人が見る記事ページは変えていない。
 - 全文ページ：`/record/{slug}.md`（`src/pages/record/[slug].md.ts`・中身の作り方は `src/lib/fulltext.ts`）。記事の頭に題名・LOG番号・シリーズ・公開日・記事ページの住所・要約を付け、本文を飾りのない Markdown にする。長いコード・ログを分けた記事は、分ける前の全文（`public/files/LOG{N}/LOG{N}_full.md`・`.html`）を使う。Studio から移した記事（本文が HTML）は turndown で Markdown に直す。画像・図解・動画・サイトの中の住所は https://pixelstomotors.com から始まる住所にする
 - 検索では記事ページを正とする：`worker/index.js` が全文ページに `Link: <記事ページ>; rel="canonical"` と文字コードを付けて返す。記事ページの head の `<link rel="alternate" type="text/markdown">` と `/llms.txt` は、すべての記事でこの全文ページを指す
-- 「AIで読む」：タイトルの下の灰色の帯（`src/pages/record/[slug].astro` の `.ai-read`）。押すと ChatGPT・Claude・Perplexity・Gemini・質問文のコピーが出る。ChatGPT・Claude・Perplexity は住所の後ろの `q=` で質問文（記事と全文ページの住所つき）を入力欄に入れて開く。Gemini は入れる方法がないので、質問文をコピーしてから開く。前回選んだものを見た人のブラウザに覚え（`p2m-site-ai`）、次から一番上に「前回」を付けて出す
+- 「AIで読む」：タイトルの下の灰色の帯（`src/pages/record/[slug].astro` の `.ai-read`）。押すと ChatGPT・Claude・Perplexity・Gemini・質問文のコピーが出る。ChatGPT・Claude・Perplexity は住所の後ろの `q=` で質問文（記事と全文ページの住所つき）を入力欄に入れて開く。Gemini は入れる方法がないので、質問文をコピーしてから開く。Gemini のアプリはテキストのファイル（.md）を読まないので、Gemini の質問文だけは全文ページではなく記事ページ（`?via=gemini` 付き）を読ませる（2026-11-03）。前回選んだものを見た人のブラウザに覚え（`p2m-site-ai`）、次から一番上に「前回」を付けて出す
 - 押した数：`/api/ev-stats` の押したもの（場所 `ai`。`AIで読む chatgpt.com` など）。全文ページを AI のクローラーが読んだ数は `/api/cr-stats` のよく読まれたページ（`.md`）
 - ボタン経由で AI が読みに来た数：質問文の全文ページの住所には、押した AI の印 `?via=chatgpt`（claude・perplexity・gemini・copy）を付けている。`worker/index.js` がこの印の付いた全文ページへの訪問を、名乗りが一覧にない読み手（Gemini・人のブラウザなど）も「その他」として残す（`AI_VIA`）。数は `/api/cr-stats` の「AIで読む経由」（押した AI ごとに、実際に読みに来たのはだれか・週ごと）
 - 各社が `q=` の扱いを変えると、入力欄に入らなくなる。そのときは `AI_TARGETS` を直す
 - 元に戻す：この節を入れた commit を `git revert` する（全文ページ・ボタン・head・llms.txt・worker・turndown がまとめて戻る）
+- TOP の「このサイトの楽しみ方」（2026-11-03・`src/components/TopAiGuide.astro`）：ヒーローの下の、閉じると1行の帯。開くと質問を4つから選び、AI で開く（ChatGPT・Claude・Perplexity・Gemini・コピー。Gemini のアプリはテキストのファイルを読まないので、Gemini には同じ中身の HTML 版 `/llms`（ファイルは /llms.html。配信の設定で /llms.html は /llms へ転送される。`src/lib/llmsGuide.ts` が共通の中身）を読ませる）。AI にはサイトの案内（`/llms.txt?via=…`）を読ませる。`/llms.txt` の記録の行には公開日を添えている（「前に来たときから」のため）。前に TOP を開いた日は見た人のブラウザに覚える（`p2m-site-top-visit`）。押した数は `/api/ev-stats` の「TOPの楽しみ方:…」、AI が読みに来た数は `/api/cr-stats` の「AIで読む経由」（ページが `/llms.txt` のもの）。質問を変えるときは `QUESTIONS` を直す
 
 ## 記事の変換の作り置き（2026-10-30）
 
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 0ed2af9..4525703 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -14,6 +14,7 @@ import LoadMore from '../components/LoadMore.astro';
 import GuideCards from '../components/GuideCards.astro';
 import ThinkingBanner from '../components/ThinkingBanner.astro';
 import OneYearBanner from '../components/OneYearBanner.astro';
+import TopAiGuide from '../components/TopAiGuide.astro';
 import { allManga } from '../lib/records';
 // Studio のトップと同じく、4コマは新しい4話
 const mangaCards = allManga.slice(0, 4);
@@ -57,6 +58,9 @@ const jsonLd = [
     <video class="hero-video" src={media("/media/hakobu_fv_voice_eleven.mp4") + "#t=0.01"} controls muted autoplay loop playsinline></video>
   </section>
 
+  <!-- このサイトの楽しみ方（AI に読ませて聞く）。何度も来ている人向けに、ヒーローのすぐ下・閉じると1行（2026-11-03） -->
+  <TopAiGuide />
+
   <!-- 解説動画：YouTube の新着を GitHub の videos.json から読む埋め込み（Studio で使っていたものをそのまま） -->
   <section class="sec"><div class="panel">
     <div class="embed-wide"><iframe src="/embeds/top-video.html" sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox" title="解説動画" data-embed style="height:450px"></iframe></div>
diff --git a/src/pages/record/[slug].astro b/src/pages/record/[slug].astro
index be1a5f6..40fe86e 100644
--- a/src/pages/record/[slug].astro
+++ b/src/pages/record/[slug].astro
@@ -113,8 +113,15 @@ const nowCard = seriesNow || siteNow;
 // 前回選んだ AI を見た人のブラウザに覚え（p2m-site-ai）、次から一番上に出す。押した数は /api/ev-stats（場所 ai）で見る
 const mdUrl = `${SITE}${fullTextPath(r.slug)}`;
 // 全文ページの住所に ?via=（どの AI から来たか）を付ける。AI が読みに来たとき、ボタン経由だと worker/index.js が分けて数える（/api/cr-stats の「AIで読む経由」）
+// Gemini のアプリはテキストのファイル（.md）を読まず HTML だけ読むので、Gemini には記事ページ（HTML）を読ませる（2026-11-03）
 const aiPromptFor = (via) => {
   const md = `${mdUrl}?via=${via}`;
+  if (via === 'gemini') {
+    const page = `${url}?via=${via}`;
+    return isEnglish
+      ? `Please read this article and answer my questions.\nArticle: ${page}\n\nFirst, briefly summarize what was done and what was found. Then tell me what I should check to reproduce it in my own environment.`
+      : `この記事を読んで、質問に答えてください。\n記事: ${page}\n\nまず、何をして何が分かったのかを短くまとめてください。そのあと、私が同じことを自分の環境で試すときに、確かめることを教えてください。`;
+  }
   return isEnglish
     ? `Please read this article and answer my questions.\nArticle: ${url}\nFull text (including code and logs): ${md}\n\nFirst, briefly summarize what was done and what was found. Then tell me what I should check to reproduce it in my own environment.`
     : `この記事を読んで、質問に答えてください。\n記事: ${url}\n全文（コード・実行ログを含む）: ${md}\n\nまず、何をして何が分かったのかを短くまとめてください。そのあと、私が同じことを自分の環境で試すときに、確かめることを教えてください。`;
diff --git a/worker/index.js b/worker/index.js
index 3eb8a53..97496c6 100644
--- a/worker/index.js
+++ b/worker/index.js
@@ -49,7 +49,8 @@ export default {
     // 検索エンジン・AI のクローラーが読みに来たか（記録の失敗でページを止めない）
     // 記事の「AIで読む」から来た全文ページ（?via=chatgpt など）は、名乗りが一覧にない読み手（Gemini など）も「その他」として残す（2026-11-01）
     try {
-      const via = /^\/record\/[^/]+\.md$/.test(url.pathname) && AI_VIA.has(url.searchParams.get('via') || '') ? url.searchParams.get('via') : '';
+      // TOP の「このサイトの楽しみ方」から来た /llms.txt?via=… も同じく数える（2026-11-03）
+      const via = (/^\/record\/[^/]+\.md$/.test(url.pathname) || (/^\/record\/[^/.]+$/.test(url.pathname) && url.searchParams.get('via') === 'gemini') || url.pathname === '/llms.txt' || url.pathname === '/llms' || url.pathname === '/llms.html') && AI_VIA.has(url.searchParams.get('via') || '') ? url.searchParams.get('via') : '';
       const bot = crawlerOf(request, url) || (via ? 'その他' : null);
       if (bot) ctx.waitUntil(crRecord(request, env, url, bot, res.status, via).catch(() => {}));
     } catch { /* 数え損ねてもページは返す */ }
```


## まだ確かめていないこと

- Perplexity は、まだ押して確かめていません
- 失敗した回の質問は、質問文が新しいものから3つと頼んでいるので、Claude と Gemini は直近の回を選びます。時期をまたいだ大きなつまずきを見せる質問文に変えるかは、まだ決めていません
- 公開したばかりの記録は、ChatGPT から全文を取れないことがあります。前に来たときからの質問で、一番新しい1〜2本が抜けることがあります
- 記事の全文ファイル（.md）は、Gemini の API が読めず、Claude も受け付けなかったことがあります。ページの種類の札（text/markdown）が原因の見込みですが、札を変えるかはまだ決めていません
