YDLIDAR X4 Proを使ってみる その2(ミニPC、Linuxでの動作確認)

はじめに

 今回はLinux環境でYDLIDAR X4 Proの動作確認を行いました。

 前回はWindows環境でしたが、ロボットに搭載するPCがLinux環境のミニPCなので、その環境での動作確認です。

▼Windows環境では以下の記事で試しました。

YDLIDAR X4 Proを使ってみる その1(Windows、GUIアプリでの動作確認)

はじめに  今回はLiDARを購入したので、動作確認を行ってみました。  LiDARは他の安価なセンサーに比べると高額で、搭載できるロボットがなかったので触っていなかった…

▼YDLIDAR X4 Proの販売ページはこちら

▼以前の記事はこちら

Jetson Xavierを使ってみる その5(NoMachine、リモートデスクトップ接続)

はじめに  今回はNoMachineを利用して、PCからJetson Xavierへのリモートデスクトップ接続を試してみました。  これまでもリモートデスクトップ接続は色々試していたの…

ROS2を使ってみる その3(環境構築、WSL2 Ubuntu 22.04)

はじめに  最近WSL2でUbuntu環境を複数インストールできるようになったので、ROS2の環境を改めて構築してみました。  大学の研究でロボットの遠隔操作にも取り組み始め…

環境を構築する

実行環境の確認

 以前Ubuntu 22.04をインストールしたミニPCを用います。

▼Ubuntuのインストールについてはこちら

USBメモリでミニPCにUbuntuをインストールする(Rufus)

はじめに  今回はUSBメモリでミニPCにUbuntuをインストールしてみました。  身近にUSBでWindowsとUbuntuを切り替えている方がいたので、できることは知っていました。た…

▼Ubuntu 22.04がインストールされています。

 ミニPCとはGoogleのリモートデスクトップで接続しています。

▼リモートデスクトップ接続についてはこちら

Google Chromeのリモートデスクトップを試してみる

はじめに  今回はGoogle Chromeのリモートデスクトップを試してみました。  普段はノートPCを持ち運んで使っているのですが、よりGPUの処理性能が高いデスクトップPCに…

 ミニPCとLiDARをUSBの変換基板で接続しました。

▼LiDARを上に載せられるようにすると、収まりが良さそうですね。

YDLidar-SDKのビルド

 今回利用するLiDARはROSにも対応しているのですが、Pythonのシンプルなコードで利用できるようにしたかったので、そのためにビルドを行いました。

▼GitHubのビルドとインストールに関するページはこちら。

https://github.com/YDLIDAR/YDLidar-SDK/blob/master/doc/howto/how_to_build_and_install.md

 コマンドを実行していくとエラーが起きていたのですが、その都度Geminiに相談しながら対処しました。まずは以下のコマンドを実行してみました。

git clone https://github.com/YDLIDAR/YDLidar-SDK.git
cd YDLidar-SDK
mkdir build
cd build
cmake ..
make
sudo make install

▼実行したところ、cmakeとmakeが見つからないというエラーが起きていました。

 cmakeとmake、その他ビルドに必要なパッケージをaptでインストールしました。

sudo apt install cmake
sudo apt install make
sudo apt install build-essential
sudo apt install swig python3-dev python3-pip

 改めて先程のコマンドを実行したところ、最後まで処理が進みました。Pythonのビルドも行いました。

cd
cd YDLidar-SDK/
pip install .
python3 setup.py build
sudo python3 setup.py install

▼問題なく進めば、ydlidarのパッケージがインストールされます。

動作を確認する

 クローンしたリポジトリのpython/examplesフォルダにサンプルコードが入っていたのですが、実行するとエラーが起きていました。

▼以下のエラーです。

[error] Error, cannot bind to the specified [serial port:/dev/ttyUSB0] and [baudrate:512000]
[error] Error initializing YDLIDAR check Comms.

 USBデバイスは認識していました。

ls /dev/ttyUSB*

 以前Windowsで動作確認をしたときは、Baudrateが128000でした。また、コードがX4 Pro用では無かったので修正する必要がありました。

 今回はplot_tof_test.pyというサンプルを修正して、動作するか確認しました。LiDARのスキャン結果をもとにプロットするサンプルです。

pip install numpy
pip install matplotlib
python3 plot_tof_test.py

 まず実行した時点で、プロットに関するfig.canvas.set_window_titleの部分でエラーが起きていました。

▼以下の部分です。

 fig.canvas.set_window_titleをfig.canvas.manager.set_window_titleに変更して、LiDARのパラメータはX4 Proに合わせて修正しました。

▼以下の部分を修正しました。

# --- 修正前 ---
# laser.setlidaropt(ydlidar.LidarPropSerialBaudrate, 512000)
# laser.setlidaropt(ydlidar.LidarPropLidarType, ydlidar.TYPE_TOF);
# laser.setlidaropt(ydlidar.LidarPropSampleRate, 20);
# laser.setlidaropt(ydlidar.LidarPropSingleChannel, False);

# --- 修正後 ---
laser.setlidaropt(ydlidar.LidarPropSerialBaudrate, 128000)
laser.setlidaropt(ydlidar.LidarPropLidarType, ydlidar.TYPE_TRIANGLE)
laser.setlidaropt(ydlidar.LidarPropSampleRate, 5)
laser.setlidaropt(ydlidar.LidarPropSingleChannel, True)

 特に、LidarPropSingleChannelをTrueにしないとエラーが起きていました。

 再度コードを実行してみました。

▼LiDARのデータを取得し、プロットすることができました!

 最終的に、以下のコードで動作確認ができました。

import os
import ydlidar
import time
import sys
from matplotlib.patches import Arc
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

RMAX = 32.0


fig = plt.figure()
fig.canvas.manager.set_window_title('YDLidar LIDAR Monitor')
lidar_polar = plt.subplot(polar=True)
lidar_polar.autoscale_view(True,True,True)
lidar_polar.set_rmax(RMAX)
lidar_polar.grid(True)
ports = ydlidar.lidarPortList();
port = "/dev/ydlidar";
for key, value in ports.items():
    port = value;
    
laser = ydlidar.CYdLidar();
laser.setlidaropt(ydlidar.LidarPropSerialPort, port);
laser.setlidaropt(ydlidar.LidarPropSerialBaudrate, 128000)
laser.setlidaropt(ydlidar.LidarPropLidarType, ydlidar.TYPE_TRIANGLE);
laser.setlidaropt(ydlidar.LidarPropDeviceType, ydlidar.YDLIDAR_TYPE_SERIAL);
laser.setlidaropt(ydlidar.LidarPropScanFrequency, 10.0);
laser.setlidaropt(ydlidar.LidarPropSampleRate, 5);
laser.setlidaropt(ydlidar.LidarPropSingleChannel, True);
laser.setlidaropt(ydlidar.LidarPropMaxAngle, 180.0);
laser.setlidaropt(ydlidar.LidarPropMinAngle, -180.0);
laser.setlidaropt(ydlidar.LidarPropMaxRange, 32.0);
laser.setlidaropt(ydlidar.LidarPropMinRange, 0.01);
scan = ydlidar.LaserScan()

def animate(num):
    
    r = laser.doProcessSimple(scan);
    if r:
        angle = []
        ran = []
        intensity = []
        for point in scan.points:
            angle.append(point.angle);
            ran.append(point.range);
            intensity.append(point.intensity);
        lidar_polar.clear()
        lidar_polar.scatter(angle, ran, c=intensity, cmap='hsv', alpha=0.95)

ret = laser.initialize();
if ret:
    ret = laser.turnOn();
    if ret:
        ani = animation.FuncAnimation(fig, animate, interval=50)
        plt.show()
    laser.turnOff();
laser.disconnecting();
plt.close();

コードの改良

 動作確認ができたコードをもとに、Geminiにコードを改良してもらいました。プロットの点が大きかったので小さくすること、UDP通信で別のPCでもデータを受信できるようにすることを要望として伝えました。

import os
import ydlidar
import time
import sys
import socket
import json
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

# --- 通信設定 (受信するPCのIPに合わせて変更してください) ---
UDP_IP = "localhost"  # 例: 受信側のPCのIPアドレス
UDP_PORT = 5005          # 受信側のポート番号

# --- LiDAR設定 ---
RMAX = 12.0              # 表示する最大距離 (m) X4 Pro/S2 Proに合わせて調整
SCAN_FREQ = 10.0         # スキャン周波数 (Hz) : 高速化
SAMPLE_RATE = 5          # サンプルレート (K)

# ソケットの準備
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# グラフの初期設定
fig = plt.figure(figsize=(8, 8))
fig.canvas.manager.set_window_title('YDLidar X4/S2 Pro Monitor & UDP Sender')
lidar_polar = plt.subplot(polar=True)
lidar_polar.autoscale_view(True, True, True)
lidar_polar.set_rmax(RMAX)
lidar_polar.grid(True)

# LiDARポートの検索と設定
ports = ydlidar.lidarPortList()
port = "/dev/ttyUSB0"

laser = ydlidar.CYdLidar()
laser.setlidaropt(ydlidar.LidarPropSerialPort, port)
laser.setlidaropt(ydlidar.LidarPropSerialBaudrate, 128000)
laser.setlidaropt(ydlidar.LidarPropLidarType, ydlidar.TYPE_TRIANGLE)
laser.setlidaropt(ydlidar.LidarPropDeviceType, ydlidar.YDLIDAR_TYPE_SERIAL)

# --- スペックに合わせた高速化設定 ---
laser.setlidaropt(ydlidar.LidarPropScanFrequency, SCAN_FREQ)
laser.setlidaropt(ydlidar.LidarPropSampleRate, SAMPLE_RATE)
laser.setlidaropt(ydlidar.LidarPropSingleChannel, True) # 重要
laser.setlidaropt(ydlidar.LidarPropMaxAngle, 180.0)
laser.setlidaropt(ydlidar.LidarPropMinAngle, -180.0)
laser.setlidaropt(ydlidar.LidarPropMaxRange, 16.0)
laser.setlidaropt(ydlidar.LidarPropMinRange, 0.05)

scan = ydlidar.LaserScan()

def animate(num):
    r = laser.doProcessSimple(scan)
    if r:
        angle = []
        ran = []
        intensity = []
        
        # 送信用データリスト (JSON用)
        # データ量を減らすため、距離が0の点は除外しても良い
        export_points = []

        for point in scan.points:
            # プロット用データ
            angle.append(point.angle)
            ran.append(point.range)
            intensity.append(point.intensity)
            
            # UDP送信用データの作成
            # フォーマット: [角度(rad), 距離(m), 強度]
            # 通信量削減のため小数点以下3桁に丸める
            if point.range > 0:
                export_points.append([
                    round(point.angle, 3),
                    round(point.range, 3),
                    int(point.intensity)
                ])

        # --- 1. プロットの更新 ---
        lidar_polar.clear()
        lidar_polar.set_rmax(RMAX)
        lidar_polar.grid(True)
        # s=2.0 で点を小さくし、高密度でも見やすくする
        lidar_polar.scatter(angle, ran, c=intensity, cmap='hsv', s=2.0, alpha=0.8)

        # --- 2. UDP送信処理 ---
        try:
            # タイムスタンプと点群データをJSON化
            payload = {
                "t": time.time(),     # タイムスタンプ
                "c": len(export_points), # 点の数
                "d": export_points    # データ本体 [[ang, dist, int], ...]
            }
            json_data = json.dumps(payload).encode('utf-8')
            
            # データサイズが大きすぎる場合は警告 (UDPパケット制限への配慮)
            if len(json_data) > 60000:
                print(f"Warning: Payload size {len(json_data)} bytes is too large for UDP.")
            else:
                sock.sendto(json_data, (UDP_IP, UDP_PORT))
                
        except Exception as e:
            print(f"UDP Send Error: {e}")

ret = laser.initialize()
if ret:
    ret = laser.turnOn()
    if ret:
        # intervalを短くして描画更新を速くする
        ani = animation.FuncAnimation(fig, animate, interval=10)
        plt.show()
    laser.turnOff()
laser.disconnecting()
plt.close()

 UDP通信で送られるデータ形式は以下のようになります。

{
  "t": 1707103500.123,      // タイムスタンプ (UNIX time)
  "c": 405,                 // 点群の数 (Count)
  "d": [                    // データ配列 (Data)
    [ -3.141, 2.501, 10 ],  // [ 角度(ラジアン), 距離(メートル), 強度 ]
    [ -3.120, 2.505, 12 ],
    ...
  ]
}

 実際にコードを実行してみました。

▼先程よりも小さな点でプロットされました。

 TailscaleのVPN接続でつながっているJetson XavierのNode-REDで、データを受信できているか確認してみました。

▼Jetson Xavierの環境についてはこちらの記事で構築しています。

Jetson Xavierを使ってみる その2(通信周りの設定、Node-RED、TailScale)

はじめに  今回は以前OSのセットアップを行ったJetson Xavierの通信周りの設定を行いました。  自宅サーバーとして使いたかったので、外部のネットワークからでもアクセ…

▼データが受信できることを確認しました。

 コードを実行したタイミングでLiDARの回転速度が遅くなった気がしたのですが、周期などは用途に応じて調整しようかなと思っています。

最後に

 Linux環境でもYDLIDAR X4 Proの動作を確認することができました。

 実際に移動ロボットに搭載して、衝突を避けたり、マップを作成したりしたいなと思っています。

▼バッテリーでミニPCに給電できることは確認済みなので、LiDARも含めロボットに搭載できそうです。

リン酸鉄リチウムイオンバッテリーを使ってみる その2(ミニPCへの電源供給、LiTime 12V12Ah LiFePO4)

はじめに  今回は以前購入したリン酸鉄リチウムイオンバッテリーを使って、ミニPCへの電源供給ができないか試してみました。  普段はACアダプタでDC12Vに変換して供給し…

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です