在我试图开发的基于代理的模型中,我很难理解如何将基于规则的决策方法结合到基于代理的模型中。
代理的接口非常简单。
public interface IAgent
{
public string ID { get; }
public Action Percept(IPercept percept);
}为了这个例子,让我们假设代理代表在一个大仓库内穿越道路的车辆,以便装卸他们的货物。他们的路线(从起点到代理目的地的道路顺序)由另一个代理,即主管指定。车辆代理的目标是遍历其指定路线,卸下货物,装载新的货物,接受主管指定的另一条路线,并重复这一过程。
车辆还必须注意可能发生的碰撞,例如在交叉点,并根据某些规则给予优先权(例如,运载最重货物的车辆具有优先权)。
据我所知,这是我想要构建的代理的内部结构:

所以车辆代理可以是:
public class Vehicle : IAgent
{
public VehicleStateUpdater { get; set; }
public RuleSet RuleSet { get; set; }
public VehicleState State { get; set; }
public Action Percept(IPercept percept)
{
VehicleStateUpdater.UpdateState(VehicleState, percept);
Rule validRule = RuleSet.Match(VehicleState);
VehicleStateUpdater.UpdateState(VehicleState, validRule);
Action nextAction = validRule.GetAction();
return nextAction;
}
}对于车辆代理人的内部状况,我正在考虑这样的事情:
public class VehicleState
{
public Route Route { get; set; }
public Cargo Cargo { get; set; }
public Location CurrentLocation { get; set; }
}对于此示例,必须为车辆代理实现3条规则。
VehicleStateUpdater必须考虑代理的当前状态、接收到的感知的类型,并相应地更改状态。因此,为了使状态反映出主管收到了例如命令,可以对其进行如下修改:
public class VehicleState
{
public Route Route { get; set; }
public Cargo Cargo { get; set; }
public Location CurrentLocation { get; set; }
// Additional Property
public RadioCommand ActiveCommand { get; set; }
}如果RadioCommand可以是值为None的枚举,请保持,继续。
但现在,如果另一辆车接近,我也必须在代理人的状态下登记。因此,我必须向VehicleState添加另一个属性。
public class VehicleState
{
public Route Route { get; set; }
public Cargo Cargo { get; set; }
public Location CurrentLocation { get; set; }
public RadioCommand ActiveCommand { get; set; }
// Additional properties
public bool IsAnotherVehicleApproaching { get; set; }
public Location ApproachingVehicleLocation { get; set; }
}这是我很难理解如何进行的地方,我有一种感觉,我没有真正遵循正确的方法。首先,我不知道如何使VehicleState类更加模块化和可扩展。第二,我不知道如何实现定义决策过程的基于规则的部分.我是否应该创建相互排斥的规则(这意味着每一种可能的状态必须不超过一条规则)?是否有一种设计方法允许我添加额外的规则,而不必来回地添加/修改VehicleState类的属性,以确保每种可能的Percept类型都可以由代理的内部状态处理?
我在“人工智能:现代方法手册”和其他资料中看到了这些例子,但现有的例子太简单,当需要设计更复杂的模型时,我无法“理解”这个概念。
我试图纳入一项规则的一个例子:
public class HoldPositionCommandRule : IAgentRule<VehicleState>
{
public int Priority { get; } = 0;
public bool ConcludesTurn { get; } = false;
public void Fire(IAgent agent, VehicleState state, IActionScheduler actionScheduler)
{
state.Navigator.IsMoving = false;
//Use action scheduler to schedule subsequent actions...
}
public bool IsValid(VehicleState state)
{
bool isValid = state.RadioCommandHandler.HasBeenOrderedToHoldPosition;
return isValid;
}
}代理决策者的样本,我也试着去实现。
public void Execute(IAgentMessage message,
IActionScheduler actionScheduler)
{
_agentStateUpdater.Update(_state, message);
Option<IAgentRule<TState>> validRule = _ruleMatcher.Match(_state);
validRule.MatchSome(rule => rule.Fire(this, _state, actionScheduler));
}如果有人能在执行基于规则的部分方面指出正确的方向,我将不胜感激。
我是用C#写的,但据我所知,它与我试图解决的更广泛的问题并没有真正的关系。
发布于 2021-07-29 16:56:52
这是我很难理解如何进行的地方,我有一种感觉,我没有真正遵循正确的方法。首先,我不知道如何使VehicleState类更加模块化和可扩展。
虽然这里有许多具体的问题,但我看到的是您在这里展示的代码中存在的许多一般性问题。你所经历的许多挣扎似乎与没有正确使用OO概念有关。创建接口和类的目的是管理复杂性。主要的问题是这里几乎没有封装,您的对象主要是“属性包”,即美化的地图/字典。让我们从您的最后一个示例VehicleState开始。首先,我不认为您需要VehicleState或VehicleStateUpdater on Vehicle。车辆对象已经具有状态,并且该状态的更新者应该是它自己。现在让我们来看一下VehicleState,我稍后再讨论这个问题:
public class VehicleState
{
public Route Route { get; set; }
public Cargo Cargo { get; set; }
public Location CurrentLocation { get; set; }
public RadioCommand ActiveCommand { get; set; }
// Additional properties
public bool IsAnotherVehicleApproaching { get; set; }
public Location ApproachingVehicleLocation { get; set; }
}这在功能上等价于带有类型的硬编码字典。而且,很多事情对我来说都没有意义。另一辆车是否正在接近并不是车辆状态的一部分。我希望这是Percept (perceptor)状态的一部分。接近车辆位置是接近车辆状态的一部分,它已经有一个位置作为其状态的一部分。
我不明白暴露Location的设置者有什么意义。更像update,它告诉车辆更新它的状态,比如Location,在这里是典型的。同样,货物设置器应该用Load和Unload方法代替。
RadioCommand似乎过于具体,您可能应该有一个Command接口和一个带有GetCommand方法的Radio类型。然后,HoldPositionCommandRule变成一个HoldPositionCommand。Command和Rule可能如下所示:
public interface IRule
{
bool public canMove();
// ...
}
public class Command
{
List<IRule> Rules { get; set; }
public addRule(IRule rule) {
getRules.add(rule)
}
}然后你就可以拥有:
public class HoldPositionRule : IRule
{
bool public canMove()
{
return false;
}
}我使用Python编写了一个小的工作示例。在任何人变得过于兴奋之前,我并不是把它作为有史以来最好的代码,也不是一个真正有趣的代理示例。我在这里的首要任务是保持简单和简短。为此,我已经消除了路线,并硬编码为一条直线30单位长。有两个规则:遵循至少2个单位的距离和保持位置。我把三辆车放在路上,当第一辆车(A)到达终点时,停止模拟。在每10个周期,领头卡车被给予一个‘保持’的命令,每3个,它被给予‘跟随’的命令。perceptor只能看到前面的车辆(或者旁边的车辆,在本例中不应该出现这种情况)。
class HoldPositionRule:
def can_move(self, distance_to_next):
return False
class FollowingRule:
def can_move(self, distance_to_next):
return distance_to_next is None or distance_to_next > 2
HOLD = HoldPositionRule()
FOLLOW = FollowingRule()
class Command:
def __init__(self, *rules):
self._rules = rules
def can_move(self, distance_to_next):
for rule in self._rules:
if not rule.can_move(distance_to_next):
return False
return True
class Radio:
def __init__(self):
self.command = Command(FOLLOW)
class Location:
def __init__(self, position):
self.position = position
def update(self, rate):
self.position += rate
def arrived(self):
return self.position >= 30
def __repr__(self):
return str(self.position)
def distance(self, other):
return other.position - self.position
def distance(a, b):
return a.location.distance(b.location)
class Environment:
def __init__(self):
self.vehicles = []
def add(self, vehicle):
self.vehicles.append(vehicle)
def visible(self, perceptor):
return [v for v in self.vehicles if distance(perceptor, v) >= 0]
ENVIRONMENT = Environment()
class Perceptor:
def __init__(self, vehicle):
self.vehicle = vehicle
self.location = vehicle.location
self.visible = []
def update(self):
self.visible = [v for v in ENVIRONMENT.visible(self) if v is not self.vehicle]
class Vehicle:
def __init__(self, id, location):
self.id = id
self.radio = Radio()
self.location = location
self.perceptor = Perceptor(self)
self.rate = 1
ENVIRONMENT.add(self)
def arrived(self):
return self.location.arrived()
def look(self):
self.perceptor.update()
def update(self):
visible = [distance(self, v) for v in self.perceptor.visible]
distance_to_next = min(visible) if len(visible) else None
if self.radio.command.can_move(distance_to_next):
self.location.update(self.rate)
def __repr__(self):
return f"{self.id}: {self.location}"
truckA = Vehicle("A", Location(0))
truckB = Vehicle("B", Location(-1))
truckC = Vehicle("C", Location(-2))
vehicles = [truckA, truckB, truckC]
for cycle in range(100):
if truckA.arrived():
break
for v in vehicles:
v.look()
if cycle % 10 == 0:
truckB.radio.command = Command(HOLD)
elif cycle % 3 == 0:
truckB.radio.command = Command(FOLLOW)
for v in vehicles:
v.update()
print(cycle, vehicles)这可能更简单,您想要做的事情,但如果您开始沿着这些路线,并使它发挥作用,您可以修改它变得更加复杂。最具挑战性的两个部分可能是perceptor如何提取环境细节,以及如何根据perceptor的输出应用规则。这在很大程度上取决于仿真的复杂性。我建议只在不足的时候开始简单地重新工作。
发布于 2021-07-29 14:10:42
我不知道如何使VehicleState类更加模块化和可扩展。(...)是否有一种设计方法允许我添加额外的规则,而不必来回地添加/修改VehicleState类的属性,以确保每种可能的Percept类型都可以由代理的内部状态处理?
因此,您需要类似于面向插件的体系结构,可以添加新组件并相互通信,而不需要修改“核心”?实现这一目标的最简单的方法是实现代理本地第二集装箱。(在C#中,内建实现应该足够了。)
class RadioCommunicationState
{
public RadioCommand CurrentCommand { get; set; }
}
class RadioCommandPerceptor : IPerceptor
{
public RadioCommandPerceptor(IRemoteRadioCommandSource source, RadioCommunicationState radioState) { /*...*/ }
public void Update() => _radioState.CurrentCommand = _source.RecentCommands.LastOrDefault();
}
class HoldPositionRule : IRule
{
public HoldPositionRule(RadioCommunicationState radioState) { /*...*/ }
public float PriorityScale { get; set; } = 42.314f; // for fine-tuning, read it from the config
public float RateSelf() => (_radioState.CurrentCommand == RadioCommand.Hold) ? 1f : 0f;
public void Apply() => _vehicleControlsState.DesiredSpeed = 0;
}
class VehicleAgent : IAgent
{
public void Update()
{
foreach (var perceptor in _perceptors)
perceptor.Update();
var rule = _rules.Max(r => r.RateSelf() * r.PriorityScale);
rule.Apply();
}
}
class Program
{
static VehicleAgent BuildVehicleAgent()
{
var collection = new ServiceCollection();
collection.AddSingleton<RadioCommunicationState>();
collection.AddSingleton<RadioCommandPerceptor>();
collection.AddSingleton<HoldPositionRule>();
collection.AddSingleton<VehicleAgent>();
// ...
return collection.BuildServiceProvider().GetService<VehicleAgent>();
}
}(如果您正在使用ML,请注意扩展ML模型有新的投入和产出通常需要完全重新培训。如果出于性能原因这是不可接受的,请选择/设计一个支持“持续学习”的模型。)
我不知道如何实现定义决策过程的基于规则的部分。我是否应该创建相互排斥的规则(这意味着每一种可能的状态必须不超过一条规则)?
FSM-like方法(即每1种状态有1条规则)是简单和可组合的。多个FSM可以并行使用以提供更好的灵活性,但它只对不需要协调的完全独立特性来说是有意义的。对于需要子步骤和协调的复杂规则,只需使用行为树;它还消除了对调度程序的需求。
如果有另一种可能让车辆代理人改变他们的路线,我会在IRouteAssignmentCommandSource中注射RouteAssignmentCommandPerceptor吗?
IRemoteRadioCommandSource只是一个抽象概念,可以在我的示例中插入一些漏洞。命令从何而来并不重要。但我想所有的命令都是通过同一个无线电频道发出的,所以把所有的命令放到同一个源中更有意义。
当我们在这里的时候,让我们改变perceptor,通过中间状态来解释一个命令。这样,就可以在不触及规则的情况下增加更多的感知者。
class TemporaryStopIntentState
{
public DateTime Target { get; init; }
}
class HoldPositionCommand
{
public DateTime ReleaseTime { get; init; }
}
class RadioCommandPerceptor : IPerceptor
{
/* ... */
public void Update()
{
if (_source.PendingCommand is HoldCommand command)
_temporaryStopIntentState.Target = command.ReleaseTime;
}
}
class HoldPositionRule : IRule
{
/* ... */
public float RateSelf() => (_temporaryStopIntentState.Target > DateTime.Now) ? 1f : 0f;
}此外,在有限状态机方法中,一次只能选择一条规则并应用,对吗?因此,在我们的示例中,如果两个规则都是“有效的”(同时存在RadioCommand.Hold和NewRoute ),那么其中一个规则将在第一个更新周期中发生,而另一个规则将在下一个周期中发生。我理解得对吗?
这一直是重量最高的规则。你要仔细调整规则,以确保没有僵局,否则行为将取决于月亮的相位。但是,您不需要依赖于单一的FSM,我们更喜欢它们,因为它们是便宜的和可组合的。
命令是令人困惑的,因为它们更类似于事件(“我看到了一只猫”),而不是可感知的状态(“真,我现在看到了红灯”)。将命令处理移动到感知器中(如上面的代码所示)可以解决这一概念上的偏差。
https://softwareengineering.stackexchange.com/questions/430453
复制相似问题