Ubuntu18.04にGPU(RTX2070)のドライバを入れる

注意: lowlatency-kernelでは使えないので,generic-kernelに変更してください.

# nouveauの存在を確認
$ lsmod | grep -i nouveau

nouveauを無効化するために,/etc/modprobe.d/blacklist-nouveau.conf に以下を書き込みます.

blacklist nouveau
options nouveau modeset=0

更新し,再起動します.

$ sudo update-initramfs -u
$ reboot

以下のコマンドで何も表示されなくなっていればOKです.

$ lsmod | grep -i nouveau

ドライバ・CUDAのインストール

# 以前にインストールしたドライバ・CUDAをアンインストール
$ sudo apt purge nvidia-*
$ sudo apt purge cuda-*

# aptのリポジトリに追加
sudo add-apt-repository ppa:graphics-drivers/ppa
sudo apt update

# インストール可能なドライバ一覧表示
$ ubuntu-drivers devices

# インストール
$ sudo ubuntu-drivers autoinstall
(または,$ sudo apt install nvidia-driver-450 )

再起動してから,

$ nvidia-smi

GPUの使用状況が見れるはず.

$ nvcc -V
$ sudo apt install nvidia-cuda-toolkit
( $ reboot )
$ nvidia-smi

VSCodeで日本語LaTexオンラインプレビュー

texlive

$ sudo apt install texlive-full

拡張機能の追加

LaTex Workshopを追加する.

設定の変更

(Ctrl+Shift+P) でコマンドパレットを開き,Preferences: Open Settings (JSON)の中に以下を書く.

{
    "editor.tabSize": 4,
    "editor.detectIndentation": false,
    "files.encoding": "utf8",

    "vim.insertModeKeyBindings": [
        {
            "before": ["j", "j"],
            "after": ["<Esc>"]
        }
    ],

    // 自動保存
    "files.autoSave":"off",
    "latex-workshop.view.pdf.viewer": "tab",
    "latex-workshop.view.pdf.zoom":"auto",
    "latex-workshop.latex.autoClean.run":"onBuilt",
    "latex-workshop.latex.autoBuild.cleanAndRetry.enabled": true,

    "latex-workshop.latex.tools": [
        {
            "command": "platex",
            "args": [
                "-interaction=nonstopmode",
                "-synctex=1",
                "-jobname=\"%DOCFILE%\"",
                "-kanji=utf8",
                "-guess-input-enc",
                "%DOCFILE%.tex"
            ],
            "name": "Step 1: platex"
        },
        {
            "command": "platex",
            "args": [
                "-synctex=1",
                "-jobname=\"%DOCFILE%\"",
                "-kanji=utf8",
                "-guess-input-enc",
                "%DOCFILE%.tex"
            ],
            "name": "Step 2: platex"
        },
        {
            "command": "dvipdfmx",
            "args": [
                "%DOCFILE%"
            ],
            "name": "Step 3: dvipdfmx"
        }
    ],
    "latex-workshop.latex.recipes": [
        {
            "name": "toolchain",
            "tools": [
                "Step 1: platex",
                "Step 2: platex",
                "Step 3: dvipdfmx",
            ]
        }
    ],
}

毎回<Esc>を押しに行くのが面倒とお思いのあなたへ(VSCode, Vim)

jjを割り当てる

(Ctrl+Shift+P) でコマンドパレットを開き,Preferences: Open Settings (JSON)の中に以下を書く.

{
    "editor.tabSize": 4,
    "editor.detectIndentation": false,
    "files.encoding": "utf8",

    "vim.insertModeKeyBindings": [
        {
            "before": ["j", "j"],
            "after": ["<Esc>"]
        }
    ] 
}

ctrl + ;でターミナルとエディタを切り替える

(Ctrl+Shift+P) でコマンドパレットを開き,Preferences: Open Keyboard Shortcuts Filesの中に以下を書く.

[    
    {
       "key": "Ctrl+j",
       "command": "extension.vim_escape",
       "when": "editorTextFocus && vim.active && !inDebugRepl"
    },
    { 
        "key": "ctrl+;", 
        "command": "workbench.action.terminal.focus"
    },
    { 
        "key": "ctrl+;", 
        "command": "workbench.action.focusActiveEditorGroup", 
        "when": "terminalFocus"
    },
    {
        "key": "ctrl+w",
        "command": "workbench.action.closeActiveEditor",
        "when": "editorTextFocus"
    },
    {
        "key": "ctrl+shift+right",
        "command": "workbench.action.moveEditorToNextGroup",
        "when": "editorTextFocus"
    },
    {
        "key": "ctrl+shift+left",
        "command": "workbench.action.moveEditorToPreviousGroup",
        "when": "editorTextFocus"
    },
    {
        "key": "ctrl+shift+tab",
        "command": "workbench.action.previousEditor",
        "when": "editorTextFocus"
    },
    {
        "key": "ctrl+tab",
        "command": "workbench.action.nextEditor",
        "when": "editorTextFocus"
    },
    {
        "key": "tab",
        "command": "tab",
        "when": "editorTextFocus"
    },
    {
        "key": "ctrl+shift+c",
        "command": "editor.action.clipboardCopyAction",
        "when": "!terminalFocus"
    }
]

(おまけ)vscodeでよく使うショートカットキーメモ

  • エクスプローラとエディタの切り替え Ctrl + Shift + E
  • エディタの分割 Ctrl + \
  • エディタグループの切り替え(なければ作成) Ctrl + 数字
  • アクティブタブを隣のエディタグループへ移動 Ctrl + Alt + ←→
  • アクティブなタブを終了 Ctrl + w

Qt5チュートリアル(C++, Ubuntu)

前回の記事でPyQtによるGUIプログラミングを紹介しました.
今回はC++で同じようなことをやっていきます. PythonからC++になった瞬間にコンパイルエラー等が起きまくって大変な目に会いますが,この記事通りに進めればできる(はず?)です.

環境構築

各自適当に$ sudo apt install qt5*とか調べながらQt5を入れてください.

やってみましょう

$ mkdir qt_tutorial
  • mainwindow.cppの作成
    qt_tutorialディレクトリの中にmainwindow.cppを作り,以下を書き込みます.
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QGridLayout>

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  QWidget window;
  window.setGeometry(1300, 300, 1000, 850);
  window.setWindowTitle("test");
  
  QGridLayout *layout = new QGridLayout;
  window.setLayout(layout);

  QPushButton *button = new QPushButton("終了",0);

  QObject::connect(button, SIGNAL(clicked()), &app, SLOT(quit()));

  layout->addWidget(button);
  window.show();

  return app.exec();
}
$ qmake -project

qt_tutorial.proの最後にQT += widgetsを加えてから,

$ qmake qt_tutorial.pro
$ make
$ ./qt_tutorial

以上!!

PyQt5で始めるGUIプログラミング

GUIプログラミング入門

てきとうなuiファイル(test.ui)を作成して,以下のコードを実行すれば,こんな感じのGUIが簡単に作成できます.f:id:ya10345:20190614220536p:plain

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys, os
from PyQt5 import QtCore, QtMultimedia, uic
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QIcon

# -------------------------------------GUI描画 ----------------------------------------------------------
class MainMenu(QWidget):
    def __init__(self, parent=None):
        super(MainMenu, self).__init__()

        ui_file = uic.loadUiType("test.ui", self)[0]
        self.ui = ui_file()
        self.ui.setupUi(self)
        
        # クリックイベント
        self.ui.pose_init.clicked.connect(self.button_clicked)
        self.ui.start_moving.clicked.connect(self.button_clicked)
        self.ui.publish.clicked.connect(self.button_clicked)
        self.ui.excute.clicked.connect(self.button_clicked)
        self.ui.go_home.clicked.connect(self.button_clicked)
        self.ui.quit.clicked.connect(self.button_clicked)

        # ボタンのクリック音の設定
        self.mediaPlayer = QtMultimedia.QMediaPlayer(self)
        self.app_root = os.path.abspath(os.path.dirname(sys.argv[0]))
        sound=QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(os.path.join(self.app_root,"click.wav")))
        self.mediaPlayer.setMedia(sound)


    # クリック処理
    def button_clicked(self):
        self.mediaPlayer.stop()
        self.mediaPlayer.play()

# ---------------------------- メイン関数 ----------------------------------------------------------
def main():
    app = QApplication([])
    window = MainMenu()
    window.show()
    sys.exit(app.exec_())
    
# -------------------------- プログラム開始点 -------------------------------------------------------
if __name__ == '__main__':
    main()

Git使い方メモ

GitとGitHub

Gitとはバージョン管理ツールです.Gitを使うと,GitHubというウェブサービス上にリポジトリをコピーすることができます.

Gitの初期設定

$ git config --global user.name "氏名"
$ git config --global user.email "メアド"
$ git config --global core.editor code

を実行するとホームディレクトリに.gitconfigファイルが出来上がっているはずです.

Gitの準備

$ git init

とすると,ディレクトリ内に.gitというディレクトリができます.

$ git remote add origin git@github.com:ユーザー名/リポジトリ名.git

Gitの流れ

gitの使い方の基本は →pull→add→commit→push→ です.

  • add : ファイルの選択
$ git add ファイル名
$ git add .                ←すべてのファイルを指定

とすることで,コミット(作ったファイルを管理対象にする)の対象として選択することができます.
addしたかどうかを忘れてしまった時は,

$ git status

で確認できます.赤い文字で書かれているファイルは変更してaddしていないファイルです.
さらに,変更内容も確認したい時は,

$ git diff

とします.

  • commit : 選択したファイルの記録
$ git commit -m "コミットメッセージ"
  • push : リモートにファイルをアップロード
$ git push origin master
  • pull : リモートのファイルをダウンロード
$ git pull origin master

commitの履歴やコミットメッセージの確認

$ git log

で確認できます.

$ git log -p

で変更内容の確認もできます.

(補足1)nothing to commit, working tree cleanと怒られたら

$ git pull --rebase origin master

(補足2)強引にマージ

$ git merge -Xtheirs (マージしたいbranch名) --allow-unrelated-histories

ROS MelodicでOpenCV(テニスボールの位置検出)

前回ROS MelodicでOpenCV - 機械屋の呟き、ROS上でOpenCVを使う準備をしたので、実際に使ってみます。

サンプルコード

以下のコードをopencv_test.cppに書き、ビルドします。

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

static const std::string OPENCV_WINDOW = "Image window";

class ImageConverter{
  ros::NodeHandle nh_;
  image_transport::ImageTransport it_;
  image_transport::Subscriber image_sub_;
  image_transport::Publisher image_pub_;

  public:
    // コンストラクタ
    ImageConverter() : it_(nh_){
      // カラー画像をサブスクライブ                                                                
      image_sub_ = it_.subscribe("/image_raw", 1, &ImageConverter::imageCb, this);
      // 処理した画像をパブリッシュ                                                                                          
      image_pub_ = it_.advertise("/image_topic", 1);
      
      
    }

    // デストラクタ
    ~ImageConverter(){
      cv::destroyWindow(OPENCV_WINDOW);
    }
    // コールバック関数
    void imageCb(const sensor_msgs::ImageConstPtr& msg){
      cv::Point2f center, p1;
      float radius;

      cv_bridge::CvImagePtr cv_ptr, cv_ptr2, cv_ptr3;
      try{
        // ROSからOpenCVの形式にtoCvCopy()で変換。cv_ptr->imageがcv::Matフォーマット。
        cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
        cv_ptr3 = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::MONO8);
      }catch (cv_bridge::Exception& e){
        ROS_ERROR("cv_bridge exception: %s", e.what());
        return;
      }

      cv::Mat hsv_image, color_mask, gray_image, bin_image, cv_image2, cv_image3;
      // RGB表色系をHSV表色系へ変換して、hsv_imageに格納
      cv::cvtColor(cv_ptr->image, hsv_image, CV_BGR2HSV);

      // 色相(Hue), 彩度(Saturation), 明暗(Value, brightness) 
      // 指定した範囲の色でマスク画像color_mask(CV_8U:符号なし8ビット整数)を生成  
      // マスク画像は指定した範囲の色に該当する要素は255(8ビットすべて1)、それ以外は0                                                      
      //cv::inRange(hsv_image, cv::Scalar(0, 0, 100, 0) , cv::Scalar(180, 45, 255, 0), color_mask);       // 白
      //cv::inRange(hsv_image, cv::Scalar(150, 100, 50, 0) , cv::Scalar(180, 255, 255, 0), color_mask);   // 赤
      cv::inRange(hsv_image, cv::Scalar(20, 50, 50, 0) , cv::Scalar(100, 255, 255, 0), color_mask);   // 黄

      // ビット毎の論理積。マスク画像は指定した範囲以外は0で、指定範囲の要素は255なので、ビット毎の論理積を適用すると、指定した範囲の色に対応する要素はそのままで、他は0になる。
      cv::bitwise_and(cv_ptr->image, cv_ptr->image, cv_image2, color_mask);
      // グレースケールに変換
      cv::cvtColor(cv_image2, gray_image, CV_BGR2GRAY);
      // 閾値70で2値画像に変換
      cv::threshold(gray_image, bin_image, 80, 255, CV_THRESH_BINARY); 

      // エッジを検出するためにCannyアルゴリズムを適用
      //cv::Canny(gray_image, cv_ptr3->image, 15.0, 30.0, 3);

      // ウインドウに円を描画                                                
      //cv::circle(cv_ptr->image, cv::Point(100, 100), 20, CV_RGB(0,255,0));

      // 輪郭を格納するcontoursにfindContours関数に渡すと輪郭を点の集合として入れてくれる
      std::vector<std::vector<cv::Point>> contours;
      cv::findContours(bin_image, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);    // 輪郭線を格納


      try{
        // 各輪郭をcontourArea関数に渡し、最大面積を持つ輪郭を探す
        double max_area=0;
        int max_area_contour=-1;
        for(int j=0; j<contours.size(); j++){
            double area = cv::contourArea(contours.at(j));
            if(max_area<area){
                max_area=area;
                max_area_contour=j;
            }
        }

        // 最大面積を持つ輪郭の最小外接円を取得
        cv::minEnclosingCircle(contours.at(max_area_contour), center, radius);
        ROS_INFO("radius = %f", radius);

        // 最小外接円を描画
        cv::circle(cv_ptr->image, center, radius, cv::Scalar(0,0,255),3,4);
        cv::circle(cv_image2, center, radius, cv::Scalar(0,0,255),3,4);
        cv::circle(bin_image, center, radius, cv::Scalar(0,0,255),3,4);
      }catch(CvErrorCallback){
        
      }

      // 画面中心から最小外接円の中心へのベクトルを描画
      p1 = cv::Point2f(cv_ptr->image.size().width/2,cv_ptr->image.size().height/2);
      cv::arrowedLine(cv_ptr->image, p1, center, cv::Scalar(0, 255, 0, 0), 3, 8, 0, 0.1);  

      // 画像サイズは縦横1/4に変更
      cv::Mat cv_half_image, cv_half_image2, cv_half_image3, cv_half_image4, cv_half_image5;
      cv::resize(cv_ptr->image, cv_half_image,cv::Size(),0.5,0.5);
      cv::resize(cv_image2, cv_half_image2,cv::Size(),0.5,0.5);
      //cv::resize(cv_ptr3->image, cv_half_image3,cv::Size(),0.5,0.5);
      cv::resize(gray_image, cv_half_image4,cv::Size(),0.5,0.5);
      cv::resize(bin_image, cv_half_image5,cv::Size(),0.5,0.5);

      // ウインドウ表示                                                                         
      cv::imshow("Original Image", cv_half_image);
      cv::imshow("Result Image", cv_half_image2);
      //cv::imshow("Edge Image", cv_half_image3);
      //cv::imshow("Gray Image", cv_half_image4);
      cv::imshow("Binary Image", cv_half_image5);
      cv::waitKey(3);
  
      // エッジ画像をパブリッシュ。OpenCVからROS形式にtoImageMsg()で変換。                                                            
      image_pub_.publish(cv_ptr3->toImageMsg());
    }
};

int main(int argc, char** argv)
{
  ros::init(argc, argv, "image_converter");
  ImageConverter ic;
  ros::spin();
  return 0;
}

実行

$ roscore
$ rosrun uvc_camera uvc_camera_node image:=/image_raw
$ rosrun opencv_tutorials opencv_test