へっぽこ元ロボコニスト

ロボコンに燃え尽きた自分が日々の出来事を書くだけのブログです

2019/6/27(raspberry piのROS上でpigpioを使ってLチカしてみる)

早く社会人になって,自分の金でデバイスを買いたい

学部4年の頃から研究でROSを使っていて常に困っていることがありました
それはROSからLEDやモータなどを動かせないということです

金を積んでDynamixelを買えば高性能なサーボを使えますし,実験ではturtlebotを代機として使えばいいとは思います
また,rosserial_arduinoを使えばPCからarduinoをノードとして操作できます
www.besttechnology.co.jp
www.turtlebot.com


ただ,学生ロボコンをしていた自分としては中華サーボのトルクが小さい物を動かして遊んでみたいですし,エンコーダの生値を読んでモータにPID制御をかけて発振させて遊んでみたいのです
また,arduinoしか動かせないのはスペック的につらいです

そこで目標としては,ROSが乗ったデバイスからLEDやモータを動かします
(ただROS2が出てきて組み込みマイコンにそもそもROS2が乗るのですぐオワコンになりそう)
今回はその最初としてRaspberry Pi上のROSからIOピンを使ってLチカをします
そのためには様々な方法が現在あります

  • WiringPi

tool-lab.com

  • RPi.GPIO

qiita.com
ですが,自分は出来るだけPWMもあって,SPIやI2Cなどの操作もしたい
さらに,ROSから動かしたいというのがあったのでpigpioを使いました

pigpioのインストール

インストールは以下のページを参考にしました
karaage.hatenadiary.jp

sudo apt-get update
sudo apt-get install pigpio

ROS上でcppプログラムを書く

書き方は以下のページを参考にしました
botalab.tech
またリファレンスはここです
abyz.me.uk


まずは適当にパッケージを作って,cppファイルを書きます
gpioという名前にしました

#include <ros/ros.h>
#include <pigpiod_if2.h>

#include <std_msgs/Bool.h>

class gpio{
  //construct
  public:
  gpio();
  
  private:
  int pi;
  
  void cb_LED(const std_msgs::Bool::ConstPtr &data);
  ros::NodeHandle nh;
  ros::Subscriber sub_led;
};

gpio::gpio(){
  pi = pigpio_start(NULL,NULL);

  sub_led = nh.subscribe("/led", 5, &gpio::cb_LED, this);
}

void gpio::cb_LED(const std_msgs::Bool::ConstPtr &data){
  if(data->data == 1){
    gpio_write(pi,26,1);
  }else{
    gpio_write(pi,26,0);
  }
}

int main(int argc, char** argv){
  ros::init(argc, argv, "gpio");
  gpio gpio;
  ros::spin();
  return 0;
}
解説
#include <pigpiod_if2.h>

ヘッダーファイルがこれです
ネットで検索しているとpythonで書かれている物ではpigpio.hをインクルードしていたりしていてるのですが何が違うかいまいち分かりません

pi = pigpio_start(NULL,NULL);

ここで初期化します
初期化に成功すると0以上の整数値が返ってきます
ローカルで動かすのでNULLを引数として入れていますが,リファレンスを見るとIPアドレスとポートを指定できるのでもしかしたら別のPCにこのノードを立てて動かせるかも?

gpio_write(pi,26,1);

第一引数
piを入れている場所に負の値が入ると動きません
ここに初期化の時に帰ってきた値を使うことで初期化失敗しているときには動きません

第二引数
ここでピン番号を選びます
今回は26なのでGPIO26(ラズパイのピン番号では37)を制御します

第三引数
ここではGPIOのHighかLowかを選びます
1だとHighで電圧をかけます

あとはROSを理解していれば分かると思います
(分からなかったらコメントで質問ください)

CMakeLists.txtの編集

大体,ノードを作るとCMakeに

add_executable(gpio src/gpio.cpp)
target_link_libraries(gpio ${catkin_LIBRARIES})

を書くと思います
ただ今回は

target_link_libraries(gpio pigpiod_if2)

をさらに追加する必要があります
(これが一番大事)
これを書いておかないとコンパイルが通りません

ROSの実行

ラズパイのGPIO26にLEDと抵抗をつないで,デーモンをコマンドプロンプト上で実行します

sudo pigpiod

そして,gpioノードを実行して,/ledにtrueを送るとLEDが光ります

これでROS上でラズパイのGPIOを制御することが出来ました

2019/6/14(ハードウェアの干渉でNorthStarのキャリブレーションが上手くいかない)

夜の本気ダンスをメトロックで見てからはまった
www.youtube.com


以前の記事です
mozyanari.hatenablog.com

NorthStarをキャリブレーションをしようとしたのですが,どれだけ値を変更しても右と左がずれていました
まあ見えているしいいかなと思っていたのですが,NorthStarを人に見せる機会があるので,このままだと体験してくれる人の感動が半減しないかと思いました

そこで,どこかハード的にいけない場所があるかなと思ってCADを見ていました
myhub.autodesk360.com

そしたら,LCDを固定する部分と本体が干渉していました
f:id:masanari7430:20190614154045p:plain
おそらくこれが原因でLCDの角度が変わっていたのでしょう

作り直します

追記
削って干渉をなくしました

2019/6/4(estimatePoseSingleMarkersのtvecとrvecからマーカ位置を計算する)

就活終わったー
後は研究して卒業するだけや

前回の記事です
mozyanari.hatenablog.com
カメラ位置を計算することは出来ていましたが,自分が欲しいのはマーカの位置です
カメラ位置を計算できるということは,マーカ位置も取得できるはずなので検索しましたが全く記事が出てこなかったため色んな記事を参照しました
stackoverflow.com
www.learnopencv.com
www.learnopencv.com

最終的には回転行列からマーカの回転角度を取得するということで動きました

  • プログラム
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenCVForUnity;
using UnityEngine.SceneManagement;

public class Opencv_test : MonoBehaviour {

    //カメラの番号
    private int camera_number = 0;

    private VideoCapture cam;
    public Mat cam_frame;

    private Texture2D cam_Texture;

    //Arucoの設定
    public ArUcoDictionary dictionaryId = ArUcoDictionary.DICT_4X4_50;
    private Dictionary ar_dict;

    //検出アルゴリズムの設定
    private DetectorParameters detect_param;

    //マーカの位置
    private Mat rvecs;
    private Mat tvecs;
    //送信するマーカ位置
    private Vector3 position;

    public GameObject camera;
    public GameObject marker;



    // Use this for initialization
    void Start () {
        cam = new VideoCapture(camera_number);
        //cam.Set(CaptureProperty.ConvertRgb, 3);
        cam.set(16, 3);
        cam_frame = new Mat();

        ar_dict = Aruco.getPredefinedDictionary((int)dictionaryId);
        detect_param = DetectorParameters.create();

        //plopIdはopenCVの2系時に名前で定義されていたが廃止された.3系以降は番号でこれを取得する
        cam_Texture = new Texture2D((int)cam.get(3), (int)cam.get(4), TextureFormat.RGB24, false);

        this.GetComponent<Renderer>().material.mainTexture = cam_Texture;

        tvecs = new Mat();
        rvecs = new Mat();
        position = new Vector3(0, 0, 0);
    }

    // Update is called once per frame
    void Update () {
        List<Mat> marker_corner = new List<Mat>(OpenCVForUnity.CvType.CV_8UC1);
        Mat marker_ids = new Mat();
        List<Mat> reject_points = new List<Mat>(OpenCVForUnity.CvType.CV_8UC1);

        cam.read(cam_frame);

        Aruco.detectMarkers(cam_frame, ar_dict, marker_corner, marker_ids, detect_param, reject_points);

        //Debug.Log(marker_ids.size());
        if (marker_ids.total() > 0)
        {
            //マーカの描画
            Aruco.drawDetectedMarkers(cam_frame, marker_corner, marker_ids, new Scalar(0, 255, 0));

            //キャリブレーションデータの設定
            Mat camMatrix = new Mat(3, 3, CvType.CV_64FC1);
            camMatrix.put(0, 0, 548.82998077504431);
            camMatrix.put(0, 1, 0);
            camMatrix.put(0, 2, 332.15854329853039);
            camMatrix.put(1, 0, 0);
            camMatrix.put(1, 1, 549.82809802204554);
            camMatrix.put(1, 2, 236.24689239842121);
            camMatrix.put(2, 0, 0);
            camMatrix.put(2, 1, 0);
            camMatrix.put(2, 2, 1.0f);

            MatOfDouble distCoeffs = new MatOfDouble(-0.088668381409835462, 0.4802348866900254, -0.0026481695882058297, 0.0019686217165377452, -0.82389338772252729);



            Aruco.estimatePoseSingleMarkers(marker_corner, (float)0.125, camMatrix, distCoeffs, rvecs, tvecs);

            Aruco.drawAxis(cam_frame, camMatrix, distCoeffs, rvecs, tvecs, (float)0.1);


            Debug.Log("x=" + tvecs.get(0, 0)[0] + "y=" + tvecs.get(0, 0)[1] + "z=" + tvecs.get(0, 0)[2]);
            //Debug.Log("y=" + tvecs.get(0, 0)[1]);
            //Debug.Log("z=" + tvecs.get(0, 0)[2]);


            Debug.Log("roll=" + (rvecs.get(0, 0)[0] * 180 / Mathf.PI) + "pitch=" + rvecs.get(0, 0)[1] * 180 / Mathf.PI + "yaw=" + rvecs.get(0, 0)[2] * 180 / Mathf.PI);


            marker.transform.position = new Vector3(0, 0, 0);

            Mat Rt = new Mat();

            OpenCVForUnity.Calib3d.Rodrigues(rvecs, Rt);


            Mat T = new Mat(3, 1, CvType.CV_64FC1);
            T.put(0, 0, tvecs.get(0, 0)[0]);
            T.put(1, 0, tvecs.get(0, 0)[1]);
            T.put(2, 0, tvecs.get(0, 0)[2]);


            //Debug.Log(R);

            Mat R = Rt.t();


            Mat camera_position = Rt.t() * (-T);

            Debug.Log(camera_position);

            //回転角度の計算
            float roll = 0;
            float pitch = 0;
            float yaw = 0;

            float a = Mathf.Sqrt((float)(R.get(0, 0)[0] * R.get(0, 0)[0] + R.get(1, 0)[0] * R.get(1, 0)[0]));
            if (a > 1e-6)
            {
                roll = Mathf.Atan2((float)R.get(2, 1)[0], (float)R.get(2, 2)[0]) * 180 / Mathf.PI;
                pitch = Mathf.Atan2((-(float)R.get(2, 0)[0]) , a) * 180 / Mathf.PI;
                yaw = Mathf.Atan2((float)R.get(1, 0)[0], (float)R.get(0, 0)[0]) * 180 / Mathf.PI;
            }
            else
            {
                roll = Mathf.Atan2(-(float)R.get(1, 2)[0], (float)R.get(1, 1)[0]) * 180 / Mathf.PI;
                pitch = Mathf.Atan2((-(float)R.get(2, 0)[0]) , a) * 180 / Mathf.PI;
                yaw = 0;
            }
            
            

            camera.transform.position = new Vector3((float)tvecs.get(0, 0)[0] * 10, -(float)tvecs.get(0, 0)[1] * 10, (float)tvecs.get(0, 0)[2] * 10);


            camera.transform.rotation = Quaternion.Euler(roll, pitch, yaw);


        }

        Utils.matToTexture2D(cam_frame, cam_Texture);
    }

    public enum ArUcoDictionary
    {
        DICT_4X4_50 = Aruco.DICT_4X4_50,
        DICT_4X4_100 = Aruco.DICT_4X4_100,
        DICT_4X4_250 = Aruco.DICT_4X4_250,
        DICT_4X4_1000 = Aruco.DICT_4X4_1000,
        DICT_5X5_50 = Aruco.DICT_5X5_50,
        DICT_5X5_100 = Aruco.DICT_5X5_100,
        DICT_5X5_250 = Aruco.DICT_5X5_250,
        DICT_5X5_1000 = Aruco.DICT_5X5_1000,
        DICT_6X6_50 = Aruco.DICT_6X6_50,
        DICT_6X6_100 = Aruco.DICT_6X6_100,
        DICT_6X6_250 = Aruco.DICT_6X6_250,
        DICT_6X6_1000 = Aruco.DICT_6X6_1000,
        DICT_7X7_50 = Aruco.DICT_7X7_50,
        DICT_7X7_100 = Aruco.DICT_7X7_100,
        DICT_7X7_250 = Aruco.DICT_7X7_250,
        DICT_7X7_1000 = Aruco.DICT_7X7_1000,
        DICT_ARUCO_ORIGINAL = Aruco.DICT_ARUCO_ORIGINAL,
    }


    void OnDestroy()
    {
        if (cam_frame != null)
            cam_frame.Dispose();
    }


}

試行錯誤の跡が残ってるのでこのままコピペしても動かないので適宜参照してください

  • マーカ位置の計算

tvecとrvecを取得するまでは前回の記事と同じ流れです

camera.transform.position = new Vector3((float)tvecs.get(0, 0)[0] * 10, -(float)tvecs.get(0, 0)[1] * 10, (float)tvecs.get(0, 0)[2] * 10);

10倍してるのはデバックするときに動きが見やすいためにしています
単位はmです
前回と異なり,tvecsをそのまま使っています

  • マーカの角度計算
float roll = 0;
float pitch = 0;
float yaw = 0;

float a = Mathf.Sqrt((float)(R.get(0, 0)[0] * R.get(0, 0)[0] + R.get(1, 0)[0] * R.get(1, 0)[0]));
if (a > 1e-6)
{
    roll = Mathf.Atan2((float)R.get(2, 1)[0], (float)R.get(2, 2)[0]) * 180 / Mathf.PI;
    pitch = Mathf.Atan2((-(float)R.get(2, 0)[0]) , a) * 180 / Mathf.PI;
    yaw = Mathf.Atan2((float)R.get(1, 0)[0], (float)R.get(0, 0)[0]) * 180 / Mathf.PI;
}else{
    roll = Mathf.Atan2(-(float)R.get(1, 2)[0], (float)R.get(1, 1)[0]) * 180 / Mathf.PI;
    pitch = Mathf.Atan2((-(float)R.get(2, 0)[0]) , a) * 180 / Mathf.PI;
    yaw = 0;
}

この部分が最も時間がかかりました
rvecsをロドリゲス変換して転置した後の行列の中身を参考にしたページをもとに計算するとなんかうまいこと動くようになりました
なので,なぜこれが上手く動くか理解していません

動けば勝ちなのでこれでいいかなと思っています

2019/6/4(estimatePoseSingleMarkersのtvecとrvecからカメラ姿勢を計算する)

ココロオークションが最近の自分の流行
www.youtube.com


Arucoを使ってマーカの位置を取得しようとしています
Unityでマーカ位置にオブジェクトを置きたいのでちゃんとした位置結果を計算しないといけません
ネットで検索しても,detectMarkers→drawDetectedMarkersで描画するだけや,detectMarkers→tvecの値から直接位置計算をやっていて正解にたどり着けませんでした

OpencvにestimatePoseSingleMarkersと同様の結果を出すsolvePnPという関数があり,これで検索すれば出てくるかなと思って検索して見つけました
stackoverflow.com
これによって自分が出来たのはマーカを原点としてカメラの位置・姿勢がどこにあるかということです.

  • 環境

Unity 2018.2.21f1
OpenCV for Unity v2.3.2

  • プログラム
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenCVForUnity;
using UnityEngine.SceneManagement;

public class Opencv_test : MonoBehaviour {

    //カメラの番号
    private int camera_number = 0;

    private VideoCapture cam;
    public Mat cam_frame;

    private Texture2D cam_Texture;

    //Arucoの設定
    public ArUcoDictionary dictionaryId = ArUcoDictionary.DICT_4X4_50;
    private Dictionary ar_dict;

    //検出アルゴリズムの設定
    private DetectorParameters detect_param;

    //マーカの位置
    private Mat rvecs;
    private Mat tvecs;
    //送信するマーカ位置
    private Vector3 position;

    public GameObject camera;
    public GameObject marker;



    // Use this for initialization
    void Start () {
        cam = new VideoCapture(camera_number);
        //cam.Set(CaptureProperty.ConvertRgb, 3);
        cam.set(16, 3);
        cam_frame = new Mat();

        ar_dict = Aruco.getPredefinedDictionary((int)dictionaryId);
        detect_param = DetectorParameters.create();

        //plopIdはopenCVの2系時に名前で定義されていたが廃止された.3系以降は番号でこれを取得する
        cam_Texture = new Texture2D((int)cam.get(3), (int)cam.get(4), TextureFormat.RGB24, false);

        this.GetComponent<Renderer>().material.mainTexture = cam_Texture;

        tvecs = new Mat();
        rvecs = new Mat();
        position = new Vector3(0, 0, 0);
    }

    // Update is called once per frame
    void Update () {
        List<Mat> marker_corner = new List<Mat>(OpenCVForUnity.CvType.CV_8UC1);
        Mat marker_ids = new Mat();
        List<Mat> reject_points = new List<Mat>(OpenCVForUnity.CvType.CV_8UC1);

        cam.read(cam_frame);

        Aruco.detectMarkers(cam_frame, ar_dict, marker_corner, marker_ids, detect_param, reject_points);

        //Debug.Log(marker_ids.size());
        if (marker_ids.total() > 0)
        {
            //マーカの描画
            Aruco.drawDetectedMarkers(cam_frame, marker_corner, marker_ids, new Scalar(0, 255, 0));

            //キャリブレーションデータの設定
            Mat camMatrix = new Mat(3, 3, CvType.CV_64FC1);
            camMatrix.put(0, 0, 548.82998077504431);
            camMatrix.put(0, 1, 0);
            camMatrix.put(0, 2, 332.15854329853039);
            camMatrix.put(1, 0, 0);
            camMatrix.put(1, 1, 549.82809802204554);
            camMatrix.put(1, 2, 236.24689239842121);
            camMatrix.put(2, 0, 0);
            camMatrix.put(2, 1, 0);
            camMatrix.put(2, 2, 1.0f);

            MatOfDouble distCoeffs = new MatOfDouble(-0.088668381409835462, 0.4802348866900254, -0.0026481695882058297, 0.0019686217165377452, -0.82389338772252729);



            Aruco.estimatePoseSingleMarkers(marker_corner, (float)0.125, camMatrix, distCoeffs, rvecs, tvecs);


            
            Debug.Log("x=" + tvecs.get(0, 0)[0]+ "y=" + tvecs.get(0, 0)[1] + "z=" + tvecs.get(0, 0)[2]);
            
            
            Debug.Log("roll=" + (rvecs.get(0, 0)[0] * 180 / Mathf.PI) + "pitch=" + rvecs.get(0, 0)[1] * 180 / Mathf.PI + "yaw=" + rvecs.get(0, 0)[2] * 180 / Mathf.PI);

            

            marker.transform.position = new Vector3(0, 0, 0);
            Mat Rt = new Mat();

            OpenCVForUnity.Calib3d.Rodrigues(rvecs,Rt);
            

            Mat T = new Mat(3, 1, CvType.CV_64FC1);
            T.put(0, 0, tvecs.get(0, 0)[0]);
            T.put(1, 0, tvecs.get(0, 0)[1]);
            T.put(2, 0, tvecs.get(0, 0)[2]);

            Mat R = Rt.t();

            Mat camera_position = Rt.t() * (-T);

            Debug.Log(camera_position);

            camera.transform.position = new Vector3((float)camera_position.get(0,0)[0]*10, (float)camera_position.get(2, 0)[0]*10, (float)camera_position.get(1, 0)[0]*10);

            float roll = Mathf.Atan2((float)-R.get(2, 1)[0], (float)R.get(2, 2)[0]) * 180 / Mathf.PI;
            float pitch = Mathf.Asin((float)R.get(2, 0)[0]) * 180 / Mathf.PI;
            float yaw = Mathf.Atan2((float)-R.get(1, 0)[0], (float)R.get(0, 0)[0]) * 180 / Mathf.PI;

            camera.transform.rotation = Quaternion.Euler(roll, yaw, pitch);


        }

        Utils.matToTexture2D(cam_frame, cam_Texture);
    }

    public enum ArUcoDictionary
    {
        DICT_4X4_50 = Aruco.DICT_4X4_50,
        DICT_4X4_100 = Aruco.DICT_4X4_100,
        DICT_4X4_250 = Aruco.DICT_4X4_250,
        DICT_4X4_1000 = Aruco.DICT_4X4_1000,
        DICT_5X5_50 = Aruco.DICT_5X5_50,
        DICT_5X5_100 = Aruco.DICT_5X5_100,
        DICT_5X5_250 = Aruco.DICT_5X5_250,
        DICT_5X5_1000 = Aruco.DICT_5X5_1000,
        DICT_6X6_50 = Aruco.DICT_6X6_50,
        DICT_6X6_100 = Aruco.DICT_6X6_100,
        DICT_6X6_250 = Aruco.DICT_6X6_250,
        DICT_6X6_1000 = Aruco.DICT_6X6_1000,
        DICT_7X7_50 = Aruco.DICT_7X7_50,
        DICT_7X7_100 = Aruco.DICT_7X7_100,
        DICT_7X7_250 = Aruco.DICT_7X7_250,
        DICT_7X7_1000 = Aruco.DICT_7X7_1000,
        DICT_ARUCO_ORIGINAL = Aruco.DICT_ARUCO_ORIGINAL,
    }

    void OnDestroy()
    {
        if (cam_frame != null)
            cam_frame.Dispose();
    }


}
  • ARマーカの認識
Aruco.detectMarkers(cam_frame, ar_dict, marker_corner, marker_ids, detect_param, reject_points);

cam_frameがカメラ画像でこれを入力することで,マーカの位置のmarker_cornerを取得します

  • ARマーカの位置の取得
Aruco.estimatePoseSingleMarkers(marker_corner, (float)0.125, camMatrix, distCoeffs, rvecs, tvecs);

マーカ位置のmarker_cornerとカメラパラメータのcamMatrixとdistCoeffsを入力します
カメラパラメータは事前に取得しておきます
mozyanari.hatenablog.com
(float)0.125はARマーカの実際の大きさをはかって入力しています
こうすることで,ARマーカとの相対的な位置関係を表す,tvecとrvecを取得出来ます

  • rvecとtvecからカメラ位置を計算

冒頭の参考文献をそのまま使っています
ロドリゲス変換

OpenCVForUnity.Calib3d.Rodrigues(rvecs,Rt);

カメラ位置の計算

Mat camera_position = Rt.t() * (-T);

カメラの回転角度計算

float roll = Mathf.Atan2((float)-R.get(2, 1)[0], (float)R.get(2, 2)[0]) * 180 / Mathf.PI;
float pitch = Mathf.Asin((float)R.get(2, 0)[0]) * 180 / Mathf.PI;
float yaw = Mathf.Atan2((float)-R.get(1, 0)[0], (float)R.get(0, 0)[0]) * 180 / Mathf.PI;

Unityに適した形で入力

camera.transform.rotation = Quaternion.Euler(roll, yaw, pitch);


これによってマーカの位置を原点として,カメラの位置・姿勢を取得できると思います

追記
マーカ位置を計算するVerも書きました
mozyanari.hatenablog.com

2019/5/3(ArucoUnityでCharuco boardを作ってカメラのキャリブレーションデータを取得する)

明日はMaker Fair Kyoto

前回の記事です
mozyanari.hatenablog.com

環境が作れたので,キャリブレーションボード(Charuco board)を作りキャリブレーションをします

normanderwan.github.io

Assets/Scenesの中のCreateMakers.unityを開きます
自分が好きなキャリブレーション用のボードを作ることができますが,手間なのでデフォルトのまま作ります
なので,そのままUnityの実行ボタンを押します
f:id:masanari7430:20190503173345p:plain


すると,Assets/Imagesフォルダが生成されて,キャリブレーション用のCharuco boardが作れます
f:id:masanari7430:20190503173251p:plain
使うのは
ArUcoUnity_ChArUcoBoard_Dict4x4_50_X_7_Y_5_SquareSize_200_MarkerSize_120.png
です

これを印刷して厚紙に貼り付けます
f:id:masanari7430:20190503174612j:plain


normanderwan.github.io


Assets/Scenesの中のCalibrateCamera.unityを開きます
f:id:masanari7430:20190503174802p:plain

まず指定するのは使うカメラです
Aruco WebcamWebcam Idを設定します
自分はノートPC内蔵カメラが0番,以降接続したUSBカメラの順番に1番...と続きます
f:id:masanari7430:20190503174908p:plain

Mono Aruco Camera Displayのチェックボックスを消します
キャリブレーション実行時にこれが起動しないようにするためらしいです
f:id:masanari7430:20190503175233p:plain


次は,ArucoCharucoBoardのArucoCharucoBoardを設定します
f:id:masanari7430:20190503175903p:plain
変更すべき場所は,Maker Side LengthとSquare Side Lengthです
Maker Side Lengthはマーカの大きさを,Square Side LengthはQRコードの一つのマスの大きさをm単位で指定します
なので,自分が印刷したCharuco boardを定規で測って入力してください
f:id:masanari7430:20190503181503j:plain
(こうすることで,印刷機が勝手にCharuco boardを拡大したり縮小したりして印刷しても大丈夫になっています)


最後に,PinholeCameraCalibrationのAruco Camera Parameters Controllerを設定します
Camera Parameterに,「自分が好きな名前.xml」を入力してください
キャリブレーションデータの名前を決める部分です
リファレンスには空欄でも勝手に名前を作るよと書いてありますが,空欄だとエラーで書き出してくれませんでした
f:id:masanari7430:20190503181936p:plain

設定が終わったらUnityの実行ボタンを押すとキャリブレーションが始まります
カメラにボードを映して「add Image」を押すとキャリブレーション用のデータを取ることができます
様々な角度で撮ってください(自撮りをあんまりしないのでリア充ならうまいことやれるんだろうなと思いながらやっていました)
これを10回程度繰り返して「Calibrate」を押すとキャリブレーションが実行されます
結果は,Assets/StreamingAssetsの中にxml形式で出力されます
「Reset」ボタンを押すと最初から始めることができます
f:id:masanari7430:20190503182739p:plain
(OpenCVではQRコードなしのキャリブレーション用ボードを用意しますが,今回の方法はQRコードがついています.これによってキャリブレーションボード全体が写っていなくてもキャリブレーションを行うことが出来るようです.ただ,QRコード全体が写っているほうが精度はもちろんいいです)

Assets/StreamingAssetsの中のxmlデータを確認します
estimatePoseSingleMarkersで必要なものは,Camera Matrices Value=cameraMatrix,DistCoeffsValues=distCoeffsです
読み方はおそらく今回のデータだと
cameraMatrix=
|578・・, 0  ,319・・|
| 0  ,581..,259・・|
| 0 , 0 , 1 |

distCoeffs=[0.0772,-0.2668,-0.0023,0.0024,-0.1549]

だと思います
f:id:masanari7430:20190503184548p:plain

これでカメラのキャリブレーションデータを取得することが出来ました
次はこのデータを使って実際にQRコードを認識して距離を取得します

2019/5/3(UnityでArucoUnityを実行できる環境を作る)

久しぶりに他人が作った英語のリファレンスを何時間も読んだ

前回の記事です
mozyanari.hatenablog.com

ArUcoのライブラリの中に,estimatePoseSingleMarkersという関数があります
マーカーの姿勢と位置を計測できる関数で,ロボットにマーカを置いてロボットまでの相対位置を取得しようと考えています
しかし,この関数を実行するには,①InputArray cameraMatrix ②InputArray distCoeffsが必要です
docs.opencv.org

①は3×3のカメラ行列で,②は今回は1×5行列のレンズ歪みパラメータです
②はリファレンスによると1×4や1×12のものもあるらしいです

つまり,事前にこれらのパラメータを取得しないといけません

ですので,Unity上でこれらのパラメータを取得します

これらのプログラムを一から書くのはとても面倒なので今回は以下のプログラムを使いました
normanderwan.github.io

このプログラムはかなり優秀で,キャリブレーション用のボードを自作できたり,魚眼カメラ,ステレオカメラのキャリブレーションもできます
ただ,今回は普通のwebカメラキャリブレーションを最短で行うことを目標にします

必要なもの

  • Unity 2018.2.21f1 (64-bit)
  • ArucoUnity(Commits on Jan 12, 2019のものを使いました)

github.com

  • ArucoUnityPlugin(ArucoUnityPlugin-v2.0.1-Windows.zipを使いました)

github.com

UnityでArucoUnityを実行できる環境を作る
原文
github.com

UnityでArucoUnityを開きます
ただ,昔のバージョンで作られたので
f:id:masanari7430:20190503170717p:plain
このような警告が出ますが,2018のバージョンで開いても大丈夫なのでこのままオープンします
開くとArucoUnityPluginが無いと警告されます

次に,警告されたArucoUnityPluginをUnityに入れます
f:id:masanari7430:20190503171030p:plain
ArucoUnityPluginの中のPluginsのフォルダを,UnityのAssets/ArucoUnityの中にドラッグ&ドロップで入れます

最後にこれを読み込ますためにUnityを再起動します
すると,警告がすべてなくなっていると思います

次は,キャリブレーション用マーカの作成です

2019/5/2(OpenCVでWebカメラの画像をモノクロにする)

GW中に高校の友達とご飯を食べに行ったときに,意外な進路をたどっていたので驚いた


前回の記事です
mozyanari.hatenablog.com

前回は自分の顔を判定する機能を実装しましたがOpenCVが動くのを確認しただけなので,次はWebカメラの画像をモノクロにしてUnity上で
表示します

今回参考にしたのは以下のページです

ssssota.hatenablog.com


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using OpenCvSharp;

public class web_camera : MonoBehaviour {
    [SerializeField] private int m_WebCamIndex = 1;
    private Texture2D texture2D;
    private VideoCapture videoCapture;
    private Mat mat;
    private Mat grey;

	// Use this for initialization
	void Start () {
        videoCapture = new VideoCapture(m_WebCamIndex);
        texture2D = new Texture2D(videoCapture.FrameWidth, videoCapture.FrameHeight, TextureFormat.RGB24, false);
        GetComponent<Renderer>().material.mainTexture = texture2D;
        mat = new Mat();
        grey = new Mat();
	}
	
	// Update is called once per frame
	void Update () {
        videoCapture.Read(mat);
        Cv2.CvtColor(mat, grey, ColorConversionCodes.BGR2GRAY);
        texture2D.LoadImage(grey.ImEncode());
	}

    private void OnDestroy()
    {
        videoCapture.Dispose();
        videoCapture = null;
    }
}

startの部分

videoCapture = new VideoCapture(m_WebCamIndex);

どのWebカメラを起動するかをここで選択して,カメラに関する情報を変数videoCaptureで取得します
自分のノートPCは内臓カメラがあったので,ここの引数は1だと内蔵カメラ,2だとUSB接続したカメラになります

texture2D = new Texture2D(videoCapture.FrameWidth, videoCapture.FrameHeight, TextureFormat.RGB24, false);

カメラ画像に関する情報をtexture2Dで保持します
1つ目の引数で画像の横幅,2つ目で画像の縦幅を入力します
ここの引数をカメラの性能が分かっているなら決め打ちでもいいかもしれませんが,基本的にvideoCaptureから取得したほうが便利です
3つ目と4つ目はいまいち理解していません

GetComponent<Renderer>().material.mainTexture = texture2D;

これを実行することでUnity上でアタッチしているオブジェクトにtexture2Dを張り付けることを示すことができます
今回はPlaneのオブジェクトにアタッチする予定です

mat = new Mat();
grey = new Mat();

参考にしたページでは書いていませんでしたが,自分の場合これを書いていないとmatが常にnullで動きませんでした


upadateの部分

videoCapture.Read(mat);

videoCaptureにはwebカメラのデータが常に更新されて保存されているので,mat形式で書きだします

Cv2.CvtColor(mat, grey, ColorConversionCodes.BGR2GRAY);

この部分がOpenCVの機能を使った部分で,変数matに入っているカメラ画像をモノクロ化(グレースケール化)して変数greyに格納しています

texture2D.LoadImage(grey.ImEncode());

texture2Dはstartの部分でUnityのオブジェクトにアタッチされていることを示されているので,texture2Dに変数greyを入力するだけで勝手に更新されてUnity上で表示してくれます
ただ,greyはmat形式なのでImEncodeを使ってpngに変換してから入力します


OnDestroyの部分
この部分は参考にしたページには書いていましたが,自分の場合Unity上で実行した後この部分でエラーが発生したのでなくてもいいのかもしれません


これをUnityのPlaneにアタッチすれば,Plane上にwebカメラの画像が出てくるはずです

2019/4/29(UnityのOpenCVが動くかどうかを調べる)

大学生活で初めてGWを実家でダラダラ過ごしてる気がする

 

 前回の記事です

mozyanari.hatenablog.com

 

UnityでOpenCVが動く環境ができたのでとりあえず何か動かして,作った環境が正解か試します

以下のページのスクリプトをそのままコピーして動かしてみます

ssssota.hatenablog.com

 

  1. 名前がTestのスクリプト作ります
  2. 参考にしたページのプログラムをコピペして貼り付けます
  3. てきとうなオブジェクトにアタッチして,実行すればプロジェクト内のフォルダに画像を生成する

 

しかし,ここで問題になることがありました

var haarcascade = new CascadeClassifier("Assets/haarcascades/haarcascade_frontalface_default.xml");

ここで,分類器?を参照しているのですが,自分が入れた環境ではそんなもの存在しておらず自分で入れる必要がありました

 

OpenCVの公式のsourceファイルにありました

opencv.org

ここでは,自分のOpenCVSharpのバージョンが4.0.0なので同じバージョンのソースをダウンロードしました

 

分類器の場所は

opencv-4.0.0\opencv-4.0.0\data\haarcascades\haarcascade_frontalface_default.xml

にありました

 

これをUnityの好きな場所にドラッグ&ドロップで導入します

自分はScrptsフォルダの中に入れたので

var haarcascade = new CascadeClassifier("Assets/Scripts/haarcascade_frontalface_default.xml");

に変更しました

 

この状態で実行すると,カメラが起動して結果の写真がプロジェクト内フォルダに出てきました

 

とりあえずこれで自分が作った環境がちゃんと動くものということが分かったのでこれで今後勉強していきます

2019/4/28(UnityにOpenCVSharp3を導入)

GW中はタケノコ掘り

 

UnityでOpenCVを使いたくて有志の方が制作したopencvsharp3を導入しました

 

schima.hatenablog.com

ネットを参考にしながらやってみましたが、エラーが発生したのでそれに関して書いておきます

 

Unityのバージョンは,Unity 2018.2.21f1 (64-bit)です

 

  • UnityにNuGetForUnity.1.1.0をインポート

以下のリンクから.unitypackageを取得します

自分が行ったときは,NuGetForUnity.1.1.0.unitypackageでした

github.com

取得した.unitypackageをunityにインポートします

 

  • OpenCVSharp3をunityへインポート

そのあと,以下のリンクに従ってOpenCVSharp3を追加します

qiita.com

 

インポートすると以下のようなAssets/Packagesが入ります

今回インポートしたOpenCVSharp3のバージョンは4.0.0でした

(NuGetはNuGetForUnity.1.1.0.unitypackageをインポートしたときのものです)

f:id:masanari7430:20190428173513p:plain

そうすると,Matなどに代表されるOpenCVの標準的なライブラリが使えるようになると思います

さっきの参考にしたページをみて試しに適当なスクリプトを作るとMatが使えるようになっているはずです

 

  • ArUcoなどの機能追加

自分がしたいのはArUcoでARマーカを認識することです

なので,以下のブログを参考にしてスクリプトを書いてみました

kobazlab.main.jp

 

けど,使おうとしたら以下のようなエラーが出ました

 

DllNotFoundException: OpenCvSharpExtern
OpenCvSharp.NativeMethods.LoadLibraries (IEnumerable`1 additionalPaths)
OpenCvSharp.NativeMethods..cctor ()
Rethrow as TypeInitializationException: An exception was thrown by the type initializer for OpenCvSharp.NativeMethods
OpenCvSharp.VideoCapture..ctor (Int32 index)
Rethrow as OpenCvSharpException: Failed to create VideoCapture
OpenCvSharp.VideoCapture..ctor (Int32 index)
webcam.Start () (at Assets/Scripts/webcam.cs:24)

 

f:id:masanari7430:20190428214852p:plain

OpenCvSharpExtern.dllが無いようです

 

というわけで,もしNuGetForUnityを使わずにOpenCVSharp3を入れるとどういうファイル構成になるかを調べるために以下のページからバージョン4.0.0のOpenCVSharp3をダウンロードしました

github.com

この時,x64の64ビットとx86の32ビットがありますが,Unityのバージョンに合わせてダウンロードしてください

つまり,UnityのバージョンとOpenCVのバージョンを合わせてダウンロードする必要があります

(もし,バージョン違いを入れるとエラー出ます)


そして,Unity経由で入るファイルと,製作者の人が出してるソースファイルを見るとOpenCvSharpExtern.dllが足りませんでした

f:id:masanari7430:20190428215442j:plain

なので,これをドラッグ&ドロップでAssets/Packages/OpenCV..../libにOpenCvSharpExtern.dllをドラッグ&ドロップで入れます

 

そうするとエラーがなくなりました

 

これで公式のOpenCVインストーラを使うことなくOpenCVをUnityに導入することができたはずです

 

次はこれを使って画像処理をしていきます

 

2019/4/23(North Star製作日記 3.NorthStarが動く環境づくり)

ポルカのライブ良かった

www.youtube.com

雫さんかわいい

 

 

以前の続きです

 

mozyanari.hatenablog.com

mozyanari.hatenablog.com

 

ハードウェアが完成したので次は開発環境を整えます

 

まずはLeapmotionのSDKをダウンロードします

https://developer.leapmotion.com/get-started

 

自分がインストールしたバージョンは

ソフトウェア バージョン:4.0.0+52173

Firmware Revision: 1.7.0

です

 

次は,この記事を参考にしてUnityの環境を作りました

psychic-vr-lab.com

kobazlab.main.jp

 

 

NorthStarを動かすのに必要なソフトウェアは

この二つが必要です

 

ただ,何度かやってみた感じProjectNorthStarは最新版だと,LeapMotionのソフトウェアがまだ世に出てない最新バージョンを要求されて接続できませんでした

そのため

 

Unityのバージョンは現時点で最新であった,2019.1.0f2

 

ProjectNorthStarのバージョンは,Commits on Feb 21, 2019時点の物を使用しました

https://github.com/leapmotion/ProjectNorthStar/tree/a5002df12803a9165f73888080ada860342069f7

最新バージョンだとLeapMotiuonが認識されないので,ここは注意が必要です

開発環境は最新で,使うソフトは数か月前で少し変ですが動けば勝ちです

 

ここまで作れば参考にしたブログから動くようになると思います

 

これでNorthStarが動くようになりました

おおよそ一か月程度で製作することが出来ました

 

友人や後輩,先生にやってもらいましたがキャリブレ―ジョン画面で手が重なって表示されるだけで驚いていたので何かロボット系と組み合わせればもっといいものが出来るのではないかと妄想しています

 

製作日記はこれで以上です

何か質問があればコメントからお願いします

2019/4/22(North Star製作日記 2.NorthStarの製作・組み立て)

3Dプリンタだいしゅき

 

 以前の続きです

mozyanari.hatenablog.com

 

購入した部品が届くまでに3Dプリンタで部品を作ります

 

使える3Dプリンタ

ロボット研究会・研究室・研究室の先生個人

の3つ当てがあったのですべて使ってみました

 

  • 研究室の3Dプリンタは大きさが12cmまでしか作れなかったので,LCD固定,スライダホルダの二つを作りました(黒色)
  • 研究室の先生個人の3Dプリンタは20cm以上だったので,スライダ,骨組みを作りました(青色)
  • ロボット研究会の3Dプリンタは失敗しても先生よりも頼みやすいので最も失敗しやすそうな,レンズの固定部分を作成しました(肌色)

f:id:masanari7430:20190413151914j:plain

f:id:masanari7430:20190413151918j:plain

f:id:masanari7430:20190413151922j:plain

f:id:masanari7430:20190413151925j:plain

 

ただ,作成するのにも何個か失敗しました

特にレンズの固定部分(肌色)は作成が何度か失敗していたらしく,後輩がやり直してくれてました(本当にありがとう) 

 

作っていると部品が集まったのでパーツの組み立てをします

まずは,レンズの固定部分(肌色)と骨組み(青色)を固定しました

f:id:masanari7430:20190422230527j:plain

しかし,この過程でM2.6のネジをしようとしたためネジ穴をM2.3にすべて拡張しています

 

LCD固定(黒色)をレンズの固定部分(肌色)につけます

ただ,ネジ穴を拡張したので

f:id:masanari7430:20190422230941j:plain

こんな感じに結構ギリギリの素材しか残っていません

(穴を拡張しているときは3Dプリンタの精度が悪かったのかなと一ミリも疑っていませんでしたw)

f:id:masanari7430:20190422231234j:plain

f:id:masanari7430:20190422231355j:plain

f:id:masanari7430:20190422234339j:plain

 

スライダホルダ(青色)とスライダ(黒)を取り付けました

f:id:masanari7430:20190422231353j:plain

 

問題のヘッドギアです

f:id:masanari7430:20190422231758j:plain

このように四角の台の上にネジを切ってある形状で,スライダ(黒)を取り付けて挟みこもうと思っても四角の台が邪魔ではまりませんでした

 

そこで,まずはスライダ(黒)を加工しました

ミニルータを使い内側を削り,四角の台がはまるほど削りました

 そして,M8のボルトで止めました

f:id:masanari7430:20190422232417j:plain

かなり強固に固定出来てるので,レンズやLCDの重みで前に傾きにくくなったので,この加工は正解だと思っています

 

最後にレンズの貼り付けです

これが面倒でなぜかぴったりはまらない

f:id:masanari7430:20190422233024j:plain

このでっぱりが引っかかったためミニルータで部品の方を削りました

f:id:masanari7430:20190422233846j:plain

 

また,またこの過程で一番困ったのは接着材でレンズを汚してしまったことです

f:id:masanari7430:20190422232916j:plain

(汚れが分かるかな?)

この時の対処方法は,①熱めの水で接着材をとりあえず溶かす②水気を取ったら眼鏡拭きで強めにこすって剥がす

だと思います

f:id:masanari7430:20190422233149j:plain

レンズが汚れると体験を損ねるので,接着剤を汚したときはかなり焦りました

 

完成したNorthStarはこんな感じです

f:id:masanari7430:20190422233308j:plain

f:id:masanari7430:20190422233311j:plain

 

間違ってM2.3にすべての穴を拡張したり,ヘッドギアの形状が少し違ったり,レンズが上手くはまらなくなったりしましたが,作ることが出来ました

 

次は,Unityで動かすまでです

2019/4/9(North Star製作日記 1.部品の購入)

本当はHoloLens2を使いたい

 

就活中に自己分析をする中で,自分はMRとロボットを組み合わせてロボットと人が共生するためのインターフェイスを作りたいと思うようになりました

ロボットは万能であることが求められていますが,今の環境は人間に最適化されていてロボットが人間にヘルプを求めることもあるように思います

その時LEDが点灯するだけでは一般の人には何が起きているか分かりませんし,表示するディスプレイをつけるのはダサい気がしました

その時将来普及するであろうMR系の装置を使って人間の視覚に直接情報を伝えることが出来れば最もスマートではないかと思っています

 

そこでHoloLensを使ってロボットの状態を人間に伝えるシステムを作ろうと考えました

HoloLensならこれ単体で動作するので,動き回るロボットと相性がいいと思っていました

またこの時HoloLens2の発表が控えていて値段も下がるという噂もあって,もしかして20万台ならHoloLens買おうかなと考えていました

しかし,発表された値段は約40万・・・・買えません

www.microsoft.com

サブクリプションでやれないこともないですが,個人の学生が相手をしてもらえるか分からないので,何か他の手段が必要でした

 

・・・・・・

 

少し時間が戻って,就活で東京に訪れた時にメルカリで行われたxR系のイベントに参加していました

vrtokyo.connpass.com

この中でコバヤシマサト @kobax_km7さんのNorth Starを初めて触りました

North Sar自体は知っていましたが,HoloLensの方がいいなと思っていたので作っていませんでした

コバヤシさんはNorth Starに手の検出をさせずにQRコードの認識をさせていて,違った使い方をしていてとても面白かったです

また,この方によるとLeapMotionの画像データを取得できるようになって,画像処理が出来るようになったそうです

docs.google.com

これなら,ロボットにQRコードを載せてれば,QRコードをスキャンすることでロボットの位置を認識,ロボット情報をNorth Starを被っている人に提示できるのではないかと考えました

 

じゃあ,North Starを作るしかない

 

というわけで,就活の合間を縫って作ることにしました

部品の購入

まずは部品の購入です

参考にしたのはこのページです

 

tech.showroom.co.jp

  • レンズ

www.smart-prototyping.com

一番安い配送方法でも,3/23に頼めば4/9に届いたので3週間もかからずに届きます

しっかりした箱に入って届きます

f:id:masanari7430:20190413153002j:plain

f:id:masanari7430:20190413152940j:plain

 

  • ケーブル類

研究室にあるPCはグラボが一応乗っていますが,650で出力端子がHDMI*1とdvi*1だったので,以下の二本のケーブルをLCDにつなぎました

 

HDMI×1

エレコム ハイスピード HDMIケーブル 4K 3DフルHD イーサネット対応 やわらかケーブル 1.5m ブラック DH-HD14EY15BK

https://www.amazon.co.jp/gp/product/B06Y4H9G9B/ref=ppx_yo_dt_b_asin_title_o01_s02?ie=UTF8&psc=1

 

DVI HDMI変換ケーブル×1

Amazonベーシック HDMI-DVI 変換ケーブル - 3.0m (タイプAオス- DVI24pinオス)

https://www.amazon.co.jp/gp/product/B014I8UU2W/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1

 

また,参考にしたブログによるとLCD電源ケーブルをまとめるために二股のmicrousbケーブルをお勧めしていたので買いました

これは実際に買ってみてよかったです

 

変換名人 microUSB延長ケーブル [ 上向きL型・オス - メス ] [ 20cm ] USBMC-CA20UL

https://www.amazon.co.jp/gp/product/B0085P8WGQ/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1

UGREEN Micro USB二股ケーブル 2.4A出力対応 1本でAndriodスマホ?タブレットを2台同時に充電 Micro USB Y字ケーブル 高速データ通信 金メッキコネクタ 超耐久 2 in 1超便利 USBケーブル 0.5m

https://www.amazon.co.jp/gp/product/B0722NG2MR/ref=ppx_yo_dt_b_asin_title_o01_s02?ie=UTF8&psc=1

f:id:masanari7430:20190422224911j:plain



 

  • ヘッドギア

 KKmoon 溶接ヘルメット 交換用 溶接ヘッドギア マスクヘッドバンド 自動遮光ヘルメットアクセサリー

https://www.amazon.co.jp/gp/product/B07CPLRHJD/ref=ppx_yo_dt_b_asin_title_o03_s00?ie=UTF8&psc=1

f:id:masanari7430:20190422224941j:plain

参考にしたブログから飛んで買いましたが,形状が少し違ったので追加工を行いました

これについては後の部分で書きますが,別にこれでも不具合はありませんでした

 

OSOYOO HDMI 3.5インチLCDディスプレイ モニター タッチスクリーン Raspberry Pi 3 2 Model B に対応 (3.5" HDMI LCD)

https://www.amazon.co.jp/gp/product/B01N5HW3BP/ref=ppx_yo_dt_b_asin_title_o01_s01?ie=UTF8&psc=1

f:id:masanari7430:20190422225414j:plain

せっかくのタッチスクリーンなのでこれが終わったら何かに流用できないかと考えています

 

  • LeapMotion

【国内正規代理店品】 Leap Motion 小型モーションコントローラー 3Dモーション キャプチャー システム

https://www.amazon.co.jp/gp/product/B00GWCATS8/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1

f:id:masanari7430:20190422225352j:plain

9900円で買いました

数年前は半値以下の時があった気がするので,その時に買えばよかったと後悔しました

 

  • ネジと接着材 

ネジと接着材はコーナンで買いました

f:id:masanari7430:20190409181203j:plain

ですが,画像を見てわかる通りネジの大きさがバラバラです

これはコーナンでM2のネジを買ったつもりでしたが,M2の場所にM2.6も混じっていて気づかずに買ってしまいました

(コーナンの野郎ゆるさねぇ・・・)

これが後の悲劇につながります

 

レンズも含めた総額は3万以内でした

 

部品の購入は以上です

 

つぎは,3Dプリンタによる部品の作成です

 

 

2018/11/11(Oculus GoとROSをROSSharpで通信させてLRFのデータ表示)

研究より趣味が好き

 

この夏にこの記事を見てOculus Goを買い,研究を一時そっちのけでVR開発をやっていました

qiita.com

 この人の記事はとても分かりやすく細かいところまでサポートしていて,この記事を書き上げた熱量には本当に頭が下がります

 

なので,この記事を参考にすれば基本的なことができますが,4か月過ぎた今少しバージョンアップされていてカメラのみでなくLRFの連携が含まれていたり,やり方が変わっているので補足的なことを書いてみたいと思います

 

インストールなどの開発環境は全て終わっているものとします

Unity 2017.4.1f1

使用するAssets

ROS# Version 1.3

github.com

Oculus Utilities for Unity 1.28.0

Oculus Utilities for Unity | Developer Center | Oculus

 

基本的な設定

PlatformをAndroidに変更

  1. File > Build Settingsを開き,Androidを選択してSwitch PlatformをクリックしてPlatformを変更

 

VRの設定

  1. Edit > Project Settings > Playerを選択
  2. XR SettingsのVirtual Reality Supportedにチェック
  3. Virtual Reality SDKsの欄にOculusを追加

 

Buildのための設定

  1. アプリのパッケージ名を設定
    Edit > Project Settings > Player > Other > Package Nameを、com.[CompanyName].[ProductName]の形で適当な組織名とプロダクト名に変更
  2. APIレベルを設定
    Edit > Project Settings > Player > Other Settings > Identification > Minimum API Level > API Level 25
  3. Scripting Runtime Versionを設定
    Edit > Project Settings > Player > Other Settings > Configuration > Scripting Runtime Versionを.NET 4.x Equivalentに変更

 

OculusUtilities Unityをインポート

  1. 解凍して,OculusUtilities.unitypackageをAssetsにインポート
    この時アップデートできるよと聞かれるので,更新する(更新しないとなんか動かなかった気がする)

 

ROS#をインポート

  1. 解凍してROS#をAssetsにインポート

 

Oculus用のカメラを設置

  1. HierarchyウィンドウからMain Cameraを削除。
  2. ProjectウィンドウからAssets/Oculus/VR/Prefabsを開き、OVRCameraRigをHierarchyウィンドウにドラッグ&ドロップ

 

 RosConnectorの設定

  1. Hierarchyウィンドウで右クリック > Create EmptyでGameObjectを作成し、名前をRosConnectorに変更。
  2. RosConnectorのInspectorウインドウを開き、Assets/RosSharp/Scripts/RosCommunicationからRosConnectorをドラッグ&ドロップ
  3. RosBridgeServerUrlを設定。
    ROS側のPCのIPアドレス(例:192.168.0.100)とポート番号(デフォルト:9090)をあわせて、"ws://192.168.0.100:9090"のように入力。

 

ここまでは@Spritaroさんの方法ですが,ここから少し違います

 

カメラ画像の受信

ImageSubscriberの作成

  1. Hierarchyウィンドウで右クリック > 3D Object > Planeで平面を作成する
  2. InspectorウィンドウでPositionを(0, 0, 10)に、Rotationを(90, 0, 180)に変更
  3. ProjectウィンドウのAssets/RosSharp/Scripts/RosCommunicationからImageSubscriberスクリプトを、RosConnectorのInspectorウィンドウにドラッグ&ドロップ
  4. ImageSubscriberのTopicを”/cv_camera/image_raw/compressed”に設定
  5. ImageSubscriberのMeshRendererに作成したPlaneをドラッグ&ドロップ

 

LRFデータの受信

Laser Scan Subscriberの作成

  1. ProjectウィンドウのAssets/RosSharp/Scripts/RosCommunicationからLaserScanSubscriberスクリプトを、RosConnectorのInspectorウィンドウにドラッグ&ドロップ
  2. LaserScanSubscriberのTopicを”/scan”に設定

 

LRFデータを表示するスクリプトはAssets/RosSharp/Scripts/RosCommunicationにMesh,Line,Spheresが用意されていますが,自分はMeshとSpheresができました

ここでは見栄えが良かったLaserScanVisualizerMeshを例に作成します

 

LaserScanVisualizerMeshの作成

  1. Hierarchyウィンドウで右クリック > Create EmptyでGameObjectを作成し、名前をLaserScanVisualizerMeshに変更
  2. InspectorウィンドウでPositionを(0, -3, 0)に、Rotationを(0, 0, 0)に変更
  3. ProjectウィンドウのAssets/RosSharp/Scripts/MessageHandlingからLaserScanWriterをLaserScanVisualizerMeshのInspectorウィンドウにドラッグ&ドロップ
  4. ProjectウィンドウのAssets/RosSharp/Scripts/SensorDataVisualizationからLaserScanVisualizerMesh.csをLaserScanVisualizerMeshのInspectorウィンドウにドラッグ&ドロップ
  5. RosConnectorに追加したLaserScanSubscriberのLaserScanWriterにLaserScanVisualizerMeshをドラッグ&ドロップ

 

これで実行すると,自分の足元にLRFのデータをMeshでつなぎ合わせた多角形が出てきます

f:id:masanari7430:20181111223852p:plain

ですが,これには少し問題がありLRFのデータ数が数百あると表示が全くできません

なので,LRFはURG-04LX-UG01の場合,urg_nodeの実行時に"cluster"を90ぐらいにして点群の数を8個にするとリアルタイムで再生できました

 

また,表示の向きをいくら変えてもカメラに対して常に水平のMeshが出来る上がるため,目の前に表示したいのですが出来ませんでした

 

後は@Spritaroさんの所を見れば,Oculus Goの向きを取得出来たり,カメラ画像を常に目の前に表示出来たりします

 

再度になりますが,この記事は@Spritaroの部分を引用した部分が多くあり申し訳なくありますが,LRFの表示に自分が何日も嵌った部分がありどうしても記事にしたくこの方法で書きました

 

ですので,これが誰かの参考になれば幸いです

 

追記2019/6/8

ROS#単体で使うときにはUnityにROS#をインポートするときに

 Scripting Runtime Versionを設定
Edit > Project Settings > Player > Other Settings > Configuration > Scripting Runtime Versionを.NET 4.x Equivalentに変更

を必ず行う

行わないとnameスペースの結びつきが無くなってそもそも動かない

※なんでそうなるかは知らない

 

 

 

 

 

2018/10/14(wiimoteを動かす)

ロボットの入力インターフェースとして、Willリモコンを使おうとした時の過程です

まずは、githubからソースを入手

git clone https://github.com/ros-drivers/joystick_drivers

 

しかし、

CMake Error at /usr/share/cmake-3.5/Modules/FindPkgConfig.cmake:578 (message):
None of the required 'libusb' found
Call Stack (most recent call first):
joystick_drivers/ps3joy/CMakeLists.txt:6 (pkg_search_module)


CMake Error at joystick_drivers/ps3joy/CMakeLists.txt:11 (message):
Failed to find libusb

 

libusbが見つからない

なんやそれと思って調べたらissueにあがってました

github.com

 

これに従って、インストール

sudo apt-get install libusb-dev

 

 

しかし、また次のエラー

 

fatal error: spnav.h: そのようなファイルやディレクトリはありません

 

これも調べると

github.com

 

これに従って、インストール

 

sudo apt-get install libspnav-dev

 

 

またエラー

 

fatal error: bluetooth/bluetooth.h: そのようなファイルやディレクトリはありません

 

これは、ここにのってました

 

github.com

 

インストール

sudo apt-get install libbluetooth-dev

 

次のエラーは

fatal error: cwiid.h: そのようなファイルやディレクトリはありません

github.com

インストール

sudo apt-get install libcwiid-dev

 

これでmakeができるようになりました

 

しかし、動かそうとした時に

Traceback (most recent call last):
File "/home/morishita/my_ws/src/joystick_drivers/wiimote/nodes/wiimote_node.py", line 93, in <module>
import wiimote.WIIMote
File "/home/morishita/my_ws/src/joystick_drivers/wiimote/src/wiimote/WIIMote.py", line 48, in <module>
import cwiid
ImportError: No module named cwiid

 

調べると、またissueが

github.com

インストール

sudo apt-get install python-cwiid

 

ここまで依存関係があったパッケージは初めてだったので書いてみました

 

 

2018/10/13(Think Pad X220のCPUが30%ぐらいになる)

 最近,ロボコンのためにThink PadのX220を使っているのですがなぜかCPUが常に30%ぐらいになっていました

 

タスクマネージャによると

「System」が30%もCPUを食っていました

さらにsystemの中でどのようなプロセスがCPUを食っているかを調べるために

「System」のCPU使用率が高い原因を調べる

ここで紹介されている「Process Explorer」を使って調べました

 

すると

「risdxc64.sys」というプロセスがCPUを食っていたので,これを調べていると公式のFAQで以下のようなページを見つけました

Ricoh Card Reader use 25% CPU - Lenovo Community

 

Ricoh Card Reader」これが悪さをしていました

これを無効化するには

バイスマネージャーの中の,記憶域コントローラの中の「Richo~」を無効化すれば治りました

f:id:masanari7430:20181013083615j:plain

さすがに中古PCでだいぶ古いのでこういう不具合も出ちゃうのかな