#drone #hat #follow

rust_drone_follow

本库旨在提供一种使无人机在头顶上方跟随佩戴帽子(主要是棒球帽)的人的方法

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 次

GPL-3.0-or-later

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);

其他控制器

这个库不包含任何其他控制器,但是您可以自由实现自己的,或者使用以下控制器。

已实现的其他控制器

过滤器

过滤器是确保检测过程中不会出现错误,从而不影响帽子跟踪和跟随的系统部分。它还负责计算帽子相对于无人机的相对速度。

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