2026年4月29日水曜日

Node.js環境構築から始める「特許庁API検索アプリ」作成

 特許庁が提供する 特許情報取得API は、

特許検索・経過管理・知財DXに非常に有用です。

しかし、

  • ブラウザから直接呼べない(CORS制限)
  • ID・パスワードをJavaScriptに書けない
  • トークン管理が難しい

という理由から、Node.jsによるプロキシサーバ構築が実質必須になります。

本記事では、

👉 Node.jsのインストール(環境構築)
👉 プロキシAPIサーバ作成
👉 ブラウザ検索アプリ作成

まで、完全な手順書として解説します。

今回はRaspberry pi5のRaspberry Pi OSを使用しています。Ubuntuでも可能と思いますが、動かない場合は適宜手直ししていただれば幸いです。

 


1. Node.js をインストールする(最初にやること)

1-1. Node.jsとは?

Node.js JavaScriptをサーバ側で動かす実行環境 です。
今回のプロキシAPIサーバは Node.js で作成します。


1-2. Node.jsのインストール方法

LinuxRaspberry Pi OS Ubuntu

sudo apt update

sudo apt install nodejs npm

 

または、Node.jsを、以下のコマンでインストールします。

# 例えば、Node.js 18.x をインストール

curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -

sudo apt-get install -y nodejs

 


1-3. インストール確認

ターミナル(コマンドプロンプト)で以下を実行します。

node -v

npm -v

バージョンが表示されれば成功です。


2. プロジェクトフォルダを作成する

mkdir jpo-api-sample

cd jpo-api-sample


3. Node.jsプロジェクト初期化

npm init -y

必要なライブラリをインストールします。

npm install express cors dotenv


4. 環境変数ファイル(.env)を作成

プロジェクト直下に .env を作成し、以下を記述します。

JPO_USERNAME=あなたの特許庁APIユーザーID

JPO_PASSWORD=あなたの特許庁APIパスワード

⚠️ このファイルはGitHub等に公開しないでください


5. ① server.jsNode.js プロキシAPIサーバ)

以下を server.js として保存してください
(コードはそのままコピー&ペーストでOKです)。

// server.js — app_progress 専用版

import express from "express";

import cors from "cors";

// Node.js v18+ fetch が標準搭載

import "dotenv/config";

 

const app = express();

 

app.use(cors());

app.use(express.json());

app.use(express.urlencoded({ extended: true }));

 

// ==================================

// トークンキャッシュ

// ==================================

let cachedToken = null;

let tokenExpireAt = null;

 

// ==================================

// 特許庁 API: トークン取得

// ==================================

async function fetchTokenFromJpo() {

  const url = "https://ip-data.jpo.go.jp/auth/token";

  const params = new URLSearchParams();

  params.append("grant_type", "password");

  params.append("username", process.env.JPO_USERNAME);

  params.append("password", process.env.JPO_PASSWORD);

 

  const response = await fetch(url, {

    method: "POST",

    headers: { "Content-Type": "application/x-www-form-urlencoded" },

    body: params

  });

 

  const data = await response.json();

 

  if (data.access_token) {

    cachedToken = data.access_token;

    tokenExpireAt = Date.now() + data.expires_in * 1000 - 5000; // 期限5秒前更新

  }

  return data;

}

 

// ==================================

// 有効なトークンを返す関数

// ==================================

async function getValidToken() {

  const now = Date.now();

  if (cachedToken && tokenExpireAt && now < tokenExpireAt) {

    return cachedToken;

  }

  const newToken = await fetchTokenFromJpo();

  return newToken.access_token;

}

 

// ==================================

// 1) トークン取得 API

// ==================================

app.post("/api/token", async (req, res) => {

  try {

    const token = await getValidToken();

    res.json({

      access_token: token,

      cached: token === cachedToken

    });

  } catch (e) {

    res.status(500).json({ error: e.toString() });

  }

});

 

// ==================================

// 2) app_progress(特許経過情報)

// 出願番号(10桁)例: 2023063418

// ==================================

app.get("/api/app_progress/:number", async (req, res) => {

  try {

    const token = await getValidToken();

    const number = req.params.number;

    const url = https://ip-data.jpo.go.jp/api/patent/v1/app_progress/${number};

 

    const response = await fetch(url, {

      headers: {

        "Authorization": Bearer ${token}

      }

    });

 

    const text = await response.text();

 

    // -------------------------------

    // JSON でも HTML でも安全に返す

    // -------------------------------

    try {

      const json = JSON.parse(text);

      return res.status(response.status).json(json);

    } catch {

      return res.status(response.status).json({

        error: "JPO API returned non-JSON response",

        status: response.status,

        body: text

      });

    }

  } catch (e) {

    res.status(500).json({ error: e.toString() });

  }

});

 

// ==================================

// サーバ起動

// ==================================

app.listen(3000, () => {

  console.log(" app_progress 専用 JPO Proxy Server 起動!");

  console.log(" トークン取得: POST http://localhost:3000/api/token");

  console.log(" 経過情報: GET http://localhost:3000/api/app_progress/{出願番号}");

});


6. サーバを起動する

node server.js

以下が表示されれば成功です。

app_progress 専用 JPO Proxy Server 起動!


7. ② ブラウザアプリ(index.html

次に、検索用のHTMLを作成します。
以下を index.html として保存し、ブラウザで開いてください。

<!DOCTYPE html>

<html lang="ja">

<head>

  <meta charset="UTF-8">

  <title>特許経過情報(app_progress)検索</title>

  <style>

    body {

      font-family: sans-serif;

      margin: 40px auto;

      max-width: 700px;

    }

    label {

      font-weight: bold;

    }

    input {

      width: 100%;

      padding: 8px;

      margin: 8px 0 16px 0;

      font-size: 16px;

    }

    button {

      padding: 10px 20px;

      font-size: 17px;

      cursor: pointer;

    }

    pre {

      background: #f7f7f7;

      border: 1px solid #ccc;

      padding: 15px;

      white-space: pre-wrap;

      margin-top: 20px;

    }

  </style>

</head>

<body>

 

<h2>📄 特許経過情報(app_progress)検索</h2>

 

<label>出願番号(10桁数字:例 2023063418</label>

<input id="appNumber" placeholder="2023063418">

 

<button onclick="search()">検索する</button>

 

<pre id="result">ここに検索結果が表示されます</pre>

 

<script>

async function search() {

  const num = document.getElementById("appNumber").value.trim();

  const result = document.getElementById("result");

 

  if (!num.match(/^[0-9]{10}$/)) {

    result.textContent = "⚠️ 出願番号は10桁の数字を入力してください。";

    return;

  }

 

  result.textContent = "問い合わせ中…";

 

  try {

    const res = await fetch(http://localhost:3000/api/app_progress/${num});

    const json = await res.json();

    result.textContent = JSON.stringify(json, null, 2);

  } catch (err) {

    result.textContent = "エラーが発生しました:\n" + err;

  }

}

</script>

 

</body>

</html>


8. 動作確認

  1. server.js を起動
  2. index.html をブラウザで開く
  3. 出願番号を入力して検索

👉 特許庁の 経過情報(app_progress が表示されます。


まとめ

Node.jsインストールからスタート
セキュアな特許庁APIプロキシ構築
ブラウザ検索UI完成

この構成を使えば、

  • 社内システム連携
  • Excel / Power BI 連携
  • AI・分析基盤への接続

まで、知財DXの土台がすぐに作れます。

ぜひこの構成をベースに、応用・拡張してみてください。

2026年4月23日木曜日

Raspberry Pi 5 で Docker を使ってローカルLLM環境を構築する方法[Ollama + Open WebUI]

 Raspberry Pi 5 の性能を活用し、Docker と Docker Compose を用いて Ollama(ローカルLLM)Open WebUI(ブラウザ操作UI) を動かす手順をまとめます。

今回ご紹介する構成は、実際に Raspberry Pi 5 上でエラーなく動作した安定構成です。


🚀 1. このガイドで構築できる環境

  • Ollama(ローカルLLMエンジン)
    → Llama2、Mistral、TinyLlama などのLLMをローカルで動作

  • Open WebUI(ブラウザUI)
    → ChatGPT のようにブラウザからローカルLLMを操作可能

  • APIアクセス可能
    → 他のアプリケーションから HTTP API で利用可能

Raspberry Pi 5(8GB RAM推奨)で快適に動作します。


🧱 2. 必要なソフト

  • Docker

  • Docker Compose

  • Raspberry Pi OS 64bit または Ubuntu Server 64bit


🔧 3. Docker & Docker Compose のインストール

以下のコマンドを実行します:

Shell

sudo apt update && sudo apt upgrade -y

sudo apt install docker.io -y

sudo systemctl enable docker

sudo systemctl start docker


sudo apt install docker-compose -y



📦 4. 使用した docker-compose.yml

この構成でエラーなく動作しました。

YAML

services:

ollama:

image: ollama/ollama:latest

container_name: ollama

ports:

- "11434:11434"

volumes:

- ./ollama-data:/root/.ollama

restart: always


openwebui:

image: ghcr.io/open-webui/open-webui:main

container_name: open-webui

depends_on:

- ollama

ports:

- "3000:8080"

volumes:

- ./openwebui-data:/app/backend/data

environment:

- OLLAMA_API_BASE=http://ollama:11434

- USE_OLLAMA_DOCKER=true

- OLLAMA_BASE_URL=/ollama

- WEBUI_AUTH=False

restart: always


volumes:

ollama-data:

openwebui-data:



▶ 5. サービスの起動

docker-composeを入れたフォルダに移動して

Shell

docker-compose up -d

その他の行を表示する

起動後のアクセス:

サービス

URL

Open WebUI

http://<RaspberryPi_IP>:3000

Ollama API

http://<RaspberryPi_IP>:11434


🧪 6. モデルの利用

コンテナに入ってモデルをダウンロード:

Shell

docker exec -it ollama bash

ollama pull tinyllama:1.1b

ollama run tinyllama:1.1b



🎉 7. 結果

  • ローカルLLM(Ollama)が正常動作

  • Web UI(Open WebUI)もエラーなしで稼働

  • モデルデータも永続化され、再起動時も維持

  • Raspberry Pi 5 でも十分に実用的なローカルAI環境を実現


🔚 まとめ

Raspberry Pi 5 は Docker と組み合わせることで、
軽量かつ強力なローカルAIサーバーとして活用できます。

今回の構成はシンプルで扱いやすく、
AIプロジェクトや自宅サーバーに最適です。

2025年12月22日月曜日

シマノの自転車の自動変速と特許

シマノは2025年6月5日に、バッテリー不要で走行中に自動変速してくれる充電不要のAI学習型オートマチック変速システム「Q'AUTO(クォート)」を発表しました。
自転車の変速器は、ワイヤ変速から電動無線変速への技術革新があったのですが、とうとうAi自動変速にまで到達してしまいました。
今年はこの技術に着目し、シマノ公式WEBサイト、Youtubeおよび、特許情報プラットフォーム(J-PlatPat)での検索結果を、NotebookLMのソースにしてブログ記事を作成しました。


— 自己給電式Di2自動変速の革新と特許動向

シマノが開発した「Q'AUTO(クォート)」は、自転車の自動変速技術に新たな可能性をもたらしました。これは単なる電動変速の延長ではなく、バッテリーレスでライダーに合わせて学習する、革新的なシステムです。

ここでは、Q'AUTOの概要から、それを支えるシマノの特許出願動向までを解説します。


1. 概要:バッテリーレスで賢い自動変速 Q'AUTO

Q'AUTOは「自己給電式Di2自動変速」システムであり、一世紀にわたるサイクリングの技術革新によって実現されました。

この技術は、主にE-BIKE(電動アシスト自転車)のテクノロジーとして語られてきたオート変速の概念を覆し、バッテリーを搭載しない通常の自転車でもオート変速を可能にしました。その目的は、あらゆる自転車利用者に対し、常に最適なギア(in the right gear)を提供することで、サイクリングをより楽しく、身近なものにすることです。

Q'AUTOの主要な革新技術

1. バッテリーレス・自己給電式 Q'AUTOはバッテリーが不要で、充電もいらないバッテリーレスの設計です。変速に必要な電力は、後輪のフリーハブ内部に備えられた発電機(ダイナモ)によって、ペダルを回すたびに自ら発電し蓄えられます。

2. アダプティブラーニング(適応学習機能) Q’AUTOシステムは、AI学習機能を用いてライダーのライディングスタイルに適応します。フリーハブ内部には、速度、回転数(ケイデンス)、傾斜を計測する3つのセンサーが内蔵されており、走行状況の情報を収集します。

システムは、6,500以上のアルゴリズムパターンから最適な変速タイミングを選択し、時間が経つにつれライダーの好みに応じた変速を行うようになります。わずか6kmほどの走行で、システムは学習した内容を適用し始めます。



2. 特許出願動向

提供された特許出願リスト(2017年から2025年公開予定分まで)を参照すると、Q'AUTOの基盤となる自動変速技術分野において、株式会社シマノが集中的かつ継続的に知的財産を構築してきたことが示されています。

年ごとの件数の変遷

リストにある「出願日」を基準に、シマノがこの技術分野に出願した件数を分析すると、開発の波が明確に見て取れます。

出願年件数
20174件
201821件
201910件
20204件
202118件
202214件
20237件
2024*3件

*注:2024年以降は公開予定分(出願年ベース)

特徴的な動向

  1. 初期の集中投資 (2018年): 2018年に年間21件と非常に多くの出願が集中しています。これは、Q'AUTOのような高度な自動変速システムを実現するための基本的な制御ロジックやセンサー連携、AI学習モデルの基礎設計などがこの時期に集中的に行われたことを示唆しています。
  2. 技術の深化期 (2021年以降): 2019年、2020年に件数が落ち着いた後、2021年(18件)、2022年(14件)と再び高い水準で出願が継続しています。この時期には、アダプティブラーニング機能や、発電機(ダイナモ)による電力供給システム(例:特開2025-154149 (No. 3))といった、Q'AUTOの核となるバッテリーレスとAI制御に関する技術が深化されたと考えられます。
  3. 技術分野の広範性: 出願内容には、「B62M6/45」(電動補助車の駆動制御)、「B62M25/08」(変速機操作装置)といった変速制御の中核技術に加え、「B62J45/00」(サイクルコンピューターなど情報の入出力や学習モデル)に関する分類が多く含まれており、Q'AUTOが単なる変速機構ではなく、走行データを基にした高度な制御システムであることが裏付けられます。

3.該当する特許出願の簡単な説明

Q'AUTOの主要な機能を支えていると考えられる具体的な特許出願を挙げます。

文献番号発明の名称想定されるQ'AUTOとの関連
特開2025-154149 (No. 3)車両用の電力供給システム、車両用の給電装置...Q'AUTOの核心であるバッテリーレス自己給電機能 における、フリーハブ内部の発電機(ダイナモ)で生成した電力の管理や供給に関する技術開発を示唆しています。
特開2025-081130 (No. 7)人力駆動車制御装置、コンピュータプログラム、人力駆動車制御方法...ライダーの走行状況を分析し、最適な変速制御データを生成・適用する、アダプティブラーニング機能 のアルゴリズム開発に関するものです。
特開2023-085936 (No. 40)人力駆動車用制御装置、学習モデルの作成方法...変速タイミングを予測し最適化するために、AI学習モデルを自転車の制御装置に組み込む技術に関わっており、Q'AUTOの「6,500以上のアルゴリズムパターン」の土台となるものです。

4.まとめ

シマノのQ'AUTOは、「完全オート・充電いらずのDi2変速」を実現した革新的なテクノロジーです。このシステムは、自己給電機能とアダプティブラーニング機能を融合させることで、バッテリーレスの一般的な自転車に自動変速の利便性をもたらしました。

特許出願動向からは、特に2018年と2021年〜2022年という二つの大きな波で、変速制御、AI学習、電源管理といった核となる要素技術に対するシマノの集中的な研究開発投資が確認されます。Q'AUTOは、これらの長年の技術開発の積み重ねが結実し、ライダーが変速操作に煩わされることなく、純粋に自転車の楽しさを満喫できるように設計された、未来のサイクリング体験を提示するシステムと言えます。

5.参考

参考にしたWEB
https://bike.shimano.com/ja-JP/technologies/details/qauto.html
https://bike.shimano.com/ja-JP/stories/article/introducing-qauto.html
https://youtu.be/ICwtdHIHz1g?list=TLGG3rErrZk_L9gyMDEyMjAyNQ
https://youtu.be/nEXO9au1JE8?list=TLGGyhHQHwuiEAgyMDEyMjAyNQ

検索式(特許情報プラットフォーム|J-PlatPat )
[自転車/TX]*[変速/TX]*[人力駆動車/CL]*[モデル/TX+AI/TX+学習/TX]*[自動/TX]

自転車のデータ測定(Bluetooth)2025

 今年は、BLEのセンサをMATLAB(PC)に接続する試みです。MATLAB(PC)とXPLOVA NOZA one(負荷固定のサイクルトレーナー)とを直接接続しました。Arduinoを介さずに気軽にテストすることができます。


MATLABなので、BLE機器を接続するためにコマンドを用いますが、以下で接続してデータの確認までが可能です。

blelist
 →NOZA-ONE 27456-197が見つかりました。
b=ble("NOZA-ONE 27456-197")
 →接続します。
c = characteristic(b,"Cycling Power","Cycling Power Measurement")
 →パワーに関連するデータを取得します。
data = read(c)
 →dataにパワーに関連データが入り、内容を確認できます。

自転車をこぎながら、データを確認してみ明日。実際のデータは、以下のようになっており、データのどこが計測値なのか特定する必要があります。
XPLOVA NOZA oneは負荷が固定です。3番目(15)の値に着目すると、ペダルの回転数を上げていくと上昇するため、パワーに関する値(トルク×回転数)であると考えました。詳細は以下のURLに記載されています。

     0     1    15     0   221    17     0     0     0     0     0    97   187    42     0    32   188   42     0    32

Cycling Power Service
※URLが変更になる場合があるため、適宜検索で探してみてください。


やはり、コマンドたと都度結果を見ながら確認できるので、Simulink+Arduinoに比べると簡単にできると思います。

さらに、今回は取得したパワーに関連するデータを、MATLAB App Designer で可視化してみました。App Designer は、簡単にGUIが作れるのでとても有難いです。会社のMATLABでもデータ分析の自動化に活用しています。

MATLAB App Designerでは、上記コマンドを以下のように記述しています。先のペアリングのところがまだ改善の余地ありです。

------------------------------------------------------------------------------------
methods (Access = private)        

        function results = funciv(app)

            % noza = table2array(blelist(1,2));

            app.b=ble("NOZA-ONE 27456-197");

            app.stop = 0;

            results = app.b.Characteristics;

        end

       function results = funciv0(app)          

            c = characteristic(app.b,"Cycling Power","Cycling Power Measurement");

            results = read(c);

        end

    end
-----------------------------------------------------------------------------------

アプリ画面は、こんな感じです。時系列のグラフおよびゲージをメインにしました。確認のためのテーブルも追加してみました。
startボタンを押すと、MATLAB(PC)とXPLOVA NOZA oneとがペアリングして、計測が開始します。stopボタンを押すと計測が終了します。


以下が、ペダルを漕いでいるときに測定したデータになります。後半、ペダルの回転数を保ったまま、ギアを下げていくとパワーに関連する値が減少していくことが確認できました。
右上のゲージも意外と良い感じでした。

今まで、測定値の可視化までなかなかできていなかったので、ここまでできてとても嬉しいです。リアルタイムで、測定値を見みられるのはとても良いですね。あとは、物理値との整合性です。ここは、来年の課題になってしまいました。


表示できる値をもう少し増やして、もう少し見栄えをよくするとさらに良くなりそうです。一旦データ収集アプリを作ることに方向転換して、その後にSimulink+Arduinoに戻ろうかと思います。


2025年8月14日木曜日

ミニPC(GMKtec G10)の電源

 65Wの電源を購入して、ACアダプタを持ち歩かなくてもよくなりました。

エレコム USB PD 充電器 65W 1ポート

UGREEN USB Type CケーブルPD対応100W/5A


Office、Matlabなど必要なソフトウェアもインストールしました。


2025年7月27日日曜日

ミニPC

以前使っていたミニPCが壊れてしまったので、新しく購入しました。
持ち歩きように使います。
今回は、SSD、メモリーが交換できるタイプにしてみました。
ACアダプタが65W必要で、以前使っていた45WのACアダプタは使えませんでした。

 GMKtec G10 mini pc

2025年5月28日水曜日

新しいロードバイク

 ロードバイクを組み立てました。カーボンのフレームは初めでです。

INTERMAX Ivector

フレーム+フォークを格安で手に入れることができました。

ステムなども同じところから入手しています。

必要になった工具などを入れると20万円を超えてしまいますが、

部品代だけだと、電動コンポにも関わらずほぼ20万円で収まっています。

とても良い買い物ができました。

ハンドルも、InterMax オリジナルアロイです。

サドルは検討中です。写真のパナソニックのものは、レーパンだと股ズレしてしまいました。