ダイナミックマイクアンプの制作

概要

リモート会議等が増えたことでマイクをパソコンに入力することが増えた。話者聴者双方の快適性のため、家にあるダイナミックマイクを通話で使用することにした。その際電圧をラインレベルまで引き上げるためのアンプを制作した。

回路のユニバーサル基板への実装と筐体への実装、加えて簡単な正弦波発生器によるテストまで完了しているが、実験中にマイク本体が故障してしまい実用テストが完了していない。現在別製品を注文中。

目標

昔使っていたカラオケ機能付きレコードプレイヤーに付属していたダイナミックマイクが残っていた。オーディオテクニカのAT-X11という型番。この出力信号をラインレベルまで引き上げ、PCへ入力、通話で使用できるようにすることを目標とした。

設計 (回路)

opアンプで非反転増幅回路を実装した。 図1に回路図を示す。電源回路はUSB microBによる5Vを使いたかったので (以前microBソケットを大量に購入し、余っているため)、opアンプは両電源用を片電源で使うことにした。

入力信号はバイアスしてopアンプに入力される。増幅回路は2段にして80dBほどの利得が得られるように設計した。設計上は、40 + 43.2 = 83.2dB。

直流成分が増幅されないように、-入力にデカップリングキャパシタを挿入し、直流成分にとってはボルテージフォロワになるように設計してある。これはマルツの レールツーレールオペアンプLMC6482使用レポート―簡易集音装置の製作実験 を参考にした。

入力段のカップリングキャパシタは、後ろの (小信号等価回路における) インピーダンスが十分高いことから、もっと小さい値でも良いはずだが (RC ハイパスフィルタを構成する)、適当に手元にあった 100μF のものを採用した。実際に計算してみると、カットオフ周波数 0.031 程度になり、こんなにいらない。むしろ、電源投入時のポップノイズが大きくマイクにかかることの方を考慮するべきだった。もしかすると、前述のマイクの故障はこれが原因かもしれない。

電源は昔使用していたモバイルルータに付属したACアダプタ (USB micro B 出力) を使用することにした。安物っぽく、スイッチングノイズが懸念されることからRLCローパスフィルタをかけた。

SVG Picture created as dynamic_micamp.svg date 2020/11/15 17:31:26 Picture generated by Eeschema-SVG 1 2 3 4 5 6 1 2 3 4 5 6 A B C D A B C D Date: 2020-11-15 KiCad E.D.A. kicad (5.1.5)-3 Rev: Size: A4 Id: 1/1 Title: Dynamic Microphone Amplifier File: dynamic_micamp.sch Sheet: / twitter.com/rollman054 Author: @rollman054 VBUS 1 D- 2 D+ 3 ID 4 GND 5 Shield 6 J2 USB_B_Micro R3 470 R6 68k GND C5 0.1u C7 0.1u 1 2 L1 220u C6 0.1u C4 470u C3 3300u R5 470 C9 100u C8 0.1u GND R1 100k GND GND GND VCC PWR_FLAG 1 2 3 J3 AudioJack3_123 1 2 3 J1 AudioJack3_123 V- 4 V+ 8 U1C NJM4580 + 5 - 6 7 U1B NJM4580 1 - 2 + 3 U1A NJM4580 C1 100u C2 10u R4 47k GND VCC VCC GND R2 100k GND

図1: 回路図

これをユニバーサル基板へ実装した。配線図を図2に示す。引き回しにはノイズ対策のための考慮事項があるはずだが、とりあえずアースループができないようにだけした。C6はopアンプの直前につけたほうが良かったかもしれない。

SVG Picture created as dynamic_micamp-brd.svg date 2020/11/15 17:50:29 Picture generated by PCBNEW 1 2 3 4 5 6 1 2 3 4 5 6 A B C D A B C D Date: 2020-11-15 KiCad E.D.A. kicad (5.1.5)-3 Rev: Size: A4 Id: 1/1 Title: Dynamic Microphone Amplifier File: dynamic_micamp.kicad_pcb Sheet: twitter.com/rollman054 Author: @rollman054 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 A O N M L K J I H G F E D C B C4 C1 C2 C5 C6 C7 C8 C9 J2 L1 R1 R2 R3 R4 R5 R6 U1 J3 J1 C3

図2: 配線図

設計 (筐体)

筐体の役割として、信号入力、信号出力用3.5mm フォーンプラグ、電源スイッチ、電源用 USB micro B ソケットからなるインターフェースを実装することと、シャーシアースをしてノイズを防ぐことがある。これを実現するために最低限のことはやった。外形を図3に示す (この画像には電源コネクタが実装されていないが、このあと四角孔部分にL字に曲げたアルミの治具とともに接着剤で (!) 固定された)。

まず、アルミ板をホームセンターで買ってきて、曲げてコの字型にした。インターフェース用の穴は、ドリルで小さい穴を開けた後リーマで大きくし、ヤスリですこし磨いた。手垢と傷がすごいので、脱脂や塗装などをしたほうがいいのだろうが、経験も知識もないので今後の課題とした。ゴム足も着いていないが、今後の課題。

基板取り付け用にM3スペーサがほしかったが、近所のホームセンターではM4からしか取り扱っていなかった。幸い手持ちに2個あったので、対角をスペーサで、残りをナットでかさ増ししてとりあえず固定した。インターネットを見ていたら、おもちゃ屋さんにあるかもしれないということで、今後の課題として物色に行くことにする。

現在下の台座部分のみできており、ふたは今後の課題。

電源用のソケットを逆向きにすればもう少し配線が楽できれいかもしれなかった。

f:id:qwpmb554:20201115175330j:plain
図3: 制作したアンプの外形

検証

ゲインの実測をするため、正弦波発生器を実装し、それを入力した。発生器を図4に示す。

<font-face font-family="Tahoma embedded" units-per-em="2048" font-weight="bold" font-style="normal" ascent="2037" descent="423"/> <missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/> Q1 NPN R1 1k R2 10k R3 10k R4 1k R5 1k C1 0.1µ C2 47n C3 47n V1 5 Q2 NPN R6 100 R7 10k R8 100k C4 0.1µ R9 100k R10 100 v-fol src vout .tran 100m startup
図4: 正弦波発生器

部品が少ない簡単な発振回路2選 を参考に、出力段にバイアスとエミッタフォロワを付加した。バイアスし直しているのは、Q2の V_BE によって波形下部がクリップされてしまうため。

この発生器によって、1.6mVP-P の正弦波が得られるので、これを当アンプに入力した。結果、1.16VP-P が得られた。この記事 によれば、ダイナミックマイクの出力電圧 (入力感度) は

1.0 ~ 3.16 [mV/Pa]

とのことなので、ラインレベルを目標とすれば要求を満たすゲインだと思われる (民生品としては大きすぎるのかもしれないが (参考: 0.31Vマザーボードに入力して壊れなかったのでよいことにする。音量を下げる分についてはソフトウェアで行えばよい)。

ちなみに、1.16VP-P / 1.6mVP-P = 725 = 57.2dB。

図5 に実験の様子を示す。左側が正弦波発生器。正弦は発生機には図? の回路に加えて、分圧抵抗による降圧をしてある。

f:id:qwpmb554:20201115181128j:plain
図5: 実験の様子

図6 に観測された波形を示す。Ch.1 がアンプの出力で Ch. 2 が正弦波発生器による入力 (エミッタフォロワ・分圧のあと)。 目視する上では大きな歪は無いように見える。歪率は解析していない。もとの入力波形が (簡単な回路のためか) かなり歪んでいるため。

f:id:qwpmb554:20201115181216j:plain
図6: 増幅前後の正弦波波形

続き

実際にマイクを通してパソコンに入力してみた結果はマイクが届き実験が完了し次第記載する。

AWS SDK for GO API Reference がもっさりするのでダウンロードして閲覧する

AWS SDK for GO API Reference (例えばEC2のリファレンスなら ec2 - Amazon Web Services - Go SDK) はかなりもっさりしている。特に、ページ内リンクを新しいタブで開くときは新たにダウンロード・ページ遷移を両方するのでしんどい思いをする。

Google Chrome によってページが再現できるようにダウンロードすることができるが、HTMLファイル内のリンクは絶対パスなので新しいタブを開くと結局もう一度ダウンロードが発生してしまう。

以下のsedスクリプトで、html内のリンクを相対パスに変えて、新しいタブで開いてもダウンロード済みのファイルが読み込まれるようになる。

sed -ie 's$https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/$./ec2 - Amazon Web Services - Go SDK.html$g' ec2\ -\ Amazon\ Web\ Services\ -\ Go\ SDK.html ec2\ -\ Amazon\ Web\ Services\ -\ Go\ SDK_files/*

その他

  • htmlソースを見ると、ページ内リンクは #hogehogeだが、ダウンロードしたファイルであっても新しいタブで開くと新たなロードが発生する (https://docs.aws.amazon.com/ にリクエストが飛ぶ)。現在のurlにページ内識別子を追記する仕様ではなく、metaタグかなにかを見るのかもしれない。

vimでgolang (あるいは他の言語) のquickfix errorformat を利用する (Debian)

Vimは、組み込みでいくつかの言語のコンパイル設定ファイルを持っている。 Debian, Vim 8.1なら、/usr/share/vim/vim81/compiler の下に ${言語名}.vim というファイルが置いてある。golangなら /usr/share/vim/vim81/compiler/go.vim。このPATHは runtimepathpackpath を見ればみつかる。

これを読み込むことで (拡張子で自動で読んでくれるわけではないらしい?) quickfix errorformatが適用される。

:compiler!  go.vim

必要があれば autcmd などで拡張子毎の設定をしておくと良さそう。

その他

:help compiler によると

What the command actually does is the following:
- Delete the "current_compiler" and "b:current_compiler" variables.
- Define the "CompilerSet" user command.  With "!" it does ":set", without "!"
  it does ":setlocal".
- Execute ":runtime! compiler/{name}.vim".  The plugins are expected to set
  options with "CompilerSet" and set the "current_compiler" variable to the
  name of the compiler.
- Delete the "CompilerSet" user command.
- Set "b:current_compiler" to the value of "current_compiler".
- Without "!" the old value of "current_compiler" is restored.

compiler/{name}.vim を読んでみると、makeprg (:make が発効されたときのコマンド)とerrorformat が設定されているようだ。 errorformatを自分で書くときののexampleとしても役立つ。

参考

  • :help runtime
  • :help compiler
  • :help current_compiler
  • :help makeprg

gitでコミット時にコードをフォーマットしてからコミットする

yoshinorin.net

上記事によると

for FILE in `git diff --staged --name-only | grep .php`; do
  # ここで言語に応じたなんらかのフォーマット処理
  git add $FILE
done

が提案されているが、ステージングエリアにdeletedがあると formatter の引数にに存在しないファイルが渡されて、 formatterによってはnon-zero exit codeが帰ってきてしまいcommitが行えない (例えば、pythonのフォーマッタ yapf はエラーコードを返す)。

deletedはフォーマットする必要はないので、diffコマンドにフィルタ --diff-filter を追加し、Deletedは削ぎ落とす。その他Unknownも落としておいた。 git-diffのオンラインマニュアルを参照してほしい。

最終的に、yapfでフォーマットする場合は以下のスクリプトを実行権限付きで .git/hooks/pre-commit に書けばよい。

for FILE in `git diff --staged --name-only --diff-filter=ACMRTUB | grep .py`; do
  yapf -i $FILE
  git add $FILE
done

pytestでpytest-profilingを使っているときにでる ValueError: Plugin already registered

概要

$ make pytest
if [ -z 1588034257 ] || [ 1587982138 -ge 1588034257 ]; then \
        docker build . -t test; \
else \
        echo "Docker container image is ready up to date."; \
fi
Docker container image is ready up to date.
docker run --rm -it test pytest --color=no tests/
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 495, in _importconftest
    return self._conftestpath2mod[key]
KeyError: PosixPath('/test/conftest.py')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/pytest", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 105, in main
    config = _prepareconfig(args, plugins)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 257, in _prepareconfig
    return pluginmanager.hook.pytest_cmdline_parse(
  File "/usr/local/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 203, in _multicall
    gen.send(outcome)
  File "/usr/local/lib/python3.8/site-packages/_pytest/helpconfig.py", line 90, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 836, in pytest_cmdline_parse
    self.parse(args)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 1044, in parse
    self._preparse(args, addopts=addopts)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 1001, in _preparse
    self.hook.pytest_load_initial_conftests(
  File "/usr/local/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 208, in _multicall
    return outcome.get_result()
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 899, in pytest_load_initial_conftests
    self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 441, in _set_initial_conftests
    self._try_load_conftest(anchor)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 447, in _try_load_conftest
    self._getconftestmodules(anchor)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 473, in _getconftestmodules
    mod = self._importconftest(conftestpath)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 520, in _importconftest
    self.consider_conftest(mod)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 575, in consider_conftest
    self.register(conftestmodule, name=conftestmodule.__file__)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 386, in register
    self.consider_module(plugin)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 581, in consider_module
    self._import_plugin_specs(getattr(mod, "pytest_plugins", []))
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 586, in _import_plugin_specs
    self.import_plugin(import_spec)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 629, in import_plugin
    self.register(mod, modname)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 379, in register
    ret = super().register(plugin, name)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 104, in register
    raise ValueError(
ValueError: Plugin already registered: pytest_profiling=<module 'pytest_profiling' from '/usr/local/lib/python3.8/site-packages/pytest_profiling.py'>
{'140074314182224': <_pytest.config.PytestPluginManager object at 0x7f6597bd2e50>, 'pytestconfig': <_pytest.config.Config object at 0x7f6597170220>, 'mark': <module '_pytest.mark' from '/usr/local/lib/python3.8/site-packages/_pytest/mark/__init__.py'>, 'main': <module '_pytest.main' from '/usr/local/lib/python3.8/site-packages/_pytest/main.py'>, 'runner': <module '_pytest.runner' from '/usr/local/lib/python3.8/site-packages/_pytest/runner.py'>, 'fixtures': <module '_pytest.fixtures' from '/usr/local/lib/python3.8/site-packages/_pytest/fixtures.py'>, 'helpconfig': <module '_pytest.helpconfig' from '/usr/local/lib/python3.8/site-packages/_pytest/helpconfig.py'>, 'python': <module '_pytest.python' from '/usr/local/lib/python3.8/site-packages/_pytest/python.py'>, 'terminal': <module '_pytest.terminal' from '/usr/local/lib/python3.8/site-packages/_pytest/terminal.py'>, 'debugging': <module '_pytest.debugging' from '/usr/local/lib/python3.8/site-packages/_pytest/debugging.py'>, 'unittest': <module '_pytest.unittest' from '/usr/local/lib/python3.8/site-packages/_pytest/unittest.py'>, 'capture': <module '_pytest.capture' from '/usr/local/lib/python3.8/site-packages/_pytest/capture.py'>, 'skipping': <module '_pytest.skipping' from '/usr/local/lib/python3.8/site-packages/_pytest/skipping.py'>, 'tmpdir': <module '_pytest.tmpdir' from '/usr/local/lib/python3.8/site-packages/_pytest/tmpdir.py'>, 'monkeypatch': <module '_pytest.monkeypatch' from '/usr/local/lib/python3.8/site-packages/_pytest/monkeypatch.py'>, 'recwarn': <module '_pytest.recwarn' from '/usr/local/lib/python3.8/site-packages/_pytest/recwarn.py'>, 'pastebin': <module '_pytest.pastebin' from '/usr/local/lib/python3.8/site-packages/_pytest/pastebin.py'>, 'nose': <module '_pytest.nose' from '/usr/local/lib/python3.8/site-packages/_pytest/nose.py'>, 'assertion': <module '_pytest.assertion' from '/usr/local/lib/python3.8/site-packages/_pytest/assertion/__init__.py'>, 'junitxml': <module '_pytest.junitxml' from '/usr/local/lib/python3.8/site-packages/_pytest/junitxml.py'>, 'resultlog': <module '_pytest.resultlog' from '/usr/local/lib/python3.8/site-packages/_pytest/resultlog.py'>, 'doctest': <module '_pytest.doctest' from '/usr/local/lib/python3.8/site-packages/_pytest/doctest.py'>, 'cacheprovider': <module '_pytest.cacheprovider' from '/usr/local/lib/python3.8/site-packages/_pytest/cacheprovider.py'>, 'freeze_support': <module '_pytest.freeze_support' from '/usr/local/lib/python3.8/site-packages/_pytest/freeze_support.py'>, 'setuponly': <module '_pytest.setuponly' from '/usr/local/lib/python3.8/site-packages/_pytest/setuponly.py'>, 'setupplan': <module '_pytest.setupplan' from '/usr/local/lib/python3.8/site-packages/_pytest/setupplan.py'>, 'stepwise': <module '_pytest.stepwise' from '/usr/local/lib/python3.8/site-packages/_pytest/stepwise.py'>, 'warnings': <module '_pytest.warnings' from '/usr/local/lib/python3.8/site-packages/_pytest/warnings.py'>, 'logging': <module '_pytest.logging' from '/usr/local/lib/python3.8/site-packages/_pytest/logging.py'>, 'reports': <module '_pytest.reports' from '/usr/local/lib/python3.8/site-packages/_pytest/reports.py'>, 'faulthandler': <module '_pytest.faulthandler' from '/usr/local/lib/python3.8/site-packages/_pytest/faulthandler.py'>, 'profiling': <module 'pytest_profiling' from '/usr/local/lib/python3.8/site-packages/pytest_profiling.py'>, 'capturemanager': <CaptureManager _method='fd' _global_capturing=<MultiCapture out=<FDCapture 1 oldfd=5 _state='started' tmpfile=<_pytest.capture.EncodedFile object at 0x7f6596ed0220>> err=<FDCapture 2 oldfd=6 _state='started' tmpfile=<_pytest.capture.EncodedFile object at 0x7f6596ed02b0>> in_=<FDCapture 0 oldfd=3 _state='started' tmpfile=<_io.TextIOWrapper name='/dev/null' mode='r' encoding='UTF-8'>> _state='started' _in_suspended=False> _capture_fixture=None>, '/test/conftest.py': <module 'conftest' from '/test/conftest.py'>}
make: *** [Makefile:26: pytest] Error 1/conftest.py'>}
make: *** [Makefile:26: pytest] Error 1

とりあえずpytest-profilingを無効にして、testを行えるようにしようと思ったが、conftest.pyを消してdockerイメージをビルドしても直らなかった。

.pyc をすべて消す方法は効かなかった https://github.com/pytest-dev/pytest/issues/3112#issuecomment-404979798

Docker imageを消してから作り直してみても効かなかった

dangling image を消して再度pytestを動かしたら治った

$ docker image prune

疑問

  • docker rmi では解決せず、 docker image prune では解決したことから、原因は中間イメージレイヤの変更であると考えられる

    • docker rmi では中間イメージレイヤは削除されないと仮定するとこういう演繹ができる
  • しかし、中間レイヤは読み込み専用のはず

  • ファイルを変更するたびに毎回イメージをビルドし直す方法をとっていたが、Dockerfileの COPY コマンド以降には ENVコマンドしかないのでファイルシステムに変更はないはず
  • なぜ中間イメージレイヤが変更されたのか?
  • 再度conftest.pyを配置してイメージをビルドしても同じエラーが出た。以前は使用可能だった。

2020-04-28 13:40 追記

  • conftest.pyを消去してもprofileは動作したので、

If a plugin is installed, pytest automatically finds and integrates it, there is no need to activate it.

Requiring plugins using a pytest_plugins variable in non-root conftest.py files is deprecated. See full explanation in the Writing plugins section.

が関係しそう。conftest.pyはrootに配置しているはずではあるが。

docs.pytest.org

Linux CLIでWi-Fiをする関係のまとめ

Prerequisites

  • Raspbian
    • wpa_supplicant
    • dhcpcd

困ったら

sudo systemctl restart dhcpcd

primitiveにやる

$ wpa_cli interface
$ wpa_cli interface [ifname]
$ add_network
0
set_network 0 ssid "SSID"
set_network 0 psk "passphrase"
enable_network 0
  • dhcpcd ${interface} または dhclient

  • アクセスポイントへの接続状況を見る

    • iw dev ${devname} link

VNCでOpenGL APplicationsを飛ばす on Ubuntu16.04 with Nvidia GPU

VNCOpenGL APplicationsを飛ばす on Ubuntu16.04 with Nvidia GPU

Prerequisites

考え方

  • 本物のX (Xorg) 以外でOpenGL applicationを動かす際には、VirtualGLなる(そのまんまな名前な)ミドルウェアが必要
  • VirtualGLを上手く動かすには、(renderingのための?)本物のXが起動している必要がある
    • 大抵、display managerがうまくうごいていればよい
  • DISPLAYに表示先のidentifierを、VGL_DISPLAYに本物のdisplayのidentifierを設定する

手順概要

  • VNCサーバ (TurboVNC) をリモートマシンにインストールする
  • VirtualGLをリモートマシンにインストールする
    • vglserver_config で設定をする
    • ${HOME}/.vnc/xstartup.turbovnc を設定する
  • Nvidiaが提供しているツールでXorgの設定をする(xrog.confの生成)
  • クライアントでVNCクライアントを起動する
  • vglrun ${opengl application}

初期設定手順

 $ sudo apt install -y turbovnc virtualgl
 $ vi ${HOME}/.vnc/xstartup.turbovnc  # xfceが立ち上がるように書いたが、おそらくなんでもよい
 $ cat ${HOME}/.vnc/xstartup.turbovnc # WMを探すコードを取り除き、xfceを起動する行を追加。Gnomeのなにかがあり、これは関係なさそうだがとりあえず残しておいた
 #!/bin/sh
 
 unset SESSION_MANAGER
 unset DBUS_SESSION_BUS_ADDRESS
 XDG_SESSION_TYPE=x11; export XDG_SESSION_TYPE
 
 # This works around pointer disappearance issues under GNOME 3
 if [ -x /usr/bin/dconf ]; then
   dconf write /org/gnome/settings-daemon/plugins/cursor/active false
 fi
 
 OS=`uname -s`
 if [ $OS = 'Linux' ]; then
   case "$WINDOWMANAGER" in
     *gnome*)
       if [ -e /etc/SuSE-release ]; then
         PATH=$PATH:/opt/gnome/bin
         export PATH
       fi
       ;;
   esac
 fi
 
 startxfce4
 $ sudo nvidia-xconfig
 $ /opt/VirtualGL/bin/vglserver_config # ウィザードに従って設定をする
 $ systemctl restart lightdm # このへんで適当にDMを再起動。意味があるのかは不明。

つかいかた

  • リモートマシンにログインし、vncserverコマンドを実行する
  • Xorgが起動しているかを確認する (ある環境の場合、lightdmが正しく起動しているか確認する)
/opt/TurboVNC/bin/vncserver :2 # :2の部分は適当に変更する。lightdmが何故か:1で起動するようになってしまい、ちょっときもちわるい。

  • クライアントマシンからVNC Client App でリモートマシンに接続
    • URI: ${remote machine IP address}:${Display Number (e.g. :2)}
    • いくつかのアプリではポート番号はポート番号を指定しないといけないかも。デフォルトだと5900 + (display number)
    • リモートデスクトップ上でターミナルを開き、vglrunを通してコマンドを実行
vglrun ${application}

感想

  • Gazeboをremoteで動かしてrender結果を(リアルタイムに)localに持ってきたかった
  • Gazeboはそれ自体がサーバー・クライアント分離に対応しているが、サーバのGazeboとクライアントのGazeboのバージョンが完全に一致していないといけない。Debianの公式リポジトリUbuntuの公式リポジトリのバージョンが合うことは(ほぼ)無いので、しんどい。
  • Gazeboのサーバ・クライアントが通信するにはhttps://wiki.ros.org/ROS/NetworkSetupが必要っぽいので、remote serverでdockerコンテナを動かす方法は簡 単には行かない(ソースはROSの話だがGazeboも一緒だろう)。
  • VNCで持ってこようとするとOpenGLが使えなくてなんとかと言われるので、VirtualGLを入れるなどしていろいろやるが、"Xlib: extension "GLX" missing on display ":1""と言われてGazeboが一瞬起動して死ぬ
  • (こちらのローカルなミスで) xorg.confが消えていた(正確に言うとバックアップしか残っていなかった)ので、GLとWindowの連携が不可能だったと思われる。
  • nvidia-xconfigコマンドでxorg.confを再生成して解決。