2020/07,江端さんの技術メモ

	m_conn->prepare(                    // HACK 1. 2文を1文に(PQExec仕様)、2. JOIN周り、 3. ORDER BYを消せないか?
		"find_shortest_path_by_dijkstra_calc",
		R"(
			INSERT INTO s_dijkstra_memoization (source, target, node, edge, length_m)
			
				SELECT
					$1, $2, d.node, d.edge, coalesce(ways.length_m, 0) AS length_m
				FROM
					pgr_dijkstra (
						'SELECT gid AS id, source, target, length_m AS cost from ways',
						start_vid:=$1::bigint, end_vid:=$2::bigint, directed:=false::boolean
					) as d LEFT JOIN ways ON ways.gid = d.edge
			
			WHERE NOT EXISTS (SELECT source, target FROM s_dijkstra_memoization WHERE source = $1 AND target = $2);
		)",
					//RETURNING node, edge, length_m;
		2
	);

のところで、問題が発生しているのは分かるのだけど、私にとっては上記な呪文であり、さっぱり分からない。

とりあえず、psql から地道に部分的に動かしてみて、問題点を探っている。

ca_sim=# SELECT gid, source, target, length_m AS cost from ways;

gid | source | target | cost
------+--------+--------+--------------------
1 | 55 | 23 | 121.17749638577058
2 | 844 | 1 | 710.1279290280786
3 | 4807 | 2 | 40.26044319993349
4 | 6296 | 3 | 251.21876036796243
5 | 22 | 4 | 2181.7785758382133
6 | 18 | 6 | 435.2439693722031
7 | 4304 | 6 | 950.7269653866767
8 | 6289 | 7 | 29.068747479235597

となるが、

ca_sim=# SELECT * FROM pgr_dijkstra(
'SELECT gid, source, target, length_m AS cost FROM ways',
2, 3
);
ERROR: Column 'id' not Found
CONTEXT: SQL function "pgr_dijkstra" statement 1

となる。pgr_dijkstraを実行する為には、パラメータの名前まで注意しなければなならないらしい。

ca_sim=# SELECT * FROM pgr_dijkstra(
'SELECT gid AS id, source, target, length_m AS cost FROM ways',
2, 3
);
seq | path_seq | node | edge | cost | agg_cost
-----+----------+------+------+--------------------+--------------------
1 | 1 | 2 | 342 | 224.26753559377073 | 0
2 | 2 | 229 | 8773 | 295.36268951709405 | 224.26753559377073
3 | 3 | 6359 | 8771 | 41.04204131422657 | 519.6302251108648
4 | 4 | 6357 | 344 | 136.12897946273213 | 560.6722664250914
5 | 5 | 231 | 8688 | 58.55503032553214 | 696.8012458878235
6 | 6 | 6294 | 8690 | 191.73693713817062 | 755.3562762133556
7 | 7 | 6296 | 4 | 251.21876036796243 | 947.0932133515262
8 | 8 | 3 | -1 | 0 | 1198.3119737194886
(8 rows)

くるしまぎれに、

start_vid:=$1::bigint, end_vid:=$2

の部分に、

1、2

を、実数でベタ書きしたら、エラーが取れて、シミュレーションが走りだした(原因不明)

しかし、

start_vid:=$1::bigint, end_vid:=$2 → $1, $2

では、

ERROR: function pgr_dijkstra(unknown, unknown, unknown, directed => boolean) is not unique
LINE 7: pgr_dijkstra(
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.

Error:ERROR: function pgr_dijkstra(unknown, unknown, unknown, directed => boolean) is not unique
LINE 7: pgr_dijkstra(
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.

となってしまう。


ブログを読んで頂いたからから、以下のアドバイスを頂いたので、その観点より挑戦。

上の箇所の$1や$2は、SQLインジェクションされないためのprepare文のプレースホルダーです。
ドキュメントの該当箇所は次のものです。
https://www.postgresql.org/docs/current/sql-prepare.html
$1と$2に該当するものをうしろから代入してあげれば動く(はず)と思います。
MySQLは、たしか「?」がプレースホルダで、SQLiteも「?」がプレースホルダ(https://docs.python.org/ja/3/library/sqlite3.html をみたら、合ってました)だったように覚えています。PostgreSQLは$+数字をつかうことになっています。

そこで、このページを印刷しながら、比較して、色々試してみた

                pgr_dijkstra(
                    (SELECT gid AS id, source, target, length_m AS cost from ways),
                    start_vid:=$1::bigint, end_vid:=$2::bigint, directed:=false::boolean
                ) as d LEFT JOIN ways ON ways.gid = d.edge

ERROR:  subquery must return only one column
LINE 8:       (SELECT gid AS id, source, target, length_m AS cost fr...
              ^

Error:ERROR:  subquery must return only one column
LINE 8:       (SELECT gid AS id, source, target, length_m AS cost fr...
            FROM
                pgr_dijkstra(
                    'SELECT gid AS id, source, target, length_m AS cost from ways',
                    (start_vid:=$1::bigint), (end_vid:=$2::bigint), directed:=false::boolean
                ) as d LEFT JOIN ways ON ways.gid = d.edge
ERROR:  syntax error at or near ":="
LINE 9:       (start_vid:=$1::bigint), (end_vid:=$2::bigint), direct...
                        ^

Error:ERROR:  syntax error at or near ":="
LINE 9:       (start_vid:=$1::bigint), (end_vid:=$2::bigint), direct...

すでに試してみたが、これもやってみた

                pgr_dijkstra(
                    'SELECT gid AS id, source, target, length_m AS cost from ways',
                    $1, $2, directed:=false::boolean
                ) as d LEFT JOIN ways ON ways.gid = d.edge
ERROR:  function pgr_dijkstra(unknown, unknown, unknown, directed => boolean) is not unique
LINE 7:      pgr_dijkstra(
             ^
HINT:  Could not choose a best candidate function. You might need to add explicit type casts.

Error:ERROR:  function pgr_dijkstra(unknown, unknown, unknown, directed => boolean) is not unique
LINE 7:      pgr_dijkstra(
             ^
HINT:  Could not choose a best candidate function. You might need to add explicit type casts.

なんかエラーが増えてきたようだ(unknownが3つになった)。

他の行と比較してみながら、start_vid と end_vid を消してみた(これが記載されていない別の行があったので)

            FROM
                pgr_dijkstra(
                    'SELECT gid AS id, source, target, length_m AS cost from ways',
                    $1::bigint, $2::bigint, directed:=false::boolean
                ) as d LEFT JOIN ways ON ways.gid = d.edge

これで動いた。

けど、理由はさっぱり分かりません。$1::bigint, $2::bigint がキャストの書式?

トライアンド&エラーでやろうとすると、こういう目にあう、という良い教訓として下さい。

2025,江端さんの技術メモ

はい、Mouse without Borders はホストを含めて最大 4台のPC まで接続して動作します。
したがって、今お使いのホストPCに加えてあと2台、合計3台のマシンを同時に制御することが可能です。

  • ホストPC … 実際にマウス・キーボードが接続されているPC

  • クライアントPC … 追加で操作するPC(最大3台)

設定画面で各PCのセキュリティコードコンピュータ名を入力すれば、3台構成でも問題なく利用できます。

もし希望があれば、3台目を追加する際の具体的な接続手順も説明できます。

中央

右側

左側

現在3台のマシンがあります。

左側の設定画面です。
問題点は 
(1)中央のマシンのマウスは、中央と右側を動き
(2)右側のマシンのマウスも、中央と右側を動き
(3)左側のマシンのマウスは、左側と中央を動き

3台共通して動かないということです。対処方法を教えてください

画像3枚を確認しました。状況を見ると、Mouse without Borders の「接続」が中央と片側のPCだけで完結していて、3台を一つのマトリックスとして認識できていません。
これだと、中央マシン経由の2台運用はできても、左 ↔ 右 の直接移動ができない状態になります。


原因

  • 全てのマシンが同じ「Security Key」と同じネットワーク上にある必要がありますが、現在はそれぞれの接続が部分的にしかリンクしていません。

  • 3台を同一の「Computer Matrix」に登録する設定が完了していません。
    (現状は、中央–右、中央–左のペア接続になっている)


対処手順(全台で共通設定にする)

  1. Security Key を統一

    • 左・中央・右、すべてのマシンで「Shared encryption key」の値を同じにします。

    • 今回の画像を見ると左と中央は tS3_hJ5-jK7_fD9/、右は非表示ですが違っている可能性があるため、手入力で合わせます。

    • 入力後「Apply」を押す。

  2. 全マシンを互いに認識させる

    • 各マシンの設定画面で、右下の「Go through the setup experience」をクリック。

    • 表示された「This is the new computer」に、ホスト(マウス・キーボードを使う中心PC)のSecurity Keyとコンピュータ名を入力。

    • これを、全てのマシン間で行い、3台すべてのサムネイルが表示される状態にする。

  3. Computer Matrix の並びを調整

    • 設定画面で3台のサムネイルをドラッグして、物理的な配置(左・中央・右)と同じ並びにする。

    • 「Two Row」のチェックは不要なら外す。

    • 「Apply」を押して保存。

  4. テスト

    • いずれかのマシンからマウスを動かし、左右両方向に移動できることを確認。

    • 全台でキーボード入力も共有されるか確認。


💡 ポイント

  • Mouse without Borders は全員が同じ Security Key を共有し、全台が同じ「Computer Matrix」に表示されている状態でないと、完全な3台運用になりません。

  • 途中のマシン経由でしか動かせない場合は、片方のPCがもう一方を認識していない証拠です。

2025,江端さんの技術メモ

pybind11とは何ですか

pybind11 とは、C++で書かれたコードをPythonから簡単に呼び出せるようにするライブラリです。具体的には、C++関数・クラスをPythonにバインディング(橋渡し)するための軽量なヘッダオンリーライブラリです。


✅ 要点まとめ

項目 内容
名称 pybind11
用途 C++コードをPythonから使えるようにする(バインディング)
特徴 ヘッダオンリー、軽量、C++11以降対応
主な代替 Boost.Python、SWIG など
主な用途 Pythonから高速なC++シミュレータや計算ライブラリを使うときなど

🧠 なぜ使うのか?

Pythonは書きやすいが遅い、C++は速いけど扱いにくい。
そのため、重い処理はC++で書き、インターフェースだけPythonで操作したいことがよくあります。

text
たとえば:
Python部分 → 学習ロジック(強化学習など)
C++部分 → シミュレータの高速実行

この「橋渡し」に pybind11 が使われます。


🔧 使用イメージ

C++側(example.cpp)

#include <pybind11/pybind11.h>

int add(int i, int j) {
    return i + j;
}

PYBIND11_MODULE(example, m) {
    m.def("add", &add, "A function which adds two numbers");
}

Python側

import example
print(example.add(1, 2))  # → 3

このように、C++の関数をまるでPythonで書いた関数のように使えるようになります。



🔚 まとめ

観点 内容
概要 C++とPythonをつなぐためのヘッダオンリーライブラリ
主目的 C++で書いた高速なロジックをPythonから呼び出す
利点 軽量・高速・簡単・CMakeと相性良好
使用場面 シミュレーション、機械学習、画像処理、ゲームエンジン等のバックエンド連携

必要であれば、あなたの railway_simulator モジュールが pybind11 で作られているかどうかも確認する手順を案内できます。

使用イメージを実際に動かすにはどうすれば良いですか

以下に、pybind11 を使って C++ 関数を Python から呼び出せるようにする最小構成の実行手順をまとめます。
Linux / WSL / Mac / MSYS2 などの UNIX互換環境を前提にしています(Windows でも MSYS2 や WSL を使えば同じです)。


✅ ゴール

C++ の関数を Python から呼び出せるようにする:

cpp
int add(int a, int b) { return a + b; }

↓ Pythonから:

python
>>> import example
>>> example.add(1, 2)
3

🔧 準備

① Python と pybind11 のインストール

以下をターミナルで実行:

bash
pip install pybind11

(C++コンパイラが必要です:gcc, clang, cl など)


📁 ② ファイルを作成

example.cpp(C++側)

#include <pybind11/pybind11.h>
int add(int i, int j) {
    return i + j;
}
PYBIND11_MODULE(example, m) {
    m.def("add", &add, "A function which adds two numbers");
}

⚙️ ③ ビルド(共有ライブラリ作成)

以下のコマンドをそのままコピー&ペースト:

c++ -O3 -Wall -shared -std=c++11 -fPIC \
    `python3 -m pybind11 --includes` \
    example.cpp -o example`python3-config --extension-suffix`

このコマンドは:

  • pybind11 のヘッダを含めて

  • example.so(もしくは .pyd)というPython拡張モジュールを作成します


✅ ④ 実行(Pythonから呼び出し)

ターミナルで Python を起動し、次を試してください:

>>> import example
>>> example.add(10, 5)
15

成功すれば pybind11 のバインディング成功です!


📎 注意点

項目 内容
ビルドは1行で 改行してしまうと command not found になります。1行にしてください
Windows の場合 -fPIC は不要/MSYS2環境を推奨します
pybind11 の場所 python3 -m pybind11 --includes により自動取得できます
Python バージョン python3.11 など環境によって異なる場合があります。必要なら python3-config を変更してください

✅ もしうまくいかなかったら…

次の情報を貼ってもらえれば、個別にビルドコマンドを調整できます:

  • OS種別(Windows / Mac / Ubuntu / WSL など)

  • Pythonバージョン(python3 --version

  • コンパイラの種類(gcc --version など)


必要であれば、CMakeLists.txt を使った構成や、.pyx ファイルとの違い(Cython との比較)についてもご説明します。

c++ -O3 -Wall -shared -std=c++11 -fPIC \
    python3 -m pybind11 --includes \
    example.cpp -o examplepython3-config --extension-suffix
> > /home/ebata/.pyenv/versions/3.8.10/bin/python3: No module named pybind11
example.cpp:1:10: fatal error: pybind11/pybind11.h: No such file or directory
    1 | #include <pybind11/pybind11.h>
      |          ^~~~~~~~~~~~~~~~~~~~~
compilation terminated.

と言われました

ありがとうございます。エラーメッセージを解析すると、以下の2つの問題が発生しています:


❌ 問題1:pybind11 がインストールされていない

bash
/home/ebata/.pyenv/versions/3.8.10/bin/python3: No module named pybind11

これは pybind11Python環境に未インストールであることを示しています。


❌ 問題2:C++ヘッダファイル pybind11/pybind11.h が見つからない

bash
example.cpp:1:10: fatal error: pybind11/pybind11.h: No such file or directory

これは Python モジュールが見つからなかったため、--includes で得られるインクルードパスも空になり、結果として pybind11.h が見つからないという流れです。


✅ 解決手順

以下の手順に従って修正してください。


① pybind11 を正しい Python 環境にインストール

あなたは pyenv を使って Python 3.8.10 を使っているようなので、次のコマンドを使います:

bash
~/.pyenv/versions/3.8.10/bin/pip install pybind11

※または pyenv の環境をアクティベートしてから:

bash
pyenv activate 3.8.10
pip install pybind11

② 再度ビルドコマンドを実行

インストール後、以下の1行コマンドを再度実行してください:

c++ -O3 -Wall -shared -std=c++11 -fPIC \
    `python3 -m pybind11 --includes` \
    example.cpp -o example`python3-config --extension-suffix`

(必要なら python3~/.pyenv/versions/3.8.10/bin/python3 に書き換えてください)


✅ うまくいけば…

カレントディレクトリに example.cpython-38-x86_64-linux-gnu.so のようなファイルができます。

Pythonで次のように実行できれば成功です:

bash
python3
>>> import example
>>> example.add(2, 3)
5

📌 備考(トラブル防止)

内容 対処法
複数バージョンの Python がある 明示的に ~/.pyenv/versions/3.8.10/bin/python3 を使う
pip install したのに ModuleNotFoundError pippython が同じ環境を見ているか確認(例:which pip, which python3
pybind11 のヘッダが見つからない python3 -m pybind11 --includes の出力結果を確認して -I/path/to/pybind11/include を手動で追加

2025,江端さんの技術メモ

以下に、Go言語で実装するシンプルな mTLS(相互TLS認証)通信プログラムを示します。
これは、サーバがクライアント証明書の検証を行い、クライアントがサーバに接続してデータを送るという最小構成のデモです。


✅ ファイル構成

.
├── server.go ← mTLS対応サーバ
├── client.go ← mTLS対応クライアント
├── ca.crt ← ルートCA証明書
├── server.crt ← サーバ証明書(caで署名)
├── server.key ← サーバ秘密鍵
├── client.crt ← クライアント証明書(caで署名)
└── client.key ← クライアント秘密鍵

🖥️ サーバ側(server.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

func main() {
	// クライアント証明書を検証するためのCA証明書読み込み
	caCert, err := os.ReadFile("ca.crt")
	if err != nil {
		log.Fatal(err)
	}
	caPool := x509.NewCertPool()
	caPool.AppendCertsFromPEM(caCert)

	// TLS構成
	tlsConfig := &tls.Config{
		ClientAuth: tls.RequireAndVerifyClientCert,
		ClientCAs:  caPool,
		MinVersion: tls.VersionTLS12,
	}

	server := &http.Server{
		Addr:      ":8443",
		TLSConfig: tlsConfig,
	}

	http.HandleFunc("/secure", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "mTLS通信成功!")
	})

	log.Println("mTLSサーバ起動中:https://localhost:8443/secure")
	err = server.ListenAndServeTLS("server.crt", "server.key")
	if err != nil {
		log.Fatal(err)
	}
}

💻 クライアント側(client.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

func main() {
	// クライアント証明書と鍵の読み込み
	cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
	if err != nil {
		log.Fatal(err)
	}

	// サーバ証明書を検証するためのCA証明書読み込み
	caCert, err := os.ReadFile("ca.crt")
	if err != nil {
		log.Fatal(err)
	}
	caPool := x509.NewCertPool()
	caPool.AppendCertsFromPEM(caCert)

	// TLS設定
	tlsConfig := &tls.Config{
		Certificates:       []tls.Certificate{cert},
		RootCAs:            caPool,
		InsecureSkipVerify: false,
	}

	client := &http.Client{
		Transport: &http.Transport{TLSClientConfig: tlsConfig},
	}

	// サーバにリクエスト
	resp, err := client.Get("https://localhost:8443/secure")
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Printf("サーバ応答: %s\n", string(body))
}

🔐 証明書の作成(OpenSSL例)

# CA作成
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=TestCA" -days 3650 -out ca.crt

# サーバ証明書
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=localhost" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650

# クライアント証明書
openssl genrsa -out client.key 2048
openssl req -new -key client.key -subj "/CN=myclient" -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 3650

✅ 実行方法

  1. 上記のOpenSSLコマンドで証明書類を生成
  2. server.go を起動

  3. client.go を別ターミナルで起動

以下のエラーが発生

ebata@DESKTOP-1QS7OI7:~/test_tls$ go run client.go
2025/07/29 02:33:59 Get "https://localhost:8443/secure": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead

x509: certificate relies on legacy Common Name field, use SANs instead
→ Go の TLS ライブラリ(特に Go 1.15 以降)では、Common Name(CN)だけの証明書ではサーバ認証に失敗します。
現在は SAN(Subject Alternative Name) が必要です。


✅ 対応策:証明書を SAN を含めて再生成する

OpenSSL の設定を使って、SAN付き証明書を発行します。


🔧 手順(openssl

1. san.cnf という設定ファイルを作成:

# san.cnf
[req]
default_bits       = 2048
distinguished_name = req_distinguished_name
req_extensions     = req_ext
prompt             = no

[req_distinguished_name]
CN = localhost

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost

2. サーバ証明書を SAN付きで再生成:

# サーバ鍵生成
openssl genrsa -out server.key 2048

# CSR作成
openssl req -new -key server.key -out server.csr -config san.cnf

# 証明書発行(CAで署名)
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out server.crt -days 3650 -extensions req_ext -extfile san.cnf

✅ 説明

項目 内容
CN Common Name。旧来のFQDN指定(非推奨)
SAN Subject Alternative Name。現在はこちらが必須
localhost クライアントから接続するホスト名と一致させる必要あり

GoはこのSANにlocalhostが含まれていないとTLS検証に失敗します。


✅ 再実行の流れ(修正後)

go run server.go
# → 起動確認「mTLSサーバ起動中:https://localhost:8443/secure」

go run client.go
# → サーバ応答: mTLS通信成功!

2025,江端さんの技術メモ

C:\の128GBが限界に達していて、このままではWindow updateもできないので、C:\128GのSSDを500GのSSDに交換することにしました。

で、業者に問い合わせしてみたら、SSD抜きの作業で1万4000円~という連絡がきまして、「ちょっと高いなぁ」と思い、自力での交換に挑戦してみました。

今回購入したのは、この2つ。

(クリックするとamazonに飛びます)

業者に頼むより1万円ほど安いですが、リストア失敗を考えると1万円なら業者に頼んでもよかったかもしれません。実際、PCの中を明けて、どこに部品があるのか探し回ることになりました。

そんでもって、それをリストアするのにもひと苦労しました。

今回は、業者に預けている時間(2~3日くらい)の方が問題でした。私は、明日も、このPCで作業したかったので。

------

最近は、マニュアル読まなくなりました。でも、こういう動画があれば、マニュアルより安心して作業できます。

一発目のシステムの再起動が上手くいかず、青冷めましたが、その後、安定して起動するようになり一安心です(このドキドキが嫌いです。1万円ならペイする不安感です)。

で、最後は、ChatGPTの登場です。

画像をアップロードしました

ディスク1のクローンを作成したのですが、C:\にはあいからわず108.91GBしかありません。346.52GBの未割り当て領域をC:\に加えて拡張したいです。手順を教えて下さい

「ディスクの管理」画面を見る限り、ディスク1上のCドライブ(108.91GB)に続けて未割り当て領域(346.52GB)が存在しているため、以下の手順で Cドライブを拡張 できます。


✅ 手順:Windows標準機能「ディスクの管理」でCドライブを拡張する方法

  1. 「ディスクの管理」を開いた状態のまま、Cドライブ(108.91 GB)を右クリック

  2. 「ボリュームの拡張(E)...」を選択

  3. 「ボリューム拡張ウィザード」が表示されるので、「次へ(N)」をクリック

  4. 「使用可能な最大サイズ」が既に入力されていることを確認して「次へ(N)」

  5. 「完了(F)」をクリック


⚠ 注意点

  • 上記は「Cドライブの直後に未割り当て領域がある場合」に限って有効です。

  • もし他のパーティションがC:と未割り当て領域の間にある場合は、この方法は使えません(サードパーティ製ソフトが必要になります)。


💡 補足

  • 操作後、Cドライブは 108.91 + 346.52 ≒ 455.43GB に拡張されます。

  • システムドライブの操作なので、念のため事前にバックアップを取っておくと安全です。

これで、明日は安心して開発環境のインストールができます。

-----

なんやかんやで、また1万円ほど使ってしまいましたが、こういう作業も含めて、『これが私の趣味みたいなものかなぁ』と、半ば諦めています。

あまり、共感得られにくい趣味です ーー 引きこもりなら「ゲーム」方がウケは言いだろうな、と思います。

学業や業務の為に「システムのリストア」をしている"引きこもり"は、理解を得られにくいように思います。

 

2025,江端さんの技術メモ

WSLでWebサーバ(テスト)を立ち上げる方法

前提: wslにはUbuntuが入っていて、emacsなどもすでに入っている

(1)スタートメニューで Ubuntu を検索 → 起動すれば、確実にUbuntuが立ち上がります。

(2)デフォルトディレクトリは、/home/ebata で、Windowsからこの場所を見るには、\\wsl$\Ubuntu\home\ebata にアクセス(エクスプローラー)

(3)make my_web_project して、cd my_web_projectして、emacsで、index.htmlを作る。
こんな感じindex.htmlを作っておく。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>WSL Webサーバ テストページ</title>
</head>
<body>
  <h1>ようこそ!</h1>
  <p>これは WSL 上で動作している Web サーバから配信されたページです。</p>
  <p>このページは <code>index.html</code> です。</p>
</body>
</html>

(4)ebata@DESKTOP-1QS7OI7:~/my_web_project$ python3 -m http.server 8888 でサーバを立ち上げる

(5)これで、ローカルから、http://localhost:8888/ でアクセスできる。

(6)ところが、リモートから、http://192.168.0.3:8888/ ではアクセスできない

(7)そこで、方法①(推奨)Windows側でポートフォワーディングを設定
Windows PowerShell を 管理者として実行して以下を実行:
netsh interface portproxy add v4tov4 listenport=8888 listenaddress=0.0.0.0 connectport=8888 connectaddress=127.0.0.1

さらに Windowsファイアウォールでポート 8888 を許可してください:
New-NetFirewallRule -DisplayName "AllowPort8888" -Direction Inbound -LocalPort 8888 -Protocol TCP -Action Allow

(8)これで、リモートから、http://192.168.0.3:8888/ ではアクセスできるようになる

2025,江端さんの技術メモ

取り敢えず、192.168.0.8  の c:\Users\tomoi\rag_projectの中身でやったこと一覧

Ollamaは、どうやって知識を蓄えることができますか?

Ollama自体(正確にはOllama上で動くLLM)は「知識を蓄える(=記憶を永続化する)」機能は原則持っていません。理由と補足は以下の通りです:


🔍 なぜOllama(+LLM)は知識を蓄えないのか?

  • Ollamaが実行するのは事前学習済みの言語モデル(例:LLaMA 3やMistral)であり、内部に知識を書き込む(=学習させ直す)機能はない

  • 一時的な文脈(会話履歴など)は保持できるが、Ollama再起動や会話のリセットで消える。


✅ 知識を“蓄える”ように使いたい場合の方法

方法1: ベクトルDB+RAGで「覚えさせる」

RAG (Retrieval-Augmented Generation) という手法を使うと、ローカルLLMでも“知識を覚えたように使う”ことができます。

構成例:

要素 内容
LLM Ollama + llama3/mistral
知識DB ベクトルDB(例えば Chroma, Weaviate, FAISS など)
統合フレームワーク LangChain, LlamaIndex など
処理内容 PDF, テキスト, Web等 → 分割 → ベクトル化 → 質問に応じて該当知識を検索 → LLMに渡す

📌 つまり、「知識」を蓄えるのはLLMではなく、外部のベクトルデータベースです。


方法2: チューニング(学習)で知識そのものを埋め込む

これは本当の意味での「知識の蓄積」になりますが、難易度が高くなります。

方法

方法名 内容 難易度
LoRA(Low-Rank Adaptation) 特定の知識をモデルに微調整として埋め込む 中〜上
Full fine-tuning モデル全体を再学習 非現実的(高コスト・GPU必須)

🧠「覚えているように見せる」簡易テク

  • LangChainやLlamaIndexで、テキストやPDFファイルを渡すだけで“知識として答える”ように見せられる。

  • これにより、対話履歴を記憶しているように錯覚させることも可能です。


現在、Ollamaが動いていますが、知識を“蓄える”ようにしたいですが、その前にRAGとチューニングの違いについて教えて下さい

はい、非常に重要な質問です。
以下に「RAG」と「チューニング(LoRAなど)」の違いと使い分けを、わかりやすく解説します。


🧠 RAG(Retrieval-Augmented Generation)とは?

▶ 特徴

  • LLMの外部に知識を持たせる。

  • 質問に応じて必要な情報を検索してLLMに与える。

  • モデル自体は一切変更しない。

▶ イメージ

LLMが「図書館で調べながら答えてくれる」ような使い方

▶ メリット

  • 高速かつ安全(モデル改変なし)

  • データの更新が簡単(PDF差し替えなど)

  • ローカルでも構築しやすい

▶ デメリット

  • 検索精度に依存する(ベクトル検索や分割方法が重要)

  • 外部検索結果に基づくため、「推論能力」に制限あり


🔧 チューニング(Fine-tuning / LoRA)とは?

▶ 特徴

  • LLMのパラメータ自体を再学習する。

  • モデル内部に知識を埋め込む。

  • LoRA(Low-Rank Adaptation)を使うと軽量に実現可能。

▶ イメージ

LLMが「訓練して記憶を更新した頭脳を持っている」ような使い方

▶ メリット

  • 対話スタイルやドメイン知識を完全に内面化できる

  • オフラインでも即答できる(検索なし)

▶ デメリット

  • 訓練が難しい(GPU・スクリプト・パラメータ設計)

  • データの入れ替えが容易でない(再チューニングが必要)

  • 学習エラーで性能が落ちることもある


🔄 RAG vs チューニング 比較表

比較項目 RAG チューニング(LoRA含む)
知識の格納先 外部DB(検索) LLM本体(学習)
導入の簡単さ ◎ 簡単 △ 難易度高
データ更新 ◎ 簡単(ファイル差替え) ✕ 毎回再学習が必要
実行速度 ◯(検索+推論) ◎(即応)
精度の安定性 ◯ 安定 △ 調整による
用途 FAQ・文書検索 特定業務への最適化

🎯 どちらを使うべきか?

目的 選択すべき手法
社内文書を検索しながら答えさせたい ✅ RAG
マニュアルからFAQを生成させたい ✅ RAG
専門用語に強いAI秘書を作りたい ✅ チューニング(LoRA)
自分の書き癖や口調を覚えさせたい ✅ チューニング(LoRA)

まずは、「Ollama + RAG(ローカルでの知識追加)」の構築方法を教えて下さい。なおOllamaはWindows11で動いています

Windows 11 上で動いている Ollama に ローカル知識を与える(RAG構成) ためには、以下のステップを踏むのが最も簡単かつ安定です。

✅ 目標

「PDFやテキストをローカルに保存 → 質問 → Ollamaがその内容に基づいて答える」構成を作る。


🛠 必要なツール構成

ツール 目的
Ollama LLM本体(例:llama3)
LangChain or LlamaIndex チャットの実行+外部知識連携
ベクトルDB 知識の保存・検索(Chromaを使用)
Python スクリプト処理

🪜 手順一覧

① Python + ライブラリをインストール

  1. Python 3.10以上をインストール(公式サイト

  2. 以下をインストール:

pip install langchain llama-index chromadb ollama

③ 知識を追加する方法

  • docs/example.txt に内容を書いて保存(例:社内マニュアル、FAQなど)

  • PDFなら PyMuPDF を使って読み込めます:

pip install pymupdf
python
from langchain.document_loaders import PyMuPDFLoader
loader = PyMuPDFLoader("docs/manual.pdf")

📁 まとめ:フォルダ構成例

rag_project/
├── docs/
│ └── example.txt ←知識ソース ├── rag_ollama.py ←実行スクリプト  

✅ 必要なインストール

以下をすべてインストール済みであることを確認してください:

bash
pip install -U langchain langchain-community langchain-ollama chromadb
ollama run nomic-embed-text # 埋め込み用モデル
ollama run llama3 # LLM本体

🆕 rag_ollama.py(最新版)

from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain_ollama import OllamaEmbeddings, OllamaLLM

# 1. 知識となるテキストを読み込み
loader = TextLoader("docs/example.txt", encoding="utf-8")
documents = loader.load()

# 2. テキストをチャンクに分割
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
texts = text_splitter.split_documents(documents)

# 3. Ollama埋め込みモデルでベクトル化
embedding = OllamaEmbeddings(model="nomic-embed-text")

# 4. ChromaでベクトルDBに保存(自動永続化)
db = Chroma.from_documents(texts, embedding, persist_directory="./chroma_db")

# 5. Ollama本体モデル(例:llama3)
llm = OllamaLLM(model="llama3")

# 6. RAG構成(Retriever + LLM)
qa = RetrievalQA.from_chain_type(llm=llm, retriever=db.as_retriever())

# 7. 対話ループ
print("終了したいときは 'exit' または 'quit' と入力してください。\n")
while True:
    query = input("質問 > ")
    if query.strip().lower() in ["exit", "quit"]:
        break
    result = qa.invoke({"query": query})
    print("答え:", result["result"])

📁 フォルダ構成例

rag_project/
                  ├── rag_ollama.py
                  ├── chroma_db/ ← 自動生成される(永続保存)
                  └── docs/
                                 └── example.txt ← 知識として使いたい文書

📌 注意点

  • .persist()不要(Chromaが自動保存)

  • import元はすべて langchain_community または langchain_ollama に変更済み

  • run() ではなく .invoke() を使用

いま実施して気が付いたのですが、RAGはText(Sample.txt)に記載されたこと以外のことはこたえられないのですか? 

ollama run llama3

ではこたえられることが、

python rag_ollama.py

ではこたえられません。

はい、その気づきはまさにRAGの本質を突いています。
ご理解のとおり:


✅ 【結論】

RAGでの回答は「与えられた知識(たとえば sample.txt)に基づくもの」に限定されます。


🔍 なぜか?

🔁 モードの違い

実行コマンド 動作内容 回答の知識源
ollama run llama3 LLMが直接回答 LLaMA3があらかじめ学習した膨大な知識(Wikipediaなど)
python rag_ollama.py RAG構成で回答 あらかじめ与えられたテキストファイルのみ

つまり、RAGは「知らないことは答えない」ように設計されています。

で、いま、このハイブリッド方法についてコーディングしているんですが、上手く動いていません。

2025,江端さんの忘備録

今、新幹線の新横浜駅にいます。
I'm at Shin-Yokohama Station waiting for the Shinkansen.

出発1時間半前です。
It's an hour and a half before departure.

正直、安堵で腑抜けているところです。
Honestly, I'm feeling utterly relieved and drained.

-----

私の勤務している会社では、会社が発行したクレジットカードを用いた新幹線チケットの購入が認められなくなりました。
At my company, we're no longer allowed to buy Shinkansen tickets using company-issued credit cards.

代わりに、「JR東海エクスプレス予約」というサービスを、会社のシステム経由で使わなければならなくなり、さらに『早割○○○』でのチケット購入が必須となりました。
Instead, we are required to use the JR Central Express Reservation system via our company system and purchase tickets under special advance-discount plans.

出張というのは、現場で何が起きるかわかないものです。
Business trips are unpredictable by nature.

人身事故がなくても、遅延や遅着は当たり前。
Delays and late arrivals are common even without personal injury accidents.

鉄道を止める規模の地震は、日本各地で週単位で発生しています。
Earthquakes large enough to halt train services occur somewhere in Japan almost every week.

加えて、豪雪・豪雨は、もはや日常レベルです。
Additionally, heavy snow and rain are frequent occurrences.

つまり、チケット予約は「怖い」のです。
In short, booking tickets is a nerve-wracking affair.

いや、チケット予約そのものはどうということはありません。
The act of booking itself isn’t the problem.

怖いのは、予測不能の事態が発生したとき、ネット経由のチケットキャンセルが、ほぼ100%の確率で失敗することを、私は経験として知っているからです。
What scares me is the near-certainty that online ticket cancellations will fail during unforeseen events—something I know from painful experience.

キャンセルができなかった場合の「損金処理」の始末が、地獄のように面倒くさい。
Handling the accounting for unused tickets when cancellation fails is a nightmare.

これだけで、2日分の通常業務を吹き飛ばすくらいの時間がかかります。
It can eat up the equivalent of two full days’ worth of regular work.

なぜ、このようなことになるのか。
Why does this happen?

―― システム(のUI:ユーザーインターフェース)が、絶望的にアホだからです。
Because the system UI—the user interface—is hopelessly stupid.

-----

ANAのチケット予約システムも含めて、大抵の予約システムのUIはアホで、
Including ANA's flight reservation system, most booking UIs are simply idiotic.

特にキャンセルや解約に関しては、
Especially when it comes to cancellations and contract terminations,

―― 意図的に手続を妨害しているとしか思えないUI設計
—The UI seems deliberately designed to obstruct the process.

になっています。
That's how it feels.

キャンセルや解約の手続きは、トップページの先頭に、ワンボタンで配置すべきです。
Cancellation and termination options should be a one-click button at the top of the homepage.

(実際、私は町内会のホームページをリメイクしたとき、トップページに脱会用PDFのリンクを貼るようにデザインしました)
(When I redesigned our neighborhood association website, I made sure to put a link to the withdrawal form PDF right on the homepage.)

-----

最近思うのですが、ホームページのデザインにも、製作者の名前(×法人名、○個人名)を表示するべきではないでしょうか。
Lately, I think that website designs should include the actual designer’s name, not the company, but the individual.

アホなUIを設計したWebデザイナーは、業界から嘲笑され、顧客から憎悪される
A web designer who builds a foolish UI should be ridiculed by the industry and hated by users—

――これくらいのフィードバックがない限り、我が国のUIデザインは、これからも"アホ"を続けることになります。
—Without that level of feedback, Japanese UI design will remain forever idiotic.

というか、新幹線の予約システムも、異なるシステムを5つくらい作ればいいんです(5つの会社に発注する)。
Honestly, they should build five separate Shinkansen reservation systems, each developed by a different company.

そうして、アホなUIのWebは競争的に淘汰されるようになればいい。
That way, the dumb ones would be weeded out through competition.

(……と書きながら、排他制御やセキュアなデータ管理を考えると、それが現実的に難しいことも理解してはいるのですが)
(Though I say that, I also understand the practical challenges like exclusive access controls and secure data management.)

-----

今、私が新横浜駅の新幹線ホームで安堵して腑抜けている理由は、
Now, the reason I'm standing limp with relief on the Shinkansen platform at Shin-Yokohama Station

―― JR東海エクスプレス予約と私のPASMOとの紐付けが成功していることを、改札で確認できたからです。
—I confirmed at the gate that my JR Express Reservation was successfully linked to my PASMO card.

この紐付け作業のために、私は弊社のシステム部門の方を1時間以上拘束し、マンツーマンのテレビ会議で説明を受けながら操作を行いました。
To complete this linking process, I spent over an hour with a system department colleague in a one-on-one video call, receiving instructions step-by-step.

ちなみに、私は現役のシステムエンジニアですよ?
Just so you know—I’m a current, active system engineer.

そんな私が、機密情報を扱うシステム部門の人とタッグを組まなければ「紐付け」が完了しないシステムって、一体何なの?
If I have to team up with someone who manages confidential data to complete this process, what kind of system would that be?

そして今日は、その「紐付け」後、最初の運用日です。
And today is the first day I’m using the system post-linkage.

―― この紐付け、絶対に失敗している。そして私は大声で叫びながら、窓口やコールセンターに助けを求めるが、誰一人としてロクな回答ができず、キャンセルもできないまま、新幹線の発車時刻が過ぎていく……
—I had foreseen the worst: the link fails, I scream at the counter and call centers, no one can help, I can't cancel, and the train departs without me.

そういう最悪の未来を予知したがゆえに、私は「1時間半前の新横浜駅到着」を選んだのです。
That foresight is precisely why I arrived at Shin-Yokohama Station 90 minutes early.

だから、自動改札機から座席案内のタグが無事に出てきたとき、私は泣きそうになるくらい嬉しかったのです。
So when the seat tag came out of the automatic gate, I was on the verge of tears with joy.

-----

システムエンジニアとして、いま私の社会システムへの信頼度は、最大値を振り切ってマイナスに突入しています。
As a system engineer, my trust in our societal systems is currently off the scale, in the negative direction.

現在の私の唯一の希望は、生成AIによる「窓口システム」です。
My only hope now lies in AI-generated front-desk systems.

電話のたらい回しが、生成AIによって完全にワンストップで処理されるなら、
If generative AI can eliminate phone-based runarounds and offer accurate one-stop service,

―― もう、人間なんぞ消えてなくなってもいい
—Then let humanity vanish already.

と、私は言い切ります。
I say that without hesitation.

2025,江端さんの技術メモ

vscodeでデバッグをする時に、同じディレクトリにmain()関数を持つGo言語プログラムが複数あると、デバッグができません。ファイル名を指定してデバッグすることはできないでしょうか。ただし、launch.json にファイル名を書き込むと、実行ファイルが固定されてしまうので、それは避けたいのですが。

launch.json を使うと、特定のGoファイルが環境として固定されるため、別のファイルをデバッグしたいときに launch.json を都度編集する必要があり、やや不便です。

ですが、以下の方法を使えば 環境を固定せずに、デバッグするファイルを柔軟に切り替えられます。

launch.json を表示・作成する方法

方法①:実行メニューから作成・表示する

  1. 左側の「実行とデバッグ」アイコン(▶️の横に虫)をクリック
    ※ または Ctrl+Shift+D を押します

1. launch.json${file} を使う

環境を固定せずに、現在開いているGoファイルをデバッグする方法です。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug Current File",
            "type": "go",
            "request": "launch",
            "mode": "debug",
            "program": "${file}"
        }
    ]
}

  • ${file} は、現在アクティブな(開いている)ファイルをデバッグ対象にする特殊変数です。
  • この設定にすると、エディタで開いているGoファイルがどれでも、F5 でデバッグが開始できます。

開いているGoのプログラムでデバッガーが動いた。これから作業がラクになるかな。

2024,江端さんの技術メモ

仮想RTSPカメラの作り方と使い方のメモ

2024/11/21
江端
この仮想RTSPカメラは、クライアント(受信側)の接続を止めて、再度接続しようとするとクライアント(受信側)に動画が表示されなくなる、という欠点が発覚しております(2024/11/26) 
をお勧めします。
https://wp.kobore.net/2024/12/06/18206/

1. 背景と要件

1.1. 背景

  1. 地車間実験において複数カメラを準備する為、複数の仮想のRTSPカメラが必要となったため、これに対応する

1.2. 仮想RTSPカメラの要件

  1. 1台のWindowsPCにて最大4台程度の仮想RTSPカメラが実現できること
  2. 仮想カメラは指定したmp4ファイルの映像を配送する
  3. 上記のmp4ファイルは繰り返し再生できることが望ましい
  4. 仮想RTSPカメラは、通常のRTSPカメラと同様にrtsp://127.0.0.1:8554/testという形式で指定できること
  5. できるだけ簡易かつ汎用的なコマンドで実現できることが望ましい。

1.3. 事前検討

  1. 仮想RTSPカメラの実現方法としては、(1)ffmpeg, (2)Gstreamer, (3)VLC(VideoLAN Client), (4)C言語(Gstreamerライブラリ)が挙げられている。
  2. 検討の結果、mp4ファイルを繰り返し再生できる方法は指定されているが、試したところ実際に動いたのはVLCのみだったの、VLCで実現することにした。
  3. vlcのバージョンは、3.0.20以上であること(3.0.14では稼動しないことを確認済)。

2. 説明用の設定

  • 仮想RTSPカメラを設定するWindows10パソコンのIPアドレスを、便宜的に、192.168.0.3として取り扱うこととする。
  • 仮想RTSPカメラを再生するWindows10パソコンのIPアドレスを、便宜的に、192.168.0.25として取り扱うこととする。

3. VLCによる仮想カメラの作り方

3.1. 事前準備: 送信側(192.168.0.3)のファイアウォール設定設定

本節の設定は、設定しなくても動くこともあるので、動かなくなった場合のみに対応する。

送信側のVLCは、RTSP制御パケット(TCP:8554)とRTPデータストリーム(UDPポート)を送信する。これらがブロックされないようにする。

3.1.1. 受信規則の設定(クライアントからのRTSP制御パケットを許可)を許可)

  1. 「Windowsセキュリティ」懼「ファイアウォールとネットワーク保護」懼「詳細設定」を開きます。
  2. 左側の「受信規則」を右クリックして「新しい規則」を選択。
  3. **「ポート」**を選択して「次へ」。
  4. TCPを選択し、「特定のローカルポート」に8554を入力して「次へ」。
  5. 「接続を許可する」を選択し「次へ」。
  6. プロファイルを選択(プライベートにチェックを入れる)し「次へ」。
  7. 名前を設定(例: VLC RTSP TCP)して「完了」をクリック。

3.1.2. 送信規則の設定(RTP/RTCPデータを送信)を送信)

  1. 同様に「送信規則」を右クリックして「新しい規則」を選択。
  2. **「ポート」**を選択して「次へ」。
  3. UDPを選択し、「特定のローカルポート」に5000-5500を入力して「次へ」。
  4. 「接続を許可する」を選択し「次へ」。
  5. プロファイルを選択(プライベートにチェックを入れる)し「次へ」。
  6. 名前を設定(例: VLC RTP UDP)して「完了」をクリック。

3.2. 事前準備: 受信側(192.168.0.25)のファイアウォール設定設定

受信側が、Windows10でない場合は、本節は無視して下さい。

受信側のGStreamerは、RTSP制御パケット(TCP:8554)とRTPデータストリーム(UDPポート)を受信します。これらを許可します。

3.2.1. 受信規則の設定(RTSPとRTPストリームの受信を許可)を許可)

  1. 「Windowsセキュリティ」懼「ファイアウォールとネットワーク保護」懼「詳細設定」を開きます。
  2. 左側の「受信規則」を右クリックして「新しい規則」を選択。
  3. **「ポート」**を選択して「次へ」。
  4. TCPを選択し、「特定のローカルポート」に8554を入力して「次へ」。
  5. 「接続を許可する」を選択し「次へ」。
  6. プロファイルを選択(プライベートにチェックを入れる)し「次へ」。
  7. 名前を設定(例: GStreamer RTSP TCP)して「完了」をクリック。

3.2.1.1. RTPストリームのポート設定設定**

  1. 再度「受信規則」を右クリックして「新しい規則」を選択。
  2. **「ポート」**を選択して「次へ」。
  3. UDPを選択し、「特定のローカルポート」に5000-5500を入力して「次へ」。
  4. 「接続を許可する」を選択し「次へ」。
  5. プロファイルを選択(プライベートにチェックを入れる)し「次へ」。
  6. 名前を設定(例: GStreamer RTP UDP)して「完了」をクリック。

3.2.2. アプリケーション許可(必要に応じて)応じて)

GStreamerがファイアウォールでブロックされている場合、gst-launch-1.0の実行ファイルを許可します。

  1. 「Windows Defender ファイアウォールを介したアプリまたは機能を許可する」をクリック。
  2. 「別のアプリを許可する」をクリックし、GStreamerの実行ファイル(例: C:\msys64\mingw64\bin\gst-launch-1.0.exe)を指定して追加。
  3. 「プライベート」にチェックを入れて「OK」をクリック。

3.3. 仮想RTSPカメラの起動方法

以下のコマンド(例示)で起動する。

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#rtp{sdp=rtsp://0.0.0.0:8554/test}" --loop

コマンドの内容は、以下の通りである。

  • "C:\Program Files\VideoLAN\VLC\vlc.exe": vlc.exeを起動するコマンドをフルパスで指定。
  • vvv: デバッグ情報を詳細に出力する。問題発生時のトラブルシューティングに役立つ。
  • "0326_JP.mp4": 再生するmp4のファイル名(このファイル名は例示であるので、使用時のファイル名を使用する)
  • rtsp://0.0.0.0:8554/test: RTSPアドレス名。0.0.0.0は特殊なアドレスで、「すべてのネットワークインターフェース」を意味する。サーバーがリッスンする対象が、特定のIPアドレスではなく、すべての有効なインターフェースで接続を受け付ける設定である。8554は、ポート番号である。RTSPのデフォルトポート番号は554であるが、アプリケーションによって異なるポート番号が指定されることがある。この例では8554を使用している。/testは、ストリームのパス名を示す。サーバー内でストリームを識別するための名前である。
  • loop: ファイルの繰り返し再生を指定する。

なお、上記のコマンドを投入すると、以下の黒い画面が立ち上がり、コマンドは直ちにリターンする。矢印は再生している位置を示している。

このコマンドを停止するには、この画面を右上の『・』を押下する。

3.4. 仮想RTSPカメラのフレームレート/画像サイズの変更方法

VLCを使って仮想RTSPカメラを実現した際に、送信するフレームレートや画像サイズ(解像度)を変更することは可能である。以下の方法で設定を変更する。

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#transcode{vcodec=h264,fps=15,width=640,height=360}:rtp{sdp=rtsp://0.0.0.0:8554/test}" --loop

  • fps=15:
    • 送信するフレームレートを15fpsに設定する。
    • 必要に応じて、他の値(例: 3060)に変更可能。
  • width=640,height=360:
    • 解像度を640x360に設定しています。
    • 必要に応じて、他の解像度(例: 1920x1080640x480)に変更可能。

3.5. 複数の仮想RTSPカメラの起動方法

今回のケースでは、1台のPCで複数のカメラを実現するので、IPアドレスを固定として、異なるポート番号を使って、複数のカメラを実現することとする。

具体的には、以下のポート番号(8554, 8555, 8556)を変えて、それぞれコマンドを押下することで、同じmp4ファイルで3台のカメラが実現できる(もちろん、mp4ファイルを変えても良い)。

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#rtp{sdp=rtsp://0.0.0.0:8554/test}" --loop

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#rtp{sdp=rtsp://0.0.0.0:8555/test}" --loop

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#rtp{sdp=rtsp://0.0.0.0:8556/test}" --loop

4. 仮想RTSPカメラの起動確認方法

4.1. VLCを使った起動確認方法

簡易な確認方法として、画像受信もVLCを使用する方法を提示する。

受信側Windows10(192.168.0.25)のVLCを立ち上げて、以下の操作を行う。

ネットワークURLに、rtsp://192.168.0.3:8554/testと入力する。

これで映像が表示されれば成功である。

4.2. GStreamerを使った起動確認方法

以下のコマンドを投入して下さい。

$ gst-launch-1.0 -v rtspsrc location=rtsp://192.168.0.3:8554/test latency=0 ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! glimagesink

(Windows10の場合は、autovideosinkでは動かないことが多い)

4.3. 注意点

仮想RTSPカメラはmp4ファイルを繰り返し再生するが、ファイル終了時にVLC(および殆どのクライアント)は、映像が停止したものと判断して再生を終了する。

現時点で、この対応方法は不明である。

以上