Compare commits
41 Commits
v0.21
...
raspios_64
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c70b7814a | ||
|
|
6e90c5d6fa | ||
|
|
c93beec30d | ||
|
|
47628ba4af | ||
|
|
98e43aba49 | ||
|
|
404b42c9f9 | ||
|
|
6b831359dc | ||
|
|
10076e35f4 | ||
|
|
76a6a58a42 | ||
|
|
62069ab80a | ||
|
|
e1643a681a | ||
|
|
6f30613ce0 | ||
|
|
d539753e72 | ||
|
|
cc80f2b4c1 | ||
|
|
1893f0528b | ||
|
|
ce1f1d9db5 | ||
|
|
8b42fcfda3 | ||
|
|
60b9d1d61d | ||
|
|
61ae5d0537 | ||
|
|
7ffcbde82e | ||
|
|
ccc53f1cfb | ||
|
|
be2b447b46 | ||
|
|
b333dd8708 | ||
|
|
4b5524e467 | ||
|
|
5b970d5197 | ||
|
|
617ae0dcdd | ||
|
|
a1d2ae9a23 | ||
|
|
9372386f6b | ||
|
|
4b97f9d4af | ||
|
|
6af1fd2837 | ||
|
|
b7545830ba | ||
|
|
4796c4acb7 | ||
|
|
de3fb77db2 | ||
|
|
036d3dccd6 | ||
|
|
24cfc54c06 | ||
|
|
173c8cbe3a | ||
|
|
2d8cd0e0ab | ||
|
|
ee667257ef | ||
|
|
3c4f57cbe7 | ||
|
|
3ee598004a | ||
|
|
673cabe7ab |
@@ -21,8 +21,8 @@
|
||||
"ROS",
|
||||
"ROS Kinetic",
|
||||
"ROS Melodic",
|
||||
"ROS Noetic",
|
||||
"OpenCV",
|
||||
"OpenVPN",
|
||||
"Gazebo",
|
||||
"GitHub",
|
||||
"FPV",
|
||||
@@ -107,9 +107,7 @@
|
||||
"UDP",
|
||||
"QR",
|
||||
"Li-ion",
|
||||
"Nvidia",
|
||||
"VirtualBox",
|
||||
"VMware"
|
||||
"Nvidia"
|
||||
],
|
||||
"code_blocks": false
|
||||
},
|
||||
|
||||
14
.travis.yml
@@ -5,12 +5,12 @@ services:
|
||||
- docker
|
||||
env:
|
||||
global:
|
||||
- DOCKER="sfalexrog/img-tool:qemu-update"
|
||||
- DOCKER="sfalexrog/img-tool:qemu-aarch64"
|
||||
- TARGET_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
|
||||
- IMAGE_VERSION=${TRAVIS_TAG:-${TRAVIS_COMMIT:0:7}}
|
||||
- IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img"
|
||||
git:
|
||||
depth: 25
|
||||
depth: 50
|
||||
jobs:
|
||||
fast_finish: true
|
||||
include:
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
- cp images/*.zip imgcache
|
||||
after_success:
|
||||
- sudo chmod -R 777 *
|
||||
- cd images && zip -9 ${IMAGE_NAME}.zip ${IMAGE_NAME} && stat --printf="Compressed image size:%s\n" ${IMAGE_NAME}.zip
|
||||
- cd images && zip ${IMAGE_NAME}.zip ${IMAGE_NAME} && stat --printf="Compressed image size:%s\n" ${IMAGE_NAME}.zip
|
||||
before_deploy:
|
||||
# Set up git user name and tag this commit
|
||||
- git config --local user.name "goldarte"
|
||||
@@ -68,6 +68,14 @@ jobs:
|
||||
- docker pull ${NATIVE_DOCKER}
|
||||
script:
|
||||
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh
|
||||
- stage: Build
|
||||
name: "Native Noetic build"
|
||||
env:
|
||||
- NATIVE_DOCKER=ros:noetic-ros-base
|
||||
before_script:
|
||||
- docker pull ${NATIVE_DOCKER}
|
||||
script:
|
||||
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh
|
||||
- stage: Build
|
||||
name: "Documentation"
|
||||
language: node_js
|
||||
|
||||
14
README.md
@@ -1,14 +1,12 @@
|
||||
# clover🍀: create autonomous drones easily
|
||||
# COEX Clover Drone Kit
|
||||
|
||||
<img src="docs/assets/clever4-front-white.png" align="right" width="400px" alt="COEX Clover Drone">
|
||||
<img src="docs/assets/clever4-front-white.png" align="right" width="400px" alt="Clover Drone">
|
||||
|
||||
Clover is an open source [ROS](https://www.ros.org)-based framework, providing user-friendly tools to control [PX4](https://px4.io)-powered drones. Clover is available as a ROS package, but is shipped mainly as a preconfigured image for Raspberry Pi. Once you've installed Raspberry Pi on your drone and flashed the image to its microSD card, taking the drone up in the air is a matter of minutes.
|
||||
Clover is an educational programmable drone kit consisting of an unassembled quadcopter, open source software and documentation. The kit includes Pixracer-compatible autopilot running PX4 firmware, Raspberry Pi 4 as companion computer, a camera for computer vision navigation as well as additional sensors and peripheral devices.
|
||||
|
||||
COEX Clover Drone is an educational programmable drone kit, suited perfectly for running clover software. The kit is shipped unassembled and includes Pixracer-compatible autopilot running PX4 firmware, Raspberry Pi 4 as a companion computer, a camera for computer vision navigation as well as additional sensors and peripheral devices. Batteries included.
|
||||
The main documentation is available [on Gitbook](https://clover.coex.tech/).
|
||||
|
||||
The main documentation is available at [https://clover.coex.tech](https://clover.coex.tech/). Official website: [coex.tech/clover](https://coex.tech/clover).
|
||||
|
||||
[__Support us on Kickstarter!__](https://www.kickstarter.com/projects/copterexpress/cloverdrone)
|
||||
Official website: <a href="https://coex.tech/clover">coex.tech/clover</a>.
|
||||
|
||||
## Video compilation
|
||||
|
||||
@@ -25,7 +23,7 @@ Preconfigured image for Raspberry Pi with installed and configured software, rea
|
||||
Image features:
|
||||
|
||||
* Raspbian Buster
|
||||
* [ROS Melodic](http://wiki.ros.org/melodic)
|
||||
* [ROS Noetic](http://wiki.ros.org/noetic)
|
||||
* Configured networking
|
||||
* OpenCV
|
||||
* [`mavros`](http://wiki.ros.org/mavros)
|
||||
|
||||
@@ -22,13 +22,21 @@ find_package(catkin REQUIRED COMPONENTS
|
||||
dynamic_reconfigure
|
||||
)
|
||||
|
||||
find_package(OpenCV 3 REQUIRED COMPONENTS core imgproc calib3d)
|
||||
# Workaround for OpenCV 3/4 support
|
||||
set(_opencv_version 4)
|
||||
find_package(OpenCV ${_opencv_version} QUIET)
|
||||
if (NOT OpenCV_FOUND)
|
||||
message(STATUS "Did not find OpenCV 4, searching for OpenCV 3")
|
||||
set(_opencv_version 3)
|
||||
endif()
|
||||
|
||||
find_package(OpenCV ${_opencv_version} REQUIRED COMPONENTS core imgproc calib3d)
|
||||
if ("${OpenCV_VERSION_MINOR}" LESS "9")
|
||||
message(STATUS "OpenCV version too low, using vendored ArUco package")
|
||||
include(vendor/VendorOpenCV.cmake)
|
||||
else()
|
||||
message(STATUS "Using system OpenCV ArUco package")
|
||||
find_package(OpenCV 3 REQUIRED COMPONENTS aruco)
|
||||
find_package(OpenCV ${_opencv_version} REQUIRED COMPONENTS aruco)
|
||||
endif()
|
||||
message(STATUS "OpenCV include dirs: ${OpenCV_INCLUDE_DIRS}")
|
||||
message(STATUS "OpenCV libraries: ${OpenCV_LIBRARIES}")
|
||||
@@ -172,6 +180,13 @@ target_link_libraries(aruco_pose
|
||||
${OpenCV_LIBRARIES}
|
||||
)
|
||||
|
||||
# Prevent aruco_pose from having undefined symbols
|
||||
set_property(TARGET aruco_pose
|
||||
APPEND
|
||||
PROPERTY LINK_FLAGS
|
||||
-Wl,--no-undefined
|
||||
)
|
||||
|
||||
#############
|
||||
## Install ##
|
||||
#############
|
||||
@@ -207,6 +222,10 @@ target_link_libraries(aruco_pose
|
||||
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||
# )
|
||||
|
||||
catkin_install_python(PROGRAMS src/genmap.py
|
||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||
#############
|
||||
## Testing ##
|
||||
#############
|
||||
|
||||
@@ -3,26 +3,11 @@
|
||||
|
||||
#include "draw.h"
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::aruco;
|
||||
|
||||
static void _cvProjectPoints2( const CvMat* object_points, const CvMat* rotation_vector,
|
||||
const CvMat* translation_vector, const CvMat* camera_matrix,
|
||||
const CvMat* distortion_coeffs, CvMat* image_points,
|
||||
CvMat* dpdrot CV_DEFAULT(NULL), CvMat* dpdt CV_DEFAULT(NULL),
|
||||
CvMat* dpdf CV_DEFAULT(NULL), CvMat* dpdc CV_DEFAULT(NULL),
|
||||
CvMat* dpddist CV_DEFAULT(NULL),
|
||||
double aspect_ratio CV_DEFAULT(0));
|
||||
|
||||
static void _projectPoints( InputArray objectPoints,
|
||||
InputArray rvec, InputArray tvec,
|
||||
InputArray cameraMatrix, InputArray distCoeffs,
|
||||
OutputArray imagePoints,
|
||||
OutputArray jacobian = noArray(),
|
||||
double aspectRatio = 0 );
|
||||
|
||||
|
||||
void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginSize,
|
||||
int borderBits, bool drawAxis) {
|
||||
|
||||
@@ -142,35 +127,194 @@ void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginS
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw a (potentially partially visible) line. */
|
||||
static void linePartial(InputOutputArray image, Point3f p1, Point3f p2, const Scalar& color,
|
||||
int thickness = 1, int lineType = LINE_8, int shift = 0)
|
||||
/**
|
||||
* @brief Convert point coordinates from world space to camera space.
|
||||
*
|
||||
* @param points A vector of points in world space.
|
||||
* @param rvec Rotation matrix or Rodrigues rotation vector.
|
||||
* @param tvec Translation vector from world to camera space.
|
||||
*
|
||||
* @return A vector of points in camera space.
|
||||
*/
|
||||
template<typename CvPointType>
|
||||
static std::vector<CvPointType> worldToCamera(const std::vector<CvPointType>& points,
|
||||
const cv::Mat& rvec, const cv::Mat& tvec)
|
||||
{
|
||||
// If both points are behind the screen, don't draw anything
|
||||
if (p1.z <= 0 && p2.z <= 0) {
|
||||
return;
|
||||
// We operate with CV_64F matrices internally to avoid precision loss
|
||||
cv::Mat rvec_64f;
|
||||
cv::Mat tvec_64f;
|
||||
rvec.convertTo(rvec_64f, CV_64F);
|
||||
tvec.convertTo(tvec_64f, CV_64F);
|
||||
|
||||
// Convert Rodrigues vector to rotation matrix
|
||||
cv::Mat rmat;
|
||||
if ((rvec_64f.cols == 3 && rvec_64f.rows == 1) ||
|
||||
(rvec_64f.cols == 1 && rvec_64f.rows == 3))
|
||||
{
|
||||
Rodrigues(rvec_64f, rmat);
|
||||
}
|
||||
Point2f p1p{p1.x, p1.y};
|
||||
Point2f p2p{p2.x, p2.y};
|
||||
// If points are on the different sides of the plane, compute intersection point
|
||||
if (p1.z * p2.z < 0) {
|
||||
// Compute intersection point with the screen
|
||||
// We denote alpha as such:
|
||||
// xi = (1 - alpha) * x1 + alpha * x2
|
||||
// yi = (1 - alpha) * y1 + alpha * y2
|
||||
// zi = (1 - alpha) * z1 + alpha * z2 = 0
|
||||
// Thus, alpha can be expressed as
|
||||
// alpha = z1 / (z1 - z2)
|
||||
float alpha = p1.z / (p1.z - p2.z);
|
||||
Point2f pi{(1 - alpha) * p1.x + alpha * p2.x, (1 - alpha) * p1.y + alpha * p2.y};
|
||||
// Now, if z1 is negative, we draw the line from (xi, yi) to (x2, y2), else we draw from (x1, y1) to (xi, yi)
|
||||
if (p1.z < 0) {
|
||||
p1p = pi;
|
||||
} else {
|
||||
p2p = pi;
|
||||
}
|
||||
else
|
||||
{
|
||||
rmat = rvec_64f.clone();
|
||||
}
|
||||
line(image, p1p, p2p, color, thickness, lineType, shift);
|
||||
// Make sure tvec has a size of (3, 1)
|
||||
if (tvec_64f.rows == 1)
|
||||
{
|
||||
tvec_64f = tvec_64f.t();
|
||||
}
|
||||
std::vector<CvPointType> result;
|
||||
result.reserve(points.size());
|
||||
for(const auto& point : points)
|
||||
{
|
||||
// Calculate point coordinates in camera frame
|
||||
// static_casts are here to silence potential narrowing conversion warnings
|
||||
CvPointType camPoint{
|
||||
static_cast<decltype(CvPointType::x)>(point.x * rmat.at<double>(0,0) + point.y * rmat.at<double>(0,1) + point.z * rmat.at<double>(0,2) + tvec_64f.at<double>(0)),
|
||||
static_cast<decltype(CvPointType::y)>(point.x * rmat.at<double>(1,0) + point.y * rmat.at<double>(1,1) + point.z * rmat.at<double>(1,2) + tvec_64f.at<double>(1)),
|
||||
static_cast<decltype(CvPointType::z)>(point.x * rmat.at<double>(2,0) + point.y * rmat.at<double>(2,1) + point.z * rmat.at<double>(2,2) + tvec_64f.at<double>(2))
|
||||
};
|
||||
result.push_back(camPoint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Project points from camera space to screen space, applying distortion in the process.
|
||||
*
|
||||
* @param points A vector of points in camera space.
|
||||
* @param cameraMatrix OpenCV intrinsic camera matrix.
|
||||
* @param distCoeffs OpenCV distortion model coefficients.
|
||||
*
|
||||
* @return A vector of points in screen space.
|
||||
*/
|
||||
template<typename CvPointType>
|
||||
static std::vector<CvPointType> cameraToScreen(const std::vector<CvPointType>& points,
|
||||
const cv::Mat& cameraMatrix,
|
||||
const cv::Mat& distCoeffs)
|
||||
{
|
||||
// We operate with CV_64F matrices internally to avoid precision loss
|
||||
cv::Mat cm_64f; // camera matrix, CV_64F
|
||||
cv::Mat dc_64f; // distortion coefficients, CV_64F
|
||||
cameraMatrix.convertTo(cm_64f, CV_64F);
|
||||
distCoeffs.convertTo(dc_64f, CV_64F);
|
||||
|
||||
// Make sure distortion vector has a size of (N, 1)
|
||||
if (dc_64f.rows == 1)
|
||||
{
|
||||
dc_64f = dc_64f.t();
|
||||
}
|
||||
|
||||
// We will always use 12 distortion coefficients,
|
||||
// and we can safely pad missing ones with zeroes
|
||||
dc_64f.resize(12, 0.0);
|
||||
|
||||
std::vector<CvPointType> result;
|
||||
result.reserve(points.size());
|
||||
|
||||
for(const auto& point : points)
|
||||
{
|
||||
// Apply perspective projection, preserving initial Z coordinate
|
||||
// Always use double-precision
|
||||
cv::Point3d camPoint{
|
||||
point.x / point.z,
|
||||
point.y / point.z,
|
||||
point.z
|
||||
};
|
||||
|
||||
// Apply distortion
|
||||
// Note that we do not consider tilted sensor distortion
|
||||
// r^2 - distance from the image center squared
|
||||
double r2 = camPoint.x * camPoint.x + camPoint.y * camPoint.y;
|
||||
// r^4 - same, but to the 4th power
|
||||
double r4 = r2 * r2;
|
||||
// r^6 - same, but to the 6th power
|
||||
double r6 = r4 * r2;
|
||||
// tg1 - first tangential shift factor (2 * x * y)
|
||||
double tg1 = 2 * camPoint.x * camPoint.y;
|
||||
// tg2 - second tangential shift factor (r^2 + 2 * x^2)
|
||||
double tg2 = r2 + 2 * camPoint.x * camPoint.x;
|
||||
// tg3 - third tangential shift factor (r^2 + 2 * y^2)
|
||||
double tg3 = r2 + 2 * camPoint.y * camPoint.y;
|
||||
// polynomial distortion factor (numerator)
|
||||
double pndist = 1 + dc_64f.at<double>(0) * r2 + dc_64f.at<double>(1) * r4 + dc_64f.at<double>(4) * r6;
|
||||
// polynomial distortion factror (denominator)
|
||||
double pddist = 1.0 / (1 + dc_64f.at<double>(5) * r2 + dc_64f.at<double>(6) * r4 + dc_64f.at<double>(7) * r6);
|
||||
// Distorted point coordinates (always double-precision)
|
||||
cv::Point3d distortedPoint{
|
||||
camPoint.x * pndist * pddist + dc_64f.at<double>(2) * tg1 + dc_64f.at<double>(3) * tg2 + dc_64f.at<double>(8) * r2 + dc_64f.at<double>(9) * r4,
|
||||
camPoint.y * pndist * pddist + dc_64f.at<double>(2) * tg3 + dc_64f.at<double>(3) * tg1 + dc_64f.at<double>(10) * r2 + dc_64f.at<double>(11) * r4,
|
||||
camPoint.z
|
||||
};
|
||||
|
||||
// Convert to screen space
|
||||
// We use static_cast here to silence potential warnings about narrowing conversions
|
||||
// (we expect that to be the case)
|
||||
CvPointType screenPoint{
|
||||
static_cast<decltype(CvPointType::x)>(distortedPoint.x * cm_64f.at<double>(0, 0) + cm_64f.at<double>(0, 2)),
|
||||
static_cast<decltype(CvPointType::y)>(distortedPoint.y * cm_64f.at<double>(1, 1) + cm_64f.at<double>(1, 2)),
|
||||
static_cast<decltype(CvPointType::z)>(distortedPoint.z)
|
||||
};
|
||||
|
||||
result.push_back(screenPoint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clip a line against a clip plane.
|
||||
*
|
||||
* This function "clips" a line (described by two points in *camera space*)
|
||||
* against a clip plane that is `clipPlaneDistance` meters away from the
|
||||
* camera focal point. If both points are further away from the focal point
|
||||
* than `clipPlaneDistance`, they will be returned unmodified. If one of the
|
||||
* points is behind the clipping plane, a point *on* the clipping plane will
|
||||
* be computed and returned as one of the points.
|
||||
*
|
||||
* If none of the points are visible, an empty vector will be returned.
|
||||
*
|
||||
* @param p1 First point on the line, in camera space.
|
||||
* @param p2 Second point on the line, in camera space.
|
||||
* @param clipPlaneDistance Distance from the focal point to the clipping plane.
|
||||
* @return A vector of zero or two points on the clipped line, in camera space.
|
||||
*/
|
||||
static std::vector<Point3f> lineClip(Point3f p1, Point3f p2, float clipPlaneDistance = 0.1f)
|
||||
{
|
||||
// We don't need to compute an intersection if both points are
|
||||
// behind us
|
||||
if (p1.z < clipPlaneDistance && p2.z < clipPlaneDistance)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
// We don't need to compute an intersection if both points are
|
||||
// in front of us
|
||||
if (p1.z > clipPlaneDistance && p2.z > clipPlaneDistance)
|
||||
{
|
||||
return {p1, p2};
|
||||
}
|
||||
// We don't really want to compute an intersection if both Z coordinates
|
||||
// are sufficiently close to each other
|
||||
if (std::abs(p1.z - p2.z) < 0.0001) // The number here is chosen arbitrarily
|
||||
{
|
||||
return {p1, p2};
|
||||
}
|
||||
// We compute the intersection as such:
|
||||
// zi = (1 - alpha) * p1.z + alpha * p2.z = clipPlaneDistance
|
||||
// alpha = (p1.z - clipPlaneDistance) / (p1.z - p2.z)
|
||||
double alpha = (p1.z - clipPlaneDistance) / (p1.z - p2.z);
|
||||
Point3f clipPlanePoint{
|
||||
static_cast<float>((1 - alpha) * p1.x + alpha * p2.x),
|
||||
static_cast<float>((1 - alpha) * p1.y + alpha * p2.y),
|
||||
clipPlaneDistance
|
||||
};
|
||||
if (p1.z < clipPlaneDistance)
|
||||
{
|
||||
return {clipPlanePoint, p2};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {p1, clipPlanePoint};
|
||||
}
|
||||
// Unreachable?
|
||||
}
|
||||
|
||||
void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _distCoeffs,
|
||||
@@ -186,647 +330,23 @@ void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _di
|
||||
axisPoints.push_back(Point3f(length, 0, 0));
|
||||
axisPoints.push_back(Point3f(0, length, 0));
|
||||
axisPoints.push_back(Point3f(0, 0, length));
|
||||
std::vector<Point3f> imagePointsZ;
|
||||
_projectPoints(axisPoints, _rvec, _tvec, _cameraMatrix, _distCoeffs, imagePointsZ);
|
||||
|
||||
// draw axis lines
|
||||
linePartial(_image, imagePointsZ[0], imagePointsZ[1], Scalar(0, 0, 255), 3);
|
||||
linePartial(_image, imagePointsZ[0], imagePointsZ[2], Scalar(0, 255, 0), 3);
|
||||
linePartial(_image, imagePointsZ[0], imagePointsZ[3], Scalar(255, 0, 0), 3);
|
||||
}
|
||||
|
||||
static CvMat _cvMat(const cv::Mat& m)
|
||||
{
|
||||
CvMat self;
|
||||
CV_DbgAssert(m.dims <= 2);
|
||||
self = cvMat(m.rows, m.dims == 1 ? 1 : m.cols, m.type(), m.data);
|
||||
self.step = (int)m.step[0];
|
||||
self.type = (self.type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG);
|
||||
return self;
|
||||
}
|
||||
|
||||
static void _projectPoints( InputArray _opoints,
|
||||
InputArray _rvec,
|
||||
InputArray _tvec,
|
||||
InputArray _cameraMatrix,
|
||||
InputArray _distCoeffs,
|
||||
OutputArray _ipoints,
|
||||
OutputArray _jacobian,
|
||||
double aspectRatio )
|
||||
{
|
||||
Mat opoints = _opoints.getMat();
|
||||
int npoints = opoints.checkVector(3), depth = opoints.depth();
|
||||
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_64F));
|
||||
|
||||
CvMat dpdrot, dpdt, dpdf, dpdc, dpddist;
|
||||
CvMat *pdpdrot = 0, *pdpdt = 0, *pdpdf = 0, *pdpdc = 0, *pdpddist = 0;
|
||||
|
||||
CV_Assert(_ipoints.needed());
|
||||
|
||||
_ipoints.create(npoints, 1, CV_MAKETYPE(depth, 3), -1, true);
|
||||
Mat imagePoints = _ipoints.getMat();
|
||||
CvMat c_imagePoints = _cvMat(imagePoints);
|
||||
CvMat c_objectPoints = _cvMat(opoints);
|
||||
Mat cameraMatrix = _cameraMatrix.getMat();
|
||||
|
||||
Mat rvec = _rvec.getMat(), tvec = _tvec.getMat();
|
||||
CvMat c_cameraMatrix = _cvMat(cameraMatrix);
|
||||
CvMat c_rvec = _cvMat(rvec), c_tvec = _cvMat(tvec);
|
||||
|
||||
double dc0buf[5] = {0};
|
||||
Mat dc0(5, 1, CV_64F, dc0buf);
|
||||
Mat distCoeffs = _distCoeffs.getMat();
|
||||
if (distCoeffs.empty())
|
||||
distCoeffs = dc0;
|
||||
CvMat c_distCoeffs = _cvMat(distCoeffs);
|
||||
int ndistCoeffs = distCoeffs.rows + distCoeffs.cols - 1;
|
||||
|
||||
Mat jacobian;
|
||||
if (_jacobian.needed())
|
||||
auto camAxisPoints = worldToCamera(axisPoints, _rvec.getMat(), _tvec.getMat());
|
||||
auto axisX = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[1]), _cameraMatrix.getMat(), _distCoeffs.getMat());
|
||||
auto axisY = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[2]), _cameraMatrix.getMat(), _distCoeffs.getMat());
|
||||
auto axisZ = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[3]), _cameraMatrix.getMat(), _distCoeffs.getMat());
|
||||
if (axisX.size() > 0)
|
||||
{
|
||||
_jacobian.create(npoints * 2, 3 + 3 + 2 + 2 + ndistCoeffs, CV_64F);
|
||||
jacobian = _jacobian.getMat();
|
||||
pdpdrot = &(dpdrot = _cvMat(jacobian.colRange(0, 3)));
|
||||
pdpdt = &(dpdt = _cvMat(jacobian.colRange(3, 6)));
|
||||
pdpdf = &(dpdf = _cvMat(jacobian.colRange(6, 8)));
|
||||
pdpdc = &(dpdc = _cvMat(jacobian.colRange(8, 10)));
|
||||
pdpddist = &(dpddist = _cvMat(jacobian.colRange(10, 10 + ndistCoeffs)));
|
||||
line(_image, Point2f{axisX[0].x, axisX[0].y}, Point2f{axisX[1].x, axisX[1].y},
|
||||
Scalar(0, 0, 255), 3);
|
||||
}
|
||||
if (axisY.size() > 0)
|
||||
{
|
||||
line(_image, Point2f{axisY[0].x, axisY[0].y}, Point2f{axisY[1].x, axisY[1].y},
|
||||
Scalar(0, 255, 0), 3);
|
||||
}
|
||||
if (axisZ.size() > 0)
|
||||
{
|
||||
line(_image, Point2f{axisZ[0].x, axisZ[0].y}, Point2f{axisZ[1].x, axisZ[1].y},
|
||||
Scalar(255, 0, 0), 3);
|
||||
}
|
||||
|
||||
_cvProjectPoints2(&c_objectPoints, &c_rvec, &c_tvec, &c_cameraMatrix, &c_distCoeffs,
|
||||
&c_imagePoints, pdpdrot, pdpdt, pdpdf, pdpdc, pdpddist, aspectRatio);
|
||||
}
|
||||
|
||||
namespace _detail
|
||||
{
|
||||
template <typename FLOAT>
|
||||
void computeTiltProjectionMatrix(FLOAT tauX,
|
||||
FLOAT tauY,
|
||||
Matx<FLOAT, 3, 3>* matTilt = 0,
|
||||
Matx<FLOAT, 3, 3>* dMatTiltdTauX = 0,
|
||||
Matx<FLOAT, 3, 3>* dMatTiltdTauY = 0,
|
||||
Matx<FLOAT, 3, 3>* invMatTilt = 0)
|
||||
{
|
||||
FLOAT cTauX = cos(tauX);
|
||||
FLOAT sTauX = sin(tauX);
|
||||
FLOAT cTauY = cos(tauY);
|
||||
FLOAT sTauY = sin(tauY);
|
||||
Matx<FLOAT, 3, 3> matRotX = Matx<FLOAT, 3, 3>(1,0,0,0,cTauX,sTauX,0,-sTauX,cTauX);
|
||||
Matx<FLOAT, 3, 3> matRotY = Matx<FLOAT, 3, 3>(cTauY,0,-sTauY,0,1,0,sTauY,0,cTauY);
|
||||
Matx<FLOAT, 3, 3> matRotXY = matRotY * matRotX;
|
||||
Matx<FLOAT, 3, 3> matProjZ = Matx<FLOAT, 3, 3>(matRotXY(2,2),0,-matRotXY(0,2),0,matRotXY(2,2),-matRotXY(1,2),0,0,1);
|
||||
if (matTilt)
|
||||
{
|
||||
// Matrix for trapezoidal distortion of tilted image sensor
|
||||
*matTilt = matProjZ * matRotXY;
|
||||
}
|
||||
if (dMatTiltdTauX)
|
||||
{
|
||||
// Derivative with respect to tauX
|
||||
Matx<FLOAT, 3, 3> dMatRotXYdTauX = matRotY * Matx<FLOAT, 3, 3>(0,0,0,0,-sTauX,cTauX,0,-cTauX,-sTauX);
|
||||
Matx<FLOAT, 3, 3> dMatProjZdTauX = Matx<FLOAT, 3, 3>(dMatRotXYdTauX(2,2),0,-dMatRotXYdTauX(0,2),
|
||||
0,dMatRotXYdTauX(2,2),-dMatRotXYdTauX(1,2),0,0,0);
|
||||
*dMatTiltdTauX = (matProjZ * dMatRotXYdTauX) + (dMatProjZdTauX * matRotXY);
|
||||
}
|
||||
if (dMatTiltdTauY)
|
||||
{
|
||||
// Derivative with respect to tauY
|
||||
Matx<FLOAT, 3, 3> dMatRotXYdTauY = Matx<FLOAT, 3, 3>(-sTauY,0,-cTauY,0,0,0,cTauY,0,-sTauY) * matRotX;
|
||||
Matx<FLOAT, 3, 3> dMatProjZdTauY = Matx<FLOAT, 3, 3>(dMatRotXYdTauY(2,2),0,-dMatRotXYdTauY(0,2),
|
||||
0,dMatRotXYdTauY(2,2),-dMatRotXYdTauY(1,2),0,0,0);
|
||||
*dMatTiltdTauY = (matProjZ * dMatRotXYdTauY) + (dMatProjZdTauY * matRotXY);
|
||||
}
|
||||
if (invMatTilt)
|
||||
{
|
||||
FLOAT inv = 1./matRotXY(2,2);
|
||||
Matx<FLOAT, 3, 3> invMatProjZ = Matx<FLOAT, 3, 3>(inv,0,inv*matRotXY(0,2),0,inv,inv*matRotXY(1,2),0,0,1);
|
||||
*invMatTilt = matRotXY.t()*invMatProjZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char* cvDistCoeffErr = "Distortion coefficients must be 1x4, 4x1, 1x5, 5x1, 1x8, 8x1, 1x12, 12x1, 1x14 or 14x1 floating-point vector";
|
||||
|
||||
static void _cvProjectPoints2Internal( const CvMat* objectPoints,
|
||||
const CvMat* r_vec,
|
||||
const CvMat* t_vec,
|
||||
const CvMat* A,
|
||||
const CvMat* distCoeffs,
|
||||
CvMat* imagePoints, CvMat* dpdr CV_DEFAULT(NULL),
|
||||
CvMat* dpdt CV_DEFAULT(NULL), CvMat* dpdf CV_DEFAULT(NULL),
|
||||
CvMat* dpdc CV_DEFAULT(NULL), CvMat* dpdk CV_DEFAULT(NULL),
|
||||
CvMat* dpdo CV_DEFAULT(NULL),
|
||||
double aspectRatio CV_DEFAULT(0) )
|
||||
{
|
||||
Ptr<CvMat> matM, _m;
|
||||
Ptr<CvMat> _dpdr, _dpdt, _dpdc, _dpdf, _dpdk;
|
||||
Ptr<CvMat> _dpdo;
|
||||
|
||||
int i, j, count;
|
||||
int calc_derivatives;
|
||||
const CvPoint3D64f* M;
|
||||
CvPoint3D64f* m;
|
||||
double r[3], R[9], dRdr[27], t[3], a[9], k[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}, fx, fy, cx, cy;
|
||||
Matx33d matTilt = Matx33d::eye();
|
||||
Matx33d dMatTiltdTauX(0,0,0,0,0,0,0,-1,0);
|
||||
Matx33d dMatTiltdTauY(0,0,0,0,0,0,1,0,0);
|
||||
CvMat _r, _t, _a = cvMat( 3, 3, CV_64F, a ), _k;
|
||||
CvMat matR = cvMat( 3, 3, CV_64F, R ), _dRdr = cvMat( 3, 9, CV_64F, dRdr );
|
||||
double *dpdr_p = 0, *dpdt_p = 0, *dpdk_p = 0, *dpdf_p = 0, *dpdc_p = 0;
|
||||
double* dpdo_p = 0;
|
||||
int dpdr_step = 0, dpdt_step = 0, dpdk_step = 0, dpdf_step = 0, dpdc_step = 0;
|
||||
int dpdo_step = 0;
|
||||
bool fixedAspectRatio = aspectRatio > FLT_EPSILON;
|
||||
|
||||
if( !CV_IS_MAT(objectPoints) || !CV_IS_MAT(r_vec) ||
|
||||
!CV_IS_MAT(t_vec) || !CV_IS_MAT(A) ||
|
||||
/*!CV_IS_MAT(distCoeffs) ||*/ !CV_IS_MAT(imagePoints) )
|
||||
CV_Error( CV_StsBadArg, "One of required arguments is not a valid matrix" );
|
||||
|
||||
int total = objectPoints->rows * objectPoints->cols * CV_MAT_CN(objectPoints->type);
|
||||
if(total % 3 != 0)
|
||||
{
|
||||
//we have stopped support of homogeneous coordinates because it cause ambiguity in interpretation of the input data
|
||||
CV_Error( CV_StsBadArg, "Homogeneous coordinates are not supported" );
|
||||
}
|
||||
count = total / 3;
|
||||
|
||||
if( CV_IS_CONT_MAT(objectPoints->type) &&
|
||||
(CV_MAT_DEPTH(objectPoints->type) == CV_32F || CV_MAT_DEPTH(objectPoints->type) == CV_64F)&&
|
||||
((objectPoints->rows == 1 && CV_MAT_CN(objectPoints->type) == 3) ||
|
||||
(objectPoints->rows == count && CV_MAT_CN(objectPoints->type)*objectPoints->cols == 3) ||
|
||||
(objectPoints->rows == 3 && CV_MAT_CN(objectPoints->type) == 1 && objectPoints->cols == count)))
|
||||
{
|
||||
matM.reset(cvCreateMat( objectPoints->rows, objectPoints->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(objectPoints->type)) ));
|
||||
cvConvert(objectPoints, matM);
|
||||
}
|
||||
else
|
||||
{
|
||||
// matM = cvCreateMat( 1, count, CV_64FC3 );
|
||||
// cvConvertPointsHomogeneous( objectPoints, matM );
|
||||
CV_Error( CV_StsBadArg, "Homogeneous coordinates are not supported" );
|
||||
}
|
||||
|
||||
if( CV_IS_CONT_MAT(imagePoints->type) &&
|
||||
(CV_MAT_DEPTH(imagePoints->type) == CV_32F || CV_MAT_DEPTH(imagePoints->type) == CV_64F) &&
|
||||
((imagePoints->rows == 1 && CV_MAT_CN(imagePoints->type) == 3) ||
|
||||
(imagePoints->rows == count && CV_MAT_CN(imagePoints->type)*imagePoints->cols == 3) ||
|
||||
(imagePoints->rows == 3 && CV_MAT_CN(imagePoints->type) == 1 && imagePoints->cols == count)))
|
||||
{
|
||||
_m.reset(cvCreateMat( imagePoints->rows, imagePoints->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(imagePoints->type)) ));
|
||||
cvConvert(imagePoints, _m);
|
||||
}
|
||||
else
|
||||
{
|
||||
// _m = cvCreateMat( 1, count, CV_64FC2 );
|
||||
CV_Error( CV_StsBadArg, "Homogeneous coordinates are not supported" );
|
||||
}
|
||||
|
||||
M = (CvPoint3D64f*)matM->data.db;
|
||||
m = (CvPoint3D64f*)_m->data.db;
|
||||
|
||||
if( (CV_MAT_DEPTH(r_vec->type) != CV_64F && CV_MAT_DEPTH(r_vec->type) != CV_32F) ||
|
||||
(((r_vec->rows != 1 && r_vec->cols != 1) ||
|
||||
r_vec->rows*r_vec->cols*CV_MAT_CN(r_vec->type) != 3) &&
|
||||
((r_vec->rows != 3 && r_vec->cols != 3) || CV_MAT_CN(r_vec->type) != 1)))
|
||||
CV_Error( CV_StsBadArg, "Rotation must be represented by 1x3 or 3x1 "
|
||||
"floating-point rotation vector, or 3x3 rotation matrix" );
|
||||
|
||||
if( r_vec->rows == 3 && r_vec->cols == 3 )
|
||||
{
|
||||
_r = cvMat( 3, 1, CV_64FC1, r );
|
||||
cvRodrigues2( r_vec, &_r );
|
||||
cvRodrigues2( &_r, &matR, &_dRdr );
|
||||
cvCopy( r_vec, &matR );
|
||||
}
|
||||
else
|
||||
{
|
||||
_r = cvMat( r_vec->rows, r_vec->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(r_vec->type)), r );
|
||||
cvConvert( r_vec, &_r );
|
||||
cvRodrigues2( &_r, &matR, &_dRdr );
|
||||
}
|
||||
|
||||
if( (CV_MAT_DEPTH(t_vec->type) != CV_64F && CV_MAT_DEPTH(t_vec->type) != CV_32F) ||
|
||||
(t_vec->rows != 1 && t_vec->cols != 1) ||
|
||||
t_vec->rows*t_vec->cols*CV_MAT_CN(t_vec->type) != 3 )
|
||||
CV_Error( CV_StsBadArg,
|
||||
"Translation vector must be 1x3 or 3x1 floating-point vector" );
|
||||
|
||||
_t = cvMat( t_vec->rows, t_vec->cols, CV_MAKETYPE(CV_64F,CV_MAT_CN(t_vec->type)), t );
|
||||
cvConvert( t_vec, &_t );
|
||||
|
||||
if( (CV_MAT_TYPE(A->type) != CV_64FC1 && CV_MAT_TYPE(A->type) != CV_32FC1) ||
|
||||
A->rows != 3 || A->cols != 3 )
|
||||
CV_Error( CV_StsBadArg, "Instrinsic parameters must be 3x3 floating-point matrix" );
|
||||
|
||||
cvConvert( A, &_a );
|
||||
fx = a[0]; fy = a[4];
|
||||
cx = a[2]; cy = a[5];
|
||||
|
||||
if( fixedAspectRatio )
|
||||
fx = fy*aspectRatio;
|
||||
|
||||
if( distCoeffs )
|
||||
{
|
||||
if( !CV_IS_MAT(distCoeffs) ||
|
||||
(CV_MAT_DEPTH(distCoeffs->type) != CV_64F &&
|
||||
CV_MAT_DEPTH(distCoeffs->type) != CV_32F) ||
|
||||
(distCoeffs->rows != 1 && distCoeffs->cols != 1) ||
|
||||
(distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 4 &&
|
||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 5 &&
|
||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 8 &&
|
||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 12 &&
|
||||
distCoeffs->rows*distCoeffs->cols*CV_MAT_CN(distCoeffs->type) != 14) )
|
||||
CV_Error( CV_StsBadArg, cvDistCoeffErr );
|
||||
|
||||
_k = cvMat( distCoeffs->rows, distCoeffs->cols,
|
||||
CV_MAKETYPE(CV_64F,CV_MAT_CN(distCoeffs->type)), k );
|
||||
cvConvert( distCoeffs, &_k );
|
||||
if(k[12] != 0 || k[13] != 0)
|
||||
{
|
||||
_detail::computeTiltProjectionMatrix(k[12], k[13],
|
||||
&matTilt, &dMatTiltdTauX, &dMatTiltdTauY);
|
||||
}
|
||||
}
|
||||
|
||||
if( dpdr )
|
||||
{
|
||||
if( !CV_IS_MAT(dpdr) ||
|
||||
(CV_MAT_TYPE(dpdr->type) != CV_32FC1 &&
|
||||
CV_MAT_TYPE(dpdr->type) != CV_64FC1) ||
|
||||
dpdr->rows != count*2 || dpdr->cols != 3 )
|
||||
CV_Error( CV_StsBadArg, "dp/drot must be 2Nx3 floating-point matrix" );
|
||||
|
||||
if( CV_MAT_TYPE(dpdr->type) == CV_64FC1 )
|
||||
{
|
||||
_dpdr.reset(cvCloneMat(dpdr));
|
||||
}
|
||||
else
|
||||
_dpdr.reset(cvCreateMat( 2*count, 3, CV_64FC1 ));
|
||||
dpdr_p = _dpdr->data.db;
|
||||
dpdr_step = _dpdr->step/sizeof(dpdr_p[0]);
|
||||
}
|
||||
|
||||
if( dpdt )
|
||||
{
|
||||
if( !CV_IS_MAT(dpdt) ||
|
||||
(CV_MAT_TYPE(dpdt->type) != CV_32FC1 &&
|
||||
CV_MAT_TYPE(dpdt->type) != CV_64FC1) ||
|
||||
dpdt->rows != count*2 || dpdt->cols != 3 )
|
||||
CV_Error( CV_StsBadArg, "dp/dT must be 2Nx3 floating-point matrix" );
|
||||
|
||||
if( CV_MAT_TYPE(dpdt->type) == CV_64FC1 )
|
||||
{
|
||||
_dpdt.reset(cvCloneMat(dpdt));
|
||||
}
|
||||
else
|
||||
_dpdt.reset(cvCreateMat( 2*count, 3, CV_64FC1 ));
|
||||
dpdt_p = _dpdt->data.db;
|
||||
dpdt_step = _dpdt->step/sizeof(dpdt_p[0]);
|
||||
}
|
||||
|
||||
if( dpdf )
|
||||
{
|
||||
if( !CV_IS_MAT(dpdf) ||
|
||||
(CV_MAT_TYPE(dpdf->type) != CV_32FC1 && CV_MAT_TYPE(dpdf->type) != CV_64FC1) ||
|
||||
dpdf->rows != count*2 || dpdf->cols != 2 )
|
||||
CV_Error( CV_StsBadArg, "dp/df must be 2Nx2 floating-point matrix" );
|
||||
|
||||
if( CV_MAT_TYPE(dpdf->type) == CV_64FC1 )
|
||||
{
|
||||
_dpdf.reset(cvCloneMat(dpdf));
|
||||
}
|
||||
else
|
||||
_dpdf.reset(cvCreateMat( 2*count, 2, CV_64FC1 ));
|
||||
dpdf_p = _dpdf->data.db;
|
||||
dpdf_step = _dpdf->step/sizeof(dpdf_p[0]);
|
||||
}
|
||||
|
||||
if( dpdc )
|
||||
{
|
||||
if( !CV_IS_MAT(dpdc) ||
|
||||
(CV_MAT_TYPE(dpdc->type) != CV_32FC1 && CV_MAT_TYPE(dpdc->type) != CV_64FC1) ||
|
||||
dpdc->rows != count*2 || dpdc->cols != 2 )
|
||||
CV_Error( CV_StsBadArg, "dp/dc must be 2Nx2 floating-point matrix" );
|
||||
|
||||
if( CV_MAT_TYPE(dpdc->type) == CV_64FC1 )
|
||||
{
|
||||
_dpdc.reset(cvCloneMat(dpdc));
|
||||
}
|
||||
else
|
||||
_dpdc.reset(cvCreateMat( 2*count, 2, CV_64FC1 ));
|
||||
dpdc_p = _dpdc->data.db;
|
||||
dpdc_step = _dpdc->step/sizeof(dpdc_p[0]);
|
||||
}
|
||||
|
||||
if( dpdk )
|
||||
{
|
||||
if( !CV_IS_MAT(dpdk) ||
|
||||
(CV_MAT_TYPE(dpdk->type) != CV_32FC1 && CV_MAT_TYPE(dpdk->type) != CV_64FC1) ||
|
||||
dpdk->rows != count*2 || (dpdk->cols != 14 && dpdk->cols != 12 && dpdk->cols != 8 && dpdk->cols != 5 && dpdk->cols != 4 && dpdk->cols != 2) )
|
||||
CV_Error( CV_StsBadArg, "dp/df must be 2Nx14, 2Nx12, 2Nx8, 2Nx5, 2Nx4 or 2Nx2 floating-point matrix" );
|
||||
|
||||
if( !distCoeffs )
|
||||
CV_Error( CV_StsNullPtr, "distCoeffs is NULL while dpdk is not" );
|
||||
|
||||
if( CV_MAT_TYPE(dpdk->type) == CV_64FC1 )
|
||||
{
|
||||
_dpdk.reset(cvCloneMat(dpdk));
|
||||
}
|
||||
else
|
||||
_dpdk.reset(cvCreateMat( dpdk->rows, dpdk->cols, CV_64FC1 ));
|
||||
dpdk_p = _dpdk->data.db;
|
||||
dpdk_step = _dpdk->step/sizeof(dpdk_p[0]);
|
||||
}
|
||||
|
||||
if( dpdo )
|
||||
{
|
||||
if( !CV_IS_MAT( dpdo ) || ( CV_MAT_TYPE( dpdo->type ) != CV_32FC1
|
||||
&& CV_MAT_TYPE( dpdo->type ) != CV_64FC1 )
|
||||
|| dpdo->rows != count * 2 || dpdo->cols != count * 3 )
|
||||
CV_Error( CV_StsBadArg, "dp/do must be 2Nx3N floating-point matrix" );
|
||||
|
||||
if( CV_MAT_TYPE( dpdo->type ) == CV_64FC1 )
|
||||
{
|
||||
_dpdo.reset( cvCloneMat( dpdo ) );
|
||||
}
|
||||
else
|
||||
_dpdo.reset( cvCreateMat( 2 * count, 3 * count, CV_64FC1 ) );
|
||||
cvZero(_dpdo);
|
||||
dpdo_p = _dpdo->data.db;
|
||||
dpdo_step = _dpdo->step / sizeof( dpdo_p[0] );
|
||||
}
|
||||
|
||||
calc_derivatives = dpdr || dpdt || dpdf || dpdc || dpdk || dpdo;
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
double X = M[i].x, Y = M[i].y, Z = M[i].z;
|
||||
double x = R[0]*X + R[1]*Y + R[2]*Z + t[0];
|
||||
double y = R[3]*X + R[4]*Y + R[5]*Z + t[1];
|
||||
double z = R[6]*X + R[7]*Y + R[8]*Z + t[2];
|
||||
double r2, r4, r6, a1, a2, a3, cdist, icdist2;
|
||||
double xd, yd, xd0, yd0, invProj;
|
||||
Vec3d vecTilt;
|
||||
Vec3d dVecTilt;
|
||||
Matx22d dMatTilt;
|
||||
Vec2d dXdYd;
|
||||
|
||||
double z0 = z;
|
||||
z = z ? 1./z : 1;
|
||||
x *= z; y *= z;
|
||||
|
||||
r2 = x*x + y*y;
|
||||
r4 = r2*r2;
|
||||
r6 = r4*r2;
|
||||
a1 = 2*x*y;
|
||||
a2 = r2 + 2*x*x;
|
||||
a3 = r2 + 2*y*y;
|
||||
cdist = 1 + k[0]*r2 + k[1]*r4 + k[4]*r6;
|
||||
icdist2 = 1./(1 + k[5]*r2 + k[6]*r4 + k[7]*r6);
|
||||
xd0 = x*cdist*icdist2 + k[2]*a1 + k[3]*a2 + k[8]*r2+k[9]*r4;
|
||||
yd0 = y*cdist*icdist2 + k[2]*a3 + k[3]*a1 + k[10]*r2+k[11]*r4;
|
||||
|
||||
// additional distortion by projecting onto a tilt plane
|
||||
vecTilt = matTilt*Vec3d(xd0, yd0, 1);
|
||||
invProj = vecTilt(2) ? 1./vecTilt(2) : 1;
|
||||
xd = invProj * vecTilt(0);
|
||||
yd = invProj * vecTilt(1);
|
||||
|
||||
m[i].x = xd*fx + cx;
|
||||
m[i].y = yd*fy + cy;
|
||||
m[i].z = z; // Just put the projected Z coordinate here, we mainly care about the sign
|
||||
|
||||
if( calc_derivatives )
|
||||
{
|
||||
if( dpdc_p )
|
||||
{
|
||||
dpdc_p[0] = 1; dpdc_p[1] = 0; // dp_xdc_x; dp_xdc_y
|
||||
dpdc_p[dpdc_step] = 0;
|
||||
dpdc_p[dpdc_step+1] = 1;
|
||||
dpdc_p += dpdc_step*2;
|
||||
}
|
||||
|
||||
if( dpdf_p )
|
||||
{
|
||||
if( fixedAspectRatio )
|
||||
{
|
||||
dpdf_p[0] = 0; dpdf_p[1] = xd*aspectRatio; // dp_xdf_x; dp_xdf_y
|
||||
dpdf_p[dpdf_step] = 0;
|
||||
dpdf_p[dpdf_step+1] = yd;
|
||||
}
|
||||
else
|
||||
{
|
||||
dpdf_p[0] = xd; dpdf_p[1] = 0;
|
||||
dpdf_p[dpdf_step] = 0;
|
||||
dpdf_p[dpdf_step+1] = yd;
|
||||
}
|
||||
dpdf_p += dpdf_step*2;
|
||||
}
|
||||
for (int row = 0; row < 2; ++row)
|
||||
for (int col = 0; col < 2; ++col)
|
||||
dMatTilt(row,col) = matTilt(row,col)*vecTilt(2)
|
||||
- matTilt(2,col)*vecTilt(row);
|
||||
double invProjSquare = (invProj*invProj);
|
||||
dMatTilt *= invProjSquare;
|
||||
if( dpdk_p )
|
||||
{
|
||||
dXdYd = dMatTilt*Vec2d(x*icdist2*r2, y*icdist2*r2);
|
||||
dpdk_p[0] = fx*dXdYd(0);
|
||||
dpdk_p[dpdk_step] = fy*dXdYd(1);
|
||||
dXdYd = dMatTilt*Vec2d(x*icdist2*r4, y*icdist2*r4);
|
||||
dpdk_p[1] = fx*dXdYd(0);
|
||||
dpdk_p[dpdk_step+1] = fy*dXdYd(1);
|
||||
if( _dpdk->cols > 2 )
|
||||
{
|
||||
dXdYd = dMatTilt*Vec2d(a1, a3);
|
||||
dpdk_p[2] = fx*dXdYd(0);
|
||||
dpdk_p[dpdk_step+2] = fy*dXdYd(1);
|
||||
dXdYd = dMatTilt*Vec2d(a2, a1);
|
||||
dpdk_p[3] = fx*dXdYd(0);
|
||||
dpdk_p[dpdk_step+3] = fy*dXdYd(1);
|
||||
if( _dpdk->cols > 4 )
|
||||
{
|
||||
dXdYd = dMatTilt*Vec2d(x*icdist2*r6, y*icdist2*r6);
|
||||
dpdk_p[4] = fx*dXdYd(0);
|
||||
dpdk_p[dpdk_step+4] = fy*dXdYd(1);
|
||||
|
||||
if( _dpdk->cols > 5 )
|
||||
{
|
||||
dXdYd = dMatTilt*Vec2d(
|
||||
x*cdist*(-icdist2)*icdist2*r2, y*cdist*(-icdist2)*icdist2*r2);
|
||||
dpdk_p[5] = fx*dXdYd(0);
|
||||
dpdk_p[dpdk_step+5] = fy*dXdYd(1);
|
||||
dXdYd = dMatTilt*Vec2d(
|
||||
x*cdist*(-icdist2)*icdist2*r4, y*cdist*(-icdist2)*icdist2*r4);
|
||||
dpdk_p[6] = fx*dXdYd(0);
|
||||
dpdk_p[dpdk_step+6] = fy*dXdYd(1);
|
||||
dXdYd = dMatTilt*Vec2d(
|
||||
x*cdist*(-icdist2)*icdist2*r6, y*cdist*(-icdist2)*icdist2*r6);
|
||||
dpdk_p[7] = fx*dXdYd(0);
|
||||
dpdk_p[dpdk_step+7] = fy*dXdYd(1);
|
||||
if( _dpdk->cols > 8 )
|
||||
{
|
||||
dXdYd = dMatTilt*Vec2d(r2, 0);
|
||||
dpdk_p[8] = fx*dXdYd(0); //s1
|
||||
dpdk_p[dpdk_step+8] = fy*dXdYd(1); //s1
|
||||
dXdYd = dMatTilt*Vec2d(r4, 0);
|
||||
dpdk_p[9] = fx*dXdYd(0); //s2
|
||||
dpdk_p[dpdk_step+9] = fy*dXdYd(1); //s2
|
||||
dXdYd = dMatTilt*Vec2d(0, r2);
|
||||
dpdk_p[10] = fx*dXdYd(0);//s3
|
||||
dpdk_p[dpdk_step+10] = fy*dXdYd(1); //s3
|
||||
dXdYd = dMatTilt*Vec2d(0, r4);
|
||||
dpdk_p[11] = fx*dXdYd(0);//s4
|
||||
dpdk_p[dpdk_step+11] = fy*dXdYd(1); //s4
|
||||
if( _dpdk->cols > 12 )
|
||||
{
|
||||
dVecTilt = dMatTiltdTauX * Vec3d(xd0, yd0, 1);
|
||||
dpdk_p[12] = fx * invProjSquare * (
|
||||
dVecTilt(0) * vecTilt(2) - dVecTilt(2) * vecTilt(0));
|
||||
dpdk_p[dpdk_step+12] = fy*invProjSquare * (
|
||||
dVecTilt(1) * vecTilt(2) - dVecTilt(2) * vecTilt(1));
|
||||
dVecTilt = dMatTiltdTauY * Vec3d(xd0, yd0, 1);
|
||||
dpdk_p[13] = fx * invProjSquare * (
|
||||
dVecTilt(0) * vecTilt(2) - dVecTilt(2) * vecTilt(0));
|
||||
dpdk_p[dpdk_step+13] = fy * invProjSquare * (
|
||||
dVecTilt(1) * vecTilt(2) - dVecTilt(2) * vecTilt(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dpdk_p += dpdk_step*2;
|
||||
}
|
||||
|
||||
if( dpdt_p )
|
||||
{
|
||||
double dxdt[] = { z, 0, -x*z }, dydt[] = { 0, z, -y*z };
|
||||
for( j = 0; j < 3; j++ )
|
||||
{
|
||||
double dr2dt = 2*x*dxdt[j] + 2*y*dydt[j];
|
||||
double dcdist_dt = k[0]*dr2dt + 2*k[1]*r2*dr2dt + 3*k[4]*r4*dr2dt;
|
||||
double dicdist2_dt = -icdist2*icdist2*(k[5]*dr2dt + 2*k[6]*r2*dr2dt + 3*k[7]*r4*dr2dt);
|
||||
double da1dt = 2*(x*dydt[j] + y*dxdt[j]);
|
||||
double dmxdt = (dxdt[j]*cdist*icdist2 + x*dcdist_dt*icdist2 + x*cdist*dicdist2_dt +
|
||||
k[2]*da1dt + k[3]*(dr2dt + 4*x*dxdt[j]) + k[8]*dr2dt + 2*r2*k[9]*dr2dt);
|
||||
double dmydt = (dydt[j]*cdist*icdist2 + y*dcdist_dt*icdist2 + y*cdist*dicdist2_dt +
|
||||
k[2]*(dr2dt + 4*y*dydt[j]) + k[3]*da1dt + k[10]*dr2dt + 2*r2*k[11]*dr2dt);
|
||||
dXdYd = dMatTilt*Vec2d(dmxdt, dmydt);
|
||||
dpdt_p[j] = fx*dXdYd(0);
|
||||
dpdt_p[dpdt_step+j] = fy*dXdYd(1);
|
||||
}
|
||||
dpdt_p += dpdt_step*2;
|
||||
}
|
||||
|
||||
if( dpdr_p )
|
||||
{
|
||||
double dx0dr[] =
|
||||
{
|
||||
X*dRdr[0] + Y*dRdr[1] + Z*dRdr[2],
|
||||
X*dRdr[9] + Y*dRdr[10] + Z*dRdr[11],
|
||||
X*dRdr[18] + Y*dRdr[19] + Z*dRdr[20]
|
||||
};
|
||||
double dy0dr[] =
|
||||
{
|
||||
X*dRdr[3] + Y*dRdr[4] + Z*dRdr[5],
|
||||
X*dRdr[12] + Y*dRdr[13] + Z*dRdr[14],
|
||||
X*dRdr[21] + Y*dRdr[22] + Z*dRdr[23]
|
||||
};
|
||||
double dz0dr[] =
|
||||
{
|
||||
X*dRdr[6] + Y*dRdr[7] + Z*dRdr[8],
|
||||
X*dRdr[15] + Y*dRdr[16] + Z*dRdr[17],
|
||||
X*dRdr[24] + Y*dRdr[25] + Z*dRdr[26]
|
||||
};
|
||||
for( j = 0; j < 3; j++ )
|
||||
{
|
||||
double dxdr = z*(dx0dr[j] - x*dz0dr[j]);
|
||||
double dydr = z*(dy0dr[j] - y*dz0dr[j]);
|
||||
double dr2dr = 2*x*dxdr + 2*y*dydr;
|
||||
double dcdist_dr = (k[0] + 2*k[1]*r2 + 3*k[4]*r4)*dr2dr;
|
||||
double dicdist2_dr = -icdist2*icdist2*(k[5] + 2*k[6]*r2 + 3*k[7]*r4)*dr2dr;
|
||||
double da1dr = 2*(x*dydr + y*dxdr);
|
||||
double dmxdr = (dxdr*cdist*icdist2 + x*dcdist_dr*icdist2 + x*cdist*dicdist2_dr +
|
||||
k[2]*da1dr + k[3]*(dr2dr + 4*x*dxdr) + (k[8] + 2*r2*k[9])*dr2dr);
|
||||
double dmydr = (dydr*cdist*icdist2 + y*dcdist_dr*icdist2 + y*cdist*dicdist2_dr +
|
||||
k[2]*(dr2dr + 4*y*dydr) + k[3]*da1dr + (k[10] + 2*r2*k[11])*dr2dr);
|
||||
dXdYd = dMatTilt*Vec2d(dmxdr, dmydr);
|
||||
dpdr_p[j] = fx*dXdYd(0);
|
||||
dpdr_p[dpdr_step+j] = fy*dXdYd(1);
|
||||
}
|
||||
dpdr_p += dpdr_step*2;
|
||||
}
|
||||
|
||||
if( dpdo_p )
|
||||
{
|
||||
double dxdo[] = { z * ( R[0] - x * z * z0 * R[6] ),
|
||||
z * ( R[1] - x * z * z0 * R[7] ),
|
||||
z * ( R[2] - x * z * z0 * R[8] ) };
|
||||
double dydo[] = { z * ( R[3] - y * z * z0 * R[6] ),
|
||||
z * ( R[4] - y * z * z0 * R[7] ),
|
||||
z * ( R[5] - y * z * z0 * R[8] ) };
|
||||
for( j = 0; j < 3; j++ )
|
||||
{
|
||||
double dr2do = 2 * x * dxdo[j] + 2 * y * dydo[j];
|
||||
double dr4do = 2 * r2 * dr2do;
|
||||
double dr6do = 3 * r4 * dr2do;
|
||||
double da1do = 2 * y * dxdo[j] + 2 * x * dydo[j];
|
||||
double da2do = dr2do + 4 * x * dxdo[j];
|
||||
double da3do = dr2do + 4 * y * dydo[j];
|
||||
double dcdist_do
|
||||
= k[0] * dr2do + k[1] * dr4do + k[4] * dr6do;
|
||||
double dicdist2_do = -icdist2 * icdist2
|
||||
* ( k[5] * dr2do + k[6] * dr4do + k[7] * dr6do );
|
||||
double dxd0_do = cdist * icdist2 * dxdo[j]
|
||||
+ x * icdist2 * dcdist_do + x * cdist * dicdist2_do
|
||||
+ k[2] * da1do + k[3] * da2do + k[8] * dr2do
|
||||
+ k[9] * dr4do;
|
||||
double dyd0_do = cdist * icdist2 * dydo[j]
|
||||
+ y * icdist2 * dcdist_do + y * cdist * dicdist2_do
|
||||
+ k[2] * da3do + k[3] * da1do + k[10] * dr2do
|
||||
+ k[11] * dr4do;
|
||||
dXdYd = dMatTilt * Vec2d( dxd0_do, dyd0_do );
|
||||
dpdo_p[i * 3 + j] = fx * dXdYd( 0 );
|
||||
dpdo_p[dpdo_step + i * 3 + j] = fy * dXdYd( 1 );
|
||||
}
|
||||
dpdo_p += dpdo_step * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( _m != imagePoints )
|
||||
cvConvert( _m, imagePoints );
|
||||
|
||||
if( _dpdr != dpdr )
|
||||
cvConvert( _dpdr, dpdr );
|
||||
|
||||
if( _dpdt != dpdt )
|
||||
cvConvert( _dpdt, dpdt );
|
||||
|
||||
if( _dpdf != dpdf )
|
||||
cvConvert( _dpdf, dpdf );
|
||||
|
||||
if( _dpdc != dpdc )
|
||||
cvConvert( _dpdc, dpdc );
|
||||
|
||||
if( _dpdk != dpdk )
|
||||
cvConvert( _dpdk, dpdk );
|
||||
|
||||
if( _dpdo != dpdo )
|
||||
cvConvert( _dpdo, dpdo );
|
||||
}
|
||||
|
||||
static void _cvProjectPoints2( const CvMat* objectPoints,
|
||||
const CvMat* r_vec,
|
||||
const CvMat* t_vec,
|
||||
const CvMat* A,
|
||||
const CvMat* distCoeffs,
|
||||
CvMat* imagePoints, CvMat* dpdr,
|
||||
CvMat* dpdt, CvMat* dpdf,
|
||||
CvMat* dpdc, CvMat* dpdk,
|
||||
double aspectRatio )
|
||||
{
|
||||
_cvProjectPoints2Internal( objectPoints, r_vec, t_vec, A, distCoeffs, imagePoints, dpdr, dpdt,
|
||||
dpdf, dpdc, dpdk, NULL, aspectRatio );
|
||||
}
|
||||
|
||||
1
aruco_pose/vendor/VendorOpenCV.cmake
vendored
@@ -7,6 +7,7 @@ endif()
|
||||
|
||||
message(STATUS "Adding vendored aruco_pose OpenCV module")
|
||||
add_library(_opencv_aruco STATIC
|
||||
vendor/aruco/src/apriltag_quad_thresh.cpp
|
||||
vendor/aruco/src/aruco.cpp
|
||||
vendor/aruco/src/charuco.cpp
|
||||
vendor/aruco/src/dictionary.cpp
|
||||
|
||||
@@ -94,7 +94,7 @@ void ptsort_(struct pt *pts, int sz)
|
||||
|
||||
// Use stack storage if it's not too big.
|
||||
cv::AutoBuffer<struct pt, 1024> _tmp_stack(sz);
|
||||
memcpy(_tmp_stack.data(), pts, sizeof(struct pt) * sz);
|
||||
memcpy(_tmp_stack, pts, sizeof(struct pt) * sz);
|
||||
|
||||
int asz = sz/2;
|
||||
int bsz = sz - asz;
|
||||
@@ -470,11 +470,11 @@ int quad_segment_agg(int sz, struct line_fit_pt *lfps, int indices[4]){
|
||||
int rvalloc_pos = 0;
|
||||
int rvalloc_size = 3*sz;
|
||||
cv::AutoBuffer<struct remove_vertex, 0> rvalloc_(std::max(1, rvalloc_size));
|
||||
memset(rvalloc_.data(), 0, sizeof(rvalloc_[0]) * rvalloc_.size()); // TODO Add AutoBuffer zero fill
|
||||
struct remove_vertex *rvalloc = rvalloc_.data();
|
||||
memset(rvalloc_, 0, sizeof(rvalloc_[0]) * rvalloc_.size()); // TODO Add AutoBuffer zero fill
|
||||
struct remove_vertex *rvalloc = rvalloc_;
|
||||
cv::AutoBuffer<struct segment, 0> segs_(std::max(1, sz)); // TODO Add AutoBuffer zero fill
|
||||
memset(segs_.data(), 0, sizeof(segs_[0]) * segs_.size());
|
||||
struct segment *segs = segs_.data();
|
||||
memset(segs_, 0, sizeof(segs_[0]) * segs_.size());
|
||||
struct segment *segs = segs_;
|
||||
|
||||
// populate with initial entries
|
||||
for (int i = 0; i < sz; i++) {
|
||||
@@ -753,8 +753,8 @@ int fit_quad(const Ptr<DetectorParameters> &_params, const Mat im, zarray_t *clu
|
||||
// efficiently computed for any contiguous range of indices.
|
||||
|
||||
cv::AutoBuffer<struct line_fit_pt, 64> lfps_(sz);
|
||||
memset(lfps_.data(), 0, sizeof(lfps_[0]) * lfps_.size()); // TODO Add AutoBuffer zero fill
|
||||
struct line_fit_pt *lfps = lfps_.data();
|
||||
memset(lfps_, 0, sizeof(lfps_[0]) * lfps_.size()); // TODO Add AutoBuffer zero fill
|
||||
struct line_fit_pt *lfps = lfps_;
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
struct pt *p;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from distutils.core import setup
|
||||
|
||||
|
||||
@@ -7,25 +7,19 @@ rospy.init_node('leds')
|
||||
|
||||
set_effect = rospy.ServiceProxy('led/set_effect', SetLEDEffect) # define proxy to ROS-service
|
||||
|
||||
print('Fill red')
|
||||
set_effect(r=255, g=0, b=0) # fill strip with red color
|
||||
rospy.sleep(2)
|
||||
|
||||
print('Fill green')
|
||||
set_effect(r=0, g=100, b=0) # fill strip with green color
|
||||
rospy.sleep(2)
|
||||
|
||||
print('Fade to blue')
|
||||
set_effect(effect='fade', r=0, g=0, b=255) # fade to blue color
|
||||
rospy.sleep(2)
|
||||
|
||||
print('Flash red')
|
||||
set_effect(effect='flash', r=255, g=0, b=0) # flash twice with red color
|
||||
rospy.sleep(2)
|
||||
rospy.sleep(5)
|
||||
|
||||
print('Blink white')
|
||||
set_effect(effect='blink', r=255, g=255, b=255) # blink with white color
|
||||
rospy.sleep(5)
|
||||
|
||||
print('Rainbow')
|
||||
set_effect(effect='rainbow') # show rainbow
|
||||
|
||||
18
builder/assets/noetic-rosdep-clover.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
async_web_server_cpp:
|
||||
debian:
|
||||
buster: [ros-noetic-async-web-server-cpp]
|
||||
led_msgs:
|
||||
debian:
|
||||
buster: [ros-noetic-led-msgs]
|
||||
ros_pytest:
|
||||
debian:
|
||||
buster: [ros-noetic-ros-pytest]
|
||||
tf2_web_republisher:
|
||||
debian:
|
||||
buster: [ros-noetic-tf2-web-republisher]
|
||||
web_video_server:
|
||||
debian:
|
||||
buster: [ros-noetic-web-video-server]
|
||||
ws281x:
|
||||
debian:
|
||||
buster: [ros-noetic-ws281x]
|
||||
@@ -3,7 +3,7 @@ Description=Launcher for the ROS master, parameter server and rosout logging nod
|
||||
|
||||
[Service]
|
||||
User=pi
|
||||
ExecStart=/bin/sh -c ". /opt/ros/melodic/setup.sh; ROS_HOSTNAME=`hostname`.local exec roscore"
|
||||
ExecStart=/bin/sh -c ". /opt/ros/noetic/setup.sh; ROS_HOSTNAME=`hostname`.local exec roscore"
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
set -e # Exit immidiately on non-zero result
|
||||
|
||||
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2020-02-14/2020-02-13-raspbian-buster-lite.zip"
|
||||
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2020-08-24/2020-08-20-raspios-buster-arm64-lite.zip"
|
||||
|
||||
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
|
||||
export LANG=${LANG:='C.UTF-8'}
|
||||
@@ -105,7 +105,7 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.
|
||||
# software install
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh'
|
||||
# examples
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/' # TODO: symlink?
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/examples' '/home/pi/'
|
||||
# network setup
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
|
||||
# avahi setup
|
||||
@@ -116,7 +116,7 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/avahi-s
|
||||
# Clover
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/clover.service' '/lib/systemd/system/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/roscore.service' '/lib/systemd/system/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/melodic-rosdep-clover.yaml' '/etc/ros/rosdep/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/noetic-rosdep-clover.yaml' '/etc/ros/rosdep/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/ros_python_paths' '/etc/sudoers.d/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/pigpiod.service' '/lib/systemd/system/'
|
||||
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'
|
||||
|
||||
@@ -55,7 +55,4 @@ echo_stamp "Set max space for syslogs"
|
||||
# https://unix.stackexchange.com/questions/139513/how-to-clear-journalctl
|
||||
sed -i 's/#SystemMaxUse=/SystemMaxUse=200M/' /etc/systemd/journald.conf
|
||||
|
||||
echo_stamp "Move /etc/ld.so.preload out of the way"
|
||||
mv /etc/ld.so.preload /etc/ld.so.preload.disabled-for-build
|
||||
|
||||
echo_stamp "End of init image"
|
||||
|
||||
@@ -21,6 +21,9 @@ INSTALL_ROS_PACK_SOURCES=$3
|
||||
DISCOVER_ROS_PACK=$4
|
||||
NUMBER_THREADS=$5
|
||||
|
||||
# Current ROS distribution
|
||||
ROS_DISTRO=noetic
|
||||
|
||||
echo_stamp() {
|
||||
# TEMPLATE: echo_stamp <TEXT> <TYPE>
|
||||
# TYPE: SUCCESS, ERROR, INFO
|
||||
@@ -68,7 +71,8 @@ my_travis_retry() {
|
||||
# TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo?
|
||||
echo_stamp "Init rosdep"
|
||||
my_travis_retry rosdep init
|
||||
echo "yaml file:///etc/ros/rosdep/melodic-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
|
||||
# FIXME: Re-add this after missing packages are built
|
||||
echo "yaml file:///etc/ros/rosdep/${ROS_DISTRO}-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
|
||||
my_travis_retry rosdep update
|
||||
|
||||
echo_stamp "Populate rosdep for ROS user"
|
||||
@@ -80,14 +84,30 @@ echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
|
||||
cd /home/pi/catkin_ws/src/clover
|
||||
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"
|
||||
|
||||
# This is sort of a hack to force "custom" packages to be installed - the ones built by COEX, linked against OpenCV 4.2
|
||||
# I **wish** OpenCV would not be such a mess, but, well, here we are.
|
||||
echo_stamp "Installing OpenCV 4.2-compatible ROS packages"
|
||||
apt install -y --no-install-recommends \
|
||||
ros-${ROS_DISTRO}-compressed-image-transport=1.14.0-0buster \
|
||||
ros-${ROS_DISTRO}-cv-bridge=1.15.0-0buster \
|
||||
ros-${ROS_DISTRO}-cv-camera=0.5.0-0buster \
|
||||
ros-${ROS_DISTRO}-image-publisher=1.15.2-0buster \
|
||||
ros-${ROS_DISTRO}-web-video-server=0.2.1-0buster
|
||||
apt-mark hold \
|
||||
ros-${ROS_DISTRO}-compressed-image-transport \
|
||||
ros-${ROS_DISTRO}-cv-bridge \
|
||||
ros-${ROS_DISTRO}-cv-camera \
|
||||
ros-${ROS_DISTRO}-image-publisher \
|
||||
ros-${ROS_DISTRO}-web-video-server
|
||||
|
||||
echo_stamp "Build and install Clover"
|
||||
cd /home/pi/catkin_ws
|
||||
# Don't try to install gazebo_ros
|
||||
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro melodic --os=debian:buster \
|
||||
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --os=debian:buster \
|
||||
--skip-keys=gazebo_ros --skip-keys=gazebo_plugins
|
||||
my_travis_retry pip install wheel
|
||||
my_travis_retry pip install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
|
||||
source /opt/ros/melodic/setup.bash
|
||||
my_travis_retry pip3 install wheel
|
||||
my_travis_retry pip3 install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
|
||||
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||
# Don't build simulation plugins for actual drone
|
||||
catkin_make -j2 -DCMAKE_BUILD_TYPE=Release -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins
|
||||
|
||||
@@ -105,23 +125,18 @@ touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/w
|
||||
|
||||
echo_stamp "Installing additional ROS packages"
|
||||
my_travis_retry apt-get install -y --no-install-recommends \
|
||||
ros-melodic-dynamic-reconfigure \
|
||||
ros-melodic-compressed-image-transport \
|
||||
ros-melodic-rosbridge-suite \
|
||||
ros-melodic-rosserial \
|
||||
ros-melodic-usb-cam \
|
||||
ros-melodic-vl53l1x \
|
||||
ros-melodic-ws281x \
|
||||
ros-melodic-rosshow
|
||||
ros-${ROS_DISTRO}-dynamic-reconfigure \
|
||||
ros-${ROS_DISTRO}-rosbridge-suite \
|
||||
ros-${ROS_DISTRO}-rosserial \
|
||||
ros-${ROS_DISTRO}-usb-cam \
|
||||
ros-${ROS_DISTRO}-vl53l1x \
|
||||
ros-${ROS_DISTRO}-ws281x \
|
||||
ros-${ROS_DISTRO}-rosshow
|
||||
|
||||
# TODO move GeographicLib datasets to Mavros debian package
|
||||
echo_stamp "Install GeographicLib datasets (needed for mavros)" \
|
||||
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
|
||||
|
||||
# FIXME: Buster comes with tornado==5.1.1 but we need tornado==4.2.1 for rosbridge_suite
|
||||
# (note that Python 3 will still have a more recent version)
|
||||
pip install tornado==4.2.1
|
||||
|
||||
echo_stamp "Running tests"
|
||||
cd /home/pi/catkin_ws
|
||||
# FIXME: Investigate failing tests
|
||||
@@ -130,15 +145,12 @@ catkin_make run_tests #&& catkin_test_results
|
||||
echo_stamp "Change permissions for catkin_ws"
|
||||
chown -Rf pi:pi /home/pi/catkin_ws
|
||||
|
||||
echo_stamp "Change permissions for examples"
|
||||
chown -Rf pi:pi /home/pi/examples
|
||||
|
||||
echo_stamp "Setup ROS environment"
|
||||
cat << EOF >> /home/pi/.bashrc
|
||||
LANG='C.UTF-8'
|
||||
LC_ALL='C.UTF-8'
|
||||
export ROS_HOSTNAME=\`hostname\`.local
|
||||
source /opt/ros/melodic/setup.bash
|
||||
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||
source /home/pi/catkin_ws/devel/setup.bash
|
||||
EOF
|
||||
|
||||
|
||||
@@ -70,8 +70,8 @@ apt-get update \
|
||||
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
|
||||
|
||||
echo "deb http://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
|
||||
echo "deb http://deb.coex.tech/opencv3 buster main" > /etc/apt/sources.list.d/opencv3.list
|
||||
echo "deb http://deb.coex.tech/rpi-ros-melodic buster main" > /etc/apt/sources.list.d/rpi-ros-melodic.list
|
||||
echo "deb http://deb.coex.tech/opencv4 buster main" > /etc/apt/sources.list.d/opencv3.list
|
||||
echo "deb http://deb.coex.tech/ros buster main" > /etc/apt/sources.list.d/rpi-ros-melodic.list
|
||||
echo "deb http://deb.coex.tech/clover buster main" > /etc/apt/sources.list.d/clover.list
|
||||
|
||||
echo_stamp "Update apt cache"
|
||||
@@ -96,21 +96,19 @@ dnsmasq \
|
||||
tmux \
|
||||
vim \
|
||||
cmake \
|
||||
libjpeg8 \
|
||||
tcpdump \
|
||||
ltrace \
|
||||
libpoco-dev \
|
||||
libzbar0 \
|
||||
python-rosdep \
|
||||
python-rosinstall-generator \
|
||||
python-wstool \
|
||||
python-rosinstall \
|
||||
python3-rosdep \
|
||||
python3-rosinstall-generator \
|
||||
python3-wstool \
|
||||
python3-rosinstall \
|
||||
build-essential \
|
||||
libffi-dev \
|
||||
monkey \
|
||||
pigpio python-pigpio python3-pigpio \
|
||||
i2c-tools \
|
||||
espeak espeak-data python-espeak \
|
||||
espeak espeak-data python-espeak python3-espeak \
|
||||
ntpdate \
|
||||
python-dev \
|
||||
python3-dev \
|
||||
@@ -142,7 +140,7 @@ my_travis_retry pip3 install butterfly[systemd]
|
||||
systemctl enable butterfly.socket
|
||||
|
||||
echo_stamp "Install ws281x library"
|
||||
my_travis_retry pip install --prefer-binary rpi_ws281x
|
||||
my_travis_retry pip3 install --prefer-binary rpi_ws281x
|
||||
|
||||
echo_stamp "Setup Monkey"
|
||||
mv /etc/monkey/sites/default /etc/monkey/sites/default.orig
|
||||
@@ -151,11 +149,11 @@ systemctl enable monkey.service
|
||||
|
||||
echo_stamp "Install Node.js"
|
||||
cd /home/pi
|
||||
wget https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-armv6l.tar.gz
|
||||
tar -xzf node-v10.15.0-linux-armv6l.tar.gz
|
||||
cp -R node-v10.15.0-linux-armv6l/* /usr/local/
|
||||
rm -rf node-v10.15.0-linux-armv6l/
|
||||
rm node-v10.15.0-linux-armv6l.tar.gz
|
||||
wget https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-arm64.tar.gz
|
||||
tar -xzf node-v10.15.0-linux-arm64.tar.gz
|
||||
cp -R node-v10.15.0-linux-arm64/* /usr/local/
|
||||
rm -rf node-v10.15.0-linux-arm64/
|
||||
rm node-v10.15.0-linux-arm64.tar.gz
|
||||
|
||||
echo_stamp "Installing ptvsd"
|
||||
my_travis_retry pip install ptvsd
|
||||
|
||||
@@ -16,9 +16,9 @@ set -ex
|
||||
|
||||
echo "Run image tests"
|
||||
|
||||
export ROS_DISTRO='melodic'
|
||||
export ROS_DISTRO='noetic'
|
||||
export ROS_IP='127.0.0.1'
|
||||
source /opt/ros/melodic/setup.bash
|
||||
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||
source /home/pi/catkin_ws/devel/setup.bash
|
||||
|
||||
cd /home/pi/catkin_ws/src/clover/builder/test/
|
||||
@@ -26,6 +26,3 @@ cd /home/pi/catkin_ws/src/clover/builder/test/
|
||||
./tests.py
|
||||
./tests_py3.py
|
||||
[[ $(./tests_clever.py) == "Warning: clever package is renamed to clover" ]] # test backwards compatibility
|
||||
|
||||
echo "Move /etc/ld.so.preload back to its original position"
|
||||
mv /etc/ld.so.preload.disabled-for-build /etc/ld.so.preload
|
||||
|
||||
@@ -6,20 +6,39 @@ set -e
|
||||
apt update
|
||||
apt install -y curl
|
||||
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
||||
python ./get-pip.py
|
||||
if [ "x${ROS_PYTHON_VERSION}" = "x3" ]; then
|
||||
PYTHON=python3
|
||||
else
|
||||
PYTHON=python
|
||||
fi
|
||||
|
||||
${PYTHON} ./get-pip.py
|
||||
|
||||
# Step 1.5: Add deb.coex.tech to apt
|
||||
curl http://deb.coex.tech/aptly_repo_signing.key 2> /dev/null | apt-key add -
|
||||
echo "deb http://deb.coex.tech/ros xenial main" > /etc/apt/sources.list.d/coex.tech.list
|
||||
echo "yaml file:///etc/ros/rosdep/coex.yaml" > /etc/ros/rosdep/sources.list.d/99-coex.list
|
||||
CODENAME=$(lsb_release -sc)
|
||||
|
||||
cat <<EOF > /etc/ros/rosdep/coex.yaml
|
||||
led_msgs:
|
||||
ubuntu:
|
||||
xenial: ros-kinetic-led-msgs
|
||||
bionic: ros-melodic-led-msgs
|
||||
debian:
|
||||
stretch: ros-kinetic-led-msgs
|
||||
buster: ros-melodic-led-msgs
|
||||
${CODENAME}: [ros-${ROS_DISTRO}-led-msgs]
|
||||
async_web_server_cpp:
|
||||
ubuntu:
|
||||
${CODENAME}: [ros-${ROS_DISTRO}-async-web-server-cpp]
|
||||
ros_pytest:
|
||||
ubuntu:
|
||||
${CODENAME}: [ros-${ROS_DISTRO}-ros-pytest]
|
||||
tf2_web_republisher:
|
||||
ubuntu:
|
||||
${CODENAME}: [ros-${ROS_DISTRO}-tf2-web-republisher]
|
||||
web_video_server:
|
||||
ubuntu:
|
||||
${CODENAME}: [ros-${ROS_DISTRO}-web-video-server]
|
||||
ws281x:
|
||||
ubuntu:
|
||||
${CODENAME}: [ros-${ROS_DISTRO}-ws281x]
|
||||
EOF
|
||||
apt update
|
||||
rosdep update
|
||||
@@ -37,7 +56,7 @@ cd /root/catkin_ws
|
||||
catkin_make
|
||||
|
||||
# Step 4: Run tests
|
||||
pip install --upgrade pytest
|
||||
${PYTHON} -m pip install --upgrade pytest
|
||||
cd /root/catkin_ws
|
||||
source devel/setup.bash
|
||||
catkin_make run_tests && catkin_test_results
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# validate all required modules installed
|
||||
|
||||
@@ -28,4 +28,4 @@ import pigpio
|
||||
from espeak import espeak
|
||||
from pyzbar import pyzbar
|
||||
|
||||
print cv2.getBuildInformation()
|
||||
print(cv2.getBuildInformation())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# test backwards compatibility
|
||||
|
||||
|
||||
@@ -30,7 +30,15 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||
|
||||
find_package(GeographicLib REQUIRED)
|
||||
|
||||
find_package(OpenCV 3 REQUIRED
|
||||
# Workaround for OpenCV 3/4 support
|
||||
set(_opencv_version 4)
|
||||
find_package(OpenCV ${_opencv_version} QUIET)
|
||||
if (NOT OpenCV_FOUND)
|
||||
message(STATUS "Did not find OpenCV 4, searching for OpenCV 3")
|
||||
set(_opencv_version 3)
|
||||
endif()
|
||||
|
||||
find_package(OpenCV ${_opencv_version} REQUIRED
|
||||
COMPONENTS
|
||||
calib3d
|
||||
imgproc
|
||||
@@ -254,6 +262,10 @@ target_link_libraries(${PROJECT_NAME}
|
||||
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
|
||||
# )
|
||||
|
||||
catkin_install_python(PROGRAMS src/selfcheck.py
|
||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||
# Only install udev rules when building a Debian package
|
||||
# FIXME: Other operating systems may have other prefixes
|
||||
string(FIND ${CMAKE_INSTALL_PREFIX} "/opt/ros" _PREFIX_INDEX)
|
||||
|
||||
@@ -4,7 +4,7 @@ A bundle for autonomous navigation and drone control.
|
||||
|
||||
## Manual installation
|
||||
|
||||
Install ROS Melodic according to the [documentation](http://wiki.ros.org/melodic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
||||
Install ROS Noetic according to the [documentation](http://wiki.ros.org/noetic/Installation), then [create a Catkin workspace](http://wiki.ros.org/catkin/Tutorials/create_a_workspace).
|
||||
|
||||
Clone this repo to directory `~/catkin_ws/src/clover`:
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<arg name="ws281x" default="true"/>
|
||||
<arg name="led_effect" default="true"/>
|
||||
<arg name="led_notify" default="true"/>
|
||||
<arg name="led_count" default="72"/>
|
||||
<arg name="led_count" default="58"/>
|
||||
<arg name="gpio_pin" default="21"/>
|
||||
|
||||
<arg name="simulator" default="false"/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<package format="2">
|
||||
<package format="3">
|
||||
<name>clover</name>
|
||||
<version>0.0.1</version>
|
||||
<description>The Clover package</description>
|
||||
@@ -37,7 +37,8 @@
|
||||
<depend>rosbridge_server</depend>
|
||||
<depend>web_video_server</depend>
|
||||
<depend>tf2_web_republisher</depend>
|
||||
<depend>python-lxml</depend>
|
||||
<depend condition="$ROS_PYTHON_VERSION == 2">python-lxml</depend>
|
||||
<depend condition="$ROS_PYTHON_VERSION == 3">python3-lxml</depend>
|
||||
<exec_depend>python-pymavlink</exec_depend>
|
||||
<!-- Use test_depend for packages you need only for testing: -->
|
||||
<!-- <test_depend>gtest</test_depend> -->
|
||||
|
||||
@@ -138,7 +138,7 @@ def mavlink_exec(cmd, timeout=3.0):
|
||||
timeout=3,
|
||||
baudrate=0,
|
||||
count=len(cmd),
|
||||
data=map(ord, cmd.ljust(70, '\0')))
|
||||
data=[ord(c) for c in cmd.ljust(70, '\0')])
|
||||
msg.pack(link)
|
||||
ros_msg = mavlink.convert_to_rosmsg(msg)
|
||||
mavlink_pub.publish(ros_msg)
|
||||
@@ -609,7 +609,7 @@ def check_rangefinder():
|
||||
|
||||
@check('Boot duration')
|
||||
def check_boot_duration():
|
||||
output = subprocess.check_output('systemd-analyze')
|
||||
output = subprocess.check_output('systemd-analyze').decode()
|
||||
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
|
||||
duration = float(r.search(output).groups()[0])
|
||||
if duration > 15:
|
||||
@@ -620,7 +620,7 @@ def check_boot_duration():
|
||||
def check_cpu_usage():
|
||||
WHITELIST = 'nodelet',
|
||||
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'"
|
||||
output = subprocess.check_output(CMD, shell=True)
|
||||
output = subprocess.check_output(CMD, shell=True).decode()
|
||||
processes = output.split('\n')
|
||||
for process in processes:
|
||||
if not process:
|
||||
@@ -636,7 +636,7 @@ def check_cpu_usage():
|
||||
def check_clover_service():
|
||||
try:
|
||||
output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(),
|
||||
stderr=subprocess.STDOUT)
|
||||
stderr=subprocess.STDOUT).decode()
|
||||
except subprocess.CalledProcessError as e:
|
||||
failure('systemctl returned %s: %s', e.returncode, e.output)
|
||||
return
|
||||
@@ -751,7 +751,7 @@ def check_rpi_health():
|
||||
# <parameter>=<value>
|
||||
# In case of `get_throttled`, <value> is a hexadecimal number
|
||||
# with some of the FLAGs OR'ed together
|
||||
output = subprocess.check_output(['vcgencmd', 'get_throttled'])
|
||||
output = subprocess.check_output(['vcgencmd', 'get_throttled']).decode()
|
||||
except OSError:
|
||||
failure('could not call vcgencmd binary; not a Raspberry Pi?')
|
||||
return
|
||||
|
||||
@@ -73,7 +73,6 @@ ros::Duration state_timeout;
|
||||
ros::Duration velocity_timeout;
|
||||
ros::Duration global_position_timeout;
|
||||
ros::Duration battery_timeout;
|
||||
ros::Duration manual_control_timeout;
|
||||
float default_speed;
|
||||
bool auto_release;
|
||||
bool land_only_in_offboard, nav_from_sp, check_kill_switch;
|
||||
@@ -489,19 +488,16 @@ void publishSetpoint(const ros::TimerEvent& event)
|
||||
publish(event.current_real);
|
||||
}
|
||||
|
||||
inline void checkManualControl()
|
||||
inline void checkKillSwitch()
|
||||
{
|
||||
if (!manual_control_timeout.isZero() && TIMEOUT(manual_control, manual_control_timeout)) {
|
||||
throw std::runtime_error("Manual control timeout, RC is switched off?");
|
||||
}
|
||||
if (!TIMEOUT(manual_control, state_timeout))
|
||||
throw std::runtime_error("Manual control timeout, can't check kill switch status");
|
||||
|
||||
if (check_kill_switch) {
|
||||
const int KILL_SWITCH_BIT = 12; // https://github.com/PX4/Firmware/blob/c302514a0809b1765fafd13c014d705446ae1113/src/modules/mavlink/mavlink_messages.cpp#L3975
|
||||
bool kill_switch = manual_control.buttons & (1 << KILL_SWITCH_BIT);
|
||||
const int KILL_SWITCH_BIT = 12; // https://github.com/PX4/Firmware/blob/c302514a0809b1765fafd13c014d705446ae1113/src/modules/mavlink/mavlink_messages.cpp#L3975
|
||||
bool kill_switch = manual_control.buttons & (1 << KILL_SWITCH_BIT);
|
||||
|
||||
if (kill_switch)
|
||||
throw std::runtime_error("Kill switch is on");
|
||||
}
|
||||
if (kill_switch)
|
||||
throw std::runtime_error("Kill switch is on");
|
||||
}
|
||||
|
||||
inline void checkState()
|
||||
@@ -531,8 +527,8 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
|
||||
// Checks
|
||||
checkState();
|
||||
|
||||
if (auto_arm) {
|
||||
checkManualControl();
|
||||
if (auto_arm && check_kill_switch) {
|
||||
checkKillSwitch();
|
||||
}
|
||||
|
||||
// default frame is local frame
|
||||
@@ -866,7 +862,6 @@ int main(int argc, char **argv)
|
||||
velocity_timeout = ros::Duration(nh_priv.param("velocity_timeout", 2.0));
|
||||
global_position_timeout = ros::Duration(nh_priv.param("global_position_timeout", 10.0));
|
||||
battery_timeout = ros::Duration(nh_priv.param("battery_timeout", 2.0));
|
||||
manual_control_timeout = ros::Duration(nh_priv.param("manual_control_timeout", 0.0));
|
||||
|
||||
transform_timeout = ros::Duration(nh_priv.param("transform_timeout", 0.5));
|
||||
telemetry_transform_timeout = ros::Duration(nh_priv.param("telemetry_transform_timeout", 0.5));
|
||||
|
||||
@@ -26,8 +26,13 @@ def test_simple_offboard_services_available():
|
||||
rospy.wait_for_service('land', timeout=5)
|
||||
|
||||
def test_web_video_server(node):
|
||||
import urllib2
|
||||
urllib2.urlopen("http://localhost:8080").read()
|
||||
try:
|
||||
# Python 2
|
||||
import urllib2 as urllib
|
||||
except ModuleNotFoundError:
|
||||
# Python 3
|
||||
import urllib.request as urllib
|
||||
urllib.urlopen("http://localhost:8080").read()
|
||||
|
||||
def test_shell(node):
|
||||
execute = rospy.ServiceProxy('exec', srv.Execute)
|
||||
|
||||
@@ -111,7 +111,7 @@ def run(req):
|
||||
'print': _print,
|
||||
'raw_input': _input}
|
||||
try:
|
||||
exec req.code in g
|
||||
exec(req.code, g)
|
||||
except Stop:
|
||||
rospy.loginfo('Program forced to stop')
|
||||
except Exception as e:
|
||||
|
||||
@@ -391,7 +391,7 @@ Blockly.Python.set_led = function(block) {
|
||||
return `set_leds([LEDState(index=${index}, r=${color.r}, g=${color.g}, b=${color.b})])\n`;
|
||||
} else {
|
||||
let parseColor = Blockly.Python.provideFunction_('parse_color', [PARSE_COLOR]);
|
||||
return `set_leds([LEDState(index=${index}, **${parseColor}(${colorCode}))])\n`;
|
||||
return `set_leds([LEDState(index=${index}, **${parseColor}(${colorCode})])\n`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 549 KiB |
|
Before Width: | Height: | Size: 549 KiB |
|
Before Width: | Height: | Size: 331 KiB After Width: | Height: | Size: 333 KiB |
|
Before Width: | Height: | Size: 466 KiB After Width: | Height: | Size: 469 KiB |
|
Before Width: | Height: | Size: 469 KiB After Width: | Height: | Size: 330 KiB |
|
Before Width: | Height: | Size: 330 KiB After Width: | Height: | Size: 472 KiB |
|
Before Width: | Height: | Size: 472 KiB After Width: | Height: | Size: 324 KiB |
|
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 461 KiB |
|
Before Width: | Height: | Size: 461 KiB After Width: | Height: | Size: 218 KiB |
|
Before Width: | Height: | Size: 564 KiB |
|
Before Width: | Height: | Size: 593 KiB |
|
Before Width: | Height: | Size: 314 KiB |
|
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 303 KiB |
|
Before Width: | Height: | Size: 362 KiB After Width: | Height: | Size: 366 KiB |
|
Before Width: | Height: | Size: 227 KiB |
|
Before Width: | Height: | Size: 241 KiB |
|
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 368 KiB |
|
Before Width: | Height: | Size: 405 KiB After Width: | Height: | Size: 405 KiB |
BIN
docs/assets/noetic.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
@@ -18,15 +18,15 @@ Note that when connected, the modem must be recognized in the system as a networ
|
||||
|
||||
Create the VPN network keys to connect Raspberry Pi and the ground station.
|
||||
|
||||
To connect Raspberry Pi to your network, install the OpenVPN package:
|
||||
To connect Raspberry Pi to your network, install the *openvpn* package:
|
||||
|
||||
```bash
|
||||
sudo apt-get install openvpn
|
||||
```
|
||||
|
||||
Move your keys to the `/etc/openvpn/client` directory. For convenience, use the graphical SFTP data transfer interface, for example: WinSCP, FileZilla, etc.
|
||||
Move your keys to the */etc/openvpn/client* directory. For convenience, use the graphical SFTP data transfer interface, for example: WinSCP, FileZilla, etc.
|
||||
|
||||
To enable the client mode, you must activate the keys you have transmitted. Keys can be generated in various formats, for example: `.ovpn`, `.conf`. The key or configuration used on your copter should be strictly in `.conf` format.
|
||||
To enable the client mode, you must activate the keys you have transmitted. Keys can be generated in various formats, for example: *.ovpn*, *.conf*. The key or configuration used on your copter should be strictly in *.conf* format.
|
||||
|
||||
Initialize the service that uses your keys to connect in client mode:
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
* [LED strip (legacy)](leds_old.md)
|
||||
* [Contribution Guidelines](contributing.md)
|
||||
* [Migration to v0.20](migrate20.md)
|
||||
* [Migration to v0.22](migrate22.md)
|
||||
* [Clover-based projects](projects.md)
|
||||
* [Drone show](clever-show.md)
|
||||
* [Innopolis Open 2020 (L22_ÆRO)](innopolis_open_L22_AERO.md)
|
||||
|
||||
@@ -4,7 +4,7 @@ For interaction with ROS topics and services on a Raspberry Pi, you can use the
|
||||
|
||||
The main tutorial for rosserial: http://wiki.ros.org/rosserial_arduino/Tutorials
|
||||
|
||||
Arduino is to be installed on Clover and connected via a USB port.
|
||||
Arudino is to be installed on Clover and connected via a USB port.
|
||||
|
||||
## Configuring Arduino IDE
|
||||
|
||||
@@ -14,7 +14,7 @@ To work with ROS and Arduino, you should understand the format of installed pack
|
||||
rosrun rosserial_arduino make_libraries.py.
|
||||
```
|
||||
|
||||
The obtained folder `ros_lib` is to be copied to `<sketches folder>/libraries` on a computer with Arduino IDE.
|
||||
The obtained folder `ros_lib` is to be copied to `<sketches folder>/libraries` on a computer with Arudino IDE.
|
||||
|
||||
## Configuring Raspberry Pi
|
||||
|
||||
@@ -24,7 +24,7 @@ To run the program on Arduino once, you can use command:
|
||||
roslaunch clover arduino.launch
|
||||
```
|
||||
|
||||
To start the link with Arduino at the startup automatically, set argument `arduino` in the Clover launch file (`~/catkin_ws/src/clover/clover/launch/clover.launch`):
|
||||
To start the link with Arduino at the startup automatically, set argument `arudino` in the Clover launch file (`~/catkin_ws/src/clover/clover/launch/clover.launch`):
|
||||
|
||||
```xml
|
||||
<arg name="arduino" default="true"/>
|
||||
@@ -202,7 +202,7 @@ getTelemetry.call(gt_req, gt_res);
|
||||
|
||||
## Problem
|
||||
|
||||
When using Arduino Nano, RAM may be insufficient. In this case, messages will appear in the Arduino IDE like:
|
||||
When using Arudino Nano, RAM may be insufficient. In this case, messages will appear in the Aruino IDE like:
|
||||
|
||||
```
|
||||
Global variables use 1837 bytes (89%) of the dynamic memory, leaving 211 bytes for local variables. The maximum is 2048 bytes.
|
||||
|
||||
@@ -98,9 +98,9 @@ rospy.init_node('my_node')
|
||||
# ...
|
||||
|
||||
def markers_callback(msg):
|
||||
print 'Detected markers:':
|
||||
print('Detected markers:'):
|
||||
for marker in msg.markers:
|
||||
print 'Marker: %s' % marker
|
||||
print('Marker: %s' % marker)
|
||||
|
||||
# Create a Subscription object. Each time a message is posted in aruco_detect/markers, the markers_callback function is called with this message as its argument.
|
||||
rospy.Subscriber('aruco_detect/markers', MarkerArray, markers_callback)
|
||||
|
||||
@@ -51,7 +51,7 @@ TODO
|
||||
|
||||
1. Unpack the power board and install the power ribbon cable.
|
||||
2. Switch the multimeter in the DC voltage measurement mode (20V or 200V range).
|
||||
3. Check the correct functioning of the power board by connecting the battery.
|
||||
3. Check the correct functionning of the power board by connecting the battery.
|
||||
* Voltage measurements are to be made between black and red wires.
|
||||
* Output voltage at the XT30 connector should be equal to the battery voltage (10 V to 12.6 V).
|
||||
* The output voltage at the power ribbon cable should be between 4.9 V to 5.3 V.
|
||||
@@ -69,7 +69,7 @@ TODO
|
||||
BLACK -> OUT-
|
||||
RED -> OUT+
|
||||
|
||||
6. Check BEC functioning.
|
||||
6. Check BEC functionning.
|
||||
* Solder the BEC to the power board:
|
||||
|
||||
BLACK -> -
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
## Installing motors
|
||||
|
||||
1. Unbox the motors.
|
||||
2. Shorten the motor wires using wire strippers or side cutters:
|
||||
2. Shorten the motor wires using wire strippers or sidecutters:
|
||||
|
||||
* Cut wires to 30 mm.
|
||||
* Strip 5 mm of insulation while taking care to not damage the cores
|
||||
@@ -315,7 +315,7 @@ The flight controller expects PPM signal from your RC gear. Switch your transmit
|
||||
<img src="../assets/assembling_clever4/lower_deck_4.png" width=300 class="zoom border center">
|
||||
|
||||
6. Connect the camera ribbon cable to the camera.
|
||||
7. Connect the laser rangefinder to the Raspberry Pi using the following pinout:
|
||||
7. Connect the laser rangefineder to the Raspberry Pi using the following pinout:
|
||||
* Connect **VCC** to pin 1 (**3v3**)
|
||||
* Connect **GND** to pin 9 (**Ground**)
|
||||
* Connect **SDA** to pin 3 (**GPIO2**)
|
||||
@@ -391,7 +391,7 @@ Perform the quadrotor components setup according to [the "Configuration" section
|
||||
|
||||
> **Warning** Be sure to **not** mount the propellers **until the setup is complete**. Do it only when you are ready to fly.
|
||||
|
||||
Attach the propellers according to their rotation direction. The battery should be disconnected during propeller installation.
|
||||
Attach the propellers according to their rotation direction. The battery should be disconnected duting propeller installation.
|
||||
|
||||
<div class="image-group">
|
||||
<img src="../assets/assembling_clever4/final_2.png" width=300 class="zoom border">
|
||||
|
||||
@@ -126,7 +126,7 @@ Ctrl+C
|
||||
Start a program `myprogram.py` using Python:
|
||||
|
||||
```bash
|
||||
python myprogram.py
|
||||
python3 myprogram.py
|
||||
```
|
||||
|
||||
Journal of the events related to `clover` package. Scroll the list by pressing Enter or Ctrl+V (scrolls faster):
|
||||
@@ -411,7 +411,7 @@ The easiest way to send the program is to copy the content of the program, creat
|
||||
- Run the program:
|
||||
|
||||
```bash
|
||||
python my_program.py
|
||||
python3 my_program.py
|
||||
```
|
||||
|
||||
> **Warning** After completion of the program , the drone can land incorrectly and continue to fly over the floor. In this case, you need to intercept control.
|
||||
|
||||
@@ -138,7 +138,7 @@ def image_callback(data):
|
||||
(x, y, w, h) = barcode.rect
|
||||
xc = x + w/2
|
||||
yc = y + h/2
|
||||
print ("Found {} with data {} with center at x={}, y={}".format(b_type, b_data, xc, yc))
|
||||
print("Found {} with data {} with center at x={}, y={}".format(b_type, b_data, xc, yc))
|
||||
|
||||
image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ cat file.py
|
||||
Run `file.py` as a Python script:
|
||||
|
||||
```bash
|
||||
python file.py
|
||||
python3 file.py
|
||||
```
|
||||
|
||||
Reboot Raspberry Pi:
|
||||
|
||||
@@ -8,7 +8,7 @@ Infrared sensors are a convenient tool for transmitting any commands to the copt
|
||||
|
||||
Most IR receivers operate and are connected the same way. Such receivers have 3 pins for connecting: G/GND — ground V/VCC — 5V power, S/OUT — signal.
|
||||
|
||||
<img src="../assets/IR_reciver_connection.png" height="500px" alt="ir receiver connection to raspberry">
|
||||
<img src="../assets/IR_reciver_connection.png" height="500px" alt="ir reciver connection to raspberry">
|
||||
|
||||
> **Hint** The signal port doesn't have to be connected to port GPIO 17; this pin may be changed during the [in/out port settings](#in/out).
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ Jetson Nano currently does not support older Raspberry Pi v1 cameras (that are b
|
||||
|
||||
Fortunately, these cameras are available using GStreamer. You can try using the [`gscam`](http://wiki.ros.org/gscam) ROS package or our [`jetson_camera`](https://github.com/sfalexrog/jetson_camera) node. The latter requires you to build OpenCV 3.4 from source with GStreamer support.
|
||||
|
||||
The GStreamer pipelines are available at [JetsonHacksNano CSI camera repository](https://github.com/JetsonHacksNano/CSI-Camera).
|
||||
The GStreamer pipelines are available at [JetsonHacksNano CSI camera reposotory](https://github.com/JetsonHacksNano/CSI-Camera).
|
||||
|
||||
You may also notice that the camera image has a red tint that is more pronounced near the edges. This can be fixed by image signal processor tuning. Generally this should be done by your camera manufacturer; [here is a sample ISP configuration](https://www.arducam.com/docs/camera-for-jetson-nano/fix-red-tint-with-isp-tuning/) from Adrucam
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ rospy.init_node('flight')
|
||||
|
||||
def range_callback(msg):
|
||||
# Process data from the rangefinder
|
||||
print 'Rangefinder distance:', msg.range
|
||||
print('Rangefinder distance:', msg.range)
|
||||
|
||||
rospy.Subscriber('rangefinder/range', Range, range_callback)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Our [Raspberry Pi image](image.md) contains preinstalled modules for interfacing
|
||||
* control individual LED colors (low-level control);
|
||||
* configure the strip to display flight events.
|
||||
|
||||
> **Caution** LED strip can consume a lot of power! Powering it from a Raspberry Pi may overload the computer's power circuitry. Consider using a separate BEC as a power source.
|
||||
> **Caution** LED strip can consume a lot of power! Powering it from a Raspyerry Pi may overload the computer's power circuitry. Consider using a separate BEC as a power source.
|
||||
|
||||
## High-level control
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Connect the +5v and GND leads of your LED to a power source and the DIN (data in
|
||||
|
||||
<img src="../assets/led_connection.png" height="400px" alt="leds">
|
||||
|
||||
> **Caution** LED strip can consume a lot of power! Powering it from a Raspberry Pi may overload the computer's power circuitry. Consider using a separate BEC as a power source.
|
||||
> **Caution** LED strip can consume a lot of power! Powering it from a Raspyerry Pi may overload the computer's power circuitry. Consider using a separate BEC as a power source.
|
||||
|
||||
<!-- -->
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ An Unmanned Aerial Vehicle (UAV) is an aircraft flying without a pilot (crew) on
|
||||
Types and configuration
|
||||
-------------------
|
||||
|
||||
There are many engines configurations: a tricopter, a hexacopter, or an octocopter, but the simplest one in terms of assembly and operation is a quadcopter, i. e., a multi-rotor platform with four engines. In turn, a quadcopter may have + and x configuration. In copters with a "+" configuration, one of the beams faces forward, while in platforms with the "x" configuration, the main direction of movement is between two adjacent beams.
|
||||
There are many engines configurations: a tricopter, a hexacopter, or an octocopter, but the simplest one in terms of assembly and operation is a quadcopter, i. e., a multi-rotor platform with four engines. In turn, a quadcopter may have + and х configuration. In copters with a "+" configuration, one of the beams faces forward, while in platforms with the "x" configuration, the main direction of movement is between two adjacent beams.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -117,11 +117,12 @@ In a closed electric circuit, the sum of all the EMF is equal to the sum of the
|
||||
|
||||
In making equations, the direction of circuit traversal is chosen and arbitrarily specified directions of currents are specified.
|
||||
|
||||
If an electric circuit contains two power sources, the directions of electromotive forces of which coincide, i.e., connected according to Fig. 1, the EMF across the entire circuit shall be equal to the sum of the EMFs of the sources, i.e.,
|
||||
If an electric circuit contains two power sources, the directions of electromotive forces of which coincide, i.е., connected according to Fig. 1, the EMF across the entire circuit shall be equal to the sum of the EMFs of the sources,
|
||||
т. i.е.,
|
||||
|
||||
**E = E1+E2.**
|
||||
|
||||
If a circuit contains two sources of EDS with opposite directions, i.e., connected according to Fig. 2, the total EMF of the circuit will be equal to the difference of EMFs of these sources
|
||||
If a circuit contains two sources of EDS with opposite directions, i.е., connected according to Fig. 2, the total EMF of the circuit will be equal to the difference of EMFs of these sources
|
||||
|
||||
**Е = Е1—Е2.**
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
|
||||
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
|
||||
land = rospy.ServiceProxy('land', Trigger)
|
||||
|
||||
# Take off 1 m
|
||||
# Take off 1 м
|
||||
navigate(x=0, y=0, z=1, frame_id='body', auto_arm=True)
|
||||
```
|
||||
|
||||
@@ -70,56 +70,6 @@ The `~/catkin_ws/src/clever/` directory is renamed to `~/catkin_ws/src/clover`.
|
||||
|
||||
For example, `~/catkin_ws/src/clever/clever/launch/clever.launch` file is now `~/catkin_ws/src/clover/clover/launch/clover.launch`.
|
||||
|
||||
<!--
|
||||
## Python 3 transition
|
||||
|
||||
Python 2 is depracated since, January 1st, 2020. The Clover platform moves to Python 3.
|
||||
|
||||
For running flight script instead of `python` command:
|
||||
|
||||
```bash
|
||||
python flight.py
|
||||
```
|
||||
|
||||
use `python3` command:
|
||||
|
||||
```bash
|
||||
python3 flight.py
|
||||
```
|
||||
|
||||
Python 3 has certain syntax differences in comparison with the old version. Instead of `print` *operator*:
|
||||
|
||||
```python
|
||||
print 'Clover is the best'
|
||||
```
|
||||
|
||||
use `print` *function*:
|
||||
|
||||
```python
|
||||
print('Clover is the best')
|
||||
```
|
||||
|
||||
The division operator operates floating points by default (instead of integer). Python 2:
|
||||
|
||||
```python
|
||||
>>> 10 / 4
|
||||
2
|
||||
```
|
||||
|
||||
Python 3:
|
||||
|
||||
```python
|
||||
>>> 10 / 4
|
||||
2.5
|
||||
```
|
||||
|
||||
For strings `unicode` type is used by default (instead of `str` type).
|
||||
|
||||
Encoding specification (`# coding: utf8`) is not necessary any more.
|
||||
|
||||
More details on all the language changes see in [appropriate article](https://sebastianraschka.com/Articles/2014_python_2_3_key_diff.html).
|
||||
-->
|
||||
|
||||
## Wi-Fi network configuration
|
||||
|
||||
Wi-Fi networks' SSID is changed to `clover-XXXX` (where X is a random number), password is changed to `cloverwifi`.
|
||||
|
||||
55
docs/en/migrate22.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Migration to version 0.20
|
||||
|
||||
## Python 3 transition
|
||||
|
||||
Python 2 is deprecated since January 1st, 2020. The Clover platform moves to Python 3.
|
||||
|
||||
For running flight script instead of `python` command:
|
||||
|
||||
```bash
|
||||
python flight.py
|
||||
```
|
||||
|
||||
use `python3` command:
|
||||
|
||||
```bash
|
||||
python3 flight.py
|
||||
```
|
||||
|
||||
Python 3 has certain syntax differences in comparison with the old version. Instead of `print` *operator*:
|
||||
|
||||
```python
|
||||
print 'Clover is the best' # this won't work
|
||||
```
|
||||
|
||||
use `print` *function*:
|
||||
|
||||
```python
|
||||
print('Clover is the best')
|
||||
```
|
||||
|
||||
The division operator operates floating points by default (instead of integer). Python 2:
|
||||
|
||||
```python
|
||||
>>> 10 / 4
|
||||
2
|
||||
```
|
||||
|
||||
Python 3:
|
||||
|
||||
```python
|
||||
>>> 10 / 4
|
||||
2.5
|
||||
```
|
||||
|
||||
For strings `unicode` type is used by default (instead of `str` type).
|
||||
|
||||
Encoding specification (`# coding: utf8`) is not necessary any more.
|
||||
|
||||
More details on all the language changes see in [appropriate article](https://sebastianraschka.com/Articles/2014_python_2_3_key_diff.html).
|
||||
|
||||
## Move to ROS Noetic
|
||||
|
||||
<img src="../assets/noetic.png" width=200>
|
||||
|
||||
ROS Melodic version was updated to ROS Noetic. See the full list of changes in the [ROS official documentation](http://wiki.ros.org/noetic/Migration).
|
||||
@@ -44,7 +44,7 @@ When using **LPE** (parameter `SYS_MC_EST_GROUP` = `local_position_estimator, at
|
||||
* `SENS_FLOW_ROT` – No rotation.
|
||||
* `SENS_FLOW_MAXHGT` – 4.0 (for the rangefinder VL53L1X)
|
||||
* `SENS_FLOW_MINHGT` – 0.01 (for the rangefinder VL53L1X)
|
||||
* Optional: `LPE_FUSION` – falg 'pub agl as lpos down' is on (see [rangefinder setup](laser.md).
|
||||
* Optional: `LPE_FUSION` – falg 'pub agl as lpos down' is on (сf. [rangefinder setup](laser.md).
|
||||
|
||||
[The `selfcheck.py` utility](selfcheck.md) will help you verify that all settings are correctly set.
|
||||
|
||||
@@ -97,7 +97,7 @@ When using Optical Flow, the maximal horizontal speed is further limited. This i
|
||||
|
||||
## Errors
|
||||
|
||||
If errors like `EKF INTERNAL CHECKS` occur, try to restart EKF2. To do so, enter in the MAVLink-console:
|
||||
If errors of `EKF INTERNAL CHECKS` occur, try to restart EKF2. To do so, enter in the MAVLink-console : в MAVLink-консоли:
|
||||
|
||||
```nsh
|
||||
ekf2 stop
|
||||
|
||||
@@ -34,10 +34,10 @@ Read more in the [GPS connection](gps.md) article.
|
||||
|
||||
> **Info** For studying Python programming language, see [tutorial](https://www.learnpython.org/en/Welcome).
|
||||
|
||||
After you've configured your positioning system, you can start writing programs for autonomous flights. Use the [SSH connection to the Raspberry Pi](ssh.md) to run your scripts. In order to run a Python script use the `python` command:
|
||||
After you've configured your positioning system, you can start writing programs for autonomous flights. Use the [SSH connection to the Raspberry Pi](ssh.md) to run your scripts. In order to run a Python script use the `python3` command:
|
||||
|
||||
```bash
|
||||
python flight.py
|
||||
python3 flight.py
|
||||
```
|
||||
|
||||
Below is a complete flight program that performs a takeoff, flies forward and lands:
|
||||
|
||||
@@ -20,7 +20,7 @@ This is a group of modules that calculates the current state of the copter using
|
||||
* Copter orientation (in the local coordinate system) – pitch, roll, yaw (one of presentations);
|
||||
* Copter position (in the local coordinate system) – x, y, z;
|
||||
* Copter speed (in the local coordinate system) – vx, vy, vz;
|
||||
* Global coordinates of the copter – latitude, longitude, altitude;
|
||||
* Global coordinates of the copter – lattitude, longitude, altitude;
|
||||
* Altitude above the surface;
|
||||
* Other parameters (the drift of gyroscopes, wind speed, etc.).
|
||||
|
||||
@@ -39,15 +39,15 @@ This is a group of modules that calculates the current state of the copter using
|
||||
|
||||
Variant 2 is the most accurate; however, it is correct to use it only if the surface the copter flies over is flat. Otherwise, the Z axis origin will move up and down with the altitude of the surface.
|
||||
|
||||
## Multicopter Position Control
|
||||
## Multicopter Control Position (flying by position)
|
||||
|
||||
These parameters adjust the flight of the copter by position (POSCTL, OFFBOARD, AUTO modes).
|
||||
|
||||
`MPC_THR_HOVER` — hovering throttle. This option is to set to the approximate percentage of throttle needed to make the copter maintain its altitude. If copter has a tendency to gain or lose altitude during the hovering mode, reduce or increase this value.
|
||||
|
||||
`MPC_XY_P` – position factor *P* of the ESC. This parameter affects how sharply the copter will react to the position commands. A too high value may cause overshoots.
|
||||
`MPC_XY_P` – position factor *P* of the ESC. This parameter affects how sharply the copter will react to the position commands. A too high value may cause {перестрелы}.
|
||||
|
||||
`MPC_XY_VEL_P` – speed factor *P* of the ESC. This parameter also affects the accuracy and sharpness of copter execution of the given position. A too high value may cause overshoots.
|
||||
`MPC_XY_VEL_P` – speed factor *P* of the ESC. This parameter also affects the accuracy and sharpness of copter execution of the given position. A too high value may cause {перестрелы}.
|
||||
|
||||
`MPC_XY_VEL_MAX` — the maximum horizontal speed in POSCTL, OFFBOARD, AUTO modes.
|
||||
|
||||
|
||||
@@ -41,21 +41,21 @@ Now you can install the ROS package itself.
|
||||
```
|
||||
|
||||
After the package has installed, initialize `rosdep`.
|
||||
Package `rosdep` will allow to easily install dependencies for the source files that you whish to compile. Running some essential components of ROS will as well require this package.
|
||||
Package `rosdep` will allow to easily install dependecies for the source files that you whish to compile. Running some essential components of ROS will as well require this package.
|
||||
|
||||
```bash
|
||||
sudo rosdep init
|
||||
rosdep update
|
||||
```
|
||||
|
||||
If you are not comfortable with entering environment variables manually each time, you may configure it in a way that it add itself in your bash session on every new shell startup:
|
||||
If you are not confortable with entering environment variables manually each time, you may configure it in a way that it add itself in your bash session on every new shell startup:
|
||||
|
||||
```bash
|
||||
echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
If you whish to install any additional packages for your ROS Melodic simply use:
|
||||
If you whish to install any additionnal packages for yout ROS Melodic simply use:
|
||||
|
||||
```bash
|
||||
sudo apt-get install ros-melodic-PACKAGE
|
||||
|
||||
@@ -63,7 +63,7 @@ An example of subscription to topic `/foo`:
|
||||
|
||||
```python
|
||||
def foo_callback(msg):
|
||||
print msg.data
|
||||
print(msg.data)
|
||||
|
||||
# Subscribing. When a message is received in topic /foo, function foo_callback will be invoked.
|
||||
rospy.Subscriber('/foo', String, foo_callback)
|
||||
|
||||
@@ -14,7 +14,7 @@ Install package `ros-melodic-desktop-full` or `ros-melodic-desktop` using the [i
|
||||
Start rviz
|
||||
---
|
||||
|
||||
To start the Clover state visualization in real time, connect to it via Wi-Fi (`clover-xxx`) and run rviz, specifying an appropriate ROS_MASTER_URI:
|
||||
To start еру visualization of the state of Clover in real time, connect to it via Wi-Fi (`clover-xxx`) and run rviz, specifying an appropriate ROS_MASTER_URI:
|
||||
|
||||
```(bash)
|
||||
ROS_MASTER_URI=http://192.168.11.1:11311 rviz
|
||||
|
||||
@@ -14,7 +14,7 @@ Consult the [official QGroundControl user guide](https://docs.qgroundcontrol.com
|
||||
|
||||
Prepare the MicroSD card for your flight controller.
|
||||
|
||||
<img src="../assets/pix-sd.png" alt="Pixracer and MicroSD-card" class="zoom center" width=400>
|
||||
<img src="../assets/pix-sd.png" alt="Pixracer и MicroSD-карта" class="zoom center" width=400>
|
||||
|
||||
* Put the card into your computer (use an adapter if necessary).
|
||||
* Format the card to FAT32 filesystem. Right click on the SD card icon in Windows Explorer and select "Format". Use the Disk Utility in macOS.
|
||||
@@ -114,7 +114,7 @@ Press the *Save* button to save the changed value to the flight controller. Chan
|
||||
|
||||
> **Hint** Note that you should fine-tune the PID parameters for each drone individually. <!-- TODO: add PID article link -->
|
||||
|
||||
#### Circuit breaker parameters
|
||||
#### Cicruit breaker parameters
|
||||
|
||||
1. Set `CBRK_USB_CHK` to 197848 to allow flights with the USB cable connected.
|
||||
2. Disable safety switch check: `CBRK_IO_SAFETY` = 22027.
|
||||
|
||||
@@ -13,7 +13,7 @@ The `simple_offboard` module of the `clover` package is intended for simplified
|
||||
|
||||
Main services are [`get_telemetry`](#gettelemetry) (receive telemetry data), [`navigate`](#navigate) (fly to a given point along a straight line), [`navigate_global`](#navigateglobal) (fly to a point specified as latitude and longitude along a straight line), [`land`](#land) (switch to landing mode).
|
||||
|
||||
Python examples
|
||||
Python samples
|
||||
---
|
||||
|
||||
You need to create proxies for services before calling them. Use the following template for your programs:
|
||||
@@ -75,14 +75,14 @@ Displaying drone coordinates `x`, `y` and `z` in the local system of coordinates
|
||||
|
||||
```python
|
||||
telemetry = get_telemetry()
|
||||
print telemetry.x, telemetry.y, telemetry.z
|
||||
print(telemetry.x, telemetry.y, telemetry.z)
|
||||
```
|
||||
|
||||
Displaying drone altitude relative to [the ArUco map](aruco.md):
|
||||
|
||||
```python
|
||||
telemetry = get_telemetry(frame_id='aruco_map')
|
||||
print telemetry.z
|
||||
print(telemetry.z)
|
||||
```
|
||||
|
||||
Checking global position availability:
|
||||
@@ -90,9 +90,9 @@ Checking global position availability:
|
||||
```python
|
||||
import math
|
||||
if not math.isnan(get_telemetry().lat):
|
||||
print 'Global position is available'
|
||||
print('Global position is available')
|
||||
else:
|
||||
print 'No global position'
|
||||
print('No global position')
|
||||
```
|
||||
|
||||
Output of current telemetry (command line):
|
||||
@@ -307,7 +307,7 @@ Landing the drone:
|
||||
res = land()
|
||||
|
||||
if res.success:
|
||||
print 'drone is landing'
|
||||
print('drone is landing')
|
||||
```
|
||||
|
||||
Landing the drone (command line):
|
||||
|
||||
@@ -8,21 +8,17 @@ In addition to [native installation instructions](simulation_native.md), we prov
|
||||
* preconfigured Gazebo simulation environment;
|
||||
* Visual Studio Code with C++ and Python plugins.
|
||||
|
||||
> **Info** The default username on the VM is `clover`, with password `clover`.
|
||||
|
||||
The VM is an easy way to set up a simulation environment, but can be used as a development environment for a real drone as well.
|
||||
|
||||
## Downloading
|
||||
|
||||
You can download the latest VM image [in the VM releases repository](https://github.com/CopterExpress/clover_vm/releases).
|
||||
|
||||
> **Note** The virtual machine should be used when native installation is not feasible or possible. You may experience reduced performance in programs that use 3D rendering, like rviz and Gazebo.
|
||||
|
||||
## Setting up the VM
|
||||
|
||||
You need to use a VM manager that supports OVF format, like [VirtualBox](https://www.virtualbox.org/wiki/Downloads), [VMware Player](https://www.vmware.com/products/workstation-player.html) or [VMware Workstation](https://www.vmware.com/products/workstation-pro.html).
|
||||
You need to use a VM manager that supports OVF format, like [Virtualbox](https://www.virtualbox.org/wiki/Downloads), [VMware Player](https://www.vmware.com/products/workstation-player.html) or [VMware Workstation](https://www.vmware.com/products/workstation-pro.html).
|
||||
|
||||
> **Note** At the time of writing VirtualBox had issues running the VM, particularly with 3D applications. We recommend using VMware Player or VMware Workstation if possible. The following steps assume you're using VMware Player.
|
||||
> **Note** At the time of writing Virtualbox had issues running the VM, particularly with 3D applications. We recommend using VMware Player or VMware Workstation if possible. The following steps assume you're using VMware Player.
|
||||
|
||||
Make sure that you have hardware virtualization enabled in your BIOS/UEFI (it may be supported by your hardware but turned off by default). The steps to enable virtualization differ from manufacturer to manufacturer, but should be described in your system manual. Consult your system's manufacturer if you're having trouble turning virtualization on.
|
||||
|
||||
@@ -37,7 +33,7 @@ Make sure that you have hardware virtualization enabled in your BIOS/UEFI (it ma
|
||||
2. Right-click on the VM name and select **Virtual Machine Settings**. In the new window, set the following parameters:
|
||||
|
||||
* increase the amount of memory available to the virtual machine (a good rule of thumb is 2048 MB per CPU core, but no less than 4 GB):
|
||||

|
||||

|
||||
* increase the amount available CPU cores:
|
||||

|
||||
* enable 3D acceleration:
|
||||
|
||||
@@ -103,7 +103,7 @@ arming = rospy.ServiceProxy('mavros/cmd/arming', CommandBool)
|
||||
|
||||
# ...
|
||||
|
||||
arming(False) # disarm
|
||||
arming(False) # дизарм
|
||||
```
|
||||
|
||||
### # {#transform}
|
||||
@@ -319,7 +319,7 @@ def flip():
|
||||
rospy.loginfo('finish flip')
|
||||
set_position(x=start.x, y=start.y, z=start.z, yaw=start.yaw) # finish flip
|
||||
|
||||
print navigate(z=2, speed=1, frame_id='body', auto_arm=True) # take off
|
||||
print(navigate(z=2, speed=1, frame_id='body', auto_arm=True)) # take off
|
||||
rospy.sleep(10)
|
||||
|
||||
rospy.loginfo('flip')
|
||||
|
||||
@@ -83,13 +83,13 @@ pi.callback(ECHO, pigpio.FALLING_EDGE, fall)
|
||||
|
||||
while True:
|
||||
# Reading the distance:
|
||||
print read_distance()
|
||||
print(read_distance())
|
||||
|
||||
```
|
||||
|
||||
### Filtering the data
|
||||
|
||||
To filter (smooth out) the data and delete [outliers](https://en.wikipedia.org/wiki/Outlier), [Kalman filter](https://en.wikipedia.org/wiki/Kalman_filter) or a simple [median filter](https://ru.wikipedia.org/wiki/Median_filter) can be used. An example of median filtering implementation:
|
||||
To filter (smooth out) the data and delete [emission](https://ru.wikipedia.org/wiki/Outbreak_%28statistics%29) [Kalman filter] (https://ru.wikipedia.org/wiki/Фильтр_Калмана) or a simple [median filter](https://ru.wikipedia.org/wiki/Median_filter) can be used. An example of median filtering implementation:
|
||||
|
||||
```python
|
||||
import collections
|
||||
@@ -97,14 +97,14 @@ import numpy
|
||||
|
||||
# ...
|
||||
|
||||
history = collections.deque(maxlen=10) # 10 - number of samples for averaging
|
||||
history = collections.deque(maxlen=10) # 10 - количество сэмплов для усреднения
|
||||
|
||||
def read_distance_filtered():
|
||||
history.append(read_distance())
|
||||
return numpy.median(history)
|
||||
|
||||
while True:
|
||||
print read_distance_filtered()
|
||||
print(read_distance_filtered())
|
||||
```
|
||||
|
||||
An example of charts of initial and filtered data:
|
||||
|
||||
@@ -12,7 +12,7 @@ Read more about the interface and the Protocol in [this article](https://habr.co
|
||||
|
||||
## Linux TTY
|
||||
|
||||
In Linux, there is the concept of POSIX Terminal Interface (read more [here](https://en.wikipedia.org/wiki/POSIX_terminal_interface). It is an abstraction over the serial or virtual interface that allows several agents to work with the device simultaneously.
|
||||
In Linux, there is the concept of Posix Terminal Interface (read more [here](https://ru.wikipedia.org/wiki/TTY-абстракция)). It is an abstraction over the serial or virtual interface that allows several agents to work with the device simultaneously.
|
||||
|
||||
An example of such abstraction in Raspbian may be `/dev/tty1` – the device for text output to the screen connected via HDMI.
|
||||
|
||||
@@ -37,7 +37,7 @@ By default, Raspberry Pi 3 PL011 is connected to the Bluetooth module. And Mini
|
||||
For the sake of convenience of working with these outputs, aliases exist in Raspbian:
|
||||
|
||||
* `/dev/serial0` – always points to the TTY device that is connected to the GPIO ports.
|
||||
* `/dev/serial1` – always points to the TTY device that is connected to the Bluetooth module.
|
||||
* `/dev/serial1` – always points to the TTY device that is connected to the Bluetooh module.
|
||||
|
||||
### Configuration of UART on Raspberry Pi 3
|
||||
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
|
||||
Сформируйте необходимые ключи VPN сети, для подключения Raspberry Pi и наземной станции.
|
||||
|
||||
Для того, чтобы подключить Raspberry Pi к вашей сети, установите пакет `openvpn`:
|
||||
Для того, чтобы подключить Raspberry Pi к вашей сети, установите пакет *openvpn*:
|
||||
|
||||
```bash
|
||||
sudo apt-get install openvpn
|
||||
```
|
||||
|
||||
Перенесите ваши ключи в директорию `/etc/openvpn/client`. Для удобства используйте графический SFTP интерфейс передачи данных, к примеру: WinSCP, FileZilla и т.д.
|
||||
Перенесите ваши ключи в директорию */etc/openvpn/client*. Для удобства используйте графический SFTP интерфейс передачи данных, к примеру: WinSCP, FileZilla и т.д.
|
||||
|
||||
Для включения режима клиента, необходимо активировать переданные вами ключи. Ключи могу быть сформированы в различных форматах, к примеру: `.ovpn`, `.conf`. Ключ или конфигурация использующийся на вашем коптере, должны быть строго в формате `.conf`.
|
||||
Для включения режима клиента, необходимо активировать переданные вами ключи. Ключи могу быть сформированы в различных форматах, к примеру: *.ovpn*, *.conf*. Ключ или конфигурация использующийся на вашем коптере, должны быть строго в формате *.conf*.
|
||||
|
||||
Инициализируйте сервис применяющий ваши ключи для подключения в режиме клиента:
|
||||
|
||||
@@ -34,7 +34,7 @@ sudo apt-get install openvpn
|
||||
sudo systemctl enable openvpn-client@config-name
|
||||
```
|
||||
|
||||
где `config-name` - название вашего конфигурационного файла.
|
||||
где *config-name* - название вашего конфигурационного файла.
|
||||
|
||||
Если все сделано правильно, при каждом перезапуске системы, сервис-клиент будет автоматически подключаться к вашей сети.
|
||||
|
||||
@@ -65,7 +65,7 @@ sudo systemctl enable openvpn-client@config-name
|
||||
|
||||
> **Info** При выборе джойстика, обратите внимание на количество рабочих каналов и на поддержку его, в QGroundControl(SDL2). Встречаются пульты поддерживающие всего 4 канала, что не удобно для такого типа управления.
|
||||
|
||||
Если изменения положения стиков отображается в окне QGroundControl, вам остается только применить параметр, определяющий, что управление коптером происходит с помощью джойстика, а не радиоаппаратуры:
|
||||
Если изменения положения стиков отображается в окне QGgroundControl, вам остается только применить параметр, определяющий, что управление коптером происходит с помощью джойстика, а не радиоаппаратуры:
|
||||
|
||||
`COM_RC_IN_MODE` - Joystick/No RC Checks
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
* [Светодиодная лента (legacy)](leds_old.md)
|
||||
* [Вклад в Клевер](contributing.md)
|
||||
* [Переход на версию 0.20](migrate20.md)
|
||||
* [Переход на версию 0.22](migrate22.md)
|
||||
* [Мероприятия](events.md)
|
||||
* [CopterHack-2021](copterhack2021.md)
|
||||
* [CopterHack-2019](copterhack2019.md)
|
||||
|
||||
@@ -110,9 +110,9 @@ rospy.init_node('my_node')
|
||||
# ...
|
||||
|
||||
def markers_callback(msg):
|
||||
print 'Detected markers:':
|
||||
print('Detected markers:'):
|
||||
for marker in msg.markers:
|
||||
print 'Marker: %s' % marker
|
||||
print('Marker: %s' % marker)
|
||||
|
||||
# Подписываемся. При получении сообщения в топик aruco_detect/markers будет вызвана функция markers_callback.
|
||||
rospy.Subscriber('aruco_detect/markers', MarkerArray, markers_callback)
|
||||
|
||||
@@ -102,50 +102,46 @@
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_2.png" width=300 class="zoom border">
|
||||
</div>
|
||||
|
||||
2. Установите регуляторы оборотов (ESC) в соответствующие места на луче.
|
||||
2. Установите регуляторы оборотов (ESC) в соответствующие места на луче и притяните хомутами.
|
||||
|
||||
<div class="image-group">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_3.png" width=300 class="zoom border">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_4.png" width=300 class="zoom border">
|
||||
</div>
|
||||
|
||||
3. Притяните регуляторы оборотов (ESC) хомутами.
|
||||
3. Отмерьте необходимое количество силового провода регуляторов оборотов(ESC), и обрежьте лишнее.
|
||||
|
||||
<img id="prop_rotation" src="../assets/assembling_soldering_clever_4/esc_5.png" width=300 class="zoom border center">
|
||||
4. Зачистите и залудите обрезанные провода
|
||||
|
||||
4. Отмерьте необходимое количество силового провода регуляторов оборотов(ESC), и обрежьте лишнее.
|
||||
5. Залудите контактные площадки на плате распределения питания.
|
||||
|
||||
5. Зачистите и залудите обрезанные провода.
|
||||
|
||||
6. Залудите контактные площадки на плате распределения питания.
|
||||
|
||||
7. Припаяйте силовые провода регуляторов оборотов к плате распределения питания.
|
||||
6. Припаяйте силовые провода регуляторов оборотов к плате распределения питания.
|
||||
|
||||
<div class="image-group">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_5.png" width=300 class="zoom border">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_6.png" width=300 class="zoom border">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_7.png" width=300 class="zoom border">
|
||||
</div>
|
||||
|
||||
> **Caution** Будьте внимательны к подписям контактов на плате. Красный провод должен идти к площадке с подписью *+*, а черный к подписи *-*.
|
||||
|
||||
8. Обрежьте лишний фазный кабель идущий от моторов.
|
||||
7. Обрежьте лишний фазный кабель идущий от моторов.
|
||||
|
||||
9. Зачистите и залудите фазные кабели.
|
||||
8. Зачистите и залудите фазные кабели.
|
||||
|
||||
10. Залудите контактные площадки регуляторов оборотов.
|
||||
9. Залудите контактные площадки регуляторов оборотов.
|
||||
|
||||
11. Припаяйте фазные кабели к контактным площадкам регуляторов в любом порядке.
|
||||
10. Припаяйте фазные кабели к контактным площадкам регуляторов в любом порядке.
|
||||
|
||||
<div class="image-group">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_7.png" width=300 class="zoom border">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_8.png" width=300 class="zoom border">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_9.png" width=300 class="zoom border">
|
||||
</div>
|
||||
|
||||
12. Припаяйте 3 разъема JST мама к 2ум площадкам *5V* и площадке *bat+*.
|
||||
11. Припаяйте 3 разъема JST мама к 2ум площадкам *5V* и площадке *bat+*
|
||||
|
||||
<div class="image-group">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_9.png" width=300 class="zoom border">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_10.png" width=300 class="zoom border">
|
||||
<img src="../assets/assembling_soldering_clever_4/esc_11.png" width=300 class="zoom border">
|
||||
</div>
|
||||
|
||||
## Установка полетного контроллера
|
||||
@@ -338,15 +334,4 @@
|
||||
|
||||
<img src="../assets/assembling_soldering_clever_4/guard_3.png" width=300 class="zoom border center">
|
||||
|
||||
4. Подключите полетный контролер к Raspberry Pi с помощью USB к кабеля.
|
||||
|
||||
<div class="image-group">
|
||||
<img src="../assets/assembling_soldering_clever_4/guard_4.png" width=300 class="zoom border">
|
||||
<img src="../assets/assembling_soldering_clever_4/guard_5.png" width=300 class="zoom border">
|
||||
</div>
|
||||
|
||||
5. Установите ремешок для крепления АКБ.
|
||||
|
||||
<img src="../assets/assembling_soldering_clever_4/guard_6.png" width=300 class="zoom border center">
|
||||
|
||||
> **Success** Дрон собран, далее произведите [настройку](setup.md).
|
||||
|
||||
@@ -126,7 +126,7 @@ Ctrl+C
|
||||
Запустить программу myprogram.py на Питоне:
|
||||
|
||||
```bash
|
||||
python myprogram.py
|
||||
python3 myprogram.py
|
||||
```
|
||||
|
||||
Журнал событий процессов Клевера. Пролистывать список можно нажатием Enter или сочетанием клавиш Ctrl+V (пролистывает быстрее):
|
||||
@@ -406,7 +406,7 @@ sudo nano /etc/sudoers
|
||||
- Запустите программу. Для этого выполните команду:
|
||||
|
||||
```bash
|
||||
python my_program.py
|
||||
python3 my_program.py
|
||||
```
|
||||
|
||||
> **Warning** После выполнения программы дрон может некорректно приземлиться и продолжать лететь над полом. В таком случае нужно перехватить управление.
|
||||
|
||||
@@ -140,7 +140,7 @@ def image_callback(data):
|
||||
(x, y, w, h) = barcode.rect
|
||||
xc = x + w/2
|
||||
yc = y + h/2
|
||||
print ("Found {} with data {} with center at x={}, y={}".format(b_type, b_data, xc, yc))
|
||||
print("Found {} with data {} with center at x={}, y={}".format(b_type, b_data, xc, yc))
|
||||
|
||||
image_sub = rospy.Subscriber('main_camera/image_raw', Image, image_callback, queue_size=1)
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ cat file.py
|
||||
Запустить Python-скрипт `file.py`:
|
||||
|
||||
```bash
|
||||
python file.py
|
||||
python3 file.py
|
||||
```
|
||||
|
||||
Перезагрузить Raspberry Pi:
|
||||
|
||||
@@ -35,9 +35,9 @@
|
||||
|
||||
### Схемы расположения контактов
|
||||
|
||||
<img src="../assets/coex_pix/coexpix-top.jpg" width=400 class=zoom>
|
||||
<img src="../assets/coexpix-top.jpg" width=400 class=zoom>
|
||||
|
||||
<img src="../assets/coex_pix/coexpix-bottom.jpg" width=400 class=zoom>
|
||||
<img src="../assets/coexpix-bottom.jpg" width=400 class=zoom>
|
||||
|
||||
> **Note** На плате ревизии 1.0 RC IN разъем располагался на месте разъема Micro SD. Распиновка самого разъема осталась такой же.
|
||||
|
||||
@@ -58,18 +58,3 @@
|
||||
### Особенности платы
|
||||
|
||||
Для повышения надежности и стабильности, плата оснащена низкошумящими понижающими преобразователями. Установлен входной LC фильтр, а также ферритовые фильтры в цепях питания.
|
||||
|
||||
## Ревизия 1.2
|
||||
|
||||
### Нововведения
|
||||
|
||||
* Заменен разъем USB Micro-B на раазъем USB Type-C.
|
||||
* Изменен слот для MicroSD карт, на более глубокий.
|
||||
* Изменены назначения пинов на разьеме I2C.
|
||||
* Добавлены ферритовые фильтры в цепи питания.
|
||||
|
||||
### Схемы расположения контактов
|
||||
|
||||
<img src="../assets/coex_pix/coexpix-top-rev1.2.jpg" width=400 class=zoom>
|
||||
|
||||
<img src="../assets/coex_pix/coexpix-bottom-rev1.2.jpg" width=400 class=zoom>
|
||||
|
||||
@@ -59,7 +59,7 @@ rospy.init_node('flight')
|
||||
|
||||
def range_callback(msg):
|
||||
# Обработка новых данных с дальномера
|
||||
print 'Rangefinder distance:', msg.range
|
||||
print('Rangefinder distance:', msg.range)
|
||||
|
||||
rospy.Subscriber('rangefinder/range', Range, range_callback)
|
||||
|
||||
|
||||
@@ -72,56 +72,6 @@ sudo systemctl restart clover
|
||||
|
||||
Например, файл `~/catkin_ws/src/clever/clever/launch/clever.launch` теперь называется `~/catkin_ws/src/clover/clover/launch/clover.launch`.
|
||||
|
||||
<!--
|
||||
## Переход на Python 3
|
||||
|
||||
Python 2 был признан [устаревшим](https://www.python.org/doc/sunset-python-2/), начиная с 1 января 2020 года. Платформа Клевера переходит на использование Python 3.
|
||||
|
||||
Для запуска полетных скриптов вместо команды `python`:
|
||||
|
||||
```bash
|
||||
python flight.py
|
||||
```
|
||||
|
||||
теперь следует использовать команду `python3`:
|
||||
|
||||
```bash
|
||||
python3 flight.py
|
||||
```
|
||||
|
||||
Синтаксис языка Python 3 имеет определенные изменения по сравнения со второй версией. Вместо *оператора* `print`:
|
||||
|
||||
```python
|
||||
print 'Clover is the best'
|
||||
```
|
||||
|
||||
теперь используется *функция* `print`:
|
||||
|
||||
```python
|
||||
print('Clover is the best')
|
||||
```
|
||||
|
||||
Оператор деления по умолчанию выполняет деление с плавающей точкой (вместо целочисленного). Python 2:
|
||||
|
||||
```python
|
||||
>>> 10 / 4
|
||||
2
|
||||
```
|
||||
|
||||
Python 3:
|
||||
|
||||
```python
|
||||
>>> 10 / 4
|
||||
2.5
|
||||
```
|
||||
|
||||
Для строк по умолчанию теперь используется тип `unicode` (вместо типа `str`).
|
||||
|
||||
Указание кодировки файла (`# coding: utf8`) перестало быть необходимым.
|
||||
|
||||
Полное описание всех изменений языка смотрите в [соответствующей статье](https://pythonworld.ru/osnovy/python2-vs-python3-razlichiya-sintaksisa.html).
|
||||
-->
|
||||
|
||||
## Настройки Wi-Fi сети
|
||||
|
||||
SSID Wi-Fi сети изменен на `clover-XXXX` (где X – случайная цифра), пароль изменен на `cloverwifi`.
|
||||
|
||||
55
docs/ru/migrate22.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Переход на версию 0.22
|
||||
|
||||
## Переход на Python 3
|
||||
|
||||
Python 2 был признан [устаревшим](https://www.python.org/doc/sunset-python-2/), начиная с 1 января 2020 года. Платформа Клевера переходит на использование Python 3.
|
||||
|
||||
Для запуска полетных скриптов вместо команды `python`:
|
||||
|
||||
```bash
|
||||
python flight.py
|
||||
```
|
||||
|
||||
теперь следует использовать команду `python3`:
|
||||
|
||||
```bash
|
||||
python3 flight.py
|
||||
```
|
||||
|
||||
Синтаксис языка Python 3 имеет определенные изменения по сравнения со второй версией. Вместо *оператора* `print`:
|
||||
|
||||
```python
|
||||
print 'Clover is the best' # this won't work
|
||||
```
|
||||
|
||||
теперь используется *функция* `print`:
|
||||
|
||||
```python
|
||||
print('Clover is the best')
|
||||
```
|
||||
|
||||
Оператор деления по умолчанию выполняет деление с плавающей точкой (вместо целочисленного). Python 2:
|
||||
|
||||
```python
|
||||
>>> 10 / 4
|
||||
2
|
||||
```
|
||||
|
||||
Python 3:
|
||||
|
||||
```python
|
||||
>>> 10 / 4
|
||||
2.5
|
||||
```
|
||||
|
||||
Для строк по умолчанию теперь используется тип `unicode` (вместо типа `str`).
|
||||
|
||||
Указание кодировки файла (`# coding: utf8`) перестало быть необходимым.
|
||||
|
||||
Полное описание всех изменений языка смотрите в [соответствующей статье](https://pythonworld.ru/osnovy/python2-vs-python3-razlichiya-sintaksisa.html).
|
||||
|
||||
## Переход на ROS Noetic
|
||||
|
||||
<img src="../assets/noetic.png" width=200>
|
||||
|
||||
Версия ROS Melodic обновлена до ROS Noetic. Смотрите полный список изменений в [официальной документации ROS](http://wiki.ros.org/noetic/Migration).
|
||||
@@ -34,10 +34,10 @@
|
||||
|
||||
> **Info** Для изучения языка программирования Python обращайтесь к [самоучителю](https://pythonworld.ru/samouchitel-python).
|
||||
|
||||
После настройки системы позиционирования становится возможным написание скриптов для автономных полетов. Для выполнения скриптов [подключитесь в Raspberry Pi по SSH](ssh.md). Для того, чтобы запустить Python-скрипт, используйте команду `python`:
|
||||
После настройки системы позиционирования становится возможным написание скриптов для автономных полетов. Для выполнения скриптов [подключитесь в Raspberry Pi по SSH](ssh.md). Для того, чтобы запустить Python-скрипт, используйте команду `python3`:
|
||||
|
||||
```bash
|
||||
python flight.py
|
||||
python3 flight.py
|
||||
```
|
||||
|
||||
Пример программы для полета (взлет, пролет вперед, посадка):
|
||||
|
||||
@@ -36,6 +36,5 @@
|
||||
|Полет коптера на точку на изображении с камеры, направленной вертикально вниз||
|
||||
|Внедрение лидара ([RPLIDAR](https://www.slamtec.com/en/Lidar)) в Клевер||
|
||||
|Зарядная станция для коптера на солнечном концентраторе|<!-- placeholder for gitbook-->|
|
||||
|Стенд для стройки коэффициентов PID на Клевере|<!-- placeholder for gitbook-->|
|
||||
|
||||
Вышеперечисленные и другие проекты вы также можете реализовать в рамках конкурса проектов [Copter Hack](https://ru.coex.tech/copterhack). Мы приглашаем команды для реализации проектов и в других форматах.
|
||||
|
||||
@@ -63,7 +63,7 @@ foo_pub.publish(data='Hello, world!') # публикуем сообщение
|
||||
|
||||
```python
|
||||
def foo_callback(msg):
|
||||
print msg.data
|
||||
print(msg.data)
|
||||
|
||||
# Подписываемся. При получении сообщения в топик /foo будет вызвана функция foo_callback.
|
||||
rospy.Subscriber('/foo', String, foo_callback)
|
||||
|
||||
@@ -75,14 +75,14 @@ land = rospy.ServiceProxy('land', Trigger)
|
||||
|
||||
```python
|
||||
telemetry = get_telemetry()
|
||||
print telemetry.x, telemetry.y, telemetry.z
|
||||
print(telemetry.x, telemetry.y, telemetry.z)
|
||||
```
|
||||
|
||||
Вывод высоты коптера относительно [карты ArUco-меток](aruco.md):
|
||||
|
||||
```python
|
||||
telemetry = get_telemetry(frame_id='aruco_map')
|
||||
print telemetry.z
|
||||
print(telemetry.z)
|
||||
```
|
||||
|
||||
Проверка доступности глобальной позиции:
|
||||
@@ -90,9 +90,9 @@ print telemetry.z
|
||||
```python
|
||||
import math
|
||||
if not math.isnan(get_telemetry().lat):
|
||||
print 'Global position is available'
|
||||
print('Global position is available')
|
||||
else:
|
||||
print 'No global position'
|
||||
print('No global position')
|
||||
```
|
||||
|
||||
Вывод текущей телеметрии (командная строка):
|
||||
@@ -307,7 +307,7 @@ set_velocity(vx=0.4, vy=0.0, vz=0, yaw=float('nan'), yaw_rate=0.4, frame_id='bod
|
||||
res = land()
|
||||
|
||||
if res.success:
|
||||
print 'Copter is landing'
|
||||
print('Copter is landing')
|
||||
```
|
||||
|
||||
Посадка коптера (командная строка):
|
||||
|
||||
@@ -10,13 +10,9 @@
|
||||
* предварительно настроенный симулятор Gazebo;
|
||||
* среда разработки Visual Studio Code с плагинами для разработки на Python и C++.
|
||||
|
||||
> **Info** Имя пользователя по умолчанию на виртуальной машине - `clover`, пароль - `clover`.
|
||||
|
||||
Виртуальная машина может использоваться как для запуска симуляторов, так и для работы с настоящим дроном.
|
||||
|
||||
## Скачивание
|
||||
|
||||
Скачать текущую версию виртуальной машины можно [в релизах репозитория виртуальной машины](https://github.com/CopterExpress/clover_vm/releases).
|
||||
Скачать текущую версию виртуальной машины можно [в релизах репозитория виртуальной машины](https://github.com/CopterExpress/clover_vm/releases)
|
||||
|
||||
> **Warning** Виртуальную машину следует использовать только в тех случаях, когда по каким-то причинам использование Ubuntu 18.04 напрямую невозможно. Производительность всех программ, особенно тех, которые используют 3D-графику - jMAVSim, Gazebo, rviz - будет существенно ниже; кроме того, в ряде случаев будут возникать графические ошибки, приводящие к частичной или полной неработоспособности указанных программ.
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ def flip():
|
||||
rospy.loginfo('finish flip')
|
||||
set_position(x=start.x, y=start.y, z=start.z, yaw=start.yaw) # finish flip
|
||||
|
||||
print navigate(z=2, speed=1, frame_id='body', auto_arm=True) # take off
|
||||
print(navigate(z=2, speed=1, frame_id='body', auto_arm=True)) # take off
|
||||
rospy.sleep(10)
|
||||
|
||||
rospy.loginfo('flip')
|
||||
|
||||
@@ -83,7 +83,7 @@ pi.callback(ECHO, pigpio.FALLING_EDGE, fall)
|
||||
|
||||
while True:
|
||||
# Читаем дистанцию:
|
||||
print read_distance()
|
||||
print(read_distance())
|
||||
|
||||
```
|
||||
|
||||
@@ -104,7 +104,7 @@ def read_distance_filtered():
|
||||
return numpy.median(history)
|
||||
|
||||
while True:
|
||||
print read_distance_filtered()
|
||||
print(read_distance_filtered())
|
||||
```
|
||||
|
||||
Пример графиков исходных и отфильтрованных данных:
|
||||
|
||||
@@ -6,3 +6,7 @@ find_package(catkin REQUIRED)
|
||||
catkin_package()
|
||||
|
||||
install(DIRECTORY launch DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
|
||||
|
||||
catkin_install_python(PROGRAMS main.py
|
||||
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
|
||||
)
|
||||
|
||||