ccf_core/mbot.rs
1//! mBot2 reference sensor vocabulary.
2//!
3//! The [mBot2](https://www.makeblock.com/mbot2) is a programmable robot by Makeblock,
4//! retailing for around **$50–$80 USD**. It runs a CyberPi microcontroller with onboard
5//! light, sound, and motion sensors.
6//!
7//! This module provides a ready-to-use [`SensorVocabulary`] implementation for the mBot2's
8//! six onboard sensor dimensions. It is the vocabulary used in the CCF reference demo —
9//! the same robot that demonstrates emergent social behaviour through accumulated experience,
10//! entirely on-device with no cloud or ML model required.
11//!
12//! # Why this exists
13//!
14//! The mBot2 demo is the existence proof for what ccf-core makes possible: a sub-$100
15//! robot that develops genuine context-sensitive social behaviour. Not scripted. Not
16//! rule-based. The trust field accumulates through real interaction, and the robot's
17//! responses emerge from what it has actually experienced in each environment.
18//!
19//! This module ships as a concrete reference so you can see exactly what a production
20//! `SensorVocabulary` implementation looks like. Your own hardware vocabulary follows
21//! the same pattern — just swap in your sensor dimensions.
22//!
23//! # See also
24//!
25//! - `examples/mbot2.rs` — full simulated CCF loop for the mBot2
26//! - [`SensorVocabulary`] — the trait to implement for your own hardware
27
28use crate::vocabulary::{ContextKey, SensorVocabulary};
29
30/// mBot2 sensor vocabulary — 6-dimensional context for the CyberPi microcontroller.
31///
32/// Covers the six onboard sensor dimensions most relevant to social behaviour:
33/// ambient light, ambient sound, nearby presence, robot motion, orientation, and
34/// time of day (set by the host application).
35///
36/// ```rust
37/// use ccf_core::mbot::{MbotSensors, BrightnessBand, NoiseBand,
38/// PresenceSignature, MotionContext, Orientation, TimePeriod};
39/// use ccf_core::vocabulary::ContextKey;
40///
41/// let key = ContextKey::new(MbotSensors {
42/// brightness: BrightnessBand::Bright,
43/// noise: NoiseBand::Quiet,
44/// presence: PresenceSignature::Close,
45/// motion: MotionContext::Static,
46/// orientation: Orientation::Upright,
47/// time_period: TimePeriod::Day,
48/// });
49/// ```
50#[derive(Clone, Debug, PartialEq, Eq, Hash)]
51pub struct MbotSensors {
52 /// Ambient light level (CyberPi light sensor).
53 pub brightness: BrightnessBand,
54 /// Ambient sound level (CyberPi microphone).
55 pub noise: NoiseBand,
56 /// Nearby presence signature (proximity / IR sensor).
57 pub presence: PresenceSignature,
58 /// Robot motion context (derived from wheel encoders).
59 pub motion: MotionContext,
60 /// Robot orientation relative to starting heading (IMU).
61 pub orientation: Orientation,
62 /// Time of day period (set by host application or RTC).
63 pub time_period: TimePeriod,
64}
65
66/// Ambient light level — quantised from the CyberPi light sensor.
67#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
68pub enum BrightnessBand {
69 /// Very low ambient light (night, dark room).
70 Dark,
71 /// Moderate ambient light (indoor daytime, lamp).
72 Dim,
73 /// High ambient light (bright room, direct sunlight).
74 Bright,
75}
76
77/// Ambient sound level — quantised from the CyberPi microphone.
78#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
79pub enum NoiseBand {
80 /// Very low ambient noise (silent room).
81 Quiet,
82 /// Moderate ambient noise (background conversation, music).
83 Moderate,
84 /// High ambient noise (crowd, machinery, shouting).
85 Loud,
86}
87
88/// Nearby presence signature — person or object detection.
89#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
90pub enum PresenceSignature {
91 /// No person detected in sensor range.
92 Absent,
93 /// Person detected at a distance (outer detection zone).
94 Far,
95 /// Person detected in close proximity (inner detection zone).
96 Close,
97}
98
99/// Robot motion context — derived from wheel encoder state.
100#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101pub enum MotionContext {
102 /// Robot is stationary (encoders at rest).
103 Static,
104 /// Robot is moving slowly (below speed threshold).
105 Slow,
106 /// Robot is moving quickly (above speed threshold).
107 Fast,
108}
109
110/// Robot orientation relative to its starting heading (IMU pitch/roll).
111#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
112pub enum Orientation {
113 /// Robot is upright — tilt within acceptable range.
114 Upright,
115 /// Robot is tilted beyond the upright threshold (picked up, on slope).
116 Tilted,
117}
118
119/// Time of day period — set by the host application or RTC.
120#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
121pub enum TimePeriod {
122 /// Daytime hours (context: active, well-lit environments typical).
123 Day,
124 /// Evening hours (context: winding down, lower light typical).
125 Evening,
126 /// Night-time hours (context: quiet, dark environments typical).
127 Night,
128}
129
130impl SensorVocabulary<6> for MbotSensors {
131 fn to_feature_vec(&self) -> [f32; 6] {
132 let b = match self.brightness {
133 BrightnessBand::Dark => 0.0,
134 BrightnessBand::Dim => 0.5,
135 BrightnessBand::Bright => 1.0,
136 };
137 let n = match self.noise {
138 NoiseBand::Quiet => 0.0,
139 NoiseBand::Moderate => 0.5,
140 NoiseBand::Loud => 1.0,
141 };
142 let p = match self.presence {
143 PresenceSignature::Absent => 0.0,
144 PresenceSignature::Far => 0.5,
145 PresenceSignature::Close => 1.0,
146 };
147 let m = match self.motion {
148 MotionContext::Static => 0.0,
149 MotionContext::Slow => 0.5,
150 MotionContext::Fast => 1.0,
151 };
152 let o = match self.orientation {
153 Orientation::Upright => 0.0,
154 Orientation::Tilted => 1.0,
155 };
156 let t = match self.time_period {
157 TimePeriod::Day => 0.0,
158 TimePeriod::Evening => 0.5,
159 TimePeriod::Night => 1.0,
160 };
161 [b, n, p, m, o, t]
162 }
163}
164
165/// Type alias for the canonical mBot2 context key.
166pub type MbotContextKey = ContextKey<MbotSensors, 6>;