質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.99%

Python3 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfe in position 63: invalid start byte

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,645

RokuroYamaki

score 11

 前提・実現したいこと

githubから以下のコードをダウンロードして実行したのですが、表題のエラーが発生し実行することができません。
Unicodeに関するエラーなのですが、解決方法を教えていただけないでしょうか。
[Github]ArmRobot

エラーの詳細を記述していきます。
ターミナル上で、上記のコードを実行した時、
"ArmRobot-MLmaster/python/unityagents/environment.py"ファイル内の138行目
"p = self._conn.recv(self._buffer_size).decode('utf-8')"にて下記のようなエラーメッセージが発生します。
デコードができないと怒られているようです。
こちらのエラーの対処法をご存知の方がおられたら教えていただけないでしょうか?

 発生している問題・エラーメッセージ

line 138, in __init__
    p = self._conn.recv(self._buffer_size).decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfe in position 63: invalid start byte

 該当のソースコード

import atexit
import io
import glob
import json
import logging
import numpy as np
import os
import socket
import subprocess
import struct

from .brain import BrainInfo, BrainParameters, AllBrainInfo
from .exception import UnityEnvironmentException, UnityActionException, UnityTimeOutException
from .curriculum import Curriculum

from PIL import Image
from sys import platform

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("unityagents")


class UnityEnvironment(object):
    def __init__(self, file_name, worker_id=0,
                 base_port=5005, curriculum=None,
                 seed=0, docker_training=False):
        """
        Starts a new unity environment and establishes a connection with the environment.
        Notice: Currently communication between Unity and Python takes place over an open socket without authentication.
        Ensure that the network where training takes place is secure.

        :string file_name: Name of Unity environment binary.
        :int base_port: Baseline port number to connect to Unity environment over. worker_id increments over this.
        :int worker_id: Number to add to communication port (5005) [0]. Used for asynchronous agent scenarios.
        :param docker_training: Informs this class whether the process is being run within a container.
        """

        atexit.register(self.close)
        self.port = base_port + worker_id
        self._buffer_size = 12000
        self._version_ = "API-3"
        self._loaded = False
        self._open_socket = False

        try:
            # Establish communication socket
            self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self._socket.bind(("localhost", self.port))
            self._open_socket = True
        except socket.error:
            self._open_socket = True
            self.close()
            raise socket.error("Couldn't launch new environment because worker number {} is still in use. "
                               "You may need to manually close a previously opened environment "
                               "or use a different worker number.".format(str(worker_id)))

        cwd = os.getcwd()
        file_name = (file_name.strip()
                     .replace('.app', '').replace('.exe', '').replace('.x86_64', '').replace('.x86', ''))
        true_filename = os.path.basename(os.path.normpath(file_name))
        logger.debug('The true file name is {}'.format(true_filename))
        launch_string = None
        if platform == "linux" or platform == "linux2":
            candidates = glob.glob(os.path.join(cwd, file_name) + '.x86_64')
            if len(candidates) == 0:
                candidates = glob.glob(os.path.join(cwd, file_name) + '.x86')
            if len(candidates) == 0:
                candidates = glob.glob(file_name + '.x86_64')
            if len(candidates) == 0:
                candidates = glob.glob(file_name + '.x86')
            if len(candidates) > 0:
                launch_string = candidates[0]

        elif platform == 'darwin':
            candidates = glob.glob(os.path.join(cwd, file_name + '.app', 'Contents', 'MacOS', true_filename))
            if len(candidates) == 0:
                candidates = glob.glob(os.path.join(file_name + '.app', 'Contents', 'MacOS', true_filename))
            if len(candidates) == 0:
                candidates = glob.glob(os.path.join(cwd, file_name + '.app', 'Contents', 'MacOS', '*'))
            if len(candidates) == 0:
                candidates = glob.glob(os.path.join(file_name + '.app', 'Contents', 'MacOS', '*'))
            if len(candidates) > 0:
                launch_string = candidates[0]
        elif platform == 'win32':
            candidates = glob.glob(os.path.join(cwd, file_name + '.exe'))
            if len(candidates) == 0:
                candidates = glob.glob(file_name + '.exe')
            if len(candidates) > 0:
                launch_string = candidates[0]
        if launch_string is None:
            self.close()
            raise UnityEnvironmentException("Couldn't launch the {0} environment. "
                                            "Provided filename does not match any environments."
                                            .format(true_filename))
        else:
            logger.debug("This is the launch string {}".format(launch_string))
            # Launch Unity environment
            if docker_training == False:
                proc1 = subprocess.Popen(
                    [launch_string,
                     '--port', str(self.port),
                     '--seed', str(seed)])
            else:
                """
                Comments for future maintenance:
                    xvfb-run is a wrapper around Xvfb, a virtual xserver where all
                    rendering is done to virtual memory. It automatically creates a
                    new virtual server automatically picking a server number `auto-servernum`.
                    The server is passed the arguments using `server-args`, we are telling
                    Xvfb to create Screen number 0 with width 640, height 480 and depth 24 bits.
                    Note that 640 X 480 are the default width and height. The main reason for
                    us to add this is because we'd like to change the depth from the default
                    of 8 bits to 24.
                    Unfortunately, this means that we will need to pass the arguments through
                    a shell which is why we set `shell=True`. Now, this adds its own
                    complications. E.g SIGINT can bounce off the shell and not get propagated
                    to the child processes. This is why we add `exec`, so that the shell gets
                    launched, the arguments are passed to `xvfb-run`. `exec` replaces the shell
                    we created with `xvfb`.
                """
                docker_ls = ("exec xvfb-run --auto-servernum"
                             " --server-args='-screen 0 640x480x24'"
                             " {0} --port {1} --seed {2}").format(launch_string,
                                                                  str(self.port),
                                                                  str(seed))
                proc1 = subprocess.Popen(docker_ls,
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.PIPE,
                                         shell=True)
        self._socket.settimeout(30)
        try:
            try:
                self._socket.listen(1)
                self._conn, _ = self._socket.accept()
                self._conn.settimeout(30)
                import pdb; pdb.set_trace()
#下記の行でエラーが発生します
                p = self._conn.recv(self._buffer_size).decode('utf-8')
                p = json.loads(p)
            except socket.timeout as e:
                raise UnityTimeOutException(
                    "The Unity environment took too long to respond. Make sure {} does not need user interaction to "
                    "launch and that the Academy and the external Brain(s) are attached to objects in the Scene."
                    .format(str(file_name)))
#teratailの仕様上10000文字以上の投稿はできないようなので、これ以下のコードは省略します。お手数ですが、上記のリンクのgithubから参照ください

 試したこと

・実行したシェルの文字コードの確認(echo $LANG)
ターミナルの文字コードはja_JP.UTF-8
ターミナルとコードの文字コードの不一致によるエラーの発生の線は薄そうです。

・デバッグ(pdb)
変数pには何も格納されていませんでした。
socketで送られてきたデータの中身を確認することは叶わず

・インターネットで調べてみるも解決方法に至るような情報は見つからず
python3でのbytes型の中で.decode('utf-8')を指定してもエラーになる
技術系メモ
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbc in position 0: invalid start byte

 補足情報(FW/ツールのバージョンなど)

macOS Sierra 10.12.6
Unity 2018.2.4.f1
ターミナル2.7.3(文字コード ja_JP.UTF-8 )
Python 3.6.6 :: Anaconda, Inc.

                            

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfe

憶測ですがdecode byte 0xfeなので、utf-8ではなくutf-16でデータが送信されているように見えます。

    def __init__(self, file_name, worker_id=0,
                 base_port=5005, curriculum=None,
                 seed=0, docker_training=False):


そしてport番号:5005Dockerコンテナではないでしょうか。

DockerコンテナのLOCALE設定を再確認してみてくださいな。

◆参考情報
Dockerではコンテナのlocaleの再確認を


公式IssueにUnity-Technologies/ml-agents/Issuess/1026

ArmRobotのProjectVersion.txt
Unityのバージョン:2018.1.0f2

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/24 15:30

    回答ありがとうございます! port番号は盲点でした。こちらport番号がDockerコンテナになっているということはDockerで実行しなければいけないということですか?いまいちDockerのことがわかっていないためお答えいただけたら幸いです。

    キャンセル

  • 2018/08/24 16:47

    @RokuroYamakiさんへ
    ごめんなさい。dockerではないみたいです。
    ぐぐって調べてみたら。Unity側に以下issueがありました。
    https://github.com/Unity-Technologies/ml-agents/issues/1026
    https://qiita.com/dora-gt/items/bacce436de2a3cdb1ef4

    キャンセル

  • 2018/08/24 17:58

    ありがとうございます!解決しました。pythonのversionとml-agentsのversionを合わせなければいけなかったようです。また新しいエラーが出てしまいましたが、頑張っていきます。

    キャンセル

  • 2018/08/24 18:12

    @RokuroYamakiさんへ
    解決してよかったです。最初、誤った情報を提示して申し訳ありませんでした。

    キャンセル

0

受信データがUTF8にデコードできないということなんで、そういう不正なデータが受信されてるんでしょう。
まずはその通信データをチェックしてはどうでしょうか

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる