本文已参与「新人创作礼」活动,一起开启掘金创作之路。
对于一些非强化学习相关参数,ML-Agent提供了SideChannel的方式
SideChannel有两种实现方式
方式1:
官方定义了两种SideChannel的类:EngineConfigurationChannel和EnvironmentParametersChannel
EngineConfigurationChannel 一般使用 set_configuration_parameters()方法:
参数 | 介绍 |
---|---|
width、height | 窗口对应尺寸. (二者必须同时定义) |
quality_level | 仿真质量 |
time_scale | 为模拟中的增量时间定义乘数。如果将其设置为较高的值,则时间将在仿真中更快地经过,但是物理性能可能无法预测。 |
target_frame_rate | 指示仿真尝试以指定的帧速率进行渲染。 |
capture_frame_rate | 指示仿真考虑两次更新之间的时间始终保持恒定,而不管实际的帧速率如何 |
from mlagents_envs.environment import UnityEnvironment
from mlagents_envs.side_channel.engine_configuration_channel import EngineConfigurationChannel
channel = EngineConfigurationChannel()
env = UnityEnvironment(side_channels=[channel])
#将时间尺度调为两倍
channel.set_configuration_parameters(time_scale = 2.0)
env.reset()
EnvironmentParametersChannel 有方法set_float_parameter类似字典,有两个参数:key和value
from mlagents_envs.environment import UnityEnvironment
from mlagents_envs.side_channel.environment_parameters_channel import EnvironmentParametersChannel
channel = EnvironmentParametersChannel()
env = UnityEnvironment(side_channels=[channel])
channel.set_float_parameter("parameter_1", 2.0)
env.reset()
...
之后你可以在C#中用如下代码获取"parameter_1":
using UnityEngine;
using Unity.MLAgents.SideChannels;
using Unity.MLAgents;
var envParameters = Academy.Instance.EnvironmentParameters;
float property1 = envParameters.GetWithDefault("parameter_1", 0.0f);
方式2:
对于较为复杂的参数,方式1可能不太好用。
可以分别在C#和Python中定义一个SideChannel的类来实现二者参数的传递
python side: 定义一个继承于SideChannel的类,该类需要如下初始化函数来保证ID一致
def init(self, channel_id: uuid.UUID) -> None: # ChannelID 注:C#为GUID,python为UUID super().init(channel_id)
然后调用函数
on_message_received(self, msg:"IncomingMessage") -> None
来接收C#传来的参数 另外定义send_string、send_bool等函数向unity发送参数
from mlagents_envs.environment import UnityEnvironment
from mlagents_envs.side_channel.side_channel import (
SideChannel,
IncomingMessage,
OutgoingMessage,
)
import uuid
from typing import List
class SideChannelTest(SideChannel):
def __init__(self, channel_id: uuid.UUID) -> None:
# ChannelID 注:C#为GUID,python为UUID
super().__init__(channel_id)
# 接收函数
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:
"""发送一个字符串给C#"""
msg = OutgoingMessage()
msg.write_string(data)
super().queue_message_to_send(msg)
def send_bool(self, data: bool) -> None:
msg = OutgoingMessage()
msg.write_bool(data)
super().queue_message_to_send(msg)
def send_int(self, data: int) -> None:
msg = OutgoingMessage()
msg.write_int32(data)
super().queue_message_to_send(msg)
def send_float(self, data: float) -> None:
msg = OutgoingMessage()
msg.write_float32(data)
super().queue_message_to_send(msg)
def send_float_list(self, data: List[float]) -> None:
msg = OutgoingMessage()
msg.write_float32_list(data)
super().queue_message_to_send(msg)
channel_id = uuid.UUID("621f0a70-4f87-11ea-a6bf-784f4387d1f7")
side_channel = SideChannelTest(channel_id)
# 在不定义file_name的情况下下面这句应该是可以直接与Unity Editor交互的,但可能因为网络问题我还未成功
# 如果你也不能成功的话,可以试试用UnityEnvironment(file_name= "exe_path", side_channels=[side_channel])
# 其中exe_path为你自unity场景中build出的exe文件的绝对路径
# 关于使用exe文件,也可参阅下面的文章
# https://blog.csdn.net/weixin_48592526/article/details/113482913
env = UnityEnvironment(side_channels=[side_channel])
env.reset()
side_channel.send_string("The environment was reset")
behavior_name = list(env.behavior_specs.keys())[0] # Get the first group_name
spec = env.behavior_specs[behavior_name]
for i in range(1000):
decision_steps, terminal_steps = env.get_steps(behavior_name)
# We send data to Unity : A string with the number of Agent at each
side_channel.send_string(
f"Step {i} occurred with {len(decision_steps)} deciding agents and "
f"{len(terminal_steps)} terminal agents"
)
env.step() # Move the simulation forward
env.close()
unity side: 与python side 及其类似,定义一个继承于SideChannel的类,该类的构造函数中也需要传入ChannelID
然后调用函数
OnMessageReceived(IncomingMessage msg)
来接收C#传来的参数 另外定义SendStingToPython 、SendBoolToPython等函数向unity发送参数
定义继承于SideChannel的类
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.SideChannels;
using System;
public class SideChannelTest: SideChannel
{
public SideChannelTest()
{
ChannelId = new Guid("621f0a70-4f87-11ea-a6bf-784f4387d1f7");
}
protected override void OnMessageReceived(IncomingMessage msg)
{
var receivedString = msg.ReadString();
Debug.Log("From Python : " + receivedString);
}
public void SendDebugStatementToPython(string logString, string stackTrace, LogType type)
{
if (type == LogType.Error)
{
var stringToSend = type.ToString() + ": " + logString + "\n" + stackTrace;
using (var msgOut = new OutgoingMessage())
{
msgOut.WriteString(stringToSend);
QueueMessageToSend(msgOut);
}
}
}
}
以下脚本要挂载在场景中的某一物体上
using UnityEngine;
using Unity.MLAgents;
public class RegisterStringLogSideChannel : MonoBehaviour
{
StringLogSideChannel stringChannel;
public void Awake()
{
// We create the Side Channel
stringChannel = new StringLogSideChannel();
// When a Debug.Log message is created, we send it to the stringChannel
Application.logMessageReceived += stringChannel.SendDebugStatementToPython;
// The channel must be registered with the SideChannelManager class
SideChannelManager.RegisterSideChannel(stringChannel);
}
public void OnDestroy()
{
// De-register the Debug.Log callback
Application.logMessageReceived -= stringChannel.SendDebugStatementToPython;
if (Academy.IsInitialized){
SideChannelManager.UnregisterSideChannel(stringChannel);
}
}
public void Update()
{
// Optional : If the space bar is pressed, raise an error !
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.LogError("This is a fake error. Space bar was pressed in Unity.");
}
}
}
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.SideChannels;
public class RegisterStringLogSideChannel : MonoBehaviour
{
SideChannelTest sideChannel;
public void Awake()
{
// We create the Side Channel
sideChannel = new SideChannelTest();
// When a Debug.Log message is created, we send it to the stringChannel
Application.logMessageReceived += sideChannel.SendDebugStatementToPython;
// The channel must be registered with the SideChannelManager class
SideChannelManager.RegisterSideChannel(sideChannel);
}
public void OnDestroy()
{
// De-register the Debug.Log callback
Application.logMessageReceived -= sideChannel.SendDebugStatementToPython;
if (Academy.IsInitialized)
{
SideChannelManager.UnregisterSideChannel(sideChannel);
}
}
public void Update()
{
// Optional : If the space bar is pressed, raise an error !
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.LogError("This is a fake error. Space bar was pressed in Unity.");
}
}
}
现在,如果您运行python脚本并在出现提示时按Play the Unity Editor,则Unity Editor中的控制台将在每个Python步骤中显示一条消息(如果你是用file_name直接与exe文件交互的话就不能看见控制台了)。另外,如果您在Unity Engine中按空格键,则消息将出现在终端中。