Unity-ML-Agents--Custom-SideChannels.md-代码解析

官方文档:

https://github.com/Unity-Technologies/ml-agents/blob/release_19/docs/ML-Agents-Overview.md#additional-features

https://github.com/Unity-Technologies/ml-agents/blob/release_19/docs/Custom-SideChannels.md

目录

1.Unity C# 示例

1.1 StringLogSideChannel类

1.1.1 ChannelId = new Guid("621f0a70-4f87-11ea-a6bf-784f4387d1f7");

1.1.2 protected override void OnMessageReceived(IncomingMessage msg)

1.1.3 public void SendDebugStatementToPython(string logString, string stackTrace, LogType type)

1.1.4 var stringToSend = type.ToString() + ": " + logString + "\n" + stackTrace

1.2 RegisterStringLogSideChannel类

1.2.1 public class RegisterStringLogSideChannel : MonoBehaviour

1.2.2 StringLogSideChannel  stringChannel

1.2.3 Awake()

1.2.4 OnDestroy()

1.2.5 Update()

2. Python示例

2.1 StringLogChannel类

2.1.1 __init__ 方法

2.1.2 on_message_received()

2.2 ML-Agents Python 与 Unity Editor 通信

2.2.1 env = UnityEnvironment(side_channels=[string_log])

2.2.2 group_name = list(env.behavior_specs.keys())[0]

2.2.3 group_spec = env.behavior_specs[group_name]

2.2.4 decision_steps, terminal_steps = env.get_steps(group_name)

2.2.5 string_log.send_string( f"Step {i} occurred with {len(decision_steps)} deciding agents and " f"{len(terminal_steps)} terminal agents"


1.Unity C# 示例

1.1 StringLogSideChannel类

using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.SideChannels;
using System.Text;
using System;

public class StringLogSideChannel : SideChannel
{
    // 创建一个唯一的 GUID 用于标识该 SideChannel
    public StringLogSideChannel()
    {
        ChannelId = new Guid("621f0a70-4f87-11ea-a6bf-784f4387d1f7");
    }

    // 在接收到消息时调用该方法,这里只是简单地输出接收到的字符串消息
    protected override void OnMessageReceived(IncomingMessage msg)
    {
        var receivedString = msg.ReadString();
        Debug.Log("From Python : " + receivedString);
    }

    // 将日志信息发送到 Python 端
    public void SendDebugStatementToPython(string logString, string stackTrace, LogType type)
    {
        if (type == LogType.Error)
        {
            // 将日志信息与堆栈跟踪合并为一个字符串
            var stringToSend = type.ToString() + ": " + logString + "\n" + stackTrace;
            // 创建一个 OutgoingMessage 对象,并将字符串信息写入其中
            using (var msgOut = new OutgoingMessage())
            {
                msgOut.WriteString(stringToSend);
                // 将消息加入发送队列,等待发送
                QueueMessageToSend(msgOut);
            }
        }
    }
}

这段代码实现了一个自定义的 SideChannel,用于向 Python 端发送调试信息。具体来说,它继承了 Unity.MLAgents.SideChannels.SideChannel 类,并覆盖了其中的 OnMessageReceived() 方法和自定义的 SendDebugStatementToPython() 方法。它还定义了一个 GUID 作为通道 ID。

OnMessageReceived() 方法中,它接收一个传入的消息,并将其作为字符串打印到 Unity 控制台中。

SendDebugStatementToPython() 方法中,它接收三个参数:一个字符串 logString,一个字符串 stackTrace 和一个 LogType 类型。如果传入的类型是 Error,它将将日志和堆栈跟踪信息连接在一起,并将结果作为字符串发送到 Python 端。

这个 SideChannel 的 ChannelId 是 "621f0a70-4f87-11ea-a6bf-784f4387d1f7",可以用于在 Python 端实例化对应的环境,并接收 Unity 控制台的调试信息。

1.1.1 ChannelId = new Guid("621f0a70-4f87-11ea-a6bf-784f4387d1f7");

    public StringLogSideChannel()
    {
        ChannelId = new Guid("621f0a70-4f87-11ea-a6bf-784f4387d1f7");
    }

这段代码定义了一个名为 StringLogSideChannel 的类,并在类的构造函数中为它的 ChannelId 属性设置了一个 Guid 值。Guid 是一种全局唯一标识符,可用于标识对象、类型、属性等。在这里,ChannelId 的 Guid 值用于标识这个 Side Channel 的唯一标识符,从而使它能够与其他 Side Channel 区别开来。

1.1.2 protected override void OnMessageReceived(IncomingMessage msg)

    protected override void OnMessageReceived(IncomingMessage msg)
    {
        var receivedString = msg.ReadString();
        Debug.Log("From Python : " + receivedString);
    }

这个方法重载了父类 SideChannel 中的 OnMessageReceived 方法,用于处理接收到的消息。在这个具体的实现中,方法会从接收到的消息中读取字符串,并将其使用 Debug.Log 输出到 Unity 的控制台中,前面加上 "From Python : " 的前缀。这个方法的具体功能实现可以根据具体需求进行修改。

1.1.3 public void SendDebugStatementToPython(string logString, string stackTrace, LogType type)

   public void SendDebugStatementToPython(string logString, string stackTrace, LogType type)
    {
        if (type == LogType.Error)
        {
            // 将日志信息与堆栈跟踪合并为一个字符串
            var stringToSend = type.ToString() + ": " + logString + "\n" + stackTrace;
            // 创建一个 OutgoingMessage 对象,并将字符串信息写入其中
            using (var msgOut = new OutgoingMessage())
            {
                msgOut.WriteString(stringToSend);
                // 将消息加入发送队列,等待发送
                QueueMessageToSend(msgOut);
            }
        }
    }

这段代码定义了一个名为 SendDebugStatementToPython 的公共方法。这个方法用于将 Unity 引擎中的调试日志发送到 Python 程序中进行记录。

方法的第一个参数 logString 是一个字符串,表示调试日志信息。第二个参数 stackTrace 是一个字符串,表示调用堆栈信息。第三个参数 type 是一个 LogType 类型的枚举值,用于表示调试日志的类型(如普通信息、警告或错误)。

如果日志类型是错误(LogType.Error),则将日志信息和堆栈跟踪合并为一个字符串,并将其写入一个 OutgoingMessage 对象中。然后将该消息添加到消息发送队列中,等待发送到 Python 程序。

问:字符串信息写入其中?怎么写入的?

在这段代码中,创建了一个 OutgoingMessage 对象 msgOut,然后调用 msgOut.WriteString(stringToSend) 将字符串信息 stringToSend 写入到这个消息对象中。最后调用 QueueMessageToSend(msgOut) 将这个消息对象加入到发送队列中,等待发送。这个消息对象中的字符串信息会随着消息一起发送到 TensorBoard 中

问:发送到 TensorBoard ?

在 Unity ML-Agents 中,可以通过将训练期间的统计信息发送到 TensorBoard,进行实时的可视化和分析。在 ML-Agents 中,将这些统计信息发送到 TensorBoard 需要借助一个叫做 SideChannel 的机制。SideChannel 是一种用于在训练期间与外部系统交换信息的方式,可以将消息传递到 TensorBoard 或其他外部系统。

1.1.4 var stringToSend = type.ToString() + ": " + logString + "\n" + stackTrace

var stringToSend = type.ToString() + ": " + logString + "\n" + stackTrace;

这行代码将LogType、日志信息和堆栈跟踪信息合并成一个字符串,用于在 SendDebugStatementToPython() 方法中将其写入 OutgoingMessage 对象中,最终发送到 TensorBoard 中。具体来说,使用了 "+" 操作符将这三部分信息连接在一起,LogType、日志信息和堆栈跟踪信息之间用 "\n" 换行符分隔

例如,如果 logString 是 "Failed to load model",stackTrace 是:

UnityEngine.Debug:LogError(Object) MLAgents.Inference.TensorFlowSharpUtils:LoadModel(String, Boolean) (at Assets/ML-Agents/Plugins/UnityTensorFlowPlugin/Scripts/Utilities/TensorFlowSharpUtils.cs:51) MLAgents.Inference.TensorFlowSharpModel:Reload() (at Assets/ML-Agents/Plugins/UnityTensorFlowPlugin/Scripts/Models/TensorFlowSharpModel.cs:63) MLAgents.Inference.BarracudaModel:Reload() (at Assets/ML-Agents/Plugins/Unity.Barracuda/Models/BarracudaModel.cs:54) MLAgents.Inference.ModelRunner:LoadModel() (at Assets/ML-Agents/Plugins/Unity.Barracuda/InferenceEngine/ModelRunner.cs:60) MLAgents.Inference.DecisionRequester:Initialize() (at Assets/ML-Agents/DecisionRequester.cs:26) MLAgents.Academy:InitializeEnvironment() (at Assets/ML-Agents/Components/Academy.cs:139) UnityEngine.Object:Instantiate(Object, Vector3, Quaternion, Transform)

则最终生成的字符串为 "Error: Failed to load model\nUnityEngine.Debug:LogError(Object)\nMLAgents.Inference.TensorFlowSharpUtils:LoadModel(String, Boolean) (at Assets/ML-Agents/Plugins/UnityTensorFlowPlugin/Scripts/Utilities/TensorFlowSharpUtils.cs:51)\nMLAgents.Inference.TensorFlowSharpModel:Reload() (at Assets/ML-Agents/Plugins/UnityTensorFlowPlugin/Scripts/Models/TensorFlowSharpModel.cs:63)\nMLAgents.Inference.BarracudaModel:Reload() (at Assets/ML-Agents/Plugins/Unity.Barracuda/Models/BarracudaModel.cs:54)\nMLAgents.Inference.ModelRunner:LoadModel() (at Assets/ML-Agents/Plugins/Unity.Barracuda/InferenceEngine/ModelRunner.cs:60)\nMLAgents.Inference.DecisionRequester:Initialize() (at Assets/ML-Agents/DecisionRequester.cs:26)\nMLAgents.Academy:InitializeEnvironment() (at Assets/ML-Agents/Components/Academy.cs:139)\nUnityEngine.Object:Instantiate(Object, Vector3, Quaternion, Transform)"。

1.2 RegisterStringLogSideChannel类

using UnityEngine;
using Unity.MLAgents;

public class RegisterStringLogSideChannel : MonoBehaviour
{
    StringLogSideChannel stringChannel;

    public void Awake()
    {
        // 创建 Side Channel 对象
        stringChannel = new StringLogSideChannel();

        // 当创建一个 Debug.Log 消息时,将其发送到 stringChannel
        Application.logMessageReceived += stringChannel.SendDebugStatementToPython;

        // 注册该 Channel
        SideChannelManager.RegisterSideChannel(stringChannel);
    }

    public void OnDestroy()
    {
        // 取消注册 Debug.Log 的回调
        Application.logMessageReceived -= stringChannel.SendDebugStatementToPython;
        if (Academy.IsInitialized){
            // 取消注册该 Channel
            SideChannelManager.UnregisterSideChannel(stringChannel);
        }
    }

    public void Update()
    {
        // 可选: 如果按下空格键,触发一个错误
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Debug.LogError("This is a fake error. Space bar was pressed in Unity.");
        }
    }
}

这段代码用于注册一个自定义的 Side Channel,用于将 Unity 中的 Debug.Log 消息发送到 Python 程序中,以便在 TensorBoard 中进行可视化和分析。具体实现包括:

①创建 StringLogSideChannel 对象作为 Side Channel,并通过注册它来让 ML-Agents 知道这个 Side Channel 的存在。

②注册一个 Application.logMessageReceived 委托,以便在 Unity 中有新的 Debug.Log 消息时触发 StringLogSideChannel 的 SendDebugStatementToPython 方法,将消息发送到 Python 程序中。

③在 Update 方法中,可以通过按下空格键来产生一个错误消息,以测试消息是否能够被成功地发送到 Python 程序中。

总之,这段代码可以将 Unity 中的 Debug.Log 消息发送到 Python 程序中,方便在 TensorBoard 中进行分析和可视化。

1.2.1 public class RegisterStringLogSideChannel : MonoBehaviour

public class RegisterStringLogSideChannel : MonoBehaviour
{

}

这行代码定义了一个名为 RegisterStringLogSideChannel 的类,这个类继承自 Unity 引擎的 MonoBehaviour 类,说明它是一个脚本组件。通常情况下,脚本组件可以被添加到游戏场景中的 GameObject 上,从而让脚本在游戏运行时被执行。

1.2.2 StringLogSideChannel  stringChannel

StringLogSideChannel stringChannel;

这行代码声明了一个名为 stringChannelStringLogSideChannel 类型的变量。它将用于管理和发送日志消息。

1.2.3 Awake()

    public void Awake()
    {
        // 创建 Side Channel 对象
        stringChannel = new StringLogSideChannel();

        // 当创建一个 Debug.Log 消息时,将其发送到 stringChannel
        Application.logMessageReceived += stringChannel.SendDebugStatementToPython;

        // 注册该 Channel
        SideChannelManager.RegisterSideChannel(stringChannel);
    }

这段代码在 Awake 方法中实例化 StringLogSideChannel 对象,并且将它注册到 Unity 的 SideChannelManager 中。还通过 Application.logMessageReceived 事件SendDebugStatementToPython 方法绑定到 Debug.Log 消息的事件上,以便将消息发送到 Python 程序。这样,当 Unity 中使用 Debug.Log 输出日志时,这些日志信息将通过 StringLogSideChannel 被发送到 Python 程序,并被记录到 TensorBoard 中。

stringChannel = new StringLogSideChannel();

这行代码创建了一个名为stringChannelStringLogSideChannel对象,并将其实例化。stringChannel变量将在后面的代码中用于调用该对象的方法和属性。

Application.logMessageReceived += stringChannel.SendDebugStatementToPython;

这行代码实现了将 Unity 的 Debug.Log 消息发送到 Python 端。具体来说,Application.logMessageReceived 是一个事件,在每次 Unity 中打印一条日志信息时都会触发。通过将 stringChannel.SendDebugStatementToPython 添加为这个事件的监听器,可以将每条日志信息都发送到 Python 端。这里的 stringChannelStringLogSideChannel 类的实例对象,而 SendDebugStatementToPython 方法则是这个实例对象中定义的用于发送日志信息的方法。

SideChannelManager.RegisterSideChannel(stringChannel);

SideChannelManager.RegisterSideChannel(stringChannel)是将自定义的Side Channel注册到ML-Agents中的全局管理器中。通过此方法,可以确保所有的agents和训练进程都可以访问到该channel,并可以向其发送和接收消息。在这个例子中,我们注册了一个StringLogSideChannel的实例,以便在Unity中生成的Debug.Log消息可以发送到Python端,从而能够在TensorBoard中查看。

1.2.4 OnDestroy()

    public void OnDestroy()
    {
        // 取消注册 Debug.Log 的回调
        Application.logMessageReceived -= stringChannel.SendDebugStatementToPython;
        if (Academy.IsInitialized){
            // 取消注册该 Channel
            SideChannelManager.UnregisterSideChannel(stringChannel);
        }
    }

这段代码是在Unity中的一个MonoBehaviour对象销毁时执行的。当这个对象被销毁时,该方法被调用,取消注册之前注册的回调和SideChannel。如果Unity的Academy对象已经被初始化,也就是说我们正在训练一个ML-Agent,那么就取消注册该Channel。这是为了确保在训练期间我们不会向已经关闭的通道发送信息。

1.2.5 Update()

    public void Update()
    {
        // 可选: 如果按下空格键,触发一个错误
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Debug.LogError("This is a fake error. Space bar was pressed in Unity.");
        }
    }

这段代码中的Update()函数,会在每一帧渲染时被Unity自动调用。在该函数中,如果检测到用户按下了键盘上的空格键,则会触发一个错误。这个错误将通过Debug.LogError()函数输出到控制台中,并通过Side Channel发送到Python程序中,在TensorBoard中记录下来。这个功能只是一个可选项,用于演示如何通过Side Channel从Unity向Python发送消息。

2. Python示例

2.1 StringLogChannel类

from mlagents_envs.environment import UnityEnvironment
from mlagents_envs.side_channel.side_channel import (
    SideChannel,
    IncomingMessage,
    OutgoingMessage,
)
import numpy as np
import uuid


# 创建 StringLogChannel 类
class StringLogChannel(SideChannel):

    def __init__(self) -> None:
        # 调用 SideChannel 的构造函数,并传入通道 ID
        super().__init__(uuid.UUID("621f0a70-4f87-11ea-a6bf-784f4387d1f7"))

    def on_message_received(self, msg: IncomingMessage) -> None:
        """
        Note: We must implement this method of the SideChannel interface to
        receive messages from Unity
        """
        # 读取字符串并输出到控制台
        print(msg.read_string())

    def send_string(self, data: str) -> None:
        # 创建 OutgoingMessage 对象,并将字符串写入其中
        msg = OutgoingMessage()
        msg.write_string(data)
        # 调用 SideChannel 的 queue_message_to_send 方法将消息加入发送队列
        super().queue_message_to_send(msg)

这段代码定义了一个名为 StringLogChannel 的类,该类继承了 SideChannel 类。在 StringLogChannel 中,我们实现了 on_message_received 方法,该方法在接收到消息时被调用。在这个方法中,我们简单地从消息中读取一个字符串并打印它。

此外,我们还定义了一个名为 send_string 的方法,该方法用于将一个字符串发送到 Unity 环境中。在这个方法中,我们将字符串添加到一个 OutgoingMessage 中,并调用 queue_message_to_send 方法将消息加入发送队列。

整个 StringLogChannel 类的目的是实现一个自定义的 SideChannel,用于在 Unity 环境和 Python 脚本之间传递字符串。通过这个自定义 SideChannel,Python 脚本可以接收来自 Unity 环境的字符串日志信息,并且可以将字符串信息发送回 Unity 环境。

2.1.1 __init__ 方法

    def __init__(self) -> None:
        # 调用 SideChannel 的构造函数,并传入通道 ID
        super().__init__(uuid.UUID("621f0a70-4f87-11ea-a6bf-784f4387d1f7"))

这一行代码定义了 StringLogChannel 类的构造函数,构造函数的主要作用是在创建类的实例时对实例的属性进行初始化。在这个构造函数中,super().__init__(uuid.UUID("621f0a70-4f87-11ea-a6bf-784f4387d1f7")) 调用了父类 SideChannel 的构造函数,将一个唯一标识符作为参数传递给父类,用于识别该通道。

这段代码是定义了一个名为 StringLogChannel 的 Python 类,继承了 Unity ML-Agents 提供的 SideChannel 类,用于与 Unity 引擎进行通信。

__init__ 方法中,该类调用了其父类 SideChannel 的构造函数,并传入了一个唯一标识该通道的 UUID。

通道 ID 的作用是让 Unity 引擎能够识别出这个通道,进而与之建立通信连接。在实际应用中,我们需要为每个自定义的 Side Channel 分配一个独有的 UUID。

2.1.2 on_message_received()

    def on_message_received(self, msg: IncomingMessage) -> None:
        """
        Note: We must implement this method of the SideChannel interface to
        receive messages from Unity
        """
        # 读取字符串并输出到控制台
        print(msg.read_string())

这行代码定义了一个名为on_message_received的方法,它是一个回调函数,用于在收到来自Unity环境的消息时被调用。该方法需要接收一个IncomingMessage类型的参数msg,表示接收到的消息。返回值为None,表示该方法不返回任何内容。

这是一个类方法(instance method),用于在接收到 Unity 端的消息时调用。它接受一个 IncomingMessage 类型的参数 msg,表示从 Unity 端接收到的消息。该方法主要的作用是读取消息内容并进行相应的处理。

这个函数是实现了 SideChannel 接口中的一个方法 on_message_received(),用于接收来自 Unity 的消息。其中的注释指出,必须实现这个方法才能从 Unity 接收消息。这个方法会读取一个字符串并输出到控制台。

2.1.3

    def send_string(self, data: str) -> None:
        # 创建 OutgoingMessage 对象,并将字符串写入其中
        msg = OutgoingMessage()
        msg.write_string(data)
        # 调用 SideChannel 的 queue_message_to_send 方法将消息加入发送队列
        super().queue_message_to_send(msg)

这一行代码定义了一个名为 send_string 的实例方法,它接收一个字符串类型的参数 data,并且没有返回值。这个方法的作用是将 data 字符串发送给 Unity 环境,以便在 Unity 环境中打印这个字符串。方法的实现包括以下步骤:

  • 创建一个 OutgoingMessage 对象,该对象用于存储待发送的消息。
  • data 字符串写入 OutgoingMessage 对象。
  • 调用父类 SideChannelqueue_message_to_send 方法,将消息加入到发送队列中,等待 Unity 环境接收。

2.2 ML-Agents Python 与 Unity Editor 通信

# 创建 StringLogChannel 类的实例
string_log = StringLogChannel()

# 通过 side_channels 参数将该 SideChannel 实例传入 Unity 环境,与 Unity Editor 建立连接
env = UnityEnvironment(side_channels=[string_log])

# 重置 Unity 环境
env.reset()

# 发送一个字符串消息到 StringLogChannel
string_log.send_string("The environment was reset")

# 获取第一个 group 的名称和 group_spec
group_name = list(env.behavior_specs.keys())[0]  
group_spec = env.behavior_specs[group_name]

# 进行 1000 次模拟
for i in range(1000):
    # 获取当前步骤的决策和终止步骤
    decision_steps, terminal_steps = env.get_steps(group_name)

    # 发送一个字符串消息到 StringLogChannel,其中包含当前步骤的一些信息
    string_log.send_string(
        f"Step {i} occurred with {len(decision_steps)} deciding agents and "
        f"{len(terminal_steps)} terminal agents"
    )

    # 进行一步模拟
    env.step()

# 关闭 Unity 环境
env.close()

这段代码使用了 ML-Agents Python 包来与 Unity Editor 之间的通信,从而控制 Unity 的环境进行学习。

首先,创建了一个 StringLogChannel 对象 string_log,它是一个自定义的 SideChannel,用于发送调试信息。接着,通过 UnityEnvironment 创建了一个与 Unity Editor 进行通信的环境 env,并将 string_log 作为输入的 side_channels 参数传入。然后,调用 env.reset() 重置环境,并向 string_log 发送一条调试信息。group_name 为第一个 group 的名称,group_spec 是该 group 的 BehaviorSpec。随后,在一个循环中,通过 env.get_steps(group_name) 获取该 group 中的决策步骤和终止步骤。然后,向 string_log 发送包含有关智能体数量的调试信息,调用 env.step() 推进环境的状态,直到达到循环次数上限,最后调用 env.close() 关闭环境。

2.2.1 env = UnityEnvironment(side_channels=[string_log])

env = UnityEnvironment(side_channels=[string_log])

这行代码创建了一个Unity环境对象(UnityEnvironment),并将一个SideChannel(string_log)作为参数传递给构造函数。SideChannel被用来在Unity环境和Python之间传递信息。通过将string_log传递给构造函数,我们告诉Unity环境我们希望使用该SideChannel进行通信。

2.2.2 group_name = list(env.behavior_specs.keys())[0]

group_name = list(env.behavior_specs.keys())[0] 

这行代码的含义是获取Unity环境中第一个Behavior的名称(Behavior是Agent的执行逻辑,例如移动、射击等),将其赋值给变量 group_name。在ML-Agents库中,一个Unity环境可以有多个Behavior,每个Behavior都由一个Brain来控制。在这里,我们只选择了第一个Behavior进行操作。

2.2.3 group_spec = env.behavior_specs[group_name]

group_spec = env.behavior_specs[group_name]

env.behavior_specs 是一个字典,它包含了 Unity 环境中每个行为的规格(specifications)。每个行为都有一个唯一的名称,称为 group_name。通过 env.behavior_specs.keys() 可以获取所有行为名称组成的列表,而 [0] 则表示取第一个行为的名称。

通过 env.behavior_specs[group_name],可以获取指定名称的行为的规格,包括它的动作空间(action space)和观测空间(observation space)等信息。具体内容取决于你在 Unity 中创建环境时定义的行为规格。

2.2.4 decision_steps, terminal_steps = env.get_steps(group_name)

decision_steps, terminal_steps = env.get_steps(group_name)

decision_stepsterminal_steps 是来自 Unity 环境的两个 AgentStep 对象列表,用于保存每个代理在给定时间步骤中的状态信息。 decision_steps 包含所有仍在决策的代理的信息,而 terminal_steps 包含已经终止的代理的信息。env.get_steps(group_name) 方法用于从环境中获取给定组名称 group_name 的所有 AgentStep 对象,然后将它们分别分配给 decision_stepsterminal_steps

2.2.5 string_log.send_string( f"Step {i} occurred with {len(decision_steps)} deciding agents and " f"{len(terminal_steps)} terminal agents"

    string_log.send_string(
        f"Step {i} occurred with {len(decision_steps)} deciding agents and "
        f"{len(terminal_steps)} terminal agents"

这一行代码的作用是发送一个字符串给Unity,该字符串包含了当前步数(i),决策步骤的Agent数量(decision_steps),以及终止步骤的Agent数量(terminal_steps)。其中,字符串使用了f-string的格式化方法,可以动态地将变量嵌入到字符串中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天寒心亦热

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值