zoukankan      html  css  js  c++  java
  • PX4地标识别及控制算法

    使用轮廓提取,多边形抽象,过滤面积较小的图形,然后过滤出四边形,再过滤掉非凸形。得到的四边形里通过简单的聚类方法寻找中心距离最近的一类,其中心的平均值即为地标中心

    创建包

    mkdir -p ~/catkin_ws/src
    cd ~/catkin_ws/src
    catkin_create_pkg landing roscpp rospy std_msgs geometry_msgs mavros
    cd ~/catkin_ws/src/landing
    mkdir msg #存放自定义消息类型
    mkdir scripts #存放python脚本,打开相机
    

    自定义消息类型
    vi src/landing/msg/center.msg

    uint32 width
    uint32 height
    float64 x
    float64 y
    bool iffind
    

    python脚本
    vi src/landing/scripts/track.py

    import rospy
    from std_msgs.msg import Header
    from sensor_msgs.msg import Image
    from landing.msg import center
    import os
    import cv2
    import numpy as np
    import time
    
    center_publish=rospy.Publisher('/center', center, queue_size=1)
    def callback(Image):
        img = np.fromstring(Image.data, np.uint8)
        img = img.reshape(240,320,3)
        track(img, Image.width, Image.height)
    
    def listener():
        rospy.init_node('track')
        rospy.Subscriber('/iris/usb_cam/image_raw', Image, callback)
        rospy.spin()
    
    def track(frame, width, height):
        img = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
        _, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
        contours = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
        rects = []
        centers = []
        for contour in contours[1]:
            if cv2.contourArea(contour) < 100: 
                continue
            epsilon = 0.02 * cv2.arcLength(contour, True)
            approx = cv2.approxPolyDP(contour, epsilon, True)
            if approx.shape[0] == 4 and cv2.isContourConvex(approx):
                rects.append(approx)
                centers.append((approx[0]+approx[1]+approx[2]+approx[3]).squeeze()/4)
    
        center_iter = list(range(len(centers)))
        result = []
        threshold = 20
        while len(center_iter) is not 0:
            j = 1
            resultnow = []
            while j < len(center_iter):
                if np.sum((centers[center_iter[0]] - centers[center_iter[j]])**2) < threshold:
                    resultnow.append(center_iter[j])
                    center_iter.pop(j)
                    j = j-1
                j = j+1
            resultnow.append(center_iter[0])
            center_iter.pop(0)
            if len(result) < len(resultnow):
                result = resultnow
        rects = np.array(rects)[result]
        if len(result) > 2:
                    centers = np.sum(np.array(centers)[result], axis=0).astype(np.double) / len(result)
                    publish(centers, width, height)
        else:
            center_temp = center()
            center_temp.iffind = False
            center_publish.publish(center_temp)
        
    def publish(centers, width, height):
        center_temp = center()
        center_temp.width = width
        center_temp.height = height
        center_temp.x = centers[1]
        center_temp.y = centers[0]
        center_temp.iffind = True
        center_publish.publish(center_temp)
    
    if __name__ == '__main__':
        listener()
    

    飞机控制

    #include <ros/ros.h>
    #include <geometry_msgs/PoseStamped.h>
    #include <geometry_msgs/TwistStamped.h>
    #include <mavros_msgs/CommandBool.h>
    #include <mavros_msgs/SetMode.h>
    #include <mavros_msgs/State.h>
    #include <landing/center.h> // 导入自定义消息类型
    
    mavros_msgs::State current_state;
    void state_cb(const mavros_msgs::State::ConstPtr &msg)
    {
       current_state = *msg;
    }
    
    geometry_msgs::PoseStamped local_position;
    void position_cb(const geometry_msgs::PoseStamped::ConstPtr &msg)
    {
       local_position = *msg;
    }
    
    landing::center landmark;
    void center_cb(const landing::center::ConstPtr &msg)
    {
       landmark = *msg;
    }
    
    int main(int argc, char **argv)
    {
       ros::init(argc, argv, "landing_node");
       ros::NodeHandle nh;
    
       ros::Subscriber state_sub = nh.subscribe<mavros_msgs::State>
                                   ("mavros/state", 10, state_cb);
       ros::Subscriber position_sub = nh.subscribe<geometry_msgs::PoseStamped>
                                      ("mavros/local_position/pose", 10, position_cb);
       ros::Subscriber center_sub = nh.subscribe<landing::center>
                                    ("center", 10, center_cb);
       ros::Publisher local_pos_pub = nh.advertise<geometry_msgs::PoseStamped>
                                      ("mavros/setpoint_position/local", 10);
       ros::Publisher local_vel_pub = nh.advertise<geometry_msgs::TwistStamped>
                                      ("mavros/setpoint_velocity/cmd_vel", 10);
       ros::ServiceClient arming_client = nh.serviceClient<mavros_msgs::CommandBool>
                                          ("mavros/cmd/arming");
       ros::ServiceClient set_mode_client = nh.serviceClient<mavros_msgs::SetMode>
                                            ("mavros/set_mode");
    
       ros::Rate rate(20.0);
    
       while(ros::ok() && current_state.connected)
       {
          ros::spinOnce();
          rate.sleep();
       }
    
       geometry_msgs::PoseStamped pose;//姿态控制
       pose.pose.position.x = 0;
       pose.pose.position.y = 0;
       pose.pose.position.z = 2;
    
       geometry_msgs::TwistStamped vel;//速度控制
    
       for(int i = 100; ros::ok() && i > 0; --i)
       {
          local_pos_pub.publish(pose);
          ros::spinOnce();
          rate.sleep();
       }
    
       mavros_msgs::SetMode offb_set_mode;
       offb_set_mode.request.custom_mode = "OFFBOARD";
    
       mavros_msgs::CommandBool arm_cmd;
       arm_cmd.request.value = true;
    
       ros::Time last_request = ros::Time::now();
    
       //起飞
       while(ros::ok())
       {
          if(current_state.mode != "OFFBOARD" && (ros::Time::now() - last_request > ros::Duration(5.0)))
          {
             if(set_mode_client.call(offb_set_mode) && offb_set_mode.response.mode_sent)
             {
                ROS_INFO("Offboard enabled");
             }
    
             last_request = ros::Time::now();
          }
          else if(!current_state.armed && (ros::Time::now() - last_request > ros::Duration(5.0)))
          {
             if(arming_client.call(arm_cmd) && arm_cmd.response.success)
             {
                ROS_INFO("Vehicle armed");
             }
    
             last_request = ros::Time::now();
          }
          else if(ros::Time::now() - last_request > ros::Duration(5.0))
          {
             break;
          }
    
          local_pos_pub.publish(pose);
    
          ros::spinOnce();
          rate.sleep();
       }
    
       //逛一圈
       last_request = ros::Time::now();
    
       while(ros::ok())
       {
          if(ros::Time::now() - last_request > ros::Duration(5.0))
          {
             break;
          }
    
          vel.twist.linear.x = 1;
          vel.twist.linear.y = 0;
          vel.twist.linear.z = 0;
          local_vel_pub.publish(vel);
          ros::spinOnce();
          rate.sleep();
       }
    
       last_request = ros::Time::now();
    
       while(ros::ok())
       {
          if(ros::Time::now() - last_request > ros::Duration(5.0))
          {
             break;
          }
    
          vel.twist.linear.x = 0;
          vel.twist.linear.y = 1;
          vel.twist.linear.z = 0;
          local_vel_pub.publish(vel);
          ros::spinOnce();
          rate.sleep();
       }
    
       last_request = ros::Time::now();
    
       while(ros::ok())
       {
          if(ros::Time::now() - last_request > ros::Duration(5.0))
          {
             break;
          }
    
          vel.twist.linear.x = -1;
          vel.twist.linear.y = 0;
          vel.twist.linear.z = 0;
          local_vel_pub.publish(vel);
          ros::spinOnce();
          rate.sleep();
       }
    
       last_request = ros::Time::now();
    
       while(ros::ok())
       {
          if(ros::Time::now() - last_request > ros::Duration(5.0))
          {
             break;
          }
    
          vel.twist.linear.x = 0;
          vel.twist.linear.y = -1;
          vel.twist.linear.z = 0;
          local_vel_pub.publish(vel);
          ros::spinOnce();
          rate.sleep();
       }
    
       //控制降落部分
       while(ros::ok())
       {
          //高度低于0.3时转为降落模式
          if(local_position.pose.position.z < 0.3)
          {
             break;
          }
    
          //如果找到地标,控制方向
          if(landmark.iffind)
          {
             //计算两方向err
             double err_x = landmark.height / 2.0 - landmark.x;
             double err_y = landmark.width / 2.0 - landmark.y;
             ROS_INFO_STREAM("state=" << err_x << " " << err_y);
             //速度控制
             vel.twist.linear.x = err_x / 400;
             vel.twist.linear.y = err_y / 400;
    
             //如果位置很正开始降落
             if(err_x < 10 && err_y < 10)
             {
                vel.twist.linear.z = -0.2;
             }
             else
             {
                vel.twist.linear.z = 0;
             }
    
             local_vel_pub.publish(vel);
             ros::spinOnce();
             rate.sleep();
          }
          //如果找不到矩形地标,回到2m高度
          else
          {
             pose.pose.position.x = local_position.pose.position.x;
             pose.pose.position.y = local_position.pose.position.y;
             pose.pose.position.z = 2;
             local_pos_pub.publish(pose);
             ros::spinOnce();
             rate.sleep();
          }
       }
    
       offb_set_mode.request.custom_mode = "AUTO.LAND";
    
       if(set_mode_client.call(offb_set_mode) && offb_set_mode.response.mode_sent)
       {
          ROS_INFO("Offboard enabled");
          last_request = ros::Time::now();
       }
    
       return 0;
    }
    

    vi landing/CMakeLists.txt

    # 按提示增加
    
    ## Generate messages in the 'msg' folder
    # add_message_files(
    #   FILES
    #   Message1.msg
    #   Message2.msg
    # )
    add_message_files(FILES center.msg)
    
    ## Generate added messages and services with any dependencies listed here
    # generate_messages(
    #   DEPENDENCIES
    #   geometry_msgs#   std_msgs
    # )
    generate_messages(DEPENDENCIES std_msgs)
    
    ## Declare a C++ executable
    ## With catkin_make all packages are built within a single CMake context
    ## The recommended prefix ensures that target names across packages don't collide
    # add_executable(${PROJECT_NAME}_node src/landing_node.cpp)
    add_executable(landing_node src/control.cpp)
    
    ## Specify libraries to link a library or executable target against
    # target_link_libraries(${PROJECT_NAME}_node
    #   ${catkin_LIBRARIES}
    # )
    target_link_libraries(landing_node
      ${catkin_LIBRARIES}
    )
    

    vi landing/package.xml

    <!-- 增加两行 -->
    <build_depend>message_generation</build_depend>
    <exec_depend>message_generation</exec_depend>
    

    编译
    catkin build

    运行

    roslaunch px4 mavros_posix_sitl.launch
    python src/landing/scripts/track.py
    rosrun landing landing_node
    

    实际效果
    https://www.bilibili.com/video/BV1RE411M755?from=search&seid=18007439676134041510

    rqt_graph

    rqt_console

  • 相关阅读:
    LeetCode Array Easy 414. Third Maximum Number
    LeetCode Linked List Medium 2. Add Two Numbers
    LeetCode Array Easy 283. Move Zeroes
    LeetCode Array Easy 268. Missing Number
    LeetCode Array Easy 219. Contains Duplicate II
    LeetCode Array Easy 217. Contains Duplicate
    LeetCode Array Easy 189. Rotate Array
    LeetCode Array Easy169. Majority Element
    LeetCode Array Medium 11. Container With Most Water
    LeetCode Array Easy 167. Two Sum II
  • 原文地址:https://www.cnblogs.com/zhangxuechao/p/14953736.html
Copyright © 2011-2022 走看看