mirror of
https://github.com/CopterExpress/clover.git
synced 2026-06-03 08:29:32 +00:00
Compare commits
20 Commits
aruco_mapp
...
md-indent-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab3f5e6c69 | ||
|
|
96cc0c7ad9 | ||
|
|
997484cd1f | ||
|
|
48b24a5fef | ||
|
|
2ae5ffe09f | ||
|
|
da29beda47 | ||
|
|
0303e645b7 | ||
|
|
979c863033 | ||
|
|
46b8390c03 | ||
|
|
e5df1cd1a0 | ||
|
|
32c04ef58d | ||
|
|
596fa9aecf | ||
|
|
f883825def | ||
|
|
d65df5d1ba | ||
|
|
a183be2708 | ||
|
|
9c9ac3150d | ||
|
|
82649cbe20 | ||
|
|
b542851b24 | ||
|
|
65d359b5c2 | ||
|
|
1b6f38f8cf |
54
README.md
54
README.md
@@ -10,7 +10,7 @@ Copter Express has implemented a large number of different autonomous drone proj
|
|||||||
|
|
||||||
Use it to learn how to assemble, configure, pilot and program autonomous CLEVER drone.
|
Use it to learn how to assemble, configure, pilot and program autonomous CLEVER drone.
|
||||||
|
|
||||||
## Preconfigured RPi 3 image
|
## Raspberry Pi image
|
||||||
|
|
||||||
**Preconfigured image for Raspberry Pi 3 with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clever/releases).**
|
**Preconfigured image for Raspberry Pi 3 with installed and configured software, ready to fly, is available [in the Releases section](https://github.com/CopterExpress/clever/releases).**
|
||||||
|
|
||||||
@@ -30,49 +30,59 @@ API description (in Russian) for autonomous flights is available [on GitBook](ht
|
|||||||
|
|
||||||
## Manual installation
|
## Manual installation
|
||||||
|
|
||||||
Install ROS Kinetic according to the [documentation](http://wiki.ros.org/kinetic/Installation).
|
Install ROS Kinetic according to the [documentation](http://wiki.ros.org/kinetic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
||||||
|
|
||||||
Clone repo to directory `/home/pi/catkin_ws/src/clever`:
|
Clone this repo to directory `~/catkin_ws/src/clever`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/catkin_ws/src
|
cd ~/catkin_ws/src
|
||||||
git clone https://github.com/CopterExpress/clever.git clever
|
git clone https://github.com/CopterExpress/clever.git clever
|
||||||
```
|
```
|
||||||
|
|
||||||
Build ROS packages:
|
All the required ROS packages (including `mavros` and `opencv`) can be installed using `rosdep`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/catkin_ws/
|
||||||
|
rosdep install -y --from-paths src --ignore-src
|
||||||
|
```
|
||||||
|
|
||||||
|
Build ROS packages (on memory constrained platforms you might be going to need to use `-j1` key):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/catkin_ws
|
cd ~/catkin_ws
|
||||||
catkin_make -j1
|
catkin_make -j1
|
||||||
```
|
```
|
||||||
|
|
||||||
Enable systemd service `roscore` (if not enabled):
|
To complete `mavros` install you'll need to install `geographiclib` datasets:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl enable /home/pi/catkin_ws/src/clever/deploy/roscore.service
|
curl https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | sudo bash
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
Enable systemd service `roscore` (if not running):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable /home/<username>/catkin_ws/src/clever/deploy/roscore.service
|
||||||
sudo systemctl start roscore
|
sudo systemctl start roscore
|
||||||
```
|
```
|
||||||
|
|
||||||
Enable systemd service `clever`:
|
To start connection to SITL, use:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl enable /home/pi/catkin_ws/src/clever/deploy/clever.service
|
roslaunch clever sitl.launch
|
||||||
sudo systemctl start clever
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dependencies
|
To start connection to the flight controller, use:
|
||||||
|
|
||||||
[ROS Kinetic](http://wiki.ros.org/kinetic).
|
```bash
|
||||||
|
roslaunch clever clever.launch
|
||||||
|
```
|
||||||
|
|
||||||
Necessary ROS packages:
|
Also, you can enable and start the systemd service:
|
||||||
|
|
||||||
* `opencv3`
|
```bash
|
||||||
* `mavros`
|
sudo systemctl enable /home/<username>/catkin_ws/src/clever/deploy/clever.service
|
||||||
* `rosbridge_suite`
|
sudo systemctl start clever
|
||||||
* `web_video_server`
|
```
|
||||||
* `cv_camera`
|
|
||||||
* `nodelet`
|
|
||||||
* `dynamic_reconfigure`
|
|
||||||
* `bondcpp`, branch `master`
|
|
||||||
* `roslint`
|
|
||||||
* `rosserial`
|
|
||||||
|
|||||||
@@ -219,4 +219,5 @@ if (CATKIN_ENABLE_TESTING)
|
|||||||
add_rostest(test/test_parser_pass.test)
|
add_rostest(test/test_parser_pass.test)
|
||||||
add_rostest(test/test_parser_empty_map.test)
|
add_rostest(test/test_parser_empty_map.test)
|
||||||
add_rostest(test/test_node_failure.test)
|
add_rostest(test/test_node_failure.test)
|
||||||
|
add_rostest(test/largemap.test)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ See examples in [`map`](map/) directory.
|
|||||||
Command for running tests:
|
Command for running tests:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
rostest aruco_pose basic.test
|
catkin_make run_tests && catkin_test_results
|
||||||
```
|
```
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
<depend>rostest</depend>
|
<depend>rostest</depend>
|
||||||
|
|
||||||
<test_depend>image_publisher</test_depend>
|
<test_depend>image_publisher</test_depend>
|
||||||
|
<test_depend>ros_pytest</test_depend>
|
||||||
|
|
||||||
<!-- The export tag contains other, unspecified, tags -->
|
<!-- The export tag contains other, unspecified, tags -->
|
||||||
<export>
|
<export>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <algorithm>
|
||||||
#include <ros/ros.h>
|
#include <ros/ros.h>
|
||||||
#include <nodelet/nodelet.h>
|
#include <nodelet/nodelet.h>
|
||||||
#include <pluginlib/class_list_macros.h>
|
#include <pluginlib/class_list_macros.h>
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
#include <tf2_ros/buffer.h>
|
#include <tf2_ros/buffer.h>
|
||||||
#include <tf2_ros/transform_listener.h>
|
#include <tf2_ros/transform_listener.h>
|
||||||
#include <tf2_ros/transform_broadcaster.h>
|
#include <tf2_ros/transform_broadcaster.h>
|
||||||
|
#include <tf2_ros/static_transform_broadcaster.h>
|
||||||
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
|
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
|
||||||
#include <message_filters/subscriber.h>
|
#include <message_filters/subscriber.h>
|
||||||
#include <message_filters/synchronizer.h>
|
#include <message_filters/synchronizer.h>
|
||||||
@@ -36,7 +38,6 @@
|
|||||||
#include <sensor_msgs/Image.h>
|
#include <sensor_msgs/Image.h>
|
||||||
#include <visualization_msgs/Marker.h>
|
#include <visualization_msgs/Marker.h>
|
||||||
#include <visualization_msgs/MarkerArray.h>
|
#include <visualization_msgs/MarkerArray.h>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include <aruco_pose/MarkerArray.h>
|
#include <aruco_pose/MarkerArray.h>
|
||||||
#include <aruco_pose/Marker.h>
|
#include <aruco_pose/Marker.h>
|
||||||
@@ -68,11 +69,13 @@ private:
|
|||||||
Mat camera_matrix_, dist_coeffs_;
|
Mat camera_matrix_, dist_coeffs_;
|
||||||
geometry_msgs::TransformStamped transform_;
|
geometry_msgs::TransformStamped transform_;
|
||||||
geometry_msgs::PoseWithCovarianceStamped pose_;
|
geometry_msgs::PoseWithCovarianceStamped pose_;
|
||||||
|
vector<geometry_msgs::TransformStamped> markers_transforms_;
|
||||||
tf2_ros::TransformBroadcaster br_;
|
tf2_ros::TransformBroadcaster br_;
|
||||||
|
tf2_ros::StaticTransformBroadcaster static_br_;
|
||||||
tf2_ros::Buffer tf_buffer_;
|
tf2_ros::Buffer tf_buffer_;
|
||||||
tf2_ros::TransformListener tf_listener_{tf_buffer_};
|
tf2_ros::TransformListener tf_listener_{tf_buffer_};
|
||||||
visualization_msgs::MarkerArray vis_array_;
|
visualization_msgs::MarkerArray vis_array_;
|
||||||
std::string known_tilt_;
|
std::string known_tilt_, map_, markers_frame_, markers_parent_frame_;
|
||||||
int image_width_, image_height_, image_margin_;
|
int image_width_, image_height_, image_margin_;
|
||||||
bool auto_flip_;
|
bool auto_flip_;
|
||||||
|
|
||||||
@@ -101,6 +104,8 @@ public:
|
|||||||
nh_priv_.param("image_width", image_width_, 2000);
|
nh_priv_.param("image_width", image_width_, 2000);
|
||||||
nh_priv_.param("image_height", image_height_, 2000);
|
nh_priv_.param("image_height", image_height_, 2000);
|
||||||
nh_priv_.param("image_margin", image_margin_, 200);
|
nh_priv_.param("image_margin", image_margin_, 200);
|
||||||
|
nh_priv_.param<std::string>("markers/frame_id", markers_parent_frame_, transform_.child_frame_id);
|
||||||
|
nh_priv_.param<std::string>("markers/child_frame_id_prefix", markers_frame_, "");
|
||||||
|
|
||||||
// createStripLine();
|
// createStripLine();
|
||||||
|
|
||||||
@@ -125,6 +130,7 @@ public:
|
|||||||
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
|
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
|
||||||
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
|
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
|
||||||
|
|
||||||
|
publishMarkersFrames();
|
||||||
publishMapImage();
|
publishMapImage();
|
||||||
vis_markers_pub_.publish(vis_array_);
|
vis_markers_pub_.publish(vis_array_);
|
||||||
|
|
||||||
@@ -422,6 +428,15 @@ publish_debug:
|
|||||||
board_->ids.push_back(id);
|
board_->ids.push_back(id);
|
||||||
board_->objPoints.push_back(obj_points);
|
board_->objPoints.push_back(obj_points);
|
||||||
|
|
||||||
|
// Add marker's static transform
|
||||||
|
if (!markers_frame_.empty()) {
|
||||||
|
geometry_msgs::TransformStamped marker_transform;
|
||||||
|
marker_transform.header.frame_id = markers_parent_frame_;
|
||||||
|
marker_transform.child_frame_id = markers_frame_ + std::to_string(id);
|
||||||
|
tf::transformTFToMsg(transform, marker_transform.transform);
|
||||||
|
markers_transforms_.push_back(marker_transform);
|
||||||
|
}
|
||||||
|
|
||||||
// Add visualization marker
|
// Add visualization marker
|
||||||
visualization_msgs::Marker marker;
|
visualization_msgs::Marker marker;
|
||||||
marker.header.frame_id = transform_.child_frame_id;
|
marker.header.frame_id = transform_.child_frame_id;
|
||||||
@@ -452,6 +467,13 @@ publish_debug:
|
|||||||
// vis_array_.markers.at(0).points.push_back(p);
|
// vis_array_.markers.at(0).points.push_back(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void publishMarkersFrames()
|
||||||
|
{
|
||||||
|
if (!markers_transforms_.empty()) {
|
||||||
|
static_br_.sendTransform(markers_transforms_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void publishMapImage()
|
void publishMapImage()
|
||||||
{
|
{
|
||||||
cv::Size size(image_width_, image_height_);
|
cv::Size size(image_width_, image_height_);
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginS
|
|||||||
// dst_sz.width = dst_sz.height = std::min(dst_sz.width, dst_sz.height); //marker should be square
|
// dst_sz.width = dst_sz.height = std::min(dst_sz.width, dst_sz.height); //marker should be square
|
||||||
double diag = std::round(std::hypot(dst_sz.width, dst_sz.height));
|
double diag = std::round(std::hypot(dst_sz.width, dst_sz.height));
|
||||||
int side = std::round(diag / std::sqrt(2));
|
int side = std::round(diag / std::sqrt(2));
|
||||||
|
side = std::max(side, 10);
|
||||||
|
|
||||||
dictionary.drawMarker(_board->ids[m], side, marker, borderBits);
|
dictionary.drawMarker(_board->ids[m], side, marker, borderBits);
|
||||||
|
|
||||||
|
|||||||
@@ -1,101 +1,131 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
import json
|
|
||||||
import rospy
|
import rospy
|
||||||
import rostest
|
import pytest
|
||||||
|
|
||||||
|
import tf2_ros
|
||||||
|
import tf2_geometry_msgs
|
||||||
from geometry_msgs.msg import PoseWithCovarianceStamped
|
from geometry_msgs.msg import PoseWithCovarianceStamped
|
||||||
from sensor_msgs.msg import Image
|
from sensor_msgs.msg import Image
|
||||||
from aruco_pose.msg import MarkerArray
|
from aruco_pose.msg import MarkerArray
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
||||||
|
|
||||||
|
|
||||||
class TestArucoPose(unittest.TestCase):
|
@pytest.fixture
|
||||||
def setUp(self):
|
def node():
|
||||||
rospy.init_node('test_aruco_detect', anonymous=True)
|
return rospy.init_node('aruco_pose_test', anonymous=True)
|
||||||
|
|
||||||
def test_markers(self):
|
@pytest.fixture
|
||||||
markers = rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=5)
|
def tf_buffer():
|
||||||
self.assertEqual(len(markers.markers), 4)
|
buf = tf2_ros.Buffer()
|
||||||
self.assertEqual(markers.header.frame_id, 'main_camera_optical')
|
tf2_ros.TransformListener(buf)
|
||||||
|
return buf
|
||||||
|
|
||||||
self.assertEqual(markers.markers[0].id, 2)
|
def approx(expected):
|
||||||
self.assertAlmostEqual(markers.markers[0].length, 0.33, places=4)
|
return pytest.approx(expected, abs=1e-4) # compare floats more roughly
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.position.x, 0.36706567854, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.position.y, 0.290484516644, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.position.z, 2.18787602301, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.orientation.x, 0.993997406299, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.orientation.y, -0.00532003481626, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.orientation.z, -0.107390951553, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.orientation.w, 0.0201999263402, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].c1.x, 415.557739258, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].c1.y, 335.557739258, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].c2.x, 509.442260742, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].c2.y, 335.557739258, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].c3.x, 509.442260742, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].c3.y, 429.442260742, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].c4.x, 415.557739258, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[0].c4.y, 429.442260742, places=4)
|
|
||||||
|
|
||||||
self.assertEqual(markers.markers[3].id, 3)
|
def test_markers(node):
|
||||||
self.assertAlmostEqual(markers.markers[3].length, 0.1, places=4)
|
markers = rospy.wait_for_message('aruco_detect/markers', MarkerArray, timeout=5)
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.position.x, -0.1805169666, places=4)
|
assert len(markers.markers) == 4
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.position.y, -0.200697302327, places=4)
|
assert markers.header.frame_id == 'main_camera_optical'
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.position.z, 0.585767514823, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.orientation.x, -0.961738074009, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.orientation.y, -0.0375180244707, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.orientation.z, -0.0115387773672, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.orientation.w, 0.271144115664, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].c1.x, 129.557723999, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].c1.y, 49.557723999, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].c2.x, 223.442276001, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].c2.y, 49.557723999, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].c3.x, 223.442276001, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].c3.y, 143.442276001, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].c4.x, 129.557723999, places=4)
|
|
||||||
self.assertAlmostEqual(markers.markers[3].c4.y, 143.442276001, places=4)
|
|
||||||
|
|
||||||
self.assertEqual(markers.markers[1].id, 1)
|
assert markers.markers[0].id == 2
|
||||||
self.assertAlmostEqual(markers.markers[1].length, 0.33, places=4)
|
assert markers.markers[0].length == approx(0.33)
|
||||||
self.assertEqual(markers.markers[2].id, 4)
|
assert markers.markers[0].pose.position.x == approx(0.36706567854)
|
||||||
self.assertAlmostEqual(markers.markers[2].length, 0.33, places=4)
|
assert markers.markers[0].pose.position.y == approx(0.290484516644)
|
||||||
|
assert markers.markers[0].pose.position.z == approx(2.18787602301)
|
||||||
|
assert markers.markers[0].pose.orientation.x == approx(0.993997406299)
|
||||||
|
assert markers.markers[0].pose.orientation.y == approx(-0.00532003481626)
|
||||||
|
assert markers.markers[0].pose.orientation.z == approx(-0.107390951553)
|
||||||
|
assert markers.markers[0].pose.orientation.w == approx(0.0201999263402)
|
||||||
|
assert markers.markers[0].c1.x == approx(415.557739258)
|
||||||
|
assert markers.markers[0].c1.y == approx(335.557739258)
|
||||||
|
assert markers.markers[0].c2.x == approx(509.442260742)
|
||||||
|
assert markers.markers[0].c2.y == approx(335.557739258)
|
||||||
|
assert markers.markers[0].c3.x == approx(509.442260742)
|
||||||
|
assert markers.markers[0].c3.y == approx(429.442260742)
|
||||||
|
assert markers.markers[0].c4.x == approx(415.557739258)
|
||||||
|
assert markers.markers[0].c4.y == approx(429.442260742)
|
||||||
|
|
||||||
def test_visualization(self):
|
assert markers.markers[3].id == 3
|
||||||
vis = rospy.wait_for_message('aruco_detect/visualization', VisMarkerArray, timeout=5)
|
assert markers.markers[3].length == approx(0.1)
|
||||||
self.assertEqual(len(vis.markers), 9)
|
assert markers.markers[3].pose.position.x == approx(-0.1805169666)
|
||||||
|
assert markers.markers[3].pose.position.y == approx(-0.200697302327)
|
||||||
|
assert markers.markers[3].pose.position.z == approx(0.585767514823)
|
||||||
|
assert markers.markers[3].pose.orientation.x == approx(-0.961738074009)
|
||||||
|
assert markers.markers[3].pose.orientation.y == approx(-0.0375180244707)
|
||||||
|
assert markers.markers[3].pose.orientation.z == approx(-0.0115387773672)
|
||||||
|
assert markers.markers[3].pose.orientation.w == approx(0.271144115664)
|
||||||
|
assert markers.markers[3].c1.x == approx(129.557723999)
|
||||||
|
assert markers.markers[3].c1.y == approx(49.557723999)
|
||||||
|
assert markers.markers[3].c2.x == approx(223.442276001)
|
||||||
|
assert markers.markers[3].c2.y == approx(49.557723999)
|
||||||
|
assert markers.markers[3].c3.x == approx(223.442276001)
|
||||||
|
assert markers.markers[3].c3.y == approx(143.442276001)
|
||||||
|
assert markers.markers[3].c4.x == approx(129.557723999)
|
||||||
|
assert markers.markers[3].c4.y == approx(143.442276001)
|
||||||
|
|
||||||
def test_debug(self):
|
assert markers.markers[1].id == 1
|
||||||
img = rospy.wait_for_message('aruco_detect/debug', Image, timeout=5)
|
assert markers.markers[1].length == approx(0.33)
|
||||||
self.assertEqual(img.width, 640)
|
assert markers.markers[2].id == 4
|
||||||
self.assertEqual(img.height, 480)
|
assert markers.markers[2].length == approx(0.33)
|
||||||
self.assertEqual(img.header.frame_id, 'main_camera_optical')
|
|
||||||
|
|
||||||
def test_map(self):
|
def test_markers_frames(node, tf_buffer):
|
||||||
pose = rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=5)
|
marker_2 = tf_buffer.lookup_transform('main_camera_optical', 'aruco_2', rospy.Time(), rospy.Duration(5))
|
||||||
self.assertEqual(pose.header.frame_id, 'main_camera_optical')
|
assert marker_2.transform.translation.x == approx(0.36706567854)
|
||||||
self.assertAlmostEqual(pose.pose.pose.position.x, -0.629167753342, places=4)
|
assert marker_2.transform.translation.y == approx(0.290484516644)
|
||||||
self.assertAlmostEqual(pose.pose.pose.position.y, 0.293822650809, places=4)
|
assert marker_2.transform.translation.z == approx(2.18787602301)
|
||||||
self.assertAlmostEqual(pose.pose.pose.position.z, 2.12641343155, places=4)
|
assert marker_2.transform.rotation.x == approx(0.993997406299)
|
||||||
self.assertAlmostEqual(pose.pose.pose.orientation.x, -0.998383794799, places=4)
|
assert marker_2.transform.rotation.y == approx(-0.00532003481626)
|
||||||
self.assertAlmostEqual(pose.pose.pose.orientation.y, -5.20919098575e-06, places=4)
|
assert marker_2.transform.rotation.z == approx(-0.107390951553)
|
||||||
self.assertAlmostEqual(pose.pose.pose.orientation.z, -0.0300861070302, places=4)
|
assert marker_2.transform.rotation.w == approx(0.0201999263402)
|
||||||
self.assertAlmostEqual(pose.pose.pose.orientation.w, 0.0482143590507, places=4)
|
|
||||||
|
|
||||||
def test_map_image(self):
|
def test_map_markers_frames(node, tf_buffer):
|
||||||
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
stamp = rospy.get_rostime()
|
||||||
self.assertEqual(img.width, 2000)
|
timeout = rospy.Duration(5)
|
||||||
self.assertEqual(img.height, 2000)
|
|
||||||
self.assertEqual(img.encoding, 'mono8')
|
|
||||||
|
|
||||||
def test_map_visualization(self):
|
marker_1 = tf_buffer.lookup_transform('aruco_map', 'aruco_map_1', stamp, timeout)
|
||||||
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
assert marker_1.transform.translation.x == approx(0)
|
||||||
|
assert marker_1.transform.translation.y == approx(0)
|
||||||
|
assert marker_1.transform.translation.z == approx(0)
|
||||||
|
|
||||||
def test_map_debug(self):
|
marker_4 = tf_buffer.lookup_transform('aruco_map', 'aruco_map_4', stamp, timeout)
|
||||||
img = rospy.wait_for_message('aruco_map/debug', Image, timeout=5)
|
assert marker_4.transform.translation.x == approx(1)
|
||||||
|
assert marker_4.transform.translation.y == approx(1)
|
||||||
|
assert marker_4.transform.translation.z == approx(0)
|
||||||
|
|
||||||
# def test_transforms(self):
|
marker_12 = tf_buffer.lookup_transform('aruco_map', 'aruco_map_12', stamp, timeout)
|
||||||
# pass
|
assert marker_12.transform.translation.x == approx(0.2)
|
||||||
|
assert marker_12.transform.translation.y == approx(0.5)
|
||||||
|
assert marker_12.transform.translation.z == approx(0)
|
||||||
|
|
||||||
|
def test_visualization(node):
|
||||||
|
vis = rospy.wait_for_message('aruco_detect/visualization', VisMarkerArray, timeout=5)
|
||||||
|
assert len(vis.markers) == 9
|
||||||
|
|
||||||
rostest.rosrun('aruco_pose', 'test_aruco_detect', TestArucoPose, sys.argv)
|
def test_debug(node):
|
||||||
|
img = rospy.wait_for_message('aruco_detect/debug', Image, timeout=5)
|
||||||
|
assert img.width == 640
|
||||||
|
assert img.height == 480
|
||||||
|
assert img.header.frame_id == 'main_camera_optical'
|
||||||
|
|
||||||
|
def test_map(node):
|
||||||
|
pose = rospy.wait_for_message('aruco_map/pose', PoseWithCovarianceStamped, timeout=5)
|
||||||
|
assert pose.header.frame_id == 'main_camera_optical'
|
||||||
|
assert pose.pose.pose.position.x == approx(-0.629167753342)
|
||||||
|
assert pose.pose.pose.position.y == approx(0.293822650809)
|
||||||
|
assert pose.pose.pose.position.z == approx(2.12641343155)
|
||||||
|
assert pose.pose.pose.orientation.x == approx(-0.998383794799)
|
||||||
|
assert pose.pose.pose.orientation.y == approx(-5.20919098575e-06)
|
||||||
|
assert pose.pose.pose.orientation.z == approx(-0.0300861070302)
|
||||||
|
assert pose.pose.pose.orientation.w == approx(0.0482143590507)
|
||||||
|
|
||||||
|
def test_map_image(node):
|
||||||
|
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
||||||
|
assert img.width == 2000
|
||||||
|
assert img.height == 2000
|
||||||
|
assert img.encoding == 'mono8'
|
||||||
|
|
||||||
|
def test_map_visualization(node):
|
||||||
|
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
||||||
|
|
||||||
|
def test_map_debug(node):
|
||||||
|
img = rospy.wait_for_message('aruco_map/debug', Image, timeout=5)
|
||||||
|
|||||||
@@ -5,23 +5,27 @@
|
|||||||
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
|
<param name="camera_info_url" value="file://$(find aruco_pose)/test/camera_info.yaml" />
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager"/>
|
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
|
||||||
|
|
||||||
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect" args="load aruco_pose/aruco_detect nodelet_manager">
|
<node pkg="nodelet" clear_params="true" type="nodelet" name="aruco_detect" args="load aruco_pose/aruco_detect nodelet_manager" required="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<param name="length" value="0.33"/>
|
<param name="length" value="0.33"/>
|
||||||
<param name="length_override/3" value="0.1"/>
|
<param name="length_override/3" value="0.1"/>
|
||||||
<param name="estimate_poses" value="true"/>
|
<param name="estimate_poses" value="true"/>
|
||||||
|
<param name="send_tf" value="true"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true">
|
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true" required="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<remap from="markers" to="aruco_detect/markers"/>
|
<remap from="markers" to="aruco_detect/markers"/>
|
||||||
<param name="type" value="map"/>
|
<param name="type" value="map"/>
|
||||||
<param name="map" value="$(find aruco_pose)/test/basic.txt"/>
|
<param name="map" value="$(find aruco_pose)/test/basic.txt"/>
|
||||||
|
<param name="markers/frame_id" value="aruco_map"/>
|
||||||
|
<param name="markers/child_frame_id_prefix" value="aruco_map_"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<test test-name="test_aruco_pose" pkg="aruco_pose" type="basic.py"/>
|
<param name="test_module" value="$(find aruco_pose)/test/basic.py"/>
|
||||||
|
<test test-name="aruco_pose_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
26
aruco_pose/test/largemap.py
Executable file
26
aruco_pose/test/largemap.py
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
import json
|
||||||
|
import rospy
|
||||||
|
import rostest
|
||||||
|
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
||||||
|
|
||||||
|
|
||||||
|
class TestArucoPose(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
rospy.init_node('test_aruco_largemap', anonymous=True)
|
||||||
|
|
||||||
|
def test_map_image(self):
|
||||||
|
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
||||||
|
self.assertEqual(img.width, 2000)
|
||||||
|
self.assertEqual(img.height, 2000)
|
||||||
|
self.assertEqual(img.encoding, 'mono8')
|
||||||
|
|
||||||
|
def test_map_visualization(self):
|
||||||
|
vis = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
||||||
|
|
||||||
|
|
||||||
|
rostest.rosrun('aruco_pose', 'test_aruco_largemap', TestArucoPose, sys.argv)
|
||||||
13
aruco_pose/test/largemap.test
Normal file
13
aruco_pose/test/largemap.test
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<launch>
|
||||||
|
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
|
||||||
|
|
||||||
|
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true" required="true">
|
||||||
|
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||||
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
|
<remap from="markers" to="aruco_detect/markers"/>
|
||||||
|
<param name="type" value="map"/>
|
||||||
|
<param name="map" value="$(find aruco_pose)/test/largemap.txt"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<test test-name="test_aruco_pose_largemap" pkg="aruco_pose" type="largemap.py"/>
|
||||||
|
</launch>
|
||||||
11
aruco_pose/test/largemap.txt
Normal file
11
aruco_pose/test/largemap.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
0 0.2 0 0 0 0 0 0
|
||||||
|
1 0.2 10 0 0 0 0 0
|
||||||
|
2 0.2 20 0 0 0 0 0
|
||||||
|
3 0.2 30 0 0 0 0 0
|
||||||
|
4 0.2 40 0 0 0 0 0
|
||||||
|
5 0.2 50 0 0 0 0 0
|
||||||
|
6 0.2 60 0 0 0 0 0
|
||||||
|
7 0.2 70 0 0 0 0 0
|
||||||
|
8 0.2 80 0 0 0 0 0
|
||||||
|
9 0.2 90 0 0 0 0 0
|
||||||
|
10 0.2 100 0 0 0 0 0
|
||||||
@@ -1,27 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
import json
|
|
||||||
import rospy
|
import rospy
|
||||||
import rostest
|
import pytest
|
||||||
|
|
||||||
from geometry_msgs.msg import PoseWithCovarianceStamped
|
|
||||||
from sensor_msgs.msg import Image
|
|
||||||
from aruco_pose.msg import MarkerArray
|
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
||||||
|
|
||||||
|
|
||||||
class TestArucoMapPass(unittest.TestCase):
|
@pytest.fixture
|
||||||
def setUp(self):
|
def node():
|
||||||
rospy.init_node('test_parser_fail', anonymous=True)
|
return rospy.init_node('aruco_pose_test', anonymous=True)
|
||||||
|
|
||||||
def test_node_failure(self):
|
def test_node_failure(node):
|
||||||
try:
|
with pytest.raises(rospy.exceptions.ROSException):
|
||||||
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
||||||
did_post_message = True
|
|
||||||
except rospy.exceptions.ROSException:
|
|
||||||
did_post_message = False
|
|
||||||
self.assertFalse(did_post_message)
|
|
||||||
|
|
||||||
|
|
||||||
rostest.rosrun('aruco_pose', 'test_aruco_map', TestArucoMapPass, sys.argv)
|
|
||||||
|
|||||||
@@ -9,5 +9,6 @@
|
|||||||
<param name="map" value="$(find aruco_pose)/test/test_node_failure.txt"/>
|
<param name="map" value="$(find aruco_pose)/test/test_node_failure.txt"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<test test-name="test_aruco_map_fail_dict" pkg="aruco_pose" type="test_node_failure.py"/>
|
<param name="test_module" value="$(find aruco_pose)/test/test_node_failure.py"/>
|
||||||
|
<test test-name="test_node_failure" pkg="ros_pytest" type="ros_pytest_runner"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,24 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
import json
|
|
||||||
import rospy
|
import rospy
|
||||||
import rostest
|
import pytest
|
||||||
|
|
||||||
from geometry_msgs.msg import PoseWithCovarianceStamped
|
|
||||||
from sensor_msgs.msg import Image
|
|
||||||
from aruco_pose.msg import MarkerArray
|
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
||||||
|
|
||||||
|
|
||||||
class TestArucoMapPass(unittest.TestCase):
|
@pytest.fixture
|
||||||
def setUp(self):
|
def node():
|
||||||
rospy.init_node('test_parser_fail', anonymous=True)
|
return rospy.init_node('aruco_pose_test_empty_map', anonymous=True)
|
||||||
|
|
||||||
|
def test_empty_map(node):
|
||||||
def test_node_failure(self):
|
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
||||||
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
assert len(markers.markers) == 0
|
||||||
self.assertEquals(len(markers.markers), 0)
|
|
||||||
|
|
||||||
|
|
||||||
rostest.rosrun('aruco_pose', 'test_aruco_map', TestArucoMapPass, sys.argv)
|
|
||||||
|
|||||||
@@ -9,5 +9,6 @@
|
|||||||
<param name="map" value="$(find aruco_pose)/test/test_parser_empty_map.txt"/>
|
<param name="map" value="$(find aruco_pose)/test/test_parser_empty_map.txt"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<test test-name="test_aruco_map_incomplete" pkg="aruco_pose" type="test_parser_empty_map.py"/>
|
<param name="test_module" value="$(find aruco_pose)/test/test_parser_empty_map.py"/>
|
||||||
|
<test test-name="test_node_empty_map" pkg="ros_pytest" type="ros_pytest_runner"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -1,75 +1,61 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
import json
|
|
||||||
import rospy
|
import rospy
|
||||||
import rostest
|
import pytest
|
||||||
|
|
||||||
from geometry_msgs.msg import PoseWithCovarianceStamped
|
|
||||||
from sensor_msgs.msg import Image
|
from sensor_msgs.msg import Image
|
||||||
from aruco_pose.msg import MarkerArray
|
from aruco_pose.msg import MarkerArray
|
||||||
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
from visualization_msgs.msg import MarkerArray as VisMarkerArray
|
||||||
|
|
||||||
|
|
||||||
class TestArucoMapPass(unittest.TestCase):
|
@pytest.fixture
|
||||||
def setUp(self):
|
def node():
|
||||||
rospy.init_node('test_parser_pass', anonymous=True)
|
return rospy.init_node('aruco_pose_test', anonymous=True)
|
||||||
|
|
||||||
def test_markers(self):
|
def approx(expected):
|
||||||
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
return pytest.approx(expected, abs=1e-4) # compare floats more roughly
|
||||||
|
|
||||||
self.assertEqual(len(markers.markers), 6)
|
def test_markers(node):
|
||||||
# FIXME: visual marker id is not ArUco marker id
|
markers = rospy.wait_for_message('aruco_map/visualization', VisMarkerArray, timeout=5)
|
||||||
# self.assertEqual(markers.markers[0].id, 1)
|
assert len(markers.markers) == 6
|
||||||
# self.assertEqual(markers.markers[1].id, 2)
|
|
||||||
# self.assertEqual(markers.markers[2].id, 3)
|
|
||||||
# self.assertEqual(markers.markers[3].id, 4)
|
|
||||||
|
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.position.x, 0, places=7)
|
assert markers.markers[0].pose.position.x == approx(0)
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.position.y, 0, places=7)
|
assert markers.markers[0].pose.position.y == approx(0)
|
||||||
self.assertAlmostEqual(markers.markers[0].pose.position.z, 0, places=7)
|
assert markers.markers[0].pose.position.z == approx(0)
|
||||||
|
|
||||||
self.assertAlmostEqual(markers.markers[1].pose.position.x, 1, places=7)
|
assert markers.markers[1].pose.position.x == approx(1)
|
||||||
self.assertAlmostEqual(markers.markers[1].pose.position.y, 1, places=7)
|
assert markers.markers[1].pose.position.y == approx(1)
|
||||||
self.assertAlmostEqual(markers.markers[1].pose.position.z, 1, places=7)
|
assert markers.markers[1].pose.position.z == approx(1)
|
||||||
|
|
||||||
self.assertAlmostEqual(markers.markers[2].pose.position.x, 1, places=7)
|
assert markers.markers[2].pose.position.x == approx(1)
|
||||||
self.assertAlmostEqual(markers.markers[2].pose.position.y, 0, places=7)
|
assert markers.markers[2].pose.position.y == approx(0)
|
||||||
self.assertAlmostEqual(markers.markers[2].pose.position.z, 0.5, places=7)
|
assert markers.markers[2].pose.position.z == approx(0.5)
|
||||||
|
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.position.x, 0, places=7)
|
assert markers.markers[3].pose.position.x == approx(0)
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.position.y, 1, places=7)
|
assert markers.markers[3].pose.position.y == approx(1)
|
||||||
self.assertAlmostEqual(markers.markers[3].pose.position.z, 0, places=7)
|
assert markers.markers[3].pose.position.z == approx(0)
|
||||||
|
|
||||||
self.assertAlmostEqual(markers.markers[4].pose.position.x, 1, places=7)
|
assert markers.markers[4].pose.position.x == approx(1)
|
||||||
self.assertAlmostEqual(markers.markers[4].pose.position.y, 0.5, places=7)
|
assert markers.markers[4].pose.position.y == approx(0.5)
|
||||||
self.assertAlmostEqual(markers.markers[4].pose.position.z, 0, places=7)
|
assert markers.markers[4].pose.position.z == approx(0)
|
||||||
|
|
||||||
self.assertAlmostEqual(markers.markers[5].pose.position.x, 2.2, places=7)
|
assert markers.markers[5].pose.position.x == approx(2.2)
|
||||||
self.assertAlmostEqual(markers.markers[5].pose.position.y, 0.2, places=7)
|
assert markers.markers[5].pose.position.y == approx(0.2)
|
||||||
self.assertAlmostEqual(markers.markers[5].pose.position.z, 0, places=7)
|
assert markers.markers[5].pose.position.z == approx(0)
|
||||||
|
|
||||||
self.assertAlmostEqual(markers.markers[0].scale.x, 0.33, places=7)
|
assert markers.markers[0].scale.x == approx(0.33)
|
||||||
self.assertAlmostEqual(markers.markers[0].scale.y, 0.33, places=7)
|
assert markers.markers[0].scale.y == approx(0.33)
|
||||||
self.assertAlmostEqual(markers.markers[1].scale.x, 0.225, places=7)
|
assert markers.markers[1].scale.x == approx(0.225)
|
||||||
self.assertAlmostEqual(markers.markers[1].scale.y, 0.225, places=7)
|
assert markers.markers[1].scale.y == approx(0.225)
|
||||||
self.assertAlmostEqual(markers.markers[2].scale.x, 0.45, places=7)
|
assert markers.markers[2].scale.x == approx(0.45)
|
||||||
self.assertAlmostEqual(markers.markers[2].scale.y, 0.45, places=7)
|
assert markers.markers[2].scale.y == approx(0.45)
|
||||||
self.assertAlmostEqual(markers.markers[3].scale.x, 0.15, places=7)
|
assert markers.markers[3].scale.x == approx(0.15)
|
||||||
self.assertAlmostEqual(markers.markers[3].scale.y, 0.15, places=7)
|
assert markers.markers[3].scale.y == approx(0.15)
|
||||||
self.assertAlmostEqual(markers.markers[4].scale.x, 0.25, places=7)
|
assert markers.markers[4].scale.x == approx(0.25)
|
||||||
self.assertAlmostEqual(markers.markers[4].scale.y, 0.25, places=7)
|
assert markers.markers[4].scale.y == approx(0.25)
|
||||||
self.assertAlmostEqual(markers.markers[5].scale.x, 0.35, places=7)
|
assert markers.markers[5].scale.x == approx(0.35)
|
||||||
self.assertAlmostEqual(markers.markers[5].scale.y, 0.35, places=7)
|
assert markers.markers[5].scale.y == approx(0.35)
|
||||||
|
|
||||||
def test_map_image(self):
|
def test_map_image(node):
|
||||||
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
img = rospy.wait_for_message('aruco_map/image', Image, timeout=5)
|
||||||
self.assertEqual(img.width, 2000)
|
assert img.width == 2000
|
||||||
self.assertEqual(img.height, 2000)
|
assert img.height == 2000
|
||||||
self.assertEqual(img.encoding, 'mono8')
|
assert img.encoding == 'mono8'
|
||||||
|
|
||||||
# def test_transforms(self):
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
rostest.rosrun('aruco_pose', 'test_aruco_map', TestArucoMapPass, sys.argv)
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<launch>
|
<launch>
|
||||||
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager"/>
|
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" required="true"/>
|
||||||
|
|
||||||
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true">
|
<node name="aruco_map" pkg="nodelet" type="nodelet" args="load aruco_pose/aruco_map nodelet_manager" clear_params="true" required="true">
|
||||||
<remap from="image_raw" to="main_camera/image_raw"/>
|
<remap from="image_raw" to="main_camera/image_raw"/>
|
||||||
<remap from="camera_info" to="main_camera/camera_info"/>
|
<remap from="camera_info" to="main_camera/camera_info"/>
|
||||||
<remap from="markers" to="aruco_detect/markers"/>
|
<remap from="markers" to="aruco_detect/markers"/>
|
||||||
@@ -9,5 +9,6 @@
|
|||||||
<param name="map" value="$(find aruco_pose)/test/test_parser_pass.txt"/>
|
<param name="map" value="$(find aruco_pose)/test/test_parser_pass.txt"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<test test-name="test_aruco_map" pkg="aruco_pose" type="test_parser_pass.py"/>
|
<param name="test_module" value="$(find aruco_pose)/test/test_parser_pass.py"/>
|
||||||
|
<test test-name="test_node_pass" pkg="ros_pytest" type="ros_pytest_runner"/>
|
||||||
</launch>
|
</launch>
|
||||||
|
|||||||
@@ -550,3 +550,6 @@ ddynamic_reconfigure:
|
|||||||
realsense2_camera:
|
realsense2_camera:
|
||||||
debian:
|
debian:
|
||||||
stretch: [ros-kinetic-realsense2-camera]
|
stretch: [ros-kinetic-realsense2-camera]
|
||||||
|
ros_pytest:
|
||||||
|
debian:
|
||||||
|
stretch: [ros-kinetic-ros-pytest]
|
||||||
|
|||||||
@@ -155,8 +155,6 @@ echo_stamp "Installing CLEVER" \
|
|||||||
&& my_travis_retry pip install -r /home/pi/catkin_ws/src/clever/clever/requirements.txt \
|
&& my_travis_retry pip install -r /home/pi/catkin_ws/src/clever/clever/requirements.txt \
|
||||||
&& source /opt/ros/kinetic/setup.bash \
|
&& source /opt/ros/kinetic/setup.bash \
|
||||||
&& catkin_make -j2 -DCMAKE_BUILD_TYPE=Release \
|
&& catkin_make -j2 -DCMAKE_BUILD_TYPE=Release \
|
||||||
&& catkin_make run_tests \
|
|
||||||
&& catkin_test_results \
|
|
||||||
&& systemctl enable roscore \
|
&& systemctl enable roscore \
|
||||||
&& systemctl enable clever \
|
&& systemctl enable clever \
|
||||||
&& echo_stamp "All CLEVER was installed!" "SUCCESS" \
|
&& echo_stamp "All CLEVER was installed!" "SUCCESS" \
|
||||||
@@ -171,7 +169,6 @@ gitbook build
|
|||||||
echo_stamp "Installing additional ROS packages"
|
echo_stamp "Installing additional ROS packages"
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
ros-kinetic-dynamic-reconfigure \
|
ros-kinetic-dynamic-reconfigure \
|
||||||
ros-kinetic-tf2-web-republisher \
|
|
||||||
ros-kinetic-compressed-image-transport \
|
ros-kinetic-compressed-image-transport \
|
||||||
ros-kinetic-rosbridge-suite \
|
ros-kinetic-rosbridge-suite \
|
||||||
ros-kinetic-rosserial \
|
ros-kinetic-rosserial \
|
||||||
@@ -181,9 +178,12 @@ apt-get install -y --no-install-recommends \
|
|||||||
ros-kinetic-rosshow
|
ros-kinetic-rosshow
|
||||||
|
|
||||||
# TODO move GeographicLib datasets to Mavros debian package
|
# TODO move GeographicLib datasets to Mavros debian package
|
||||||
echo_stamp "Install GeographicLib datasets (needs for mavros)" \
|
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
|
||||||
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
||||||
|
|
||||||
|
echo_stamp "Running tests"
|
||||||
|
catkin_make run_tests && catkin_test_results
|
||||||
|
|
||||||
echo_stamp "Change permissions for catkin_ws"
|
echo_stamp "Change permissions for catkin_ws"
|
||||||
chown -Rf pi:pi /home/pi/catkin_ws
|
chown -Rf pi:pi /home/pi/catkin_ws
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ ntpdate \
|
|||||||
python-dev \
|
python-dev \
|
||||||
python3-dev \
|
python3-dev \
|
||||||
python-systemd \
|
python-systemd \
|
||||||
|
mjpg-streamer=2.0 \
|
||||||
&& echo_stamp "Everything was installed!" "SUCCESS" \
|
&& echo_stamp "Everything was installed!" "SUCCESS" \
|
||||||
|| (echo_stamp "Some packages wasn't installed!" "ERROR"; exit 1)
|
|| (echo_stamp "Some packages wasn't installed!" "ERROR"; exit 1)
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ pigpiod -v
|
|||||||
i2cdetect -V
|
i2cdetect -V
|
||||||
butterfly -h
|
butterfly -h
|
||||||
espeak --version
|
espeak --version
|
||||||
|
mjpg_streamer --version
|
||||||
|
|
||||||
# ros stuff
|
# ros stuff
|
||||||
|
|
||||||
|
|||||||
@@ -239,3 +239,8 @@ target_link_libraries(clever
|
|||||||
|
|
||||||
## Add folders to be run by python nosetests
|
## Add folders to be run by python nosetests
|
||||||
# catkin_add_nosetests(test)
|
# catkin_add_nosetests(test)
|
||||||
|
|
||||||
|
if (CATKIN_ENABLE_TESTING)
|
||||||
|
find_package(rostest REQUIRED)
|
||||||
|
add_rostest(test/basic.test)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
<param name="known_tilt" value="map"/>
|
<param name="known_tilt" value="map"/>
|
||||||
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
|
||||||
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>
|
||||||
|
<param name="markers/frame_id" value="aruco_map"/>
|
||||||
|
<param name="markers/child_frame_id_prefix" value="aruco_map_"/>
|
||||||
</node>
|
</node>
|
||||||
|
|
||||||
<!-- vpe publisher from aruco markers -->
|
<!-- vpe publisher from aruco markers -->
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
<node pkg="tf2_ros" type="static_transform_publisher" name="rangefinder_frame" args="0 0 -0.05 0 1.5707963268 0 base_link rangefinder"/>
|
<node pkg="tf2_ros" type="static_transform_publisher" name="rangefinder_frame" args="0 0 -0.05 0 1.5707963268 0 base_link rangefinder"/>
|
||||||
|
|
||||||
<!-- Copter visualization -->
|
<!-- Copter visualization -->
|
||||||
<node name="copter_visualization" pkg="mavros_extras" type="copter_visualization" if="$(arg viz)">
|
<node name="visualization" pkg="mavros_extras" type="visualization" if="$(arg viz)">
|
||||||
<remap to="mavros/local_position/pose" from="local_position"/>
|
<remap to="mavros/local_position/pose" from="local_position"/>
|
||||||
<remap to="mavros/setpoint_position/local" from="local_setpoint"/>
|
<remap to="mavros/setpoint_position/local" from="local_setpoint"/>
|
||||||
<param name="fixed_frame_id" value="map"/>
|
<param name="fixed_frame_id" value="map"/>
|
||||||
|
|||||||
@@ -30,13 +30,13 @@
|
|||||||
<depend>nodelet</depend>
|
<depend>nodelet</depend>
|
||||||
<depend>mavros</depend>
|
<depend>mavros</depend>
|
||||||
<depend>mavros_extras</depend>
|
<depend>mavros_extras</depend>
|
||||||
<depend>lxml</depend>
|
|
||||||
<depend>cv_camera</depend>
|
<depend>cv_camera</depend>
|
||||||
<depend>cv_bridge</depend>
|
<depend>cv_bridge</depend>
|
||||||
<depend>opencv3</depend>
|
<depend>opencv3</depend>
|
||||||
<depend>mjpg-streamer</depend>
|
|
||||||
<depend>rosbridge_server</depend>
|
<depend>rosbridge_server</depend>
|
||||||
<depend>web_video_server</depend>
|
<depend>web_video_server</depend>
|
||||||
|
<depend>tf2_web_republisher</depend>
|
||||||
|
<depend>python-lxml</depend>
|
||||||
<exec_depend>python-pymavlink</exec_depend>
|
<exec_depend>python-pymavlink</exec_depend>
|
||||||
<!-- Use test_depend for packages you need only for testing: -->
|
<!-- Use test_depend for packages you need only for testing: -->
|
||||||
<!-- <test_depend>gtest</test_depend> -->
|
<!-- <test_depend>gtest</test_depend> -->
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ TwistStamped velocity;
|
|||||||
NavSatFix global_position;
|
NavSatFix global_position;
|
||||||
BatteryState battery;
|
BatteryState battery;
|
||||||
|
|
||||||
// Common subcriber callback template that stores message to the variable
|
// Common subscriber callback template that stores message to the variable
|
||||||
template<typename T, T& STORAGE>
|
template<typename T, T& STORAGE>
|
||||||
void handleMessage(const T& msg)
|
void handleMessage(const T& msg)
|
||||||
{
|
{
|
||||||
|
|||||||
29
clever/test/basic.py
Executable file
29
clever/test/basic.py
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import rospy
|
||||||
|
import pytest
|
||||||
|
from mavros_msgs.msg import State
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def node():
|
||||||
|
return rospy.init_node('clever_test', anonymous=True)
|
||||||
|
|
||||||
|
def test_state(node):
|
||||||
|
state = rospy.wait_for_message('mavros/state', State, timeout=10)
|
||||||
|
assert state.connected == False
|
||||||
|
assert state.armed == False
|
||||||
|
assert state.guided == False
|
||||||
|
assert state.mode == ''
|
||||||
|
|
||||||
|
def test_simple_offboard_services_available():
|
||||||
|
rospy.wait_for_service('get_telemetry', timeout=5)
|
||||||
|
rospy.wait_for_service('navigate', timeout=5)
|
||||||
|
rospy.wait_for_service('navigate_global', timeout=5)
|
||||||
|
rospy.wait_for_service('set_position', timeout=5)
|
||||||
|
rospy.wait_for_service('set_velocity', timeout=5)
|
||||||
|
rospy.wait_for_service('set_attitude', timeout=5)
|
||||||
|
rospy.wait_for_service('set_rates', timeout=5)
|
||||||
|
rospy.wait_for_service('land', timeout=5)
|
||||||
|
|
||||||
|
def test_web_video_server(node):
|
||||||
|
import urllib2
|
||||||
|
urllib2.urlopen("http://localhost:8080").read()
|
||||||
37
clever/test/basic.test
Executable file
37
clever/test/basic.test
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
<launch>
|
||||||
|
<!-- Verify all the required nodes basically work -->
|
||||||
|
|
||||||
|
<node pkg="mavros" type="mavros_node" name="mavros" required="true" output="screen">
|
||||||
|
<param name="fcu_url" value="udp://@127.0.1:14557"/>
|
||||||
|
<rosparam command="load" file="$(find clever)/launch/mavros_config.yaml"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node name="visualization" pkg="mavros_extras" type="visualization" required="true">
|
||||||
|
<remap to="mavros/local_position/pose" from="local_position"/>
|
||||||
|
<remap to="mavros/setpoint_position/local" from="local_setpoint"/>
|
||||||
|
<param name="fixed_frame_id" value="map"/>
|
||||||
|
<param name="child_frame_id" value="base_link"/>
|
||||||
|
<param name="marker_scale" value="1"/>
|
||||||
|
<param name="max_track_size" value="20"/>
|
||||||
|
<param name="num_rotors" value="4"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node name="web_video_server" pkg="web_video_server" type="web_video_server" required="true" output="screen">
|
||||||
|
<param name="default_stream_type" value="ros_compressed"/>
|
||||||
|
<param name="publish_rate" value="1.0"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node pkg="tf2_ros" type="static_transform_publisher" name="map_flipped_frame" args="0 0 0 3.1415926 3.1415926 0 map map_flipped" required="true"/>
|
||||||
|
|
||||||
|
<node name="simple_offboard" pkg="clever" type="simple_offboard" required="true" output="screen">
|
||||||
|
<param name="reference_frames/body" value="map"/>
|
||||||
|
<param name="reference_frames/base_link" value="map"/>
|
||||||
|
</node>
|
||||||
|
|
||||||
|
<node name="tf2_web_republisher" pkg="tf2_web_republisher" type="tf2_web_republisher" required="true"/>
|
||||||
|
|
||||||
|
<node name="rc" pkg="clever" type="rc" required="true" output="screen"/>
|
||||||
|
|
||||||
|
<param name="test_module" value="$(find clever)/test/basic.py"/>
|
||||||
|
<test test-name="basic_test" pkg="ros_pytest" type="ros_pytest_runner"/>
|
||||||
|
</launch>
|
||||||
BIN
docs/assets/cam_calib1.png
Normal file
BIN
docs/assets/cam_calib1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/assets/cam_calib2.png
Normal file
BIN
docs/assets/cam_calib2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 236 KiB |
BIN
docs/assets/cam_calib3.png
Normal file
BIN
docs/assets/cam_calib3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 338 KiB |
BIN
docs/assets/cam_calib4.png
Normal file
BIN
docs/assets/cam_calib4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -55,7 +55,7 @@ rosrun aruco_pose genmap.py length x y dist_x dist_y first > ~/catkin_ws/src/cle
|
|||||||
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > ~/catkin_ws/src/clever/aruco_pose/map/test_map.txt
|
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > ~/catkin_ws/src/clever/aruco_pose/map/test_map.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
Также, можно создать карту в специальном [конструкторе](arucogenmap.md)
|
Также, можно создать карту в специальном [конструкторе](arucogenmap.md).
|
||||||
|
|
||||||
### Проверка
|
### Проверка
|
||||||
|
|
||||||
|
|||||||
@@ -1,79 +1,99 @@
|
|||||||
# Калибровка камеры
|
# Калибровка камеры
|
||||||
|
|
||||||
Компьютерное зрение получает все более широкое распространение. Зачастую, алгоритмы компьютерного зрения работают неточно, получая искаженное изображение с камеры, что особенно характерно для fisheye-камер.
|
Для точной работы систем компьютерного зрения (например, для навигации по ArUco-маркерам) используемая камера должна быть откалибрована.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
> Изображение "скруглено" ближе к краям.
|
> Изображение "скруглено" ближе к краям.
|
||||||
|
Какой-либо алгоритм компьютерного зрения будет воспринимать информацию с этой картинки неправильно.
|
||||||
|
|
||||||
Какой-либо алгоритм компьютерного зрения будет воспринимать информацию с этой картинки неправильно. Для устранения подобных искажений камера, получающая изображения, должна быть откалибрована в соответствии со своими особенностями.
|
## Установка приложения
|
||||||
|
|
||||||
## Установка скрипта
|
|
||||||
|
|
||||||
Для начала, необходимо установить необходимые библиотеки:
|
Для начала, необходимо установить необходимые библиотеки:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
pip install numpy
|
pip install numpy
|
||||||
pip install opencv-python
|
pip install opencv-python
|
||||||
pip install glob
|
|
||||||
pip install pyyaml
|
pip install pyyaml
|
||||||
pip install urllib.request
|
pip install urllib2
|
||||||
|
pip install flask, flask-wtf
|
||||||
```
|
```
|
||||||
|
|
||||||
Затем скачиваем скрипт из репозитория:
|
Затем скачиваем исходный код из репозитория и проводим установку:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/tinderad/clever_cam_calibration.git
|
git clone https://github.com/tinderad/calibration_web_2.7.git
|
||||||
```
|
cd calibration_web_2.7.git
|
||||||
|
|
||||||
Переходим в скачанную папку и устанавливаем скрипт:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd clever_cam_calibration
|
|
||||||
sudo python setup.py build
|
sudo python setup.py build
|
||||||
sudo python setup.py install
|
sudo python setup.py install
|
||||||
```
|
```
|
||||||
|
|
||||||
Если вы используете Windows, тогда скачайте архив из [репозитория](https://github.com/tinderad/clever_cam_calibration/archive/master.zip), распакуйте его и установите:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd path\to\archive\clever_cam_calibration\
|
|
||||||
python setup.py build
|
|
||||||
python setup.py install
|
|
||||||
```
|
|
||||||
|
|
||||||
> path\to\archive - путь до распакованного архива.
|
|
||||||
|
|
||||||
## Подготовка к калибровке
|
## Подготовка к калибровке
|
||||||
|
|
||||||
Вам необходимо подготовить калибровочную мишень. Она представляет собой «шахматную доску». Файл можно взять [отсюда](https://www.oreilly.com/library/view/learning-opencv-3/9781491937983/assets/lcv3_ac01.png).
|
Вам необходимо подготовить калибровочную мишень. Она представляет собой «шахматную доску». Файл можно взять [отсюда](https://www.oreilly.com/library/view/learning-opencv-3/9781491937983/assets/lcv3_ac01.png).
|
||||||
Наклейте распечатанную мишень на любую твердую поверхность. Посчитайте количество пересечений в длину и в ширину доски, измерьте размер клетки (в мм).
|
Наклейте распечатанную мишень на любую твердую поверхность. Посчитайте количество пересечений в длину и в ширину доски, измерьте размер клетки (в мм), как указано на изображении.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Включите Клевер и подключитесь к его Wi-Fi.
|
Включите Клевер и подключитесь к его Wifi.
|
||||||
|
|
||||||
> Перейдите на 192.168.11.1:8080 и проверьте, получает ли компьютер изображения из топика image_raw.
|
> Перейдите на _192.168.11.1:8080_ и проверьте, получает ли компьютер изображения из топика _image_raw_.
|
||||||
|
|
||||||
## Калибровка
|
## Калибровка
|
||||||
|
|
||||||
Запустите скрипт **_calibrate_cam_**:
|
Подключитесь к Клеверу по протоколу SSH (например, при помощи PuTTY).
|
||||||
|
|
||||||
**Windows:**
|
Запустите приложение:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
>path\to\python\Scripts\calibrate_cam.exe
|
>cd calibration_web_2.7/ccc_server
|
||||||
|
>python app.py
|
||||||
```
|
```
|
||||||
|
|
||||||
> path\to\Python - путь до директории Python
|
Далее вам необходимо на компьютере открыть в браузере страницу по адресу _192.168.11.1:8081_
|
||||||
|
|
||||||
**Linux:**
|
> Порт можно настроить в файле _ccc_server/config.py_.
|
||||||
|
|
||||||
|
На открытой странице необходимо ввести параметры калибровочной мишени: количество перекрестий в длину и ширину, длину ребра квадрата. Для начала калибровки нажмите кнопку **_Start Calibration_**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
На следующей странице при помощи кнопки **_Catch photo_** можно делать фотографии калибровочной мишени.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Если программа нашла на изображении указанную мишень, откроется страница, на которой вам необходимо подтвердить корректность найденных перекрестий.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Если перекрестия были распознаныы правильно, нажмите на клавишу **_Add_**, и перейдите к получению новых фотографий. В противном же случае, ели перекристия были распознаны некорректно, пропустите данную фотографию при помощи клавиши **_Skip_**.
|
||||||
|
|
||||||
|
>В большинстве случаев найденные углы будут подсвечиваться разными цветами, но иногда подсветка будет становиться красной. это происходит в том случае, если углы распознаны, но неточно.
|
||||||
|
|
||||||
|
Чтобы откалибровать камеру, вам требуется сделать как минимум 25 фото шахматной доски с различных ракурсов. После преодоления данного порога появится кнопка **_Finish_**, по нажатию на которую начнется генерация калибровочного файла.
|
||||||
|
|
||||||
|
>Это может занять некоторое время.
|
||||||
|
|
||||||
|
На открывшейся странице выведется информация о результате калибровки: имя файла и re-projection error.
|
||||||
|
>re-projection error - отклонение от стандартной математической модели. Чем эта величина меньше, тем точнее проведена калибровка.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Программа обработает все полученные фотографии, и создаст **_.yaml_** файл в нынешней директории. При помощи этого файла можно будет выравнивать искажения на изображениях, полученных с этой камеры.
|
||||||
|
|
||||||
|
> Если вы поменяете разрешение получаемого изображения, вам нужно будет снова калибровать камеру.
|
||||||
|
|
||||||
|
## Предыдущая версия
|
||||||
|
|
||||||
|
Также вы можете воспользоваться предыдущей версией программы, не имеющей web-интерфейса.
|
||||||
|
|
||||||
|
Запустите скрипт **_calibrate_cam_**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
>calibrate_cam
|
>calibrate_cam
|
||||||
```
|
```
|
||||||
|
|
||||||
Задайте параметры доски:
|
Задайте параметры мишени:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
>calibrate_cam
|
>calibrate_cam
|
||||||
@@ -95,8 +115,6 @@ help, catch (key: Enter), delete, restart, stop, finish
|
|||||||
|
|
||||||
Чтобы откалибровать камеру, вам требуется сделать как минимум 25 фото шахматной доски с различных ракурсов.
|
Чтобы откалибровать камеру, вам требуется сделать как минимум 25 фото шахматной доски с различных ракурсов.
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Чтобы сделать фото, введите команду **_catch_**.
|
Чтобы сделать фото, введите команду **_catch_**.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -125,10 +143,10 @@ Calibration successful!
|
|||||||
|
|
||||||
**Калибровка по существующим изображениям:**
|
**Калибровка по существующим изображениям:**
|
||||||
|
|
||||||
Если же у вас уже есть изображения, то вы можете откалибровать камеру по ним при помощи скрипта **_calibrate_cam_ex_**.
|
Если же у вас уже есть изображения, то вы можете откалибровать камеру по ним при помощи скрипта **_calibrate_from_dir_**.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
>calibrate_cam_ex
|
>calibrate_from_dir
|
||||||
```
|
```
|
||||||
|
|
||||||
Указываем характеристики мишени, а так же путь до папки с изображениями:
|
Указываем характеристики мишени, а так же путь до папки с изображениями:
|
||||||
@@ -145,32 +163,31 @@ Path: # Путь до папки с изображениями
|
|||||||
|
|
||||||
Программа обработает все полученные фотографии, и создаст файл **_camera_info_****_._****_yaml_** в нынешней директории. При помощи этого файла можно будет выравнивать искажения на изображениях, полученных с этой камеры.
|
Программа обработает все полученные фотографии, и создаст файл **_camera_info_****_._****_yaml_** в нынешней директории. При помощи этого файла можно будет выравнивать искажения на изображениях, полученных с этой камеры.
|
||||||
|
|
||||||
> Если вы поменяете разрешение получаемого изображения, вам нужно будет снова калибровать камеру.
|
|
||||||
|
|
||||||
## Исправление искажений
|
## Исправление искажений
|
||||||
|
|
||||||
За получение исправленного изображения отвечает функция **_get_undistorted_image(cv2_image, camera_info)_**:
|
За получение исправленного изображения отвечает функция
|
||||||
|
**clever_cam_calibration._get_undistorted_image(cv2_image, camera_info)_**:
|
||||||
|
|
||||||
* **_cv2_image_**: Закодированное в массив cv2 изображение.
|
* **_cv2_image_**: Закодированное в массив cv2 изображение.
|
||||||
* **_camera_****___****_info_**: Путь до файла калибровки.
|
* **_camera_****___****_info_**: Путь до файла калибровки.
|
||||||
|
|
||||||
Функция возвращает массив cv2, в котором закодировано исправленное изображение.
|
Функция возвращает массив cv2, в котором закодировано исправленное изображение.
|
||||||
|
|
||||||
> Если вы используете fisheye-камеру, поставляемую вместе с Клевером, то для обработки изображений разрешением 320x240 или 640x480 вы можете использовать уже существующие параметры калибровки. Для этого в качестве аргумента **_camera_info_** передайте параметры **_clever_cam_calibration.clevercamcalib.CLEVER_FISHEYE_CAM_320_** или **_clever_cam_calibration.clevercamcalib.CLEVER_FISHEYE_CAM_640_** соответственно.
|
> Если вы используете fisheye-камеру, поставляемую вместе с Клевером, то для обработки изображений разрешением 320x240 или 640x480 вы можете использовать уже существующие параметры калибровки. Для этого в качестве аргумента **_camera_info_** передайте параметры **_clever_cam_calibration.CLEVER_FISHEYE_CAM_320_** или **_clever_cam_calibration.CLEVER_FISHEYE_CAM_640_** соответственно.
|
||||||
|
|
||||||
## Примеры работы
|
## Примеры работы
|
||||||
|
|
||||||
Изначальные изображения:
|
Изначальные изображения:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Иcправленные изображения:
|
Иcправленные изображения:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Пример использования
|
## Пример использования
|
||||||
|
|
||||||
@@ -179,7 +196,7 @@ Path: # Путь до папки с изображениями
|
|||||||
Данная программа получает изображения с камеры Клевера и выводит их на экран в исправленном виде, используя существующий калибровочный файл.
|
Данная программа получает изображения с камеры Клевера и выводит их на экран в исправленном виде, используя существующий калибровочный файл.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import clevercamcalib.clevercamcalib as ccc
|
import clever_cam_calibration as ccc
|
||||||
import cv2
|
import cv2
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -198,13 +215,11 @@ cv2.destroyAllWindows()
|
|||||||
Чтобы применить параметры калибровки к системе ArUco-навигации, требуется перенести калибровочный .yaml файл на Raspberry Pi Клевера и инициализировать его.
|
Чтобы применить параметры калибровки к системе ArUco-навигации, требуется перенести калибровочный .yaml файл на Raspberry Pi Клевера и инициализировать его.
|
||||||
|
|
||||||
> Не забудьте подключиться к WiFI Клевера.
|
> Не забудьте подключиться к WiFI Клевера.
|
||||||
|
|
||||||
Для передачи файла используется протокол SFTP. В данном примере используется программа WinSCP.
|
Для передачи файла используется протокол SFTP. В данном примере используется программа WinSCP.
|
||||||
|
|
||||||
Подключимся к Raspberry Pi по SFTP:
|
Подключимся к Raspberry Pi по SFTP:
|
||||||
|
|
||||||
> Пароль: _**raspberry**_
|
> Пароль: _**raspberry**_
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Нажимаем “Войти”. Переходим в _**/home/pi/catkin_ws/src/clever/clever/camera_info/**_ и копируем туда калибровочный .yaml файл:
|
Нажимаем “Войти”. Переходим в _**/home/pi/catkin_ws/src/clever/clever/camera_info/**_ и копируем туда калибровочный .yaml файл:
|
||||||
@@ -225,4 +240,4 @@ cv2.destroyAllWindows()
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
> Не забудьте изменить разрешение камеры.
|
> Не забудьте изменить разрешение камеры в *main_camera.launch*.
|
||||||
|
|||||||
@@ -7,28 +7,30 @@ Wi-Fi адаптер на Raspberry Pi имеет два основных реж
|
|||||||
|
|
||||||
При использовании [образа для RPi](microsd_images.md) по умолчанию Wi-Fi адаптер работает в [режиме точки доступа](wifi.md).
|
При использовании [образа для RPi](microsd_images.md) по умолчанию Wi-Fi адаптер работает в [режиме точки доступа](wifi.md).
|
||||||
|
|
||||||
|
<!-- markdownlint-disable MD029 -->
|
||||||
|
|
||||||
## Изменение пароля или SSID (имени сети)
|
## Изменение пароля или SSID (имени сети)
|
||||||
|
|
||||||
1. Отредактируйте файл `/etc/wpa_supplicant/wpa_supplicant.conf` (используя [SSH-соединение](ssh.md)):
|
1. Отредактируйте файл `/etc/wpa_supplicant/wpa_supplicant.conf` (используя [SSH-соединение](ssh.md)):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
|
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
Измените значение параметра `ssid`, чтобы изменить название Wi-Fi сети, и параметра `psk`, чтобы изменить пароль. Например:
|
Измените значение параметра `ssid`, чтобы изменить название Wi-Fi сети, и параметра `psk`, чтобы изменить пароль. Например:
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
network={
|
network={
|
||||||
ssid="my-super-ssid"
|
ssid="my-super-ssid"
|
||||||
psk="cleverwifi123"
|
psk="cleverwifi123"
|
||||||
mode=2
|
mode=2
|
||||||
proto=RSN
|
proto=RSN
|
||||||
key_mgmt=WPA-PSK
|
key_mgmt=WPA-PSK
|
||||||
pairwise=CCMP
|
pairwise=CCMP
|
||||||
group=CCMP
|
group=CCMP
|
||||||
auth_alg=OPEN
|
auth_alg=OPEN
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Перезагрузите Raspberry Pi.
|
2. Перезагрузите Raspberry Pi.
|
||||||
|
|
||||||
@@ -40,87 +42,89 @@ Wi-Fi адаптер на Raspberry Pi имеет два основных реж
|
|||||||
|
|
||||||
1. Выключите службу `dnsmasq`.
|
1. Выключите службу `dnsmasq`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl stop dnsmasq
|
sudo systemctl stop dnsmasq
|
||||||
sudo systemctl disable dnsmasq
|
sudo systemctl disable dnsmasq
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Включите получение IP адреса на беспроводном интерфейсе DHCP клиентом. Для этого удалите из файла `/etc/dhcpcd.conf` строки:
|
2. Включите получение IP адреса на беспроводном интерфейсе DHCP клиентом. Для этого удалите из файла `/etc/dhcpcd.conf` строки:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
interface wlan0
|
interface wlan0
|
||||||
static ip_address=192.168.11.1/24
|
static ip_address=192.168.11.1/24
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Настройте `wpa_supplicant` для подключения к существующей точке доступа. Для этого замените содержимое файла `/etc/wpa_supplicant/wpa_supplicant.conf` на:
|
3. Настройте `wpa_supplicant` для подключения к существующей точке доступа. Для этого замените содержимое файла `/etc/wpa_supplicant/wpa_supplicant.conf` на:
|
||||||
|
|
||||||
```
|
```
|
||||||
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
||||||
update_config=1
|
update_config=1
|
||||||
country=GB
|
country=GB
|
||||||
|
|
||||||
network={
|
network={
|
||||||
ssid="SSID"
|
ssid="SSID"
|
||||||
psk="password"
|
psk="password"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
где `SSID` – название сети, а `password` – пароль.
|
где `SSID` – название сети, а `password` – пароль.
|
||||||
|
|
||||||
4. Перезапустите службу `dhcpcd`.
|
4. Перезапустите службу `dhcpcd`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl restart dhcpcd
|
sudo systemctl restart dhcpcd
|
||||||
```
|
```
|
||||||
|
|
||||||
## Переключение адаптера в режим точки доступа
|
## Переключение адаптера в режим точки доступа
|
||||||
|
|
||||||
1. Включите статический IP адрес на беспроводном интерфейсе. Для этого добавьте в файл `/etc/dhcpcd.conf` строки:
|
1. Включите статический IP адрес на беспроводном интерфейсе. Для этого добавьте в файл `/etc/dhcpcd.conf` строки:
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
interface wlan0
|
interface wlan0
|
||||||
static ip_address=192.168.11.1/24
|
static ip_address=192.168.11.1/24
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Настроите `wpa_supplicant` на работу в режиме точки доступа. Для этого замените содержимое файла `/etc/wpa_supplicant/wpa_supplicant.conf` на:
|
2. Настроите `wpa_supplicant` на работу в режиме точки доступа. Для этого замените содержимое файла `/etc/wpa_supplicant/wpa_supplicant.conf` на:
|
||||||
|
|
||||||
```
|
```
|
||||||
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
||||||
update_config=1
|
update_config=1
|
||||||
country=GB
|
country=GB
|
||||||
|
|
||||||
network={
|
network={
|
||||||
ssid="CLEVER-1234"
|
ssid="CLEVER-1234"
|
||||||
psk="cleverwifi"
|
psk="cleverwifi"
|
||||||
mode=2
|
mode=2
|
||||||
proto=RSN
|
proto=RSN
|
||||||
key_mgmt=WPA-PSK
|
key_mgmt=WPA-PSK
|
||||||
pairwise=CCMP
|
pairwise=CCMP
|
||||||
group=CCMP
|
group=CCMP
|
||||||
auth_alg=OPEN
|
auth_alg=OPEN
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
где `CLEVER-1234` – название сети, а `cleverwifi` – пароль.
|
где `CLEVER-1234` – название сети, а `cleverwifi` – пароль.
|
||||||
|
|
||||||
3. Перезагрузите `systemd`.
|
3. Перезагрузите `systemd`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Включите службу `dnsmasq`.
|
4. Включите службу `dnsmasq`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl enable dnsmasq
|
sudo systemctl enable dnsmasq
|
||||||
sudo systemctl start dnsmasq
|
sudo systemctl start dnsmasq
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Перезагрузите службу `dhcpcd`.
|
5. Перезагрузите службу `dhcpcd`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl restart dhcpcd
|
sudo systemctl restart dhcpcd
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<!-- markdownlint-enable MD029 -->
|
||||||
|
|
||||||
___
|
___
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user