RerunでWMX3のログを漏れなく可視化する
This content is not available in your language yet.
「RerunでWMX3のログを可視化する」の続きです。
前回は位置情報をWindowsアプリのループの速度で取得していました。 そのため、取得頻度にばらつきがあったり、そもそも早くても数ミリ秒レベルでしか取得できないため、実際には不連続なデータになっていました。
今回は、WMX3の軸の位置をサイクルレベルで連続したデータとしてまとめて取得することで、この問題を解消します。 コードは以下です。
実行するには、wmx3-rerun-memlogでcargo runします。
すると、Rerunビューワが起動し、軸0の指令位置をロギングします。
ログの頻度は約500ミリ秒ごとですが、もれなく連続してロギングできます。
コード説明
C側の実装
メモリログ機能を使うため、以下の関数を新しく追加しました。
int start_memlog(int axis);int stop_memlog();int get_memlog(double pos[1000], long long cycle_counter[1000], size_t* pCount);実装の詳細は省略します。WMX3 User Manualにメモリログの使い方は記載されています。
Rust側の実装
main.rsで、メモリログを使用し、一括取得した位置データをRerunで可視化します。
use std::{thread::sleep, time::Duration};
// lib.rsでビルドしたFFIコードを参照use wmx3_rerun::*;
use std::time::Instant;
fn main() -> Result<(), Box<dyn std::error::Error>> { let ret = unsafe { open_wmx() }; println!("open_wmx = {}", ret);
// rerunビューワをスポーン。 let rec = rerun::RecordingStreamBuilder::new("wmx3-rerun").spawn()?;
// あらかじめ波形を登録しておく。 rec.log_static( "axis0/cmdpos0", &rerun::SeriesLines::new() .with_colors([[255, 0, 0]]) .with_names(["cmdpos0"]) .with_widths([2.0]), )?;
let ret = unsafe { start_memlog(0) }; println!("start_memlog = {}", ret);
// 10秒間メモリログで指令位置を取得してプロットに適用する。横軸はサイクルカウンタとする。 let start = Instant::now(); while start.elapsed() < Duration::from_secs(10) { // 一括で1000サイクル分とってくる。実際に取得したサイズはcountで得る。 const N: usize = 1000; let mut pos: [f64; N] = [0.0; N]; let mut cycle_counter: [i64; N] = [0; N]; let mut count: usize = 0; let ret = unsafe { get_memlog(pos.as_mut_ptr(), cycle_counter.as_mut_ptr(), &mut count) }; println!("get_memlog = {} count {}", ret, count);
for i in 0..count { rec.set_time_sequence("cycle", cycle_counter[i]); rec.log("axis0/cmdpos0", &rerun::Scalars::single(pos[i]))?; }
// 直近1000サイクル取得するので、周期が1msなら1000ms分になる。 // 500msならsleepしても漏れなくとれる。 sleep(Duration::from_millis(500)); }
let ret = unsafe { stop_memlog() }; println!("stop_memlog = {}", ret);
let ret = unsafe { close_wmx() }; println!("close_wmx = {}", ret);
Ok(())}メモリログの開始と終了を追記します。
let ret = unsafe { start_memlog(0) }; println!("start_memlog = {}", ret);
// 省略
let ret = unsafe { stop_memlog() }; println!("stop_memlog = {}", ret);最大1000サイクルのデータを一括取得します。
C側にRustのメモリを渡す必要があるので、as_mut_ptr()というメソッドを利用します。
// 一括で1000サイクル分とってくる。実際に取得したサイズはcountで得る。 const N: usize = 1000; let mut pos: [f64; N] = [0.0; N]; let mut cycle_counter: [i64; N] = [0; N]; let mut count: usize = 0; let ret = unsafe { get_memlog(pos.as_mut_ptr(), cycle_counter.as_mut_ptr(), &mut count) }; println!("get_memlog = {} count {}", ret, count);いよいよプロットへの追加です。
count個のデータを取得したので、その回数だけループを回します。
まず、サイクルカウンタを横軸に適用します。set_time_sequenceというメソッドに変わったことに注意してください。
位置データを取得した際に、一緒にサイクルカウンタというデータも取得しました。
サイクルカウンタはWMX3内のサイクル番号を表していて、通信開始から1サイクルごとに+1されるカウンタです。
次に、そのサイクルでの位置をプロットします。
for i in 0..count { rec.set_time_sequence("cycle", cycle_counter[i]); rec.log("axis0/cmdpos0", &rerun::Scalars::single(pos[i]))?; }これは500ms待機するコードです。 メモリログ機能により、最大で1000サイクル、つまり周期1msで1000ms分のデータをもれなく取れるため、 500ms待機しても連続してデータロギングができることがわかります。
// 直近1000サイクル取得するので、周期が1msなら1000ms分になる。 // 500msならsleepしても漏れなくとれる。 sleep(Duration::from_millis(500));実行結果です。予めWOSで往復運転させています。 サイズが2MBと大きいので、リンクにしています。見てもいいよという方はリンクをクリックしてご覧ください。
アニメーション画像を見る。