LoginSignup
8
11

More than 1 year has passed since last update.

Python使ってUnity上の箱庭ロボットのカメラデータを取得してみよう

Last updated at Posted at 2023-03-18

はじめに

これ、Turtlebot3 の 3D モデルを Unity 上に取り込み、モーター、センサ系を再現したものです。

image.png

この Unity 上のロボットを Python 使って制御する方法をご紹介します。

仕組み

このロボットをPythonでコントロールするためのプラットフォームとして、TOPPERS/箱庭を利用します。
最近ですと、以下の記事で EV3 というロボットをPython 使って強化学習した実績があるものです。

ロボットの部品群

モーター

差動モーターで駆動します。両輪それぞれにモーターが割り当てられており、モーターの回転速度差で直進、右曲がり、左曲がりができます。

image.png

レーザスキャナ

2D のレーザスキャナです。360度(1度単位)の障害物検出ができます。

image.png

カメラセンサ

前方にカメラセンサを付けています。
640×480 のカメラデータが取れます。

image.png

ロボットの通信データ

ロボットの通信データは、ROS2のトピックデータ型で定義しています。
(cmd_vel 以外はすべてセンサデータです)

ROSトピックデータ型 名前 サイズ[単位:バイト] channel_id
geometry_msgs/Twist cmd_vel 48 0
sensor_msgs/JointState joint_states 440 1
sensor_msgs/Imu imu 432 2
nav_msgs/Odometry odom 944 3
tf2_msgs/TFMessage tf 320 4
sensor_msgs/Image image 1,024,280 5
sensor_msgs/CompressedImage image/compressed 102,664 6
sensor_msgs/CameraInfo camera_info 580 7
sensor_msgs/LaserScan scan 3,044 8

Python側のプログラム

通信データ

Python側のプログラムでは、これらの通信データは、dictionary形式で読めます。

モーターコントロール(cmd_vel)

linearのxが速度で、angularのzが回転方向です。
それ以外は無視されます。

linear:
  x: 0
  y: 0
  z: 0
angular:
  x: 0
  y: 0
  z: 0

カメラデータ(image/compressed)

data の中にjpeg形式のデータが格納されています。

header:
  stamp:
    sec: 1678519485
    nanosec: 759772000
  frame_id: camera_link
format: jpeg
data: <生のバイナリデータ>

レーザスキャナ(scan)

ranges の中にスキャンした結果が配列として360個格納されています。

header:
  stamp:
    sec: 1678519496
    nanosec: 958531000
  frame_id: base_scan
angle_min: 0
angle_max: 6.2657318115234375
angle_increment: 0.01745329238474369
time_increment: 2.9880000511184335e-05
scan_time: 0
range_min: 0.11999999731779099
range_max: 3.5
ranges:
  - 3.5
  :(全部で360個あります)

通信ライブラリ

Unity上のTurtlebot3を制御するためのプログラムは以下の通りです。

class HakoRoboModelTb3:
    def __init__(self, hako):
        self.hako = hako
        self.hako.create_pdu_channel(0, 48, 'Twist')
        self.hako.subscribe_pdu_channel(1, 440, 'JointState')
        self.hako.subscribe_pdu_channel(2, 432, 'Imu')
        self.hako.subscribe_pdu_channel(3, 944, 'Odometry')
        self.hako.subscribe_pdu_channel(4, 320, 'TFMessage')
        self.hako.subscribe_pdu_channel(5, 1024280, 'Image')
        self.hako.subscribe_pdu_channel(6, 102664, 'CompressedImage')
        self.hako.subscribe_pdu_channel(7, 580, 'CameraInfo')
        self.hako.subscribe_pdu_channel(8, 3044, 'LaserScan')
        self.binary_data = bytearray(48)
        self.actions = { 0: binary_reader.binary_read(self.hako.offmap, 'Twist', self.binary_data) }

    def delta_usec(self):
        #20msec
        return 20000

    def camera_data(self, sensors):
        return (sensors[6]['data'])

    def laser_scan(self, sensors):
        return (sensors[8]['ranges'])

    def get_forward_distance(self, scan_datas):
        min1 = min(scan_datas[0:15])
        min2 = min(scan_datas[345:359])
        return min(min1, min2)
            
    def get_right_distance(self, scan_datas):
        return min(scan_datas[60:120])
    
    def foward(self, speed):
        self.actions[0]['linear']['x'] = speed
    
    def turn(self, speed):
        self.actions[0]['angular']['z'] = speed
            
    def stop(self):
        self.actions[0]['linear']['x'] = 0.0
        self.actions[0]['angular']['z'] = 0.0

    def step(self):
        for channel_id in self.actions:
            self.hako.write_pdu(channel_id, self.actions[channel_id])

上記ライブラリを使ったサンプルプログラムは以下の通りです。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
import sys
from hako_binary import offset_map
from hako_binary import binary_writer
from hako_binary import binary_reader
import hako_env
import hako
import time
import signal
import hako_robomodel_tb3 as tb3

def handler(signum, frame):
  print(f'SIGNAL(signum={signum})')
  sys.exit(0)
  
print("START TB3 TEST")

# signal.SIGALRMのシグナルハンドラを登録
signal.signal(signal.SIGINT, handler)

#create hakoniwa env
env = hako_env.make("base_practice_1", "tb3")
print("WAIT START:")
env.hako.wait_event(hako.HakoEvent['START'])
print("WAIT RUNNING:")
env.hako.wait_state(hako.HakoState['RUNNING'])
print("GO:")

#do simulation
robo = env.robo()
for episode in range(1):
  total_time = 0
  done = False
  state = 0
  total_reward = 0
  #100secs
  while not done and total_time < 4000:
    sensors = env.hako.execute()
      
    img = env.robo().camera_data(sensors)
    with open("camera.jpg" , 'bw') as f:
        f.write(img)
    
    scan_datas = env.robo().laser_scan(sensors)
    if env.robo().get_forward_distance(scan_datas) >= 0.2:
        env.robo().foward(0.5)
        env.robo().turn(0.0)
    else:
        env.robo().foward(0.0)
        env.robo().turn(-0.5)
    env.robo().step()  
    total_time = total_time + 1
  env.reset()
print("END")
env.reset()
sys.exit(0)

カメラデータは、上記プログラムのここで取得してファイルに保存しています。

    img = env.robo().camera_data(sensors)
    with open("camera.jpg" , 'bw') as f:
        f.write(img)

実際に保存したカメラデータはこんな感じです。

camera.jpg

Unityのセットアップ手順

基本的なインストール手順は前回の記事の通りですが、Unity環境のセットアップのクローン対象リポジトリが異なります。

git clone -b unity-asset-for-tb3 --recursive https://github.com/toppers/hakoniwa-ros2sim.git

Unityエディタを起動後、プロジェクトビューから「Configurator」というシーンをダブルクリックします。

image.png

起動すると、下図のようになります。

image.png

ヒエラルキービューを開き、「Robot」内にあるオブジェクトを「delete」キーで削除し、以下のTurtlebot3モデル(MiconTB3RoboModel)を配置します。

image.png

MiconTB3RoboModelは、プロジェクトビューの Assets/Resources/Hakoniwa/Robots 配下にあります。

配置が終わったら、メニューから環境構成を外部出力します。

image.png

出力完了後、以下のコマンドで箱庭環境にインストールします。

bash hako-install.bash opt all

そして、Unityのシミュレーション用のシーンとして、「Toppers_Course」を起動します。

image.png

Python側のセットアップ手順

Python 側のセットアップ手順は、前回の記事の通りです。

ただし、Turtlebot3を動かすには、以下のファイルを変更する必要があります。

  • workspace/runtime/asset_def.txt

変更前:dev/ai/ai_qtable.py
変更後:dev/ai/test_tb3-01.py:mqtt

test_tb3.py は、以下にあります。

シミュレーション実行方法

hakoniwa-base 上で、以下のように docker コンテナを起動します。

$ bash docker/run.bash runtime
ASSET_DEF=asset_def.txt
PYTHON_PROG=dev/ai/test_tb3.py
PDU_COMM=mqtt
INFO: ACTIVATING MOSQUITTO
[13703.343514]~DLT~   13~INFO     ~FIFO /tmp/dlt cannot be opened. Retrying later...
INFO: ACTIVATING HAKO-MASTER
OPEN RECIEVER UDP PORT=172.30.224.33:54001
OPEN SENDER UDP PORT=172.30.224.33:54002
mqtt_url=mqtt://172.30.224.33:1883
PUBLISHER Connecting to the MQTT server...
PUBLISHER CONNECTED to the MQTT server...
delta_msec = 20
max_delay_msec = 100
INFO: shmget() key=255 size=12160
Server Start: 172.30.224.33:50051
INFO: ACTIVATING :dev/ai/test_tb3.py
START TB3 TEST
create_channel: id=0 size=48
WAIT START:

この状態で、Unityのシミュレーションを開始します。

image.png

この状態で、「開始」ボタンをクリックすると動き出します。

デモ

上記の手順で実行したときのデモをお見せします。

カメラデータは、ローカルのファイルシステム上に保存されていますので、そのデータをWebブラウザ上で眺めています。

8
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
11