Processing math: 100%

2019/10/23

OpenMPを使用した並列化

背景


OpenMPを利用して並列化を行い、処理を高速化する必要が出てきたため、OpenMPで並列化処理を記述する方法について記述する

記事の目的


OpenMPで並列化処理を行う際のテンプレートを作成する

OpenMP


ここでは、OpenMPを利用したCプログラムの記述方法について記載する。

OpenMPとは

OpenMP
OpenMPは、OpenMP ARBが提供する並列コンピューティング環境において共有メモリのマルチスレッド処理をサポートするために開発されたAPIである。

利点

  • マルチスレッド並列なプログラムをディレクティブを挿入するだけで実現できる
  • gccなどで標準サポートされており、導入が容易である

テンプレート

OpenMPを利用して並列処理を行うテンプレートを記載する。


  1. // CMakeLists.txt に追記
  2. # For OpenMP
  3. find_package(OpenMP REQUIRED)
  4. if(OpenMP_FOUND)
  5. message(STATUS "Use OpenMP")
  6. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
  7. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
  8. endif()

  1. // openmp_sample_code.cpp
  2. #include <iostream>
  3. #ifdef _OPENMP
  4. // ヘッダーファイルをインクルード
  5. #include <omp.h>
  6. #endif
  7. int main()
  8. {
  9. #ifdef _OPENMP
  10. // スレッド数を設定(今回は4スレッド)
  11. omp_set_num_threads(4);
  12. #endif
  13. #ifdef _OPENMP
  14. // スレッド数取得
  15. std::cout << "The number of processors is "<< omp_get_num_procs() << std::endl;
  16. #endif
  17. #ifdef _OPENMP
  18. // ここから並列処理を行う
  19. // 【注】カッコ内の処理は、スレッド数回実行される
  20. #pragma omp parallel
  21. #endif
  22. {
  23. int a = 0, b= 0;
  24. #ifdef _OPENMP
  25. // section毎に並列処理を行う
  26. #pragma omp sections
  27. #endif
  28. {
  29. #ifdef _OPENMP
  30. #pragma omp section
  31. #endif
  32. {
  33. // 並列化される処理 1
  34. a ++;
  35. }
  36. #ifdef _OPENMP
  37. #pragma omp section
  38. #endif
  39. {
  40. // 並列化される処理 2
  41. b ++;
  42. }
  43. }
  44. #ifdef _OPENMP
  45. // ここまでの並列処理が完了するまで待機
  46. #pragma omp barrier
  47. // シングルスレッド処理
  48. // 【注】マルチスレッド処理内におけるプライベート変数は引き継がれない場合がある
  49. // このコードでは、aまたはbのどちらかが0になる
  50. #pragma omp single
  51. #endif
  52. {
  53. std::cout << "a=" << a << std::endl;
  54. std::cout << "b=" << b << std::endl;
  55. }
  56. }
  57. size_t i;
  58. int c = 0;
  59. int sum = 0;
  60. #ifdef _OPENMP
  61. // forループを並列化
  62. // 【注】 #pragma omp parallel内で#pragma omp parallel forすると、2重に並列化するため注意
  63. //     上記の場合、#pragma omp parallel forを#pragma omp forとする
  64. // iを各スレッドのプライベート変数に指定
  65. // 並列化した際にスレッドをを静的(static)に割り当て、チャンクサイズを2に指定
  66. // (この場合、i=0,1,4,5,...とi=2,3,6,7,...でスレッドが作られる)
  67. // (動的に割り当てる場合はdynamicとする。ただし、スレッド生成コストが増大する)
  68. // sumは、i*iの処理が並列で行われた後、+=の処理がまとめて行われる
  69. // orderedは、forループ内にorderedセクションがあることを示す
  70. #pragma omp parallel for private(i) schedule(static, 2) reduction(+, sum) ordered
  71. #endif
  72. for(i = 0; i < 10; ++i)
  73. {
  74. sum += i*i;
  75. #ifdef _OPENMP
  76. // criticalセクション内は、最大で1つのスレッドしか実行されない
  77. // ただし、スレッド待ちの処理コストが発生する
  78. #pragma omp critical
  79. #endif
  80. {
  81. c += i*i;
  82. std::cout << "c=" << c << std::endl;
  83. }
  84. #ifdef _OPENMP
  85. // orderedセクション内は、順番に実行される
  86. #pragma omp ordered
  87. #endif
  88. {
  89. std::cout << "i=" << i << std::endl;
  90. }
  91. std::cout << "sum=" << sum << std::endl;
  92. }
  93. }

まとめ


  • OpenMPでプログラミングをする際のテンプレートを調査、記載した

参考文献



変更履歴


  1. 2019/10/23: 新規作成
  2. 2019/12/15: コード修正

2019/10/19

特許作成方法のフレームワーク

背景


仕事の中で特許作成を行っているが、アイディア構想から明細書作成までの進め方が定まっておらず、無駄に時間がかかっていた。特許作成の工数削減を削減するため、フレームワークを構築する。

記事の目的


特許作成のフレームワークを策定する

特許作成のフレームワーク


ここでは、特許作成のフレームワークに関する説明について記載する。下記の5ステップで特許創出を行う。

1. 特許に必要な要素の確認

まず、特許創出を開始するにあたり、留意点に関して確認する。特許を創出する際、以下の2つのポイントを意識する。
  • 新規性
  • 新規性は、内容が以下の3項目を満たすことを指す
    • 出願の時点で、守秘義務がない人に知られていない発明であること
    • 出願の時点で、工場見学や報道発表などの公開された場所で実施されていない発明であること
    • 出願の時点で、学会誌、論文、特許などの刊行物や、インターネット上で公開されていない発明であること
  • 進歩性
  • 進歩性は、内容が以下の6項目を満たすことを指す
    • 既存技術に対する最適材料の選択や設計変更に関する発明でないこと
    • 既存技術を組み合わせた発明でないこと
    • 課題に対して既存技術の適用を行った発明でないこと
    • 解く課題が既存の発明と共通している発明でないこと
    • 既存技術と共通の作用や機能を有した発明でないこと
    • 引用文献内に内容の一部が含まれている発明でないこと

2. アイディアの創出

ここでは、アイディアの創出方法について記載する。特許のアイディアを考える際、下記の項目に関して箇条書きで記載する。下記項目に漏れがあると、実用的でないものになる可能性がある。
  1. 背景
  2. 発明を行う分野、領域での実情やトレンドに関して記述する。
    (例) 紙に字を書く道具として、鉛筆が普及している。
  3. 課題
  4. 発明により解きたい課題について記述する。課題が簡潔に記述できるものであるほど、実用性が高い可能性がある。
    (例) 現在の鉛筆は円柱型であり、傾いた机の上に置くと転がって机から落ちやすい問題がある。
  5. 目的
  6. 発明の目的(作用や機能)について記述する。目的も簡潔に記述できるものほど、実用性が高い可能性がある。
    (例) 鉛筆を転がらないようにする。
  7. 方法
  8. 目的を実現するための手法について、具体的に記述する。
    (例) 鉛筆を角型にする。

後のステップで90%以上が没になるため、思いつく限り多くアイディアを創出する。

3.先行特許の調査

ここでは、先行特許の調査方法について記載する。ここで、思いついたアイディアが既出ではないことを確認する。
  1. 先行特許の検索
  2. J-PlatPat等を使用し、関連キーワードなどで先行特許の検索を行う。 この時、検索結果の特許数が100-200件になるように調整する。これは、絞り込み過ぎると関連特許を見落とす可能性があり、逆に多すぎると確認工数が掛かり過ぎるためである。
  3. 1次スクリーニング
  4. 検索結果の特許全てに対して、請求項と図面のみを確認して関連特許であるかを確認する。この時、判定できないものは関連特許とみなす。
  5. 2次スクリーニング
  6. 1次スクリーニング結果、関連特許とみなしたものに対して、実施例を確認し関連特許であるかを確認する。
  7. 新規性・進歩性の確認
  8. 関連特許とアイディアを比較し、「課題」、「目的」、「方法」の3項目で新規性、進歩性があるか確認する。この時、アイディアが関連特許の問題点を解決するものであれば、「課題」にこの特許を記述する。

4.請求項の作成

ここでは、請求項の作成方法について記載する。
  1. アイディアの分解
  2. IT関連の特許の場合、処理のフローチャートやシステムの構成図を作成し、アイディアを要素毎に分解する。
  3. 進歩性の明確化
  4. アイディアの各要素に対して、関連特許との比較を行い、進歩性のある要素を絞り込む。進歩性のある要素が少ないほど、普遍性が増すため特許としての価値が向上する。
  5. 要素の並べ替え
  6. アイディアの要素を必要性順に並べ替える。特許化する際は、要素数を減らしたほうが普遍性が増すため、特許の価値が向上する。
  7. 請求項の作成
  8. アイディアの要素のうち必須な要素(進歩性がある要素と必要性が高い要素)を1項に記載し、2項目以降に必要性順に要素を記述する。

5.実施例の作成

ここでは、実施例の作成方法について記載する。実施例には、実際に実装する場合の処理などを記載する。
  1. 図表の作成
  2. 発明を実施に関する図や絵、写真を作成する。図を用いることで発明の内容が理解しやすくなる。また、UI関連の特許の場合、図は必須である。
  3. アイディアの具体化
  4. アルゴリズムやIO、データ形式などを詳細に記載する。
  5. バリエーションの作成
  6. 実施例は、考えられるパターンをより多く記述する。特にUI関連の特許の場合は、表示方式のバリエーションを網羅するように図を作成する。

まとめ


  • 特許作成の一連の流れについて記載した

参考文献



変更履歴


  1. 2019/10/19: 新規作成

2019/09/29

perfを用いたシステムのボトルネック解析方法

背景


システムの処理速度を改善するために、ボトルネック解析を行う必要があった。
ボトルネック解析の方法と、プロファイリングに使用したperfの使用方法に関して調査を行った。

記事の目的


perfを使用し、ボトルネック解析を行う

perf


ここでは、perfの導入方法及び使用方法について記載する。

perfとは

perf(Performance analysis tools for Linux)とはLinuxカーネル2.6.31以降で使用可能なLinuxの性能解析ツールである。
実行されているプロセス毎のCPU使用率やプロセス内で呼ばれている関数の割合などを調査できる。

利点

  • gprofのように、プログラム作成時に専用のライブラリを入れたり、コンパイル時にオプションをつける必要がない
  • フレームグラフにして、ビジュアライズできる

導入方法(Ubuntu編)

Ubuntu16.04へperfを導入する手順について記載する。
  1. OSのカーネルバージョンを調べる
    1. $ uname -a
    2. Linux emptySet 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
    上記の場合、カーネルバージョンは「4.4.0-116-generic」である。
  2. 上記カーネルバージョンのlinux-toolsパッケージをインストールする
    1. $ sudo apt install linux-tools-<カーネルバージョン>

使用方法

perfの使用方法は、下記の通りである。

  • コマンド内のボトルネック解析を行う場合
    1. perfで記録しながらコマンドを実行する
      1. $ perf record コマンド
      デフォルトで、記録結果ファイル「perf.data」が作成される。(-o [ファイル名.data]オプションでファイル名指定可能)
    2. perfで記録したファイルの内容を表示する(-i [ファイル名.data]オプションでファイル名指定可能)
      1. $ perf report
      例: ring_buffer_sample(l.148をbuf.setBufferSize(1000);に変更した)の場合
      1. Samples: 44 of event 'cpu-clock', Event count (approx.): 11000000
      2. Overhead Command Shared Object Symbol
      3. 11.36% ring_buffer_sam libc-2.23.so [.] vfprintf
      4. 9.09% ring_buffer_sam libc-2.23.so [.] __GI___libc_write
      5. 6.82% ring_buffer_sam libc-2.23.so [.] _IO_file_xsputn@@GLIBC_2.2.5
      6. 6.82% ring_buffer_sam [unknown] [k] 0xffffffff8184e9b5
      7. 6.82% ring_buffer_sam [unknown] [k] 0xffffffff8184ef5a
      8. 4.55% ring_buffer_sam ring_buffer_sample [.] std::mutex::unlock
      9. 4.55% ring_buffer_sam [unknown] [k] 0xffffffff81508f2b
      10. 2.27% ring_buffer_sam libc-2.23.so [.] _IO_file_write@@GLIBC_2.2.5
      11. 2.27% ring_buffer_sam libc-2.23.so [.] fprintf
      12. 2.27% ring_buffer_sam ring_buffer_sample [.] RingBuffer::getBufferSize
      13. 2.27% ring_buffer_sam ring_buffer_sample [.] main
      14. 2.27% ring_buffer_sam ring_buffer_sample [.] std::mutex::lock
      15. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff810031e8
      16. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff81087b91
      17. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff8109b061
      18. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff8109b069
      19. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff811c3b76
      20. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff81213c9a
      21. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff81214ba0
      22. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff8140cbaf
      23. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff8140cbb3
      24. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff8140dee9
      25. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff8150410d
      26. 2.27% ring_buffer_sam [unknown] [k] 0xffffffff8150420f
      27. For a higher level overview, try: perf report --sort comm,dso
      上記の例の場合、vfprintf(標準出力)が処理量の11%を占めているため、標準出力を減らすことが高速化に有効であることがわかる。
  • システム全体のボトルネック解析を行う場合
    1. perfで記録する
      1. $ sudo perf record -ag
    2. perfで記録したファイルの内容を表示する
      1. $ perf report
      例: ring_buffer_sample(l.148をbuf.setBufferSize(100000);に変更した)を実行した状態で取得した場合
      1. Samples: 181K of event 'cpu-clock', Event count (approx.): 45387500000
      2. Overhead Command Shared Object Symbol
      3. 59.48% swapper [kernel.kallsyms] [k] 0xffffffff810656d6
      4. 1.98% ring_buffer_sam [kernel.kallsyms] [k] 0xffffffff8109b061
      5. 0.91% compiz [kernel.kallsyms] [k] 0xffffffff810031e8
      6. 0.85% gnome-terminal- [kernel.kallsyms] [k] 0xffffffff8109d9e0
      7. 0.83% gnome-terminal- libglib-2.0.so.0.4800.2 [.] g_string_insert_uni
      8. 0.80% gnome-terminal- [kernel.kallsyms] [k] 0xffffffff810031e8
      9. 0.76% compiz libX11.so.6.3.0 [.] 0x0000000000026e60
      10. 0.65% gnome-terminal- libglib-2.0.so.0.4800.2 [.] g_utf8_get_char
      11. 0.57% compiz libX11.so.6.3.0 [.] 0x0000000000026e62
      12. 0.57% compiz libX11.so.6.3.0 [.] 0x0000000000026f22
      13. 0.57% kworker/u8:2 [kernel.kallsyms] [k] 0xffffffff8109c9fa
      14. 0.54% compiz libX11.so.6.3.0 [.] 0x0000000000026f20
      15. 0.45% ring_buffer_sam [kernel.kallsyms] [k] 0xffffffff810ac14d
      16. 0.44% gnome-terminal- libglib-2.0.so.0.4800.2 [.] g_string_append_uni
      17. 0.42% ring_buffer_sam libc-2.23.so [.] vfprintf
      18. 0.41% ring_buffer_sam [kernel.kallsyms] [k] 0xffffffff8184ef5a
      19. 0.41% ring_buffer_sam [kernel.kallsyms] [k] 0xffffffff8184e9b5
      20. 0.41% ring_buffer_sam libc-2.23.so [.] __GI___libc_write
      21. 0.38% ring_buffer_sam libc-2.23.so [.] _IO_file_xsputn@@GL
      22. 0.37% kworker/u8:1 [kernel.kallsyms] [k] 0xffffffff8109c9fa
      23. 0.36% gnome-terminal- libvte-2.91.so.0.4200.5 [.] 0x00000000000135b2
      24. For a higher level overview, try: perf report --sort comm,dso
      上記の例の場合、swapper(スワッピング処理)が処理量の60%を占めているため、メモリの利用率をチェックする必要がある。
  • フレームグラフでボトルネック解析結果を可視化する場合
    1. フレームグラフ作成用のスクリプトをダウンロードする
      1. $ wget https://raw.githubusercontent.com/brendangregg/FlameGraph/master/stackcollapse-perf.pl
      2. $ wget https://raw.githubusercontent.com/brendangregg/FlameGraph/master/flamegraph.pl
    2. perfで記録する
      1. $ sudo perf record -ag
    3. perfで記録したファイルからフレームグラフを作成する
      1. $ sudo perf script> perf_data.txt
      2. $ perl stackcollapse-perf.pl perf_data.txt|perl flamegraph.pl > flamegraph.svg
      例: ring_buffer_sample(l.148をbuf.setBufferSize(100000);に変更した)を実行した状態で取得した場合
      1. firefox ./flamegraph.svg
      flamegraph
      フレームグラフでは、関数名で左から右にソートされており、コールスタックは上に行くほど深くなる。 一番上で横幅が広い関数がCPUを長く使っているため、ボトルネックになっている。
  • timechartを取得する場合
    1. perfで記録する
      1. $ sudo perf timechart record
    2. perfで記録した結果ファイルをsvgファイル(タイムチャート)として出力する(-p [pid or プロセス名]オプションで特定のプロセスのみを抜き出せる)
      1. $ sudo perf timechart -o timechart.svg
      例: ring_buffer_sample(l.148をbuf.setBufferSize(100000);に変更した)を実行した状態で取得した場合
      1. $ firefox ./timechart.svg
      timechart
      上記ファイルの上半分がCPUの情報、下半分が実行されているプロセスの情報である。 各CPUの使用率のタイムチャートや、各プロセスの処理タイミングなどが分かる。 CPUに無駄なアイドル時間(Idle)がある場合や、プロセスにCPU待ち時間(Waiting CPU)や無駄な読み書き待ち時間(Blocked on IO)がある場合、処理系の最適化を図る必要がある。

その他コマンド一覧

  • perf top
  • リアルタイムに関数単位でcpu使用率を閲覧できる
    1. $ perf top
    2. ---------------------------------------------------------------------------
    3. PerfTop: 260 irqs/sec kernel:61.5% exact: 0.0% [1000Hz
    4. cycles], (all, 2 CPUs)
    5. ---------------------------------------------------------------------------
    6.  
    7. samples pcnt function DSO
    8. _______ _____ ______________________________ __________________
    9.  
    10. 80.00 23.7% read_hpet [kernel.kallsyms]
    11. 14.00 4.2% system_call [kernel.kallsyms]
    12. 14.00 4.2% __ticket_spin_lock [kernel.kallsyms]
    13. 14.00 4.2% __ticket_spin_unlock [kernel.kallsyms]
    14. 8.00 2.4% hpet_legacy_next_event [kernel.kallsyms]
    15. 7.00 2.1% i8042_interrupt [kernel.kallsyms]
    16. 7.00 2.1% strcmp [kernel.kallsyms]
    17. 6.00 1.8% _raw_spin_unlock_irqrestore [kernel.kallsyms]
    18. ...
  • perf list
  • 指定可能なイベントの一覧を表示できる
    1. $ perf list
    2.  
    3. List of pre-defined events (to be used in -e):
    4.  
    5. cpu-cycles OR cycles [Hardware event]
    6. instructions [Hardware event]
    7. cache-references [Hardware event]
    8. cache-misses [Hardware event]
    9. branch-instructions OR branches [Hardware event]
    10. branch-misses [Hardware event]
    11. bus-cycles [Hardware event]
    12. ...
  • perf stat
  • 実行したコマンドのパフォーマンスカウンターが閲覧できる
    1. $ perf stat - make -j
    2.  
    3. Performance counter stats for 'make -j':
    4. 8117.370256 task clock ticks # 11.281 CPU utilization factor
    5. 678 context switches # 0.000 M/sec
    6. 133 CPU migrations # 0.000 M/sec
    7. 235724 pagefaults # 0.029 M/sec
    8. 24821162526 CPU cycles # 3057.784 M/sec
    9. 18687303457 instructions # 2302.138 M/sec
    10. 172158895 cache references # 21.209 M/sec
    11. 27075259 cache misses # 3.335 M/sec
    12. Wall-clock time elapsed: 719.554352 msecs

備考

  • Symbolが16進数標記で読めない場合、addr2lineコマンドで関数名を確認できる場合もある

まとめ


  • perfを使用し、ボトルネック解析を行う方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/29: 新規作成
  2. 2020/05/04: コマンド一覧追加

2019/09/26

CUDAの導入方法(Ubuntu編)

背景


GPUで並列処理を行うために、CUDAの導入が必要となった。

記事の目的


UbuntuにCUDAを導入する

CUDA


ここでは、CUDAの導入方法について記載する。

CUDAとは

CUDAは、nvidia社が提供する並列処理ライブラリである。

利点

  • 実装に関する情報が豊富である
  • TensorflowやOpenCVなど、機械学習や画像処理に関するツールはが基本的に対応している
  • C言語ライクな記述でプログラミングできる

導入方法

OSやマシンにあったCUDAのインストーラ方法は、公式ページから調べることができる。
今回は、Ubuntu16.04へCUDAを導入する手順について記載する。
  1. CUDAのアーカイブをダウンロードし、aptに登録する
    1. $ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-ubuntu1604.pin
    2. $ sudo mv cuda-ubuntu1604.pin /etc/apt/preferences.d/cuda-repository-pin-600
  2. OSやマシンにあった承認キーをダウンロードページから探す(今回は、/ubuntu1604/x86_64/7fa2af80.pub)
  3. 承認キーをダウンロードする
    1. $ sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub
  4. CUDAのレポジトリを登録する
    1. $ sudo add-apt-repository "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/ /"
  5. CUDAをインストールする
    1. $ sudo apt-get update
    2. $ sudo apt-get -y install cuda
  6. PCを再起動する
    1. $ sudo reboot

CUDAのサンプルプログラム

CUDAのサンプルプログラムのmake方法は、下記の通りである。
  1. $ /usr/local/cuda-10.1/bin/cuda-install-samples-10.1.sh ~
  2. $ cd ~/NVIDIA_CUDA-10.1_Samples
  3. $ make
上記の場合、~/NVIDIA_CUDA-10.1_Samples内にサンプルプログラムの実行ファイルが作成される。

アンインストール方法

CUDAのアンインストール方法について記載する。
  1. $ sudo apt remove cuda-10-1
  2. $ sudo apt autoremove
  3. $ sudo apt remove libcudnn7 libcudnn7-dev libcudnn7-doc
  4. $ rm -rfv ~/NVIDIA_CUDA-10.1_Samples/

まとめ


  • UbuntuにCUDAを導入する方法ついて調査、記載した

参考文献



変更履歴


  1. 2019/09/26: 新規作成

2019/09/24

NvidiaGPUの温度を取得するスクリプトを作成(Linux)

背景


GPUを搭載したPCでのソフトウェアのヒートランを行った際、GPUの温度をモニタリングする必要があったため、スクリプトを作成した。

記事の目的


NvidiaGPUの温度を取得するスクリプトを作成する

nvidia-smi


ここでは、NvidiaGPUの温度を取得するスクリプトの記述方法について記載する。

スクリプトの作成方法

スクリプトの作成方法は以下の通りである。
  1. スクリプトgpu_temperature_monitoring.shを作成する
    1. #!/bin/bash
    2. echo "Start to record GPU temperature!"
    3. DIR_NAME=~/gpu_temperature_log/`date +%Y%m%d`
    4. LOG_NAME=./`date +%Y%m%d_%H%M%S`_gpu_temperature.log
    5. mkdir -p $DIR_NAME
    6. cd $DIR_NAME
    7. echo "Date,Temperature[C]" >> $LOG_NAME
    8. while :
    9. do
    10. nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits | awk '{ "date +\"%Y/%m/%d %T\"" | getline var; print var "," $0 }' >> $LOG_NAME
    11. sleep 1
    12. done
    13. exit 0
  2. スクリプトに実行権限を与える
    1. $ chmod 777 ./gpu_temperature_monitoring.sh

スクリプトの実行方法

実行方法は以下の通りである。
  1. $ ./gpu_temperature_monitoring.sh
  2. Start to record GPU temperature!
~/gpu_temperature_monitoring_log/日付/にGPU温度のログが作成される
  1. $ less ./gpu_temperature_log/20190923/20190923_234227_gpu_temperature.log
  2. Date,Temperature[C]
  3. 2019/09/23 23:42:27,51
  4. 2019/09/23 23:42:28,51
  5. 2019/09/23 23:42:29,51
  6. 2019/09/23 23:42:30,52

まとめ


  • NvidiaGPUの温度を取得するスクリプトを作成した

参考文献



変更履歴


  1. 2019/09/24: 新規作成

2019/09/21

UbuntuへのJupyter notebookの導入方法

背景


仕事で、データ解析や機械学習の学習環境用に可読性の高いプログラミング環境を構築する必要があった。


記事の目的


Jupyter notebookを導入する

Jupyterの導入


ここでは、Jupyter notebookの導入方法と使用方法について記載する。

Jupyter notebookとは

Jupyter notebook
Jupyter notebookとはブラウザ上で実行し、実行結果を記録しながらプログラミングを進めるためのツールである。データ分析の現場や、研究機関などでも頻繁に使われている。

利点

  • PythonやRubyなど多様な言語に対応している
  • markdownでメモ、コメントが記述できる
  • グラフや表を表示できる

導入方法

Jupyter notebookの導入手順は、下記の通りである。

Pyenv+Anacondaの場合

  1. Pyenvを導入する
  2. 最新のAnaconda (Python3系) のバージョンを確認する
    1. $ pyenv install -l | grep anaconda3
    2. anaconda3-2.0.0
    3. anaconda3-2.0.1
    4. anaconda3-2.1.0
    5. anaconda3-2.2.0
    6. anaconda3-2.3.0
    7. anaconda3-2.4.0
    8. anaconda3-2.4.1
    9. anaconda3-2.5.0
    10. anaconda3-4.0.0
    11. anaconda3-4.1.0
  3. 最新のAnaconda (ここでは4.1.0) をインストールし、デフォルトとして設定する
    1. $ pyenv install anaconda3-4.1.0
    2. $ pyenv global anaconda3-4.1.0
    3. $ echo 'export PATH="$PYENV_ROOT/versions/anaconda3-4.1.0/bin:$PATH"' >> ~/.bashrc
    4. $ echo 'alias activate="source $PYENV_ROOT/versions/anaconda3-4.1.0/bin/activate"' >> ~/.bashrc
    5. $ source ~/.bashrc
  4. Pythonの環境を確認する
    1. $ python --version
    2. Python 3.5.1 :: Anaconda 4.1.0 (64-bit)

pipで直接インストールする場合

  1. 必要なパッケージをインストールする
    1. $ pip install numpy
    2. $ pip install scipy
    3. $ pip install matplotlib
    4. $ pip install Pillow
    5. $ pip install ipython[all]
  2. Jupyter Notebookをインストールする
    1. $ pip install jupyter

使用方法

Jupyter notebookの使用手順は、下記の通りである。
  1. ターミナル上で下記を実行する
    1. $ jupyter notebook

備考

  • matplotlibのグラフをインラインで表示するためには、はじめに下記を記述する
    1. %matplotlib inline
    2. import matplotlib.pyplot as plt


まとめ


  • Jupyterの導入方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/21: 新規作成

2019/09/19

黒画面でカーソルが点滅し、OSが起動しない場合の対処方法

背景


クローンしたUbuntuをIntel NUC上で起動させようとした際に、黒画面でカーソルが点滅した状態となり、OSが起動しない現象が生じた。

記事の目的


Boot Repairでブートローダーの問題を修復し、OSが起動できるようにする

Boot Repair


ここでは、Boot Repairでブートローダーの問題を修復する方法について記載する。

修復手順

Boot Repairでブートローダーの問題を修復する方法は以下の通りである。
  1. Ubuntuをメディアブートで起動する(言語は必ず「英語」を選択。日本語を選択すると、後述のBoot Repairがインストールできない場合がある)
  2. Boot Repairをインストールする
  3. [Ctrl]+[ALT]+[t]キーの同時押しでターミナルを立ち上げ、下記を入力する。
    1. $ sudo add-apt-repository ppa:yannubuntu/boot-repair
    2. $ sudo apt update
    3. $ sudo apt install -y boot-repair
  4. デスクトップ画面左上の「Activities」をクリックし、「Boot Repair」を検索、実行する
  5. 「Recommended repair」ボタンをクリックし、修復を実行する
  6. Boot Repair

まとめ


  • Boot Repairでブートローダーの問題を修正する方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/19: 新規作成
  2. 2020/05/07: 修復手順に画像追加

2019/09/17

Xvfbで仮想ディスプレイを作成する方法

背景


映像出力の無い状況下で、WebRTCといった映像系のWebアプリケーションを動作させようとすると、正常に動作しない場合があったため、仮想ディスプレイを作成する方法を調査した。

記事の目的


Xvfbで仮想ディスプレイを作成し、アプリケーションの映像出力を仮想ディスプレイに設定する

Xvfb


ここでは、Xvfbで仮想ディスプレイを作成する方法について記載する。

Xvfbの導入方法

Xvfbの導入方法は以下の通りである。
  1. xvfbをapt installする
    1. $ sudo apt install xvfb
  2. Ubuntu起動時にxvfbを自動起動するように設定する
    1. $ sudo nano /etc/rc.local
    2. ...
    3. Xvfb :99 -ac -screen 0 1024x768x16 &
    4. exit 0
    上記の設定で、ディスプレイ番号99が仮想ディスプレイとして設定される。
  3. PCを再起動する

仮想ディスプレイを映像出力先としてアプリケーションを実行する方法

実行方法は以下の通りである。(例としてFirefoxを使用)
  1. $ env DISPLAY=:99.0 firefox

備考

  • 描画処理は動いているため、その分CPU(GPU)リソースは消費される

まとめ


  • Xvfbで仮想ディスプレイを作成した

参考文献



変更履歴


  1. 2019/09/17: 新規作成

2019/09/14

Kerasの導入方法

背景


仕事で、ディープラーニングのモデルを学習させ、結果をエッジに組み込む必要が出てきた。ここでは、モデルの学習環境構築の方法について記載する。

記事の目的


Kerasを導入する

Kerasの導入


ここでは、Kerasの導入方法と使用方法について記載する。

Kerasとは

Keras
KerasはPythonで書かれた、TensorFlowまたはCNTK,Theano上で実行可能な高水準のニューラルネットワークライブラリである。

利点

  • pipで簡単に導入できる
  • Tensorflowと比べて学習コストが小さい
  • 学習モデルをTensorflowなどに移植可能

導入方法

Kerasの導入手順は、下記の通りである。(GPU対応版)
  1. CUDAをインストールする(現時点では、CUDA10.0)
  2. cuDNNをインストールする(要ユーザ登録)
  3. Python 3.6をインストールする
  4. tensorflow-gpuをインストールする
    1. $ pip install tensorflow-gpu
  5. Kerasをインストールする
    1. $ pip install keras
GPU版のTensorflowが動作しているかは、下記で確認できる。
  1. $ python
  2. >>> from tensorflow.python.client import device_lib
  3. >>> device_lib.list_local_devices()
  4. ...
  5. [name: "/device:CPU:0"
  6. device_type: "CPU"
  7. memory_limit: XXXXXXXXX
  8. locality {
  9. }
  10. incarnation: 10479716717325198190
  11. , name: "/device:GPU:0"
  12. device_type: "GPU"
  13. memory_limit: XXXXXXXXXX
  14. locality {
  15. bus_id: 1
  16. links {
  17. }
  18. }
  19. incarnation: 3656906984786649108
  20. physical_device_desc: "device: 0, name: GeForce GTX XXX XGB, pci bus id: 0000:01:00.0, compute capability: X.X"
  21. ]
CPU版の場合、GPUに関する記述がない。

使用方法

Kerasの使用手順は、下記の通りである。
  1. $ python
  2. >>> import keras
  3. Using TensorFlow backend.

まとめ


  • Kerasの導入方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/14: 新規作成

2019/09/12

google testでrosパッケージの単体テストを行う方法

背景


rosパッケージを作成する際に、品質担保のために通常単体テストを行う。
googleテストの使用方法やCMakeListsの書き方を備忘録として記載する。

記事の目的


google testでrosパッケージの単体テストを行う

google test


google testでrosパッケージの単体テストを行う方法について記載する。

google testとは

google testは、google社が提供するオープンソースのテストフレームワークである。

特徴

  • マクロを使用してテストケースを容易に記述できる
  • rosの標準のテストフレームワークである

テンプレートのパッケージ構成

google testでrosパッケージの単体テストを行うrosパッケージの構成を記載する。
  1. $ tree
  2. .
  3. ├── CMakeLists.txt -> /opt/ros/kinetic/share/catkin/cmake/toplevel.cmake
  4. └── sample_pkg # google testでテストを行うサンプルパッケージ
  5. ├── CMakeLists.txt # CMakeListsファイル
  6. ├── include
  7. └── sample_pkg
  8. └── sample.hpp # headerファイル
  9. ├── launch
  10. └── sample.launch #(オプション)launchファイル
  11. ├── package.xml # Packageファイル
  12. ├── src
  13. ├── main.cpp # ソースファイル(main関数のみ)
  14. └── sample.cpp # ソースファイル(その他関数, クラス)
  15. └── test
  16. └── utest.cpp # 単体テストファイル

テンプレートのheaderファイルとソースファイル

テンプレートのheaderファイルとソースファイルを記載する。
  1. // sample.hpp
  2. #include <ros/ros.h>
  3. namespace sample
  4. {
  5. int func1(int a, int b);
  6. bool func2(int c, int d);
  7. class class3
  8. {
  9. public:
  10. class3();
  11. ~class3();
  12. void func4(int e);
  13. void func5(int &f);
  14. protected:
  15. int internal_variable_ = 0;
  16. };
  17. };

  1. // sample.cpp
  2. #include <sample_pkg/sample.hpp>
  3. int sample::func1(int a, int b)
  4. {
  5. return a+b;
  6. }
  7. bool sample::func2(int a, int b)
  8. {
  9. if (a == b)
  10. {
  11. return true;
  12. }
  13. return false;
  14. }
  15. sample::class3::class3()
  16. {}
  17. sample::class3::~class3()
  18. {}
  19. void sample::class3::func4(int e)
  20. {
  21. internal_variable_ = e;
  22. }
  23. void sample::class3::func5(int &f)
  24. {
  25. f = internal_variable_;
  26. }

  1. // main.cpp
  2. #include <sample_pkg/sample.hpp>
  3. int main(int argc, char **argv){
  4. int A = sample::func1(1,3);
  5. return A;
  6. }


テンプレートの単体テストファイル

テンプレートの単体テストファイルを記載する。
  1. // utest.cpp
  2. #include <sample_pkg/sample.hpp>
  3. #include <gtest/gtest.h>
  4. // sample::func1のテスト
  5. TEST(Factorial1Test, func1Test)
  6. {
  7. int A = sample::func1(1,2);
  8. EXPECT_EQ(3, A); // A == 3 かチェック
  9. }
  10. // sample::func2のテスト
  11. TEST(Factorial2Test, func2Test)
  12. {
  13. EXPECT_TRUE(sample::func2(1,1)); // a == bの時、戻り値がtrueかチェック
  14. EXPECT_FALSE(sample::func2(1,2)); // a != bの時、戻り値がfalseかチェック
  15. }
  16. // sample::class3のテスト
  17. TEST(Class3Test, func4_5Test1)
  18. {
  19. sample::class3 c;
  20. int a;
  21. c.func5(a);
  22. EXPECT_EQ(0, a); // 初期状態でc.func5(a)のa==0かチェック
  23. }
  24. // sample::class3のテスト
  25. TEST(Class3Test, func4_5Test2)
  26. {
  27. sample::class3 c;
  28. int a = 4;
  29. int b = 0;
  30. c.func4(a);
  31. c.func5(b);
  32. EXPECT_EQ(a, b); // c.func4(a)を行った後、c.func5(b)のb==aかチェック
  33. }
  34. // Run all the tests that were declared with TEST()
  35. int main(int argc, char **argv){
  36. testing::InitGoogleTest(&argc, argv);
  37. return RUN_ALL_TESTS();
  38. }

テンプレートのCMakeListsファイルとPackageファイル

テンプレートのCMakeListsファイルとPackageファイルを記載する。
  1. # CMakeLists.txt
  2. cmake_minimum_required(VERSION 2.8.3)
  3. project(sample_pkg)
  4. ## Compile as C++11, supported in ROS Kinetic and newer
  5. add_compile_options(-std=c++11 -Wall -g -O3)
  6. ## Find catkin macros and libraries
  7. find_package(catkin REQUIRED COMPONENTS
  8.              roscpp
  9. )
  10. ###################################
  11. ## catkin specific configuration ##
  12. ###################################
  13. ## Declare things to be passed to dependent projects
  14. catkin_package(
  15.    INCLUDE_DIRS include
  16.    LIBRARIES sample_pkg
  17. )
  18. ###########
  19. ## Build ##
  20. ###########
  21. ## Specify additional locations of header files
  22. include_directories(include
  23.                     /usr/local/include
  24.                     ${catkin_INCLUDE_DIRS}
  25. )
  26. ## Declare a C++ library
  27. add_library(${PROJECT_NAME}_lib
  28.             src/sample.cpp
  29. )
  30. ## Declare a C++ executable
  31. add_executable(${PROJECT_NAME}
  32.                src/main.cpp
  33. )
  34. ## Specify libraries to link a library or executable target against
  35. target_link_libraries(${PROJECT_NAME}_lib
  36.                       ${catkin_LIBRARIES}
  37. )
  38. target_link_libraries(${PROJECT_NAME}
  39.       ${PROJECT_NAME}_lib
  40.                       ${catkin_LIBRARIES}
  41. )
  42. #############
  43. ## Install ##
  44. #############
  45. ## Mark executables and/or libraries for installation
  46. install(TARGETS ${PROJECT_NAME}
  47.         ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  48.         LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
  49.         RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
  50. )
  51. ## Mark cpp header files for installation
  52. install(DIRECTORY include/${PROJECT_NAME}/
  53.         DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
  54.         FILES_MATCHING PATTERN "*.hpp"
  55. )
  56. #############
  57. ## Testing ##
  58. #############
  59. ## Add gtest based cpp test target and link libraries
  60. catkin_add_gtest(${PROJECT_NAME}_test test/utest.cpp)
  61. target_link_libraries(${PROJECT_NAME}_test
  62.                       ${PROJECT_NAME}_lib
  63.                       ${catkin_LIBRARIES}
  64. )

  1. <!-- package.xml -->
  2. <?xml version="1.0"?>
  3. <package format="2">
  4. <name>sample_pkg</name>
  5. <version>0.0.0</version>
  6. <description>The sample package</description>
  7. <!-- One maintainer tag required, multiple allowed, one person per tag -->
  8. <maintainer email="xxx@yyy.zz">EmptySet</maintainer>
  9. <!-- One license tag required, multiple allowed, one license per tag -->
  10. <!-- Commonly used license strings: -->
  11. <!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
  12. <license>MIT</license>
  13. <!-- Url tags are optional, but multiple are allowed, one per tag -->
  14. <!-- Optional attribute type can be: website, bugtracker, or repository -->
  15. <url type="website">https://ittechnicalmemos.blogspot.com</url>
  16. <!-- Author tags are optional, multiple are allowed, one per tag -->
  17. <!-- Authors do not have to be maintainers, but could be -->
  18. <author email="xxx@yyy.zz">EmptySet</author>
  19. <!-- The *depend tags are used to specify dependencies -->
  20. <!-- Dependencies can be catkin packages or system dependencies -->
  21. <buildtool_depend>catkin</buildtool_depend>
  22. <build_depend>roscpp</build_depend>
  23. <build_export_depend>roscpp</build_export_depend>
  24. <exec_depend>roscpp</exec_depend>
  25. </package>

テンプレートの単体テスト実行方法

テンプレートの単体テスト実行方法及び結果を記載する。
  1. $ catkin_make run_tests
  2. ...
  3. - run_tests.py: execute commands
  4. /home/emptySet/work_space/devel/lib/sample_pkg/sample_pkg_test --gtest_output=xml:/home/emptySet/work_space/build/test_results/sample_pkg/gtest-sample_pkg_test.xml
  5. [==========] Running 4 tests from 3 test cases.
  6. [----------] Global test environment set-up.
  7. [----------] 1 test from Factorial1Test
  8. [ RUN ] Factorial1Test.func1Test
  9. [ OK ] Factorial1Test.func1Test (0 ms)
  10. [----------] 1 test from Factorial1Test (0 ms total)
  11. [----------] 1 test from Factorial2Test
  12. [ RUN ] Factorial2Test.func2Test
  13. [ OK ] Factorial2Test.func2Test (0 ms)
  14. [----------] 1 test from Factorial2Test (0 ms total)
  15. [----------] 2 tests from Class3Test
  16. [ RUN ] Class3Test.func4_5Test1
  17. [ OK ] Class3Test.func4_5Test1 (0 ms)
  18. [ RUN ] Class3Test.func4_5Test2
  19. [ OK ] Class3Test.func4_5Test2 (0 ms)
  20. [----------] 2 tests from Class3Test (0 ms total)
  21. [----------] Global test environment tear-down
  22. [==========] 4 tests from 3 test cases ran. (0 ms total)
  23. [ PASSED ] 4 tests.
  24. -- run_tests.py: verify result "/home/emptySet/work_space/build/test_results/sample_pkg/gtest-sample_pkg_test.xml"
  25. ...

テスト結果がNGの場合は、下記のように表示される。
  1. $ catkin_make run_tests
  2. ...
  3. /home/emptySet/work_space/src/sample_pkg/test/utest.cpp:31: Failure
  4. Value of: a
  5. Actual: 4
  6. Expected: 2
  7. [ FAILED ] Class3Test.func4_5Test2 (0 ms)
  8. ...
  9. [ FAILED ] 1 test, listed below:
  10. [ FAILED ] Class3Test.func4_5Test2
  11. 1 FAILED TEST
  12. ...

備考

  • google testのマクロはここを参照。

まとめ


  • google testでrosパッケージの単体テストを行う方法を調査、記載した

参考文献



変更履歴


  1. 2019/09/12: 新規作成

2019/09/09

Visual Studio Codeのプラグイン導入

背景


仕事で、ドキュメント作成やプログラミング作成にVisual Studio Code (以下vscode)を使用している。
個人的によく使うプラグインについて記載する。

記事の目的


ドキュメント作成やプログラミング作成でよく使うプラグインを記載する

Visual Studio Codeのプラグイン導入


ここでは、vscodeのプラグイン導入方法と使用方法について記載する。

Code Spell Checker

Code Spell Checker
Code Spell Checkerは、英語のスペルチェックを自動で行ってくれるプラグインである。

利点
  • プログラミングの際のクラス・関数・変数名、文言などのスペルミスを未然に防ぐことができる
  • スペースで区切られていない単語(ThisIsAPen)のスペルチェックも可能

使用方法
  • 自動的に、スペルミスした単語が強調される

Markdown Preview Enhanced


Markdown Preview Enhancedは、マークダウン形式で記載したドキュメントをプレビューできるプラグインである。

利点
  • マークダウン形式のドキュメントを変更すると、プレビュー画面へ変更が即座に反映される
  • htmlやpdf形式などで出力することができる

使用方法
  • ctr+shift+v: プレビュー画面表示
  • プレビュー画面右クリック: 保存タブ表示(html, pdfなど)

PlantUML

PlantUML

PlantUMLは、UML形式で記述した図をプレビューできるプラグインである。


利点
  • UML形式形式の図を変更すると、プレビュー画面へ変更が即座に反映される
  • 画像形式などで出力することができる

使用方法
  • Alt+D: プレビュー画面表示

まとめ


  • Visual Studio Codeのプラグインについて調査、記載した

参考文献



変更履歴


  1. 2019/09/10: 新規作成

2019/09/07

Visual Studio Code導入手順

背景


仕事で、ドキュメント作成やプログラミング作成にVisual Studio Code (以下vscode)を使用している。
そこで、Windows及びUbuntuへのvscodeの導入方法と、初期設定について記載する。

記事の目的


Visual Studio Codeを導入する

Visual Studio Codeの導入


ここでは、vscodeの導入方法と使用方法について記載する。

vscodeとは

Visual Studio Code

vscodeは、Microsoftが開発したオープンソースのエディターである。

利点

  • Windows, MacOS, Linuxで動作し、操作方法がほぼ共通である
  • 多くのプラグインが存在し、ワンクリックで導入できる
  • 比較的軽量で、使用中にストレスを感じることがない
  • デフォルトでgitと連携しており、commit, push, pull, diffがワンクリックで行え、バージョン管理や変更点確認が容易にできる

導入方法(Windows編)

Windowsへのvscodeの導入手順は、下記の通りである。
  1. 公式サイトから、インストーラをダウンロードする
  2. インストーラを起動し、インストール行う
  3. vscodeのインストーラ(Windows)

導入方法(Ubuntu編)

Ubuntuへのvscodeの導入手順は、下記の通りである。
  1. vscodeのインストールに必要なプログラムをインストールする
    1. $ sudo apt install curl apt-transport-https
  2. aptのリポジトリで使用するマイクロソフトのキーをダウンロードする
    1. $ curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
  3. ダウンロードしてきたキーを/etc/apt/trusted.gpg.d/にコピーする
    1. $ sudo install -o root -g root -m 644 microsoft.gpg /etc/apt/trusted.gpg.d/
  4. リポジトリを登録する
    1. $ sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
  5. インストールする
    1. $ sudo apt update
    2. $ sudo apt install code
  6. 起動する
    1. $ code .

初期設定方法

初期設定方法は、下記の通りである。
  • 日本語化
    1. vscodeを開く
    2. 「view」を選択する
    3. 「comanndo palette」を選択する
    4. 「configure display language」を選択する
    5. 「locale」を「en」から「ja」に書き換える。
      1. "locale":"ja" // Changes will not take effect until VS Code has been restarted.
    6. 左側にある四角いアイコン(extension)をクリックする
    7. 検索窓から「Japanese Language Pack for Visual Studio Code」を検索しインストールする
    8. vscodeを再起動する
  • 全角・半角スペースの表示設定
    1. vscodeを開く
    2. 「code」から「preferences」>「Settings」を選択する
    3. 「Editor:Render Whitespace」の項目を「all」に変更する

アンインストール方法(Ubuntu編)

Ubuntuからのvscodeのアンインストールは、下記の通りである。
  1. vscodeのインストールに使用したプログラムとvscodeをアンインストールする
    1. $ sudo apt remove apt-transport-https curl code
    2. $ sudo apt autoremove
  2. リポジトリを削除する
    1. $ sudo rm /etc/apt/sources.list.d/vscode.list
  3. ダウンロードしてきたキーを削除する
    1. $ sudo rm /etc/apt/trusted.gpg.d/microsoft.gpg

まとめ


  • Visual Studio Codeの導入方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/07: 新規作成

2019/09/05

実行中のrosノードを全て終了させるスクリプトの作成方法

背景


実行している複数rosノードをCtr+Cでkillする際、時々一部のノードが終了されずにバックグラウンドで実行される場合があった。このような場合、次にrosノードを起動した際に不具合が生じる可能性がある。そこで、実行中のrosノードを全てkillするスクリプトを作成した。

記事の目的


実行中のrosノードを全てkillするスクリプトを作成する

kill_all_rosnode.sh


ここでは、実行中のrosノードを全てkillするスクリプトの記述方法について記載する。

スクリプトの作成方法

スクリプトの作成方法は以下の通りである。
  1. スクリプトkill_all_rosnode.shを作成する
    1. #!/bin/bash
    2. echo "Kill all ros nodes!"
    3. ps aux | grep ros | grep -v grep | awk '{ print "kill -9", $2 }' | sh
    4. exit 0
  2. スクリプトに実行権限を与える
    1. $ chmod +x ./kill_all_rosnode.sh

スクリプトの実行方法

実行方法は以下の通りである。
  1. $ ./kill_all_rosnode.sh
  2. Kill all ros nodes!

まとめ


  • 実行中のrosノードを全てkillするスクリプトを作成した

参考文献



変更履歴


  1. 2019/09/05: 新規作成

2019/09/03

USBポートでデバイス名を固定する方法(Linux)

背景


同一デバイスを複数接続し、それらのデバイス名を固定する必要があった。デバイスが同一の為、udevのベンダーIDとプロダクトIDを利用してデバイス名を固定することができなかった。そこで、接続したUSBポートを用いた識別を行った。

記事の目的


同一デバイスを、接続したUSBポートの場所で識別し、デバイス名を固定する

接続したUSBポートでデバイスを識別


ここでは、ベンダーIDとプロダクトIDを利用した通常の識別方法と、接続したUSBポートの場所でデバイスを識別する方法についてそれぞれ記載する。

ベンダーIDとプロダクトIDを利用した識別方法

ベンダーIDとプロダクトIDを利用したデバイスの識別及びデバイス名の固定方法は下記の通りである。

  1. デバイスのベンダーIDとプロダクトIDの検索
  2. lsusbを用いて、デバイスのベンダーIDとプロダクトIDを検索する
    1. $ lsusb
    2. Bus 002 Device 002: ID idVendor:idProduct Intel Corp.
    3. Bus 003 Device 016: ID 054c:06c1 Sony Corp.
    4. :
    5. Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
  3. udevルールの作成
  4. /etc/udev/rules.dにルールファイルを作成する
    1. $ sudo nano /etc/udev/rules.d/80-nfc.rules
    2. SUBSYSTEM=="usb", ATTR{idVendor}=="054c", ATTR{idProduct}=="06c1", SYMLINK+="nfc"
    これで、Bus 003 Device 016: ID 054c:06c1 Sony Corp./dev/nfcとして毎回認識されるようになった。

接続したUSBポートの場所を利用した識別方法

上記方法では、同一のデバイスの場合、識別できない。そこで、接続したUSBポートで識別する。
  1. デバイスが接続されたUSBポートを探索する
  2. /dev/serial/by-path/以下に、接続したデバイスがUSBポートの位置で識別され、配置されている
    1. $ ls /dev/serial/by-path/
    2. pci-0000:00:1a.0-usb-0:1:1.0-port0
    3. pci-0000:00:1d.0-usb-0:2:1.0-port0
  3. シンボリックリンクを作成し、デバイス名を固定する
  4. /dev/にシンボリックリンクを作成することで、そのUSBポートに接続したデバイスのデバイス名が固定される
    1. $ sudo ln -s /dev/serial/by-path/pci-0000:00:1a.0-usb-0:1:1.0-port0 /dev/nfc
    これで、USBポートpci-0000:00:1a.0-usb-0:1:1.0-port0に接続されたデバイスが/dev/nfcとして毎回認識されるようになった。

備考

  • /dev/serial/by-id/以下に、接続したデバイスがシリアル番号で識別され、配置されている
  • /dev/disk/以下に、接続したディスクが/dev/serial/と同様の形でに配置されている

まとめ


  • 同一デバイスを、接続したUSBポートの場所で識別し、デバイス名を固定する方法について記載した

参考文献



変更履歴


  1. 2019/09/03: 新規作成

2019/09/01

Doxygen対応のコメント記載方法(C, C++)

背景


仕事でプログラミングする際に、関数に説明を追加する場合が多いため、Doxygen対応のコメント方法について記載する。

記事の目的


Doxygen対応のコメント記載方法について記す

Doxygen対応のコメント


ここでは、Doxygen対応のコメント記載方法について記す。

ファイルへのコメント

ファイルの先頭に、ファイル内のプログラムの説明を記載する。
  1. /**
  2. * @file ファイル名.h
  3. * @brief 簡単な説明
  4. * @author 書いた人
  5. * @date 作成日付
  6. */

関数へのコメント

関数毎に、関数の説明を記載する。
  1. /**
  2. * @fn func1(int, int)
  3. * ここに関数の説明を書く
  4. * @brief 要約説明
  5. * @param arg1 引数の説明
  6. * @param arg2 引数の説明
  7. * @return 戻り値の説明
  8. * @sa 参照すべき関数を書けばリンクが貼れる
  9. * @detail 詳細な説明
  10. */
  11. void func1(int arg1, int arg2)
  12. {}

変数へのコメント

変数毎に、変数の説明を記載する。
  1. //! 変数へのコメント
  2. int a = 0;

マクロへのコメント

マクロ毎に、マクロの説明を記載する。
  1. /** @def
  2. * マクロのコメント
  3. */
  4. #define MAX_XXX 256

列挙体へのコメント

列挙体毎に、列挙体の説明を記載する。
  1. /**
  2. * @enum Enum
  3. * 列挙体の説明
  4. */
  5. enum Enum {
  6. //! 列挙体の各要素の説明
  7. EnumItem1 = 0x00,
  8. //! 列挙体の各要素の説明
  9. EnumItem2 = 0x01
  10. };

構造体へのコメント

構造体体毎に、構造体の説明を記載する。
  1. /**
  2. * @struct 構造体名
  3. * @brief 構造体の説明
  4. **/
  5. struct Struct{
  6. //! 構造体の各要素の説明
  7. StructItem1 = 0x00,
  8. //! 構造体の各要素の説明
  9. StructItem2 = 0x01
  10. };

クラスへのコメント

クラス毎に、クラスの説明を記載する。
  1. int global_var1
  2. int global_var2
  3. int global_var3
  4. int global_var4
  5. /*! @class Class1
  6. @brief クラスの説明
  7. */
  8. class Class1 {
  9. public:
  10. /*! メンバ1の説明 */
  11. int member1;
  12. int member2; /*!< メンバ2の説明を横につける */
  13. /*! メソッド1の説明 */
  14. int method1(int var1, int var2);
  15. /*! メソッド2の説明。詳細説明
  16. @param[out] var1 var1の説明
  17. @param[in] var2 var2の説明
  18. @param[in,out] var3 var3の説明
  19. @par Refer
  20. - 参照するグローバル変数 global_var1
  21. - 参照するグローバル変数 global_var2
  22. @par Modify
  23. - 変更するグローバル変数 global_var3
  24. - 変更するグローバル変数 global_var4
  25. @return 成功 0, 失敗 0 以外 など
  26. @exception 例外。不要であればnoneを記述
  27. */
  28. int method2(int var1, int var2, int var3) {
  29. ...
  30. }
  31. };

まとめ


  • Doxygen対応のコメント記載方法を調査、記載した

参考文献



変更履歴


  1. 2019/09/01: 新規作成

2019/08/28

C++でリングバッファを実装

背景


仕事でプログラミングする際に、リングバッファを多用するため、テンプレートを作成する

記事の目的


リングバッファのテンプレートを作成する

リングバッファ


ここでは、リングバッファの記述方法について記載する。

仕様

テンプレートの仕様について記載する。
  • 複数のスレッドから、バッファサイズの変更、バッファの書き込み、バッファの読み込みが可能
  • データが格納されていないバッファにアクセスすると、エラーが表示され、デフォルトのデータが返される

テンプレート

リングバッファを使用するテンプレートを記載する。
  1. // ring_buffer.cpp
  2. #include <stdio.h>
  3. #include <mutex>
  4. // リングバッファに格納するデータ形式
  5. struct Data
  6. {
  7. int number = 0;
  8. };
  9. // 前方宣言
  10. class RingBuffer
  11. {
  12. public:
  13. // コンストラクタ(リングバッファのサイズを指定(2の冪乗が最適))
  14. RingBuffer(uint buffer_size_in);
  15. // デストラクタ
  16. ~RingBuffer();
  17. // リングバッファのサイズを指定(0より大の整数、2の冪乗が最適)
  18. void setBufferSize(uint buffer_size_in);
  19. // リングバッファのサイズを取得
  20. uint getBufferSize();
  21. // リングバッファにデータを書き込み
  22. void writeBuffer(Data data_in);
  23. // リングバッファのデータを読み込み(0: 最新, BufferSize: 最古)
  24. Data readBuffer(uint read_pointer_in);
  25. private:
  26. // コンストラクタ(宣言時に必ずバッファサイズを宣言する)
  27. RingBuffer();
  28. // リングバッファのポインタ
  29. Data *data_;
  30. // リングバッファのサイズ
  31. uint buffer_size_ = 0;
  32. // リングバッファ内に書き込まれたデータ数
  33. uint data_size_ = 0;
  34. // 書き込みポインタ(最新データのポインタ)
  35. uint write_pointer_ = 0;
  36. // mutex(読み書き、バッファサイズ変更を排他)
  37. std::mutex mtx_;
  38. };
  39. // コンストラクタ(プライベート関数)
  40. RingBuffer::RingBuffer()
  41. {}
  42. // コンストラクタ(リングバッファのサイズを指定)
  43. RingBuffer::RingBuffer(uint buffer_size_in)
  44. {
  45. // リングバッファのサイズを指定、メモリ上にバッファを確保
  46. setBufferSize(buffer_size_in);
  47. }
  48. // デストラクタ(リングバッファを削除)
  49. RingBuffer::~RingBuffer()
  50. {
  51. if (buffer_size_ > 0)
  52. {
  53. delete[] data_;
  54. }
  55. }
  56. // リングバッファのサイズを指定、メモリ上にバッファを確保
  57. void RingBuffer::setBufferSize(uint buffer_size_in)
  58. {
  59. if (buffer_size_in > 0)
  60. {
  61. // 排他
  62. std::lock_guard<std::mutex> lock(mtx_);
  63. // すでにメモリ上にリングバッファが確保されている場合は、一旦削除
  64. if (buffer_size_ > 0)
  65. {
  66. delete data_;
  67. }
  68. // リングバッファのサイズを保持
  69. buffer_size_ = buffer_size_in;
  70. // 内部変数の初期化
  71. data_size_ = 0;
  72. write_pointer_ = 0;
  73. // メモリ上にリングバッファを確保
  74. data_ = new Data[buffer_size_in];
  75. }
  76. else
  77. {
  78. fprintf(stderr, "Error: [RingBuffer] Please set buffer size more than 0.\n");
  79. }
  80. }
  81. // リングバッファのサイズを取得
  82. uint RingBuffer::getBufferSize()
  83. {
  84. // 排他
  85. std::lock_guard<std::mutex> lock(mtx_);
  86. return buffer_size_;
  87. }
  88. // リングバッファにデータ書き込み
  89. void RingBuffer::writeBuffer(Data data_in)
  90. {
  91. // リングバッファが確保されている場合のみ処理
  92. if (buffer_size_ > 0)
  93. {
  94. // 排他
  95. std::lock_guard<std::mutex> lock(mtx_);
  96. // 書き込みポインタを進める
  97. write_pointer_ ++;
  98. write_pointer_ %= buffer_size_;
  99. // データを書き込み
  100. data_[write_pointer_] = data_in;
  101. // 書き込んだデータ数を保持
  102. if (data_size_ < buffer_size_)
  103. {
  104. data_size_ ++;
  105. }
  106. }
  107. else
  108. {
  109. fprintf(stderr, "Error: [RingBuffer] Buffer is not allocated.\n");
  110. }
  111. }
  112. Data RingBuffer::readBuffer(uint read_pointer_in)
  113. {
  114. // 過去にデータが書き込まれていないデータのポインタにはアクセスさせない
  115. if (read_pointer_in < data_size_)
  116. {
  117. // 排他
  118. std::lock_guard<std::mutex> lock(mtx_);
  119. // 現在の最新データのポインタを0, 古いほど値が大きくなる(最大: Buffer Size)
  120. uint read_pointer = (buffer_size_+write_pointer_-read_pointer_in)%buffer_size_;
  121. return data_[read_pointer] ;
  122. }
  123. else
  124. {
  125. fprintf(stderr, "Error: [RingBuffer] Read pointer is out of buffer. (%d / %d)\n",
  126. read_pointer_in, data_size_);
  127. Data data;
  128. return data;
  129. }
  130. }
  131. // テストコード
  132. int main()
  133. {
  134. fprintf(stdout, "Error pattern -------\n");
  135. // 宣言(エラー)
  136. RingBuffer buf(0);
  137. Data data_1;
  138. data_1.number = 0;
  139. // データ書き込み(エラー)
  140. buf.writeBuffer(data_1);
  141. // データ読み込み(エラー)
  142. buf.readBuffer(1);
  143. // リングバッファのサイズを再設定
  144. buf.setBufferSize(10);
  145. // データ読み込み(エラー)
  146. buf.readBuffer(1);
  147. fprintf(stdout, "Normal pattern ------\n");
  148. for (uint i=0; i<2*buf.getBufferSize(); i++)
  149. {
  150. Data data_2;
  151. data_2.number = i;
  152. // データ書き込み(正常)
  153. buf.writeBuffer(data_2);
  154. fprintf(stdout, "%d / %d\n",i, 2*buf.getBufferSize()-1);
  155. // 1. データ読み込み(最古のデータ 0 or 10)
  156. fprintf(stdout, "1. buf.data[%d].number: %d\n",
  157. i%buf.getBufferSize(),
  158. buf.readBuffer(i%buf.getBufferSize()).number);
  159. // 2. データ読み込み(最新のデータ 0 to 19)
  160. fprintf(stdout, "2. buf.data[%d].number: %d\n",
  161. 0,buf.readBuffer(0).number);
  162. }
  163. return 0;
  164. }

テンプレートのコンパイル方法

テンプレートのコンパイル方法を記載する。
  1. $ g++ ./ring_buffer.cpp -std=c++11 -o ring_buffer_sample

テンプレートの実行結果

テンプレートの実行結果を記載する。
  1. $ ./ring_buffer_sample
  2. Error pattern -------
  3. Error: [RingBuffer] Please set buffer size more than 0.
  4. Error: [RingBuffer] Buffer is not allocated.
  5. Error: [RingBuffer] Read pointer is out of buffer. (1 / 0)
  6. Error: [RingBuffer] Read pointer is out of buffer. (1 / 0)
  7. Normal pattern ------
  8. 0 / 19
  9. 1. buf.data[0].number: 0
  10. 2. buf.data[0].number: 0
  11. 1 / 19
  12. 1. buf.data[1].number: 0
  13. 2. buf.data[0].number: 1
  14. 2 / 19
  15. 1. buf.data[2].number: 0
  16. 2. buf.data[0].number: 2
  17. 3 / 19
  18. 1. buf.data[3].number: 0
  19. 2. buf.data[0].number: 3
  20. 4 / 19
  21. 1. buf.data[4].number: 0
  22. 2. buf.data[0].number: 4
  23. 5 / 19
  24. 1. buf.data[5].number: 0
  25. 2. buf.data[0].number: 5
  26. 6 / 19
  27. 1. buf.data[6].number: 0
  28. 2. buf.data[0].number: 6
  29. 7 / 19
  30. 1. buf.data[7].number: 0
  31. 2. buf.data[0].number: 7
  32. 8 / 19
  33. 1. buf.data[8].number: 0
  34. 2. buf.data[0].number: 8
  35. 9 / 19
  36. 1. buf.data[9].number: 0
  37. 2. buf.data[0].number: 9
  38. 10 / 19
  39. 1. buf.data[0].number: 10
  40. 2. buf.data[0].number: 10
  41. 11 / 19
  42. 1. buf.data[1].number: 10
  43. 2. buf.data[0].number: 11
  44. 12 / 19
  45. 1. buf.data[2].number: 10
  46. 2. buf.data[0].number: 12
  47. 13 / 19
  48. 1. buf.data[3].number: 10
  49. 2. buf.data[0].number: 13
  50. 14 / 19
  51. 1. buf.data[4].number: 10
  52. 2. buf.data[0].number: 14
  53. 15 / 19
  54. 1. buf.data[5].number: 10
  55. 2. buf.data[0].number: 15
  56. 16 / 19
  57. 1. buf.data[6].number: 10
  58. 2. buf.data[0].number: 16
  59. 17 / 19
  60. 1. buf.data[7].number: 10
  61. 2. buf.data[0].number: 17
  62. 18 / 19
  63. 1. buf.data[8].number: 10
  64. 2. buf.data[0].number: 18
  65. 19 / 19
  66. 1. buf.data[9].number: 10
  67. 2. buf.data[0].number: 19

備考


まとめ


  • C++でリングバッファを記述するテンプレートを記載した

参考文献



変更履歴


  1. 2019/08/28: 新規作成
  2. 2019/08/28: 備考追記
  3. 2019/09/02: 仕様追記

2019/08/26

forループをCUDAで並列化する方法

背景


CUDAを利用して並列化を行い、処理を高速化する必要が出てきたため、CUDAで並列化処理を記述する方法について記述する

記事の目的


CUDAで並列化処理を行う際のテンプレートを作成する

CUDA


ここでは、CUDAを利用したCプログラムの記述方法について記載する。

CUDAとは

CUDAは、nvidia社が提供するGPUを利用した並列演算プログラミング基盤である

利点

  • ユーザーが多く、情報も入手しやすい
  • ライブラリが充実している
  • 導入が容易である

テンプレート

CUDAを利用して並列処理を行うテンプレートを記載する。
  1. // cuda_sample_code.cu
  2. #include <stdio.h>
  3. // デバイス(GPU)側のスレッドの設定 //////////////////////////////////
  4. // BLOCK_SIZE_Xは、1ブロックあたりのスレッド数を表す
  5. // GPUの種類により、1ブロックあたりのスレッド数の制限が異なる
  6. // 最適なスレッド数を設定しないと、カーネル関数の処理がスキップされる
  7. // 注)上記の場合、エラーでプロセスが落ちる事はない
  8. #define BLOCK_SIZE_X 512
  9. // カーネル関数(GPUで処理する関数)vec_sumの宣言 /////////////////////
  10. // この関数がGPUで並列実行される
  11. __global__
  12. void vec_sum(float k, float *a, float *b, float *c)
  13. {
  14. // iは、"for(int i=0; i < grid.x * block.x; i++)" の値を取る
  15. // ただし、並列処理のため、i++順に処理されるわけではない
  16. int i = blockIdx.x*blockDim.x + threadIdx.x;
  17. c[i] = k*a[i] + b[i];
  18. // GPU内のスレッドの同期 ////////////////////////////////////////
  19. // block_size * grid_size個の全てのスレッドが、ここで同期する
  20. __syncthreads();
  21. }
  22. // カーネル関数を呼び出す関数を宣言 /////////////////////////////////
  23. void cuda_handler(float *a, float *b, float *c)
  24. {
  25. // デバイス用のポインタを宣言 ///////////////////////////////////
  26. float *d_a, *d_b, *d_c;
  27. // デバイス(GPU)側の配列のメモリを確保 //////////////////////////
  28. // デバイス側のメモリ確保処理は重いため、回数は減らした方が良い
  29. cudaMalloc(&d_a, N*sizeof(float));
  30. cudaMalloc(&d_b, N*sizeof(float));
  31. cudaMalloc(&d_c, N*sizeof(float));
  32. // ホスト側の配列内のデータをデバイス側にコピー /////////////////
  33. cudaMemcpy(d_a, a, N*sizeof(float), cudaMemcpyHostToDevice);
  34. cudaMemcpy(d_b, b, N*sizeof(float), cudaMemcpyHostToDevice);
  35. // デバイス側の配列cの全ての要素に0をセット /////////////////////
  36. cudaMemset(d_c, 0, N*sizeof(float));
  37. // cudaを利用した処理のうち、最後のエラーを取得し、表示 /////////
  38. // ここでは、メモリへのデータセットのエラーチェックに利用している
  39. // 注)nvccのコンパイルオプションに"-g -G"を追加しないと動作しない
  40. // 注)エラーチェックをアクティブにすると、性能が極端に落ちる
  41. checkCudaErrors(cudaGetLastError());
  42. // 並列処理するスレッド数を定義 /////////////////////////////////
  43. // 総スレッド数は block_size * grid_size = N 個である
  44. // x, y, zの3次元まで設定可能である
  45. dim3 block_size (BLOCK_SIZE_X, 1, 1);
  46. dim3 grid_size (N / block.x, 1, 1);
  47. // カーネル関数(GPUで処理する関数)の呼び出し ////////////////////
  48. // カーネル関数内では、デバイス側のメモリ内のデータのみ操作可能
  49. vec_sum<<<grid_size, block_size>>>(2.0f, d_a, d_b, d_c);
  50. // cudaを利用した処理のうち、最後のエラーを取得し、表示 /////////
  51. // ここでは、vec_sumのエラーチェックに利用している
  52. // 注)nvccのコンパイルオプションに"-g -G"を追加しないと動作しない
  53. // 注)エラーチェックをアクティブにすると、性能が極端に落ちる
  54. checkCudaErrors(cudaGetLastError());
  55. // この行までに実行されたカーネル関数の処理が完了するまで待機 ///
  56. // デバイスとホストの処理は非同期である
  57. // 同期処理を行うか、cudaMemcpyするまで、互いは独立して動作する
  58. cudaThreadSynchronize();
  59. // 計算結果をホスト側にコピー ///////////////////////////////////
  60. cudaMemcpy(c, d_c, N*sizeof(float), cudaMemcpyDeviceToHost);
  61. // デバイス(GPU)側の配列のメモリを開放 //////////////////////////
  62. cudaFree(d_a);
  63. cudaFree(d_b);
  64. cudaFree(d_c);
  65. // cudaを利用した処理のうち、最後のエラーを取得し、表示 /////////
  66. // ここでは、メモリへのデータセットのエラーチェックに利用している
  67. // 注)nvccのコンパイルオプションに"-g -G"を追加しないと動作しない
  68. // 注)エラーチェックをアクティブにすると、性能が極端に落ちる
  69. checkCudaErrors(cudaGetLastError());
  70. }
  71. int main(void)
  72. {
  73. // 計算回数の設定 ///////////////////////////////////////////////
  74. // N = 512×2048
  75. int N = 1<<20;
  76. // ホスト用のポインタを宣言 /////////////////////////////////////
  77. float *a, *b, *c;
  78. // ホスト側の配列のメモリを確保 /////////////////////////////////
  79. a = (float*)malloc(N*sizeof(float));
  80. b = (float*)malloc(N*sizeof(float));
  81. c = (float*)malloc(N*sizeof(float));
  82. // a, bの配列にそれぞれ1,2を代入////////////////////////////////
  83. for (int i = 0; i < N; i++) {
  84. a[i] = 1.0f;
  85. b[i] = 2.0f;
  86. }
  87. // cudaでの処理を行う関数 ///////////////////////////////////////
  88. cuda_handler(a, b, c);
  89. // 計算結果の確認 ///////////////////////////////////////////////
  90. float maxError = 0.0f;
  91. for (int i = 0; i < N; i++)
  92. {
  93. maxError = max(maxError, abs(c[i]-4.0f));
  94. }
  95. printf("Max error: %f", maxError);
  96. // ホスト側の配列のメモリを開放 /////////////////////////////////
  97. free(a);
  98. free(b);
  99. free(c);
  100. return 0;
  101. }

テンプレートのコンパイル方法

テンプレートのコンパイル方法を記載する。
  1. nvcc -lcuda ./cuda_sample_code.cu -o ./cuda_sample
デバッグログを出力する場合は、下記のように記載する。
  1. nvcc -lcuda -g -G ./cuda_sample_code.cu -o ./cuda_sample

テンプレートの実行結果

テンプレートの実行結果を記載する。
  1. $ ./cuda_sample
  2. Max error: 0.000000

まとめ


  • CUDAでプログラミングをする際のテンプレートを調査、記載した

参考文献



変更履歴


  1. 2019/08/26: 新規作成

2019/08/25

make時にGPUのアーキテクチャ番号を自動設定させる方法

背景


複数種類の開発環境でまたがってCUDAを利用したプログラムを開発する際、マシンに搭載されたGPUのアーキテクチャに応じて、CMakeLists.txt内のアーキテクチャ番号を毎回書き換える必要があった。メンテナンスの効率化のため、どのGPU搭載のマシンでmakeした場合でも、GPUの種類を識別して、そのGPUに最適なバイナリファイルを生成する環境を構築する。

記事の目的


makeする際に自動で搭載GPUのアーキテクチャを識別し、そのGPUに最適なバイナリーを出力するようにmake環境を構築する

仮想/実アーキテクチャの設定


ここでは、UbuntuにおけるマルチGPU対応のmake環境を構築するための方法について記載する。

nvccのコンパイルオプション

nvccのコンパイルオプションのうち、アーキテクチャに関する部分について記述する。
  • --gpu-architecture (-arch)
  • *.cuが対象にしている仮想アーキテクチャを指定する。したがって、基本的にはcompute_*の中から選択する。
  • --gpu-code (-code)
  • 仮想アーキテクチャのコード(*.ptx)から生成して出力に加えるアーキテクチャを指定する。PTXを含めたいときはcompute_*、特定のGPU向けバイナリを含めたいときはsm_*を選択する。
  • --generate-code arch=compute_*,code=\"compute_*,sm_*\"
  • 上記2つのオプションを一度に設定できる

導入方法

make環境を構築する手順は、下記の通りである。
  1. check_cuda.cuをCMakeLists.txtと同じ階層に置く
    1. // check_cuda.cu
    2. #include <stdio.h>
    3. int main(int argc, char **argv){
    4. cudaDeviceProp dP;
    5. float min_cc = 3.0;
    6. int rc = cudaGetDeviceProperties(&dP, 0);
    7. if(rc != cudaSuccess) {
    8. cudaError_t error = cudaGetLastError();
    9. printf("CUDA error: %s", cudaGetErrorString(error));
    10. return rc; /* Failure */
    11. }
    12. if((dP.major+(dP.minor/10)) < min_cc) {
    13. printf("Min Compute Capability of %2.1f required: %d.%d found",
    14. min_cc, dP.major, dP.minor);
    15. printf(" Not Building CUDA Code");
    16. return 1; /* Failure */
    17. } else {
    18. printf("%d%d", dP.major, dP.minor);
    19. return 0; /* Success */
    20. }
    21. }
  2. CMakeLists.txtを編集する
  3. 下記のコードを追加することで、$CUDA_NVCC_FLAGSにコンパイルしたマシンのアーキテクチャを自動設定できる。
    1. cmake_minimum_required(VERSION 3.0)
    2. # Find CUDA
    3. find_package(CUDA)
    4. if (CUDA_FOUND)
    5. #Get CUDA compute capability
    6. set(OUTPUTFILE ${CMAKE_CURRENT_SOURCE_DIR}/cuda_script) # No suffix required
    7. set(CUDAFILE ${CMAKE_CURRENT_SOURCE_DIR}/check_cuda.cu)
    8. execute_process(COMMAND nvcc -lcuda ${CUDAFILE} -o ${OUTPUTFILE})
    9. execute_process(COMMAND ${OUTPUTFILE}
    10. RESULT_VARIABLE CUDA_RETURN_CODE
    11. OUTPUT_VARIABLE ARCH)
    12. execute_process(COMMAND rm ${OUTPUTFILE})
    13. if(${CUDA_RETURN_CODE} EQUAL 0)
    14. set(CUDA_SUCCESS "TRUE")
    15. else()
    16. set(CUDA_SUCCESS "FALSE")
    17. endif()
    18. if (${CUDA_SUCCESS})
    19. message(STATUS "CUDA Architecture: -arch=sm_${ARCH}")
    20. message(STATUS "CUDA Version: ${CUDA_VERSION_STRING}")
    21. message(STATUS "CUDA Path: ${CUDA_TOOLKIT_ROOT_DIR}")
    22. message(STATUS "CUDA Libararies: ${CUDA_LIBRARIES}")
    23. message(STATUS "CUDA Performance Primitives: ${CUDA_npp_LIBRARY}")
    24. set(CUDA_NVCC_FLAGS "{$CUDA_NVCC_FLAGS};--generate-code arch=compute_${ARCH},code=\"compute_${ARCH},sm_${ARCH}\"")
    25. else()
    26. message(WARNING -arch=sm_${ARCH})
    27. endif()
    28. endif()

まとめ


  • 搭載GPUのアーキテクチャを識別し、そのGPUに最適なバイナリーを出力するmake環境を構築する手順について調査、記載した

参考文献



変更履歴


  1. 2019/08/25: 新規作成

2019/08/24

CUDA10のコンパイラでrosのパッケージがコンパイルできない問題の対処法

背景


CUDA10のnvccを用いてrosのパッケージをコンパイルする際、エラーが発生してコンパイルできない問題が生じた。

記事の目的


CUDA10のnvccでrosパッケージをコンパイルできるようにする

nvccのバグ


ここでは、CUDA10のnvccでrosパッケージをコンパイルできない現象、原因及び対策について記載する。

現象

CUDA10のnvccでrosパッケージをコンパイルすると、下記のエラーが発生する場合がある。
  1. $ catkin_make
  2. ...
  3. /usr/include/pcl-1.7/pcl/point_cloud.h:586:100 error: template-id getMapping used as a declarator
  4. friend boost::shared_ptr& detail::getMapping(pcl::PointCloud &p);
  5. ^
  6. /usr/include/pcl-1.7/pcl/point_cloud.h:586:100 error: getMapping is neither function nor member function; cannot be declared friend
  7. cc1plus: error: expected ‘;’ at end of member declaration
  8. /usr/include/pcl-1.7/pcl/point_cloud.h:586:111: error: expected ‘)’ before ‘&’ token
  9. ...
  10. make: *** [all] Error 2

原因

nvccが利用しているgnuコンパイラのバグが原因である。このバグは、「friend関数によりnamespace内で定義された関数を使用する」記述があるコードで発生する。ところで、rosは独自に手を加えたPointCloudLibrary(PCL)を使用しているが、PCLのコード内に上記の記述が存在する('/usr/include/pcl-1.7/pcl/point_cloud.h'内 l.586)。nvccのコンパイラは、上記箇所をコンパイルする際に、関数のnamespace部分を削除してしまう(detail::getMapping→getMapping)。そのため、コンパイラ自身が関数を見つけられなくなり、エラーとなる。
  1. $less /usr/include/pcl-1.7/pcl/point_cloud.h
'point_cloud.h'内の該当箇所
  1.  
  2. namespace detail
  3. {
  4. template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
  5. getMapping (pcl::PointCloud<PointT>& p);
  6. } // namespace detail
  1.  
  2. protected:
  3. // This is motivated by ROS integration. Users should not need to access mapping_.
  4. boost::shared_ptr<MsgFieldMap> mapping_;
  5. friend boost::shared_ptr<MsgFieldMap>& detail::getMapping<PointT>(pcl::PointCloud<PointT> &p);
  6. public:
  7. EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  8. };
  9. namespace detail
  10. {
  11. template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
  12. getMapping (pcl::PointCloud<PointT>& p)
  13. {
  14. return (p.mapping_);
  15. }
  16. } // namespace detail

対策

'/usr/include/pcl-1.7/pcl/point_cloud.h'を下記のように書き換えることで回避できる。
  1. // Add ---------------------------------------------------------------------- //
  2. template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
  3. getMapping (pcl::PointCloud<PointT>& p);
  4. // -------------------------------------------------------------------------- //
  5. namespace detail
  6. {
  7. template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
  8. getMapping (pcl::PointCloud<PointT>& p);
  9. } // namespace detail
  1.  
  2. protected:
  3. // This is motivated by ROS integration. Users should not need to access mapping_.
  4. boost::shared_ptr<MsgFieldMap> mapping_;
  5. // Change ------------------------------------------------------------------- //
  6. friend boost::shared_ptr<MsgFieldMap>& getMapping<PointT>(pcl::PointCloud<PointT> &p);
  7. // friend boost::shared_ptr<MsgFieldMap>& detail::getMapping<PointT>(pcl::PointCloud<PointT> &p);
  8. // -------------------------------------------------------------------------- //
  9. public:
  10. EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  11. };
  12. // Add ---------------------------------------------------------------------- //
  13. template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
  14. getMapping (pcl::PointCloud<PointT>& p)
  15. {
  16. return (p.mapping_);
  17. }
  18. // -------------------------------------------------------------------------- //
  19. namespace detail
  20. {
  21. template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
  22. getMapping (pcl::PointCloud<PointT>& p)
  23. {
  24. return (p.mapping_);
  25. }
  26. } // namespace detail

備考

  • ros-kinetic、cuda 10.1で検証した
  • コンパイルが通ることと、実行可能であることを確認した

まとめ


  • rosをapt install時にインストールされるpclのヘッダーファイルを変更することにより、CUDA10のnvccでrosパッケージがコンパイルできなくなる問題を回避した

参考文献



変更履歴


  1. 2019/08/24: 新規作成

2019/08/20

Pyenvの導入(Ubuntu)

背景


Ubuntu開発用PCで、Python2、Python3、Anacondaを使用しているため、手軽に切り替えられる方法が必要だった。

記事の目的


pyenvを導入する

Pyenv


ここでは、Ubuntuにおけるpyenvの導入方法と使用方法について記載する。

Pyenvとは

Pyenvは、Pythonの複数のバージョンを使い分けるコマンドラインツールである。

利点

  • shellで書かれていてPython自体に依存しない
  • pyenv install <Version>のように打つだけで好きなバージョンがインストールでき、切り替えも容易にできる
  • virtualenvへのバインディングを標準で搭載し、バージョンを切り替えるのと同じインターフェースでパッケージ環境も切り替えられる

導入方法

UbuntuへのPyenvの導入手順は、下記の通りである。
  1. 必要ライブラリをインストールする
    1. $ sudo apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
    2. libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
    3. xz-utils tk-dev libffi-dev liblzma-dev python-openssl git
  2. gitからPyenvのを取得する
    1. $ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
  3. Pyenvにパスを通して有効化する
    1. $ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
    2. $ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
    3. $ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
    4. $ source ~/.bash_profile

使用方法

Pyenvの使用方法は、下記の通りである。
  • Pythonのインストール
    1. $ pyenv install <Version>
  • Pythonのアンインストール
    1. $ pyenv uninstall <Version>
  • インストールされたPythonのバージョン確認
    1. $ pyenv versions
    2. * system
    3. <Version>
  • Pythonのバージョン切り替え(全体)
    1. $ pyenv global <Version>
  • Pythonのバージョン切り替え(現在のディレクトリのみ)
    1. $ pyenv local <Version>

アンインストール方法

Pyenvのアンインストール方法は、下記の通りである。
  1. pyenvのアンインストール
    1. $ rm -rf $(pyenv root)
  2. (オプション)bash_profileかzshrcにpyenv関連の情報がある場合、削除
    1. $ nano ~/.bash_profile
    もしくは、
    1. $ nano ~/.zshrc
    下記を削除
    1. export PYENV_ROOT="$HOME/.pyenv"
    2. export PATH="$PYENV_ROOT/bin:$PATH"
    3. eval "$(pyenv init -)"
    4. eval "$(pyenv virtualenv-init -)"
  3. 再起動

まとめ


  • Pyenvの導入方法と使用方法について調査、記載した

参考文献



変更履歴


  1. 2019/08/20: 新規作成
  2. 2020/05/04: アンインストール方法追記

2019/08/17

PythonでSocketIO(クライアント編)

背景


PythonでSocketIO通信を行う必要があったため、今後の開発の効率化のためにSocketIO通信を行うクラスのテンプレートを作成する。

記事の目的


PythonでSocketIOクライアントを作成する

python-socketio


python-socketioを使用したSocketIOクライアントを作成する

選定理由

python-socketioの選定理由は、下記の通りである。

導入方法

python-socketioの導入手順は、下記の通りである。
  1. pipを利用して必要なライブラリをインストールする
    1. $ pip install requests python-socketio

テンプレート

python-socketioの使用テンプレートは、下記の通りである。
  1. # socketioライブラリのインポート
  2. import socketio
  3. # ログ出力用ライブラリのインポート
  4. from logging import getLogger, StreamHandler, DEBUG, INFO, ERROR
  5. # 時間関係ライブラリのインポート
  6. import time
  7. # 終了シグナルをキャッチするライブラリのインポート
  8. import signal
  9. # マルチスレッドライブラリのインポート
  10. import threading
  11. # ロガーのインスタンス作成
  12. logger = getLogger(__name__)
  13. stream_handler = StreamHandler()
  14. # ログレベルを設定
  15. log_level = INFO
  16. logger.setLevel(log_level)
  17. stream_handler.setLevel(log_level)
  18. # ロガーにハンドラーをセット
  19. logger.addHandler(stream_handler)
  20. # SocketIOのテンプレート
  21. class SocketIOClient:
  22. # namespaceの設定用クラス
  23. class NamespaceClass(socketio.ClientNamespace):
  24. def on_connect(self):
  25. pass
  26. def on_disconnect(self):
  27. pass
  28. def on_message(self, data):
  29. pass
  30. def on_server_to_client(self, data):
  31. pass
  32. # 接続時に呼ばれるイベント
  33. def on_connect(self):
  34. logger.info('Connected to server (%s:%d, namespace="%s", query="%s")',\
  35. self.ip_,self.port_,self.namespace_,self.query_)
  36. self.is_connect_ = True
  37. # 切断時に呼ばれるイベント
  38. def on_disconnect(self):
  39. logger.info('Disconnected from server (%s:%d, namespace="%s", query="%s")',\
  40. self.ip_,self.port_,self.namespace_,self.query_)
  41. self.is_connect_ = False
  42. def on_message(self, data):
  43. logger.info('Received message %s', str(data))
  44. # サーバーからイベント名「server_to_client」でデータがemitされた時に呼ばれる
  45. def on_server_to_client(self, data):
  46. logger.info('Received message %s', str(data))
  47. # Namespaceクラス内の各イベントをオーバーライド
  48. def overload_event(self):
  49. self.Namespace.on_connect = self.on_connect
  50. self.Namespace.on_disconnect = self.on_disconnect
  51. self.Namespace.on_message = self.on_message
  52. self.Namespace.on_server_to_client = self.on_server_to_client
  53. # 初期化
  54. def __init__(self,ip,port,namespace,query):
  55. self.ip_ = ip
  56. self.port_ = port
  57. self.namespace_ = namespace
  58. self.query_ = query
  59. self.is_connect_ = False
  60. self.sio_ = socketio.Client()
  61. self.Namespace = self.NamespaceClass(self.namespace_)
  62. self.overload_event()
  63. self.sio_.register_namespace(self.Namespace)
  64. # 接続確認
  65. def isConnect(self):
  66. return self.is_connect_
  67. # 接続
  68. def connect(self):
  69. # 接続先のURLとqueryの設定
  70. url = 'ws://'+self.ip_+':'+str(self.port_)+'?query='+self.query_
  71. logger.info('Try to connect to server(%s:%d, namespace="%s", query="%s")',\
  72. self.ip_,self.port_,self.namespace_,self.query_)
  73. try:
  74. self.sio_.connect(url, namespaces=self.namespace_)
  75. except:
  76. logger.error('Cannot connect to server(%s:%d, namespace="%s", query="%s")',\
  77. self.ip_,self.port_,self.namespace_,self.query_)
  78. else:
  79. if not self.is_connect_:
  80. logger.error('Namespace may be invalid.(namespace="%s")',\
  81. self.namespace_)
  82. # 切断
  83. def disconnect(self):
  84. try:
  85. self.sio_.disconnect()
  86. except:
  87. logger.error('Cannot disconnect from server(%s:%d, namespace="%s", query="%s")',\
  88. self.ip_,self.port_,self.namespace_,self.query_)
  89. else:
  90. self.is_connect_ = False
  91. # 再接続
  92. def reconnect(self):
  93. self.disconnect()
  94. time.sleep(5)
  95. self.connect()
  96. # クライアントからデータ送信(send)する
  97. def sendData(self, data):
  98. try:
  99. self.sio_.send(data, namespace=self.namespace_)
  100. except:
  101. logger.error('Has not connected to server(%s:%d, namespace="%s", query="%s")',\
  102. self.ip_,self.port_,self.namespace_,self.query_)
  103. else:
  104. logger.info('Send message %s (namespace="%s")', str(data), self.namespace_)
  105. # 独自定義のイベント名「client_to_server」で、クライアントからデータ送信(emit)する
  106. def emitData(self, data):
  107. try:
  108. self.sio_.emit('client_to_server', data, namespace=self.namespace_)
  109. except:
  110. logger.error('Has not connected to server(%s:%d, namespace="%s", query="%s")',\
  111. self.ip_,self.port_,self.namespace_,self.query_)
  112. else:
  113. logger.info('Emit message %s (namespace="%s")', \
  114. str(data), self.namespace_)
  115. # メインの処理
  116. def run(self):
  117. while True:
  118. self.connect()
  119. time.sleep(1)
  120. if self.is_connect_:
  121. break
  122. p = threading.Thread(target=self.sio_.wait)
  123. p.setDaemon(True)
  124. p.start()
  125. if __name__ == '__main__':
  126. # Ctrl + C (SIGINT) で終了
  127. signal.signal(signal.SIGINT, signal.SIG_DFL)
  128. # SocketIO Client インスタンスを生成
  129. sio_client = SocketIOClient('localhost', 10000, '/test', 'secret')
  130. # SocketIO Client インスタンスを実行
  131. sio_client.run()
  132. # データを送信
  133. for i in range(10):
  134. sio_client.sendData({'test_data': 'send_from_client'})
  135. sio_client.emitData({'test_data': 'emit_from_client'})
  136. time.sleep(1)
  137. # 切断
  138. sio_client.disconnect()
  139. logger.info('Finish')
  140. # 終了
  141. exit()

備考

  • namespaceは、必ず「/」(スラッシュ)から始まる必要がある
  • 上記テンプレートは、サーバーと通信テストができる

まとめ


  • Python-SocketIOを利用したクライアントモジュールを作成した

参考文献



変更履歴


  1. 2019/08/17: 新規作成

PythonでSocketIO(サーバー編)

背景


PythonでSocketIO通信を行う必要があったため、今後の開発の効率化のためにSocketIO通信を行うクラスのテンプレートを作成する。

記事の目的


PythonでSocketIOサーバーを作成する

python-socketio


python-socketioを使用したSocketIOサーバーを作成する

選定理由

python-socketioの選定理由は、下記の通りである。

導入方法

python-socketioの導入手順は、下記の通りである。
  1. pipを利用してインストールする
    1. $ pip install requests eventlet python-socketio

テンプレート

python-socketioの使用テンプレートは、下記の通りである。
  1. # socketioライブラリのインポート
  2. import socketio
  3. # ログ出力用ライブラリのインポート
  4. from logging import getLogger, StreamHandler, DEBUG, INFO, ERROR
  5. # 時間関係ライブラリのインポート
  6. import time
  7. # 終了シグナルをキャッチするライブラリのインポート
  8. import signal
  9. # eventletライブラリのインポート
  10. import eventlet
  11. # マルチスレッドライブラリをインポート
  12. import threading
  13. # ロガーのインスタンス作成
  14. logger = getLogger(__name__)
  15. stream_handler = StreamHandler()
  16. # ログレベルを設定
  17. log_level = INFO
  18. logger.setLevel(log_level)
  19. stream_handler.setLevel(log_level)
  20. # ロガーにハンドラーをセット
  21. logger.addHandler(stream_handler)
  22. # SocketIOのテンプレート
  23. class SocketIOServer:
  24. # namespaceの設定用クラス
  25. class NamespaceClass(socketio.Namespace):
  26. def on_connect(self, sid, environ):
  27. pass
  28. def on_disconnect(self, sid):
  29. pass
  30. def on_message(self, sid, data):
  31. pass
  32. def on_client_to_server(self, sid, data):
  33. pass
  34. # 接続時に呼ばれるイベント
  35. def on_connect(self, sid, environ):
  36. logger.info('Connected to client (sid: %s, environ: %s)',\
  37. str(sid), str(environ))
  38. self.is_connect_ = True
  39. # 切断時に呼ばれるイベント
  40. def on_disconnect(self, sid):
  41. logger.info('Disconnected from client (sid: %s)',str(sid))
  42. self.is_connect_ = False
  43. # サーバーからデータ送信(send)する
  44. def send_data(self, data):
  45. try:
  46. self.sio_.send(data, namespace=self.namespace_)
  47. except:
  48. logger.error('Has not connected to client')
  49. else:
  50. logger.info('Send message %s (namespace="%s")',\
  51. str(data), self.namespace_)
  52. # 独自定義のイベント名「server_to_client」で、サーバーからデータ送信(emit)する
  53. def emit_data(self, data):
  54. try:
  55. self.sio_.emit('server_to_client', data, namespace=self.namespace_)
  56. except:
  57. logger.error('Has not connected to client')
  58. else:
  59. logger.info('Emit message %s (namespace="%s")',\
  60. str(data), self.namespace_)
  61. def on_message(self, sid, data):
  62. logger.info('Received message %s', str(data))
  63. self.send_data({'test_ack': 'send_from_server'})
  64. # クライアントからイベント名「on_client_to_server」でデータがemitされた時に呼ばれる
  65. def on_client_to_server(self, sid, data):
  66. logger.info('Received message %s', str(data))
  67. self.emit_data({'test_ack': 'emit_from_server'})
  68. # Namespaceクラス内の各イベントをオーバーライド
  69. def overload_event(self):
  70. self.Namespace.on_connect = self.on_connect
  71. self.Namespace.on_disconnect = self.on_disconnect
  72. self.Namespace.on_message = self.on_message
  73. self.Namespace.on_client_to_server = self.on_client_to_server
  74. # 初期化
  75. def __init__(self,ip,port,namespace):
  76. self.ip_ = ip
  77. self.port_ = port
  78. self.namespace_ = namespace
  79. self.is_connect_ = False
  80. self.sio_ = socketio.Server(async_mode='eventlet')
  81. self.app_ = socketio.WSGIApp(self.sio_)
  82. self.Namespace = self.NamespaceClass(self.namespace_)
  83. self.overload_event()
  84. self.sio_.register_namespace(self.Namespace)
  85. # 接続確認
  86. def isConnect(self):
  87. return self.is_connect_
  88. # 切断
  89. def disconnect(self,sid):
  90. try:
  91. self.sio_.disconnect(sid,namespace=self.namespace_)
  92. except:
  93. logger.error('Cannot disconnect from Client(sid="%s", namespace="%s")',\
  94. namespace=self.namespace_)
  95. else:
  96. self.is_connect_ = False
  97. # 開始
  98. def start(self):
  99. try:
  100. logger.info('Start listening(%s:%d, namespace="%s")',\
  101. self.ip_,self.port_,self.namespace_)
  102. eventlet.wsgi.server(eventlet.listen((self.ip_, self.port_)), self.app_)
  103. except:
  104. logger.error('Cannot start listening(%s:%d, namespace="%s")',\
  105. self.ip_,self.port_,self.namespace_)
  106. # メインの処理
  107. def run(self):
  108. p = threading.Thread(target=self.start)
  109. p.setDaemon(True)
  110. p.start()
  111. if __name__ == '__main__':
  112. # Ctrl + C (SIGINT) で終了
  113. signal.signal(signal.SIGINT, signal.SIG_DFL)
  114. # SocketIO Server インスタンスを生成
  115. sio_server = SocketIOServer('localhost', 10000, '/test')
  116. # SocketIO Server インスタンスを実行
  117. sio_server.run()
  118. # 接続待ち
  119. while not sio_server.isConnect():
  120. time.sleep(1)
  121. # 切断待ち
  122. while sio_server.isConnect():
  123. time.sleep(1)
  124. logger.info('Finish')
  125. # 終了
  126. exit()

備考

  • namespaceは、必ず「/」(スラッシュ)から始まる必要がある
  • eventletを使用した場合、他の作業スレッドから、SocketIO内のEmitやSendを呼び出せない
  • 上記テンプレートは、クライアントと通信テストができる

まとめ


  • Python-SocketIOを利用したサーバーモジュールを作成した

参考文献



変更履歴


  1. 2019/08/17: 新規作成

2019/08/15

クローンしたUbuntuをIntel NUC上で起動した際に、OSが起動しなくなる問題の対処法

背景


Intel NUCへUbuntuをクローンして同一環境のPCを増やした際に、"no bootable devices found"のメッセージが表示され、OSが起動しなくなった。

記事の目的


Intel NUC上で、クローンしたUbuntu起動できるようにする

フォールバック起動に変更


ここでは、起動しない原因と対処法について記載する。

起動しない原因

クローンしたUbuntuが起動しない原因は下記の通りである。
  • EFI下では、OSを起動するブートローダーにはUbuntuなどのOS固有の名称が付けられる
  • マシンがブートローダーを検索できるように、ブートローダーをファームウェアのNVRAMに登録する必要がある
  • 通常のインストール時には、NVRAMへの登録も自動的に行われる
  • クローンした場合はNVRAMへの登録が行われないため、ブートローダーが見つけられず、OSを起動できない

対処方法

EFI下では、ブートローダーが見つからない場合、フォールバックとして起動するブートローダー名が設定されている。
そこで、フォールバック用のブートローダーを作成する。
  1. メディアブート等でOSを起動する
  2. 対象のOSがインストールされたメディア(SSD、HDD)の中のEFI System Partition (ESP)をマウントする
  3. EFI/BOOTディレクトリ(フォールバック用のブートローダーディレクトリ)を作成する
  4. EFI/ubuntu/*を、EFI/BOOT/*へコピーする

対処方法例(Ubuntuメディアブートの場合)

Ubuntuメディアブートを行った場合の対処方法は、下記の通りである。
  1. $ sudo fdisk -l
  2. Device Start End Sectors Size Type
  3. /dev/sda1 2048 1128447 1126400 550M EFI System
  4. /dev/sda2 1128448 79626398 78497951 37.4G Linux filesystem
  5. /dev/sda3 79628288 85917854 6289567 3G Linux swap
  6. $ sudo mount /dev/sda1 /mnt #「Type」が「EFI System」のものをマウントする
  7. $ cd /mnt
  8. $ sudo mkdir EFI/BOOT #フォールバック用のブートローダーディレクトリ
  9. $ sudo cp EFI/ubuntu/* EFI/BOOT/

まとめ


  • EFI下のPCでクローンしたUbuntuを使用する場合、OSが起動しなくなる問題の原因と対策について記載した

参考文献



変更履歴


  1. 2019/08/15: 新規作成

Blogger上でコードを埋め込む方法

背景


ソフトウェアエンジニアとして活動する中で、個人の備忘録をかねてBloggerで技術メモを作成することにした。記事内でコードを表示する場合があるため、Bloggerの記事でコードを埋め込む方法を調査した。

記事の目的


Bloggerの記事でコードを表示する

google-code-prettify


ここでは、技術メモ内におけるコードの埋め込み方法について記載する。技術メモでは、基本的にgoogle-code-prettifyでコードの表示を行う。

選定理由

google-code-prettifyの選定理由は、下記の通りである。
  • 複数の言語をサポートしている
  • 行番号を表示できる
  • PC表示時にスクロールバーを表示可能
  • 導入が容易

導入方法

google-code-prettifyの導入手順は、下記の通りである。
  1. Blogger管理画面を開く
  2. 「テーマ」 を選び、 「HTMLの編集」をクリックする
  3. 下記Javascriptコードを、html内の<head>...</head>に貼り付ける
    1. <!-- google-code-prettify -->
    2. <script src='https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?skin=sons-of-obsidian'/>
    3. <style type='text/css'>
    4. pre.prettyprint {
    5.       overflow: auto;
    6. }
    7. .prettyprint ol.linenums &gt; li {
    8. list-style-type: decimal;
    9. }
    10. </style>
    11. <!-- /google-code-prettify -->

使用方法

google-code-prettifyの使用方法は、下記の通りである。
  1. <pre class="prettyprint linenums">
  2. int sample_code(int b)
  3. {
  4. int a = 10;
  5. a += b;
  6. return a;
  7. }
  8. </pre>
上記の通り記述すると、下記のように表示される。
  1. int sample_code(int b)
  2. {
  3. int a = 10;
  4. a += b;
  5. return a;
  6. }

備考

  • 下記のように、言語を指定することも可能である。指定できる言語は、ここを参照。
    1. <pre class="prettyprint lang-yaml linenums" >
    2. - name : tomato
    3. email: tomato@gmail.com
    4. - name : potato
    5. email: potato@gmail.com
    6. </pre>
  • 記事にコード埋め込む際は、HTMLに直接記述する。「作成」で記述した場合、正しく作成されない場合がある
  • <style type='text/css'>...</style>にスタイルに関する記述を追記することで、見た目の変更が可能である
  • Javascriptコードの4-6行目で、スクロールバー表示を設定している
  • Javascriptコードの7-9行目で、行番号を毎行表示に設定している

まとめ


  • 技術メモ作成にあたり、Bloggerにコードを表示させる方法を記述した
  • google-code-prettifyは、htmlの<head>...</head>間に、該当Javascriptコードを記入することで使用可能である

参考文献



変更履歴


  1. 2019/08/15: 新規作成
  2. 2019/08/23: 備考追記

Blogger上で数式を表示する方法

背景


ソフトウェアエンジニアとして活動する中で、個人の備忘録をかねてBloggerで技術メモを作成することにした。記事内で数式を扱う場合があるため、Bloggerの記事で数式を表示する方法を調査した。

記事の目的


Bloggerの記事で数式を表示する

MathJax


ここでは、技術メモ内における数式の作成方法について記載する。
技術メモでは、基本的にMathJaxで数式の作成を行う。

選定理由

MathJaxの選定理由は、下記の通りである。
  • Tex形式で数式を記述できる
  • ブログ記事内に直接数式を記述することで、数式が自動生成される

導入方法

MathJaxの導入手順は、下記の通りである。
  1. Blogger管理画面を開く
  2. 「テーマ」 を選び、 「HTMLの編集」をクリックする
  3. 下記Javascriptコードを、html内の<head>...</head>に貼り付ける
    1. <!-- MathJax -->
    2. <script
    3. src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js'
    4. type='text/javascript'>
    5. MathJax.Hub.Config({
    6. HTML: ['input/TeX','output/HTML-CSS'],
    7. TeX: { extensions: ['AMSmath.js','AMSsymbols.js'],
    8. equationNumbers: { autoNumber: 'AMS' } },
    9. extensions: ['tex2jax.js'],
    10. jax: ['input/TeX','output/HTML-CSS'],
    11. tex2jax: { inlineMath: [ ['$','$'], ['\\(','\\)'] ],
    12. displayMath: [ ['$$','$$'], ['\\[','\\]'] ],
    13. processEscapes: true },
    14. 'HTML-CSS': { availableFonts: ['TeX'],
    15. linebreaks: { automatic: true } }
    16. });
    17. </script>
    18. <!-- /MathJax -->

使用方法

MathJaxの使用方法は、下記の通りである。

  • インライン表示の場合
    1. インライン表示: $e^{i\pi}=-1$
    インライン表示: e^{i\pi}=-1
  • 独立行表示の場合
    1. $$\sum_{i=1}^{n}i = \frac{1}{2}n(n+1)$$
    \sum_{i=1}^{n}i = \frac{1}{2}n(n+1)

備考

  • MathJaxのJavascriptコードURL:(https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js)は将来的に変更される可能性がある。有効なURLはMathJaxのGetting Startに記載されている。
  • Broggerのテーマによっては、数式が表示されない。現状動作確認しているのは、「シンプル」のみである。
  • BroggerのテーマのHTMLを編集する際、「"」(ダブルクオート)を使用すると、文字化けし、正しく動作しない場合がある。 

まとめ


  • 技術メモ作成にあたり、Bloggerに数式を表示させる方法を記述した
  • MathJaxは、htmlの<head>...</head>間に、該当Javascriptコードを記入することで使用可能である

参考文献



変更履歴


  1. 2019/08/15: 新規作成

MQTTの導入

背景 IoTデバイスの接続環境構築のため、MQTT(mosquitto)の導入を行った。 記事の目的 MQTT(mosquitto)をUbuntuに導入する mosquitto ここではmosquittoについて記載する。 MQTT MQTT(Message Qu...