use clankers::prelude::*;
const FRAME_TIMEOUT: Duration = Duration::from_secs(10);
#[clankers::node]
async fn main(ctx: RobotContext) -> RobotResult<()> {
let node = RobotNode::new(ctx.node_name().as_str()).await?;
let mut camera = node
.subscribe::<ImageMsg>("/camera/image_raw", QosProfile::sensor_data())
.await?;
let detections_pub = node
.publish::<DetectionArray>("/detections", QosProfile::default())
.await?;
let model_cfg = ctx.model_config("detector")?;
let model_path = ctx.resolve_path(&model_cfg.path);
let mut model = ModelBuilder::from_config(&model_cfg, model_path.clone())?.build()?;
let input_name = model.engine().input_specs()[0].name.clone();
tracing::info!("clankeRS node: {}", ctx.node_name());
tracing::info!("Loaded model {}", model_path.display());
loop {
let frame = match tokio::time::timeout(FRAME_TIMEOUT, camera.next()).await {
Ok(Some(frame)) => frame,
Ok(None) => break,
Err(_) => {
tracing::warn!(
"no frames on /camera/image_raw for {FRAME_TIMEOUT:?}; is a publisher \
running? feed this node with `clankers replay <log.mcap>`"
);
break;
}
};
let tensor = ImageTensor::from_ros_msg(&frame)?
.resize(224, 224)?
.normalize_imagenet()?
.to_nchw()?;
let shape = tensor.nchw_shape();
let view = tensor.as_nchw_view(&shape)?;
let outputs = model.run_named([(input_name.as_str(), view)])?;
let detections = output_to_detections(
&outputs
.first()
.ok_or_else(|| RobotError::Model("model produced no outputs".into()))?
.to_f32_vec()?,
);
if let Some(stats) = model.stats() {
tracing::debug!(
latency_ms = stats.latency_ms(),
clankers_copies = stats.clankers_copies,
"inference"
);
}
detections_pub
.publish(DetectionArray {
stamp_nanos: Timestamp::now().as_nanos(),
frame_id: frame.frame_id.clone(),
detections,
})
.await?;
}
Ok(())
}
fn output_to_detections(output: &[f32]) -> Vec<Detection> {
output
.iter()
.enumerate()
.filter(|(_, &score)| score > 0.1)
.map(|(class_id, &score)| Detection {
class_id: class_id as u32,
class_name: format!("class_{class_id}"),
score,
x: 0.0,
y: 0.0,
width: 1.0,
height: 1.0,
})
.collect()
}