| Crates.io | mecha10-behavior-patterns |
| lib.rs | mecha10-behavior-patterns |
| version | 0.1.23 |
| created_at | 2025-12-30 23:03:41.708882+00 |
| updated_at | 2025-12-30 23:03:41.708882+00 |
| description | Common behavior patterns for Mecha10 - subsumption, ensemble, and more |
| homepage | |
| repository | https://github.com/mecha10/mecha10 |
| max_upload_size | |
| id | 2013572 |
| size | 152,056 |
Common behavior patterns as reusable BehaviorNode implementations for robotics and AI systems.
This package provides two fundamental behavior patterns that solve common robotics problems:
Both patterns implement the BehaviorNode trait from mecha10-behavior-runtime, making them composable with other behaviors.
[dependencies]
mecha10-behavior-patterns = "0.1.0"
The subsumption architecture organizes behaviors in priority layers. Higher priority behaviors can override lower priority ones. This is essential for safety-critical systems.
Success or Running, that status is returned immediatelyFailure, the next lower priority layer is triedFailureuse mecha10_behavior_patterns::prelude::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let ctx = Context::new("robot").await?;
// Create subsumption architecture
let mut subsumption = SubsumptionNode::new()
.add_named_layer(10, "emergency_stop", Box::new(EmergencyStopBehavior))
.add_named_layer(5, "avoid_obstacle", Box::new(AvoidObstacleBehavior))
.add_named_layer(1, "navigate", Box::new(NavigateBehavior));
// Execute
let mut executor = BehaviorExecutor::new(Box::new(subsumption), 30.0);
executor.run_until_complete(&ctx).await?;
Ok(())
}
We recommend this priority scheme:
The ensemble pattern combines multiple AI models or behaviors to make more robust decisions. This is useful for redundancy, fault tolerance, and multi-modal sensor fusion.
Conservative - All models must agree (high precision, low recall)
let ensemble = EnsembleNode::new(EnsembleStrategy::Conservative)
.add_model(Box::new(YoloV8), 1.0)
.add_model(Box::new(YoloV10), 1.0);
Optimistic - Any model can succeed (high recall, low precision)
let ensemble = EnsembleNode::new(EnsembleStrategy::Optimistic)
.add_model(Box::new(PreciseSensor), 1.0)
.add_model(Box::new(FallbackSensor), 1.0);
Majority - More than half must agree (balanced)
let ensemble = EnsembleNode::new(EnsembleStrategy::Majority)
.add_model(Box::new(Model1), 1.0)
.add_model(Box::new(Model2), 1.0)
.add_model(Box::new(Model3), 1.0);
WeightedVote - Weighted voting with threshold
let ensemble = EnsembleNode::new(EnsembleStrategy::WeightedVote)
.with_threshold(0.6) // 60% threshold
.add_model(Box::new(HighAccuracyModel), 0.5)
.add_model(Box::new(FastModel), 0.3)
.add_model(Box::new(BackupModel), 0.2);
use mecha10_behavior_patterns::prelude::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let ctx = Context::new("detector").await?;
// Combine multiple YOLO models with weighted voting
let ensemble = EnsembleNode::new(EnsembleStrategy::WeightedVote)
.with_threshold(0.7)
.add_named_model("yolov8", Box::new(YoloV8Detector), 0.4)
.add_named_model("yolov10", Box::new(YoloV10Detector), 0.4)
.add_named_model("custom", Box::new(CustomDetector), 0.2);
// Execute
let mut executor = BehaviorExecutor::new(Box::new(ensemble), 10.0);
let (status, stats) = executor.run_until_complete(&ctx).await?;
println!("Detection complete: {} in {} ticks", status, stats.tick_count);
Ok(())
}
Subsumption and Ensemble can be combined for powerful control architectures:
// Safety layer uses ensemble of multiple safety checks
let safety_ensemble = EnsembleNode::new(EnsembleStrategy::Conservative)
.add_model(Box::new(BatteryCheck), 1.0)
.add_model(Box::new(TemperatureCheck), 1.0)
.add_model(Box::new(ObstacleCheck), 1.0);
// Navigation uses ensemble of path planners
let nav_ensemble = EnsembleNode::new(EnsembleStrategy::WeightedVote)
.add_model(Box::new(AStarPlanner), 0.6)
.add_model(Box::new(RRTPlanner), 0.4);
// Combine with subsumption
let control = SubsumptionNode::new()
.add_layer(10, Box::new(safety_ensemble))
.add_layer(1, Box::new(nav_ensemble));
Both patterns support JSON configuration (when used with the node registry):
{
"type": "subsumption",
"layers": [
{
"priority": 10,
"name": "emergency_stop",
"node": "emergency_stop_behavior",
"config": { "max_speed": 0.0 }
},
{
"priority": 5,
"name": "avoid_obstacle",
"node": "obstacle_avoidance",
"config": { "safety_distance": 0.5 }
}
]
}
{
"type": "ensemble",
"strategy": "weighted_vote",
"threshold": 0.6,
"models": [
{
"name": "yolov8",
"node": "yolo_v8_detector",
"weight": 0.4,
"config": { "confidence": 0.7 }
},
{
"name": "yolov10",
"node": "yolo_v10_detector",
"weight": 0.6,
"config": { "confidence": 0.7 }
}
]
}
# Run tests
cargo test -p mecha10-behavior-patterns
# Run clippy
cargo clippy -p mecha10-behavior-patterns -- -D warnings
Both patterns are built on top of mecha10-behavior-runtime and implement the BehaviorNode trait:
#[async_trait]
pub trait BehaviorNode: Send + Sync + Debug {
async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus>;
// ... lifecycle methods ...
}
This makes them fully composable with:
Both patterns are designed for real-time performance with minimal overhead.
MIT