Gazebo シミュレート上でLIDARをPythonで処理する(ROS2-Foxy)

Python
スポンサーリンク

スポンサーリンク

はじめに

前回の記事では、cmd_vel と IMU のデータを取得・送信しました。

今回の記事では、LIDAR を Python で取得していきます。

前提条件

前提条件は、以下の通りです。

Gazebo上のLIDARのデータを取得する

前回までの記事が完了していれば、Pythonプログラムを作成するだけです。

cd ~/ros_ws/src/ros2_first_test/ros2_first_test
touch get_lidar.py

今作成した get_lidar.py に、以下のコードを記述してください。

import rclpy 
from rclpy.node import Node 
from sensor_msgs.msg import LaserScan

class getLidar(Node):
    def __init__(self):
        # wake up node name
        super().__init__("lidar_subscriber")
        self.subscription = self.create_subscription(LaserScan, "/scan", self.get_scan_values, 10)
        self.subscription

    def get_scan_values(self, scan_data):
        print(scan_data)

def main(args=None):
    rclpy.init(args=args)
    gl = getLidar()
    rclpy.spin_once(gl)
    rclpy.shutdown()

if __name__ == "__main__":
    main()

プログラムの内容は、前回の記事で説明したので省略します。

次に、setup.py にPythonプログラムを追加します。

'console_scripts': [
    'translate_cmdvel=ros2_first_test.translate_cmdvel:main',
    'get_imu=ros2_first_test.get_imu:main',
    'get_lidar=ros2_first_test.get_lidar:main',    ← これを追加
],

ここまで出来たら、ビルドを実行し、シミュレータを起動します。

cd ~/ros_ws
colcon build --packages-select ros2_first_test
source ~/.bashrc
ros2 launch ros2_first_test first_gazebo.py

シミュレータが起動したら、以下の画像のように箱をgazebo 上に設置します。

この状態で、別のターミナルを開き、以下のコマンドを実行します。

ros2 run ros2_first_test get_lidar

すると、ターミナルに以下の数値が表示されます。

sensor_msgs.msg.LaserScan(
  header=std_msgs.msg.Header(
    stamp=builtin_interfaces.msg.Time(sec=67, nanosec=590000000), 
    frame_id='lidar_link'
  ), 
  angle_min=0.0, 
  angle_max=3.140000104904175, 
  angle_increment=0.008746517822146416, 
  time_increment=0.0, 
  scan_time=0.0, 
  range_min=0.15000000596046448, 
  range_max=6.0, 
  ranges=[inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, 3.1079890727996826, 3.1089258193969727, 3.1121914386749268, 3.112844944000244, 3.0926382541656494, 3.1010019779205322, 3.0826656818389893, 3.095937967300415, 3.082425832748413, 3.080289363861084, 3.0816826820373535, 3.088649272918701, 3.0784549713134766, 3.07792592048645, 3.061919689178467, 3.055903196334839, 3.061077356338501, 3.059711217880249, 3.058401584625244, 3.070952892303467, 3.066819667816162, 3.065356969833374, 3.057581663131714, 3.0550143718719482, 3.0587191581726074, 3.073047637939453, 3.0550243854522705, 3.043236017227173, 3.051419496536255, 3.0651824474334717, 3.068891763687134, 3.057870388031006, 3.0892016887664795, 3.0878193378448486, 3.0551300048828125, 3.0699400901794434, 3.0737242698669434, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf], 
  intensities=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])

LIDARの受信データの詳細については以前の記事で説明しました。
公式サイトの説明はこちらにあります。

今回はレーザーの強度を取得するオプションを設定していないので、intensities は全て 0 です。
ranges に障害物の位置と距離が格納されています。

データをよりわかりやすくするために、get_lidar.py の get_scan_values を編集します。

def get_scan_values(self, scan_data):
    ranges = scan_data.ranges
    ranges = np.array(ranges)
    length = len(ranges)
    inf = float('inf')
    ranges[ranges == inf] = 0.0
    ranges_args = ranges.nonzero()
    scan_value = ranges[ranges_args]
    print("length={}, \nranges_args={}, \nscan_value={}".format(length, ranges_args, scan_value))

ビルドして get_lidar を実行します。

cd ~/ros_ws
colcon build --packages-select ros2_first_test
source ~/bashrc
ros2 run ros2_first_test get_lidar

出力は以下の通りです。

length=360,
ranges_args=(array([154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
       167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
       180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
       193, 194, 195, 196, 197, 198]),),
scan_value=[2.5432742 2.5297503 2.5515137 2.5337625 2.51462   2.5241334 2.519027
 2.4925215 2.4989135 2.494576  2.4987764 2.4967434 2.4946518 2.465911
 2.4933004 2.4965827 2.4842067 2.4813433 2.4806154 2.4651732 2.4824047
 2.4775321 2.4687994 2.476408  2.472752  2.4631271 2.4753494 2.457275
 2.4878418 2.4712384 2.4547858 2.4587235 2.4736233 2.4698026 2.4759266
 2.4691157 2.46807   2.4671834 2.470698  2.4616892 2.461269  2.477951
 2.4782434 2.5058408 2.5037754]

今回は 0° ~ 180° の間で 0.5° 毎にレーザーを拾うように設定してあります。

まず、ranges_args ですが、今回は0.5°毎に取得しているので、全ての数値を 1/2 にした数値が、そのまま障害物のある角度となります。
ranges_args = ranges_args[0] / 2 と追加すれば角度が出力されることになります。

scan_value は、障害物までの距離(m)を示します。今回は 2.46m ~ 2.54m の位置に障害物があります。

おわりに

今回は Gazebo シミュレート上で LIDAR の値を取得しました。
これと、以前紹介したディファレンシャルドライブの /cmd_vel へ動作命令を出すプログラムを併せると、障害物を避けながら動いたり、障害物の手前で停止したりと、シミュレーションの幅が広がります。

次回は Gazebo 上のカメラを OpenCV で処理する方法を紹介します。

コメント

タイトルとURLをコピーしました