16 个版本 (5 个重大更新)
0.6.1 | 2020年6月9日 |
---|---|
0.6.0 | 2020年6月1日 |
0.5.2 | 2020年5月31日 |
0.4.0 | 2020年4月29日 |
0.1.3 | 2020年3月24日 |
#330 in 视频
每月下载量 41 次
64KB
1K SLoC
rust_drone_follow
这是一个 Rust 库,旨在能够控制无人机,以便它能够跟随检测到向下拍摄的摄像头中的帽子。(例如,兼容的无人机是:Parrot AR Drone 2.0)
此库包含一些基本的检测器和过滤器,但可以通过它们实现的特性进行扩展。
HatFollower
本库的核心是 HatFollower 结构体,它是三个类型的泛型。它需要一个检测器、一个控制器和一个过滤器。
pub fn new(detector: D, controller: C, filter: F, settings: HatFollowerSettings, stop_channel: Option<Receiver<i32>>) -> HatFollower<D, C, F> {
//...
}
它有一个 run
函数,该函数将启动无人机并开始跟随帽子。
实例化和使用示例
!!! 注意,如果您使用真实的控制器,并且在实例化此结构体时没有传递接收器,或者您没有在单独的线程上运行它,它将无限期地运行 !!!
fn main() {
let mut s = HatFollower::new(
NaiveDetector::new(Hat::new(
LabColor::new(0, 20, -127),
LabColor::new(80, 127, -20),
1200.0
)),
MockController::new("test.mp4", 1280, 720),
NoFilter::new(),
HatFollowerSettings::new(),
None,
);
s.run();
}
设置
您可以通过在开始时给 HatFollower 一个不同的设置结构体来更改设置。有三个预制的设置,但您始终可以创建自己的。
new()
=> 返回具有默认设置的设置: (显示视频,不保存,不绘制)
debug()
=> 返回具有完整调试设置的设置: (显示视频,保存视频,绘制所有)
silent()
=> 返回具有静默设置的设置: (无视频,不保存,不绘制)
pub struct HatFollowerSettings {
/// Radius of circle around the center that is considered to be okay (if the drone is over this
/// circle it tries to stay there, otherwise it tries to get over it).
pub center_threshold: f64,
/// Minimum amount of change in speeds needed to issue a new move command.
pub min_change: f64,
/// Under how many frames(as time measurement) we want the drone to reach the center.
pub frames_to_be_centered: f64,
/// Sets whether the program should save the video.
pub save_to_file: Option<String>,
/// Sets whether the program should save commands in a file denoting the frame
pub save_commands: Option<String>,
/// Sets whether the program should show the image real-time.
pub show_video: bool,
/// Sets whether the program should draw the detection markers on the video.
pub draw_detection: bool,
/// Sets whether the program should draw the filter markers on the video.
pub draw_filter: bool,
/// Sets whether the program should draw a circle marking the center_threshold.
pub draw_center: bool,
/// Experimental feature, tries to counteract the speed of the hat. Might not work well.
pub counteract_velocity: bool,
}
检测器
检测器是系统的一部分,负责处理视频帧,检测其上的帽子(如果有的话),并保存其坐标,将其转换为笛卡尔坐标系,其中视频帧的中心是其中心点。它还计算帽子面向的角度。(通常与棒球帽一起使用)
pub trait Detector {
/// Should return the position of the detected object in the descartes coordinate system.
fn get_detected_position(&self) -> Option<GeometricPoint>;
/// Should return the angle that the object turning towards.
fn get_detected_angle(&self) -> Option<f64>;
/// Should return the certainty of the detection, mostly the certainty of the angle detection.
fn get_detection_certainty(&self) -> f64;
/// Should recalculate it's values based on a new image given to it.
fn detect_new_position(&mut self, img: &Mat, old_pos: Option<Point>, p_c: &PointConverter);
/// Should display visually some parts of the detection. (optional)
fn draw_on_image(&self, m_d: &mut MarkerDrawer);
}
NaiveDetector
此检测器包含在库中。它需要一个 Hat 结构体,该结构体编码三个属性
-
在 Lab 颜色空间中接受值最低的颜色
-
在 Lab 颜色空间中接受值最高的颜色
-
帽子的平均大小。
示例(寻找深蓝色棒球帽)
let hat = Hat::new(
LabColor::new(0, 20, -127),
LabColor::new(80, 127, -20),
1200.0
);
如果我们有这个结构,我们可以简单地实例化我们的NaiveDetector
let naive_detector = NaiveDetector::new(hat);
我们可以通过调用它的detect_new_position
方法在新的图像上检测到帽子。
这个检测器将始终忽略之前的位子,并且只使用来自新视频帧的信息。
控制器
控制器是系统的一部分,负责处理无人机和帽子跟随者之间的通信。它还提供了有关无人机某些属性的重要信息,例如视频分辨率和速度乘数(即无人机移动给定距离的速度)。
pub trait Controller {
/// Should handle connecting to the drone.
fn init(&mut self);
/// Should handle disconnecting from the drone.
fn shutdown(&mut self);
/// Should make the drone take off and assume the correct height.
fn takeoff(&mut self);
/// Should make the drone land.
fn land(&mut self);
/// Negative values ([-1.0, 0.0)) mean going towards the first direction, positive values
/// ((0.0, 1.0])) mean going towards the second direction.
fn move_all(&mut self, left_right: f64, back_front: f64, down_up: f64, turn_left_right: f64);
/// Should halt all movement
fn stop(&mut self);
/// Should return the video's height in pixels
fn get_video_height(&self) -> usize;
/// Should return the video's width in pixels
fn get_video_width(&self) -> usize;
/// Should return a link to an external resource that OpenCV can read
fn get_opencv_url(&self) -> String;
/// Conversion rate between pixels/dt and drone speed which is in (-1.0, 1.0), where dt is the
/// time difference between frames
fn get_kv(&self) -> f64;
/// Conversion rate between da/dt and drone turn speed which is in (-1.0, 1.0), where dt is the
/// time difference between frames, and da is the angle difference between frames.
fn get_ka(&self) -> f64;
}
MockController
这个库只提供一个MockController,它返回一个视频文件的链接,帽子跟随者将读取该文件,并忽略所有发送给它的命令。这对于测试预录制的视频中的检测非常有用。
视频将由OpenCV读取,因此MockController也将支持OpenCV支持的所有格式。
实例化时,您必须提供视频文件的路径字符串和视频分辨率。
let mock_controller = MockController::new("test.mp4", 1280, 720);
其他控制器
这个库不包含任何其他控制器,但是您可以自由实现自己的,或者使用以下控制器。
已实现的其他控制器
- Parrot AR Drone 2.0 (VaranTavers) (二进制): parrot_hat_follow
过滤器
过滤器是确保检测过程中不会出现错误,从而不影响帽子跟踪和跟随的系统部分。它还负责计算帽子相对于无人机的相对速度。
pub trait Filter {
/// Updates the estimation based on new information.
fn update_estimation(&mut self, point: Option<GeometricPoint>, angle: Option<f64>, cert: f64);
/// Returns the estimated position of the hat.
fn get_estimated_position(&self) -> Option<GeometricPoint>;
/// Returns the estimated angle of the hat.
fn get_estimated_angle(&self) -> f64;
/// Returns the estimated horizontal speed of the hat.
fn get_estimated_vx(&self) -> f64;
/// Returns the estimated vertical speed of the hat.
fn get_estimated_vy(&self) -> f64;
/// Returns the certainty of the estimation.
fn get_estimation_certainty(&self) -> f64;
/// Returns the certainty of the estimation.
fn draw_on_image(&self, m_d: &mut MarkerDrawer);
}
NoFilter
这个库包含一个过滤器,它不进行过滤,仅从最后一点和当前点之间的差异计算速度。
示例
let no_filter = NoFilter::new();
MemoryFilter
这与NoFilter相同,只是在点不再被检测到(离开了帧)的情况下,它保留其最后已知的位置(这样无人机将尝试移动到该位置),直到给定的帧数。
示例
let memory_filter = MemoryFilter::new(100);
KalmanFilter
这部分尚未实现。请稍后再查看。
使用自定义检测器/过滤器在图像上绘制
如您所注意到的,绘制图像是通过使用MarkerDrawer结构来完成的,该结构将其绘图命令保存自身,然后在图片上绘制它们。以下函数是可以使用的
impl MarkerDrawer {
// If you need to use this in your own code, you can instantiate one with new()
pub fn new() -> MarkerDrawer { /*...*/ }
// Draws a small circle with radius of 5 and thickness 2, when draw on image is called
pub fn point(&mut self, point: &GeometricPoint, color: Scalar) { /*...*/ }
// Draws a line with thickness 1, between the two given points when draw on image is called
pub fn line(&mut self, point1: &GeometricPoint, point2: &GeometricPoint, color: Scalar) { /*...*/ }
// Draws a small circle with the given radius and thickness 1, when draw on image is called
pub fn circle(&mut self, point: &GeometricPoint, radius: i32, color: Scalar) { /*...*/ }
// Draws the saved Markers on the given image, requires a PointConverter.
pub fn draw_on_image(&mut self, img: &mut Mat, p_c: &PointConverter) { /*...*/ }
}
其他有用的实用工具
VideoExporter
可用于将帧导出到多个视频文件。
用法
fn main() {
let mut video_exporter = VideoExporter::new();
let mut video = VideoCapture::new_from_file_with_backend("video_file.mp4", CAP_ANY).unwrap();
let mut img = Mat::zeros_size(Size::new(1,1), CV_8U).unwrap().to_mat().unwrap();
loop {
match video.read(&mut img) {
Ok(true) => {
// Draw something to the frame
// ...
video_exporter.save_frame("test.mp4", &img);
}
_ => {
break;
}
}
}
}
TextExporter
可用于同时保存多个文件中的文本。
用法
fn main() {
let mut text_exporter = TextExporter::new();
let mut video = VideoCapture::new_from_file_with_backend("video_file.mp4", CAP_ANY).unwrap();
let mut img = Mat::zeros_size(Size::new(1,1), CV_8U).unwrap().to_mat().unwrap();
loop {
match video.read(&mut img) {
Ok(true) => {
let data = 42;
// Do some calculations
// ...
text_exporter.save_frame("test.mp4", format!("{}", data));
}
_ => {
break;
}
}
}
}
HatFileReader
读取以下格式的文件
video_file_name
l1 a1 b1
l2 a2 b2
hat_size
其中l1,l2的范围为0 - 100,a1,a2,b1,b2的范围为-127 - 127且为整数,hat_size为双精度浮点数,video_file_name为包含视频文件路径的字符串。
您可以使用这些结果将其输入MockController或NaiveDetector。
此之后的任何其他行都将不被读取。
用法
let (filename, hat) = hat_file_reader::read_file("kek.hat");
示例文件
./kek.mp4
0 20 -127
80 127 -20
15200.0
PointConverter
将点从OpenCV转换为具有O在图片中心的笛卡尔坐标系中的点。
实例化
let p_c = PointConverter::new(640, 368);
之后,您可以使用convert_from_image_coords
将OpenCV Point转换为GeometricPoint(用于计算),并可以使用convert_to_image_coords
将GeometricPoint转换为OpenCV点。
依赖关系
~2.5MB
~25K SLoC