Compare commits

..

12 Commits
22 ... ros-book

Author SHA1 Message Date
Oleg Kalachev
d383a1c858 Minor fix 2020-10-06 02:38:29 +03:00
Oleg Kalachev
d15eb7785e Merge branch 'master' into ros-book 2020-10-06 02:31:40 +03:00
Oleg Kalachev
bb9d8edb78 Merge branch 'master' into ros-book 2020-10-06 02:23:01 +03:00
Oleg Kalachev
852b854676 Add REP abbreviation expansion 2020-08-10 21:26:13 +03:00
Oleg Kalachev
72491ade0e Update docs/ru/ros.md
Co-authored-by: Alexey Rogachevskiy <sfalexrog@gmail.com>
2020-08-07 00:41:42 +03:00
Oleg Kalachev
f5ee72940c Add timeout for wait_for_message 2020-08-06 15:36:09 +03:00
Oleg Kalachev
b6cedecdf0 Add wait_for_message info 2020-08-06 15:33:55 +03:00
Oleg Kalachev
8b1dddce67 Move working with multiple machines to ROS advanced article 2020-08-05 23:04:41 +03:00
Oleg Kalachev
a89bc82f2b Merge branch 'master' into ros-book 2020-08-05 13:48:06 +03:00
Oleg Kalachev
156527641a Continue working on ROS book 2020-08-05 13:44:32 +03:00
Oleg Kalachev
4f9e4b1a28 Merge branch 'master' into ros-book 2020-08-05 11:21:05 +03:00
Oleg Kalachev
46f8f6eb89 docs: add ROS book draft 2020-05-10 12:09:43 +03:00
240 changed files with 1509 additions and 137154 deletions

View File

@@ -21,9 +21,7 @@
"ROS", "ROS",
"ROS Kinetic", "ROS Kinetic",
"ROS Melodic", "ROS Melodic",
"ROS Noetic",
"OpenCV", "OpenCV",
"OpenVPN",
"Gazebo", "Gazebo",
"GitHub", "GitHub",
"FPV", "FPV",
@@ -108,9 +106,7 @@
"UDP", "UDP",
"QR", "QR",
"Li-ion", "Li-ion",
"Nvidia", "Nvidia"
"VirtualBox",
"VMware"
], ],
"code_blocks": false "code_blocks": false
}, },

View File

@@ -5,12 +5,12 @@ services:
- docker - docker
env: env:
global: global:
- DOCKER="sfalexrog/img-tool:qemu-aarch64" - DOCKER="sfalexrog/img-tool:qemu-update"
- TARGET_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git" - TARGET_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
- IMAGE_VERSION=${TRAVIS_TAG:-${TRAVIS_COMMIT:0:7}} - IMAGE_VERSION=${TRAVIS_TAG:-${TRAVIS_COMMIT:0:7}}
- IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img" - IMAGE_NAME="$(basename -s '.git' ${TARGET_REPO})_${IMAGE_VERSION}.img"
git: git:
depth: 25 depth: 50
jobs: jobs:
fast_finish: true fast_finish: true
include: include:
@@ -38,7 +38,7 @@ jobs:
- cp images/*.zip imgcache - cp images/*.zip imgcache
after_success: after_success:
- sudo chmod -R 777 * - 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: before_deploy:
# Set up git user name and tag this commit # Set up git user name and tag this commit
- git config --local user.name "goldarte" - git config --local user.name "goldarte"
@@ -68,14 +68,6 @@ jobs:
- docker pull ${NATIVE_DOCKER} - docker pull ${NATIVE_DOCKER}
script: script:
- docker run --rm -v $(pwd):/root/catkin_ws/src/clover ${NATIVE_DOCKER} /root/catkin_ws/src/clover/builder/standalone-install.sh - 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 - stage: Build
name: "Documentation" name: "Documentation"
language: node_js language: node_js

View File

@@ -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). Official website: <a href="https://coex.tech/clover">coex.tech/clover</a>.
[__Support us on Kickstarter!__](https://www.kickstarter.com/projects/copterexpress/cloverdrone)
## Video compilation ## Video compilation
@@ -25,7 +23,7 @@ Preconfigured image for Raspberry Pi with installed and configured software, rea
Image features: Image features:
* Raspbian Buster * Raspbian Buster
* [ROS Noetic](http://wiki.ros.org/noetic) * [ROS Melodic](http://wiki.ros.org/melodic)
* Configured networking * Configured networking
* OpenCV * OpenCV
* [`mavros`](http://wiki.ros.org/mavros) * [`mavros`](http://wiki.ros.org/mavros)

View File

@@ -22,21 +22,13 @@ find_package(catkin REQUIRED COMPONENTS
dynamic_reconfigure dynamic_reconfigure
) )
# Workaround for OpenCV 3/4 support find_package(OpenCV 3 REQUIRED COMPONENTS core imgproc calib3d)
set(_opencv_version 4)
find_package(OpenCV ${_opencv_version} QUIET COMPONENTS core imgproc calib3d)
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") if ("${OpenCV_VERSION_MINOR}" LESS "9")
message(STATUS "OpenCV version too low, using vendored ArUco package") message(STATUS "OpenCV version too low, using vendored ArUco package")
include(vendor/VendorOpenCV.cmake) include(vendor/VendorOpenCV.cmake)
else() else()
message(STATUS "Using system OpenCV ArUco package") message(STATUS "Using system OpenCV ArUco package")
find_package(OpenCV ${_opencv_version} REQUIRED COMPONENTS aruco) find_package(OpenCV 3 REQUIRED COMPONENTS aruco)
endif() endif()
message(STATUS "OpenCV include dirs: ${OpenCV_INCLUDE_DIRS}") message(STATUS "OpenCV include dirs: ${OpenCV_INCLUDE_DIRS}")
message(STATUS "OpenCV libraries: ${OpenCV_LIBRARIES}") message(STATUS "OpenCV libraries: ${OpenCV_LIBRARIES}")
@@ -180,13 +172,6 @@ target_link_libraries(aruco_pose
${OpenCV_LIBRARIES} ${OpenCV_LIBRARIES}
) )
# Prevent aruco_pose from having undefined symbols
set_property(TARGET aruco_pose
APPEND
PROPERTY LINK_FLAGS
-Wl,--no-undefined
)
############# #############
## Install ## ## Install ##
############# #############
@@ -222,10 +207,6 @@ set_property(TARGET aruco_pose
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} # DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# ) # )
catkin_install_python(PROGRAMS src/genmap.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
############# #############
## Testing ## ## Testing ##
############# #############

View File

@@ -110,14 +110,17 @@ public:
image_transport::ImageTransport it(nh_); image_transport::ImageTransport it(nh_);
image_transport::ImageTransport it_priv(nh_priv_); image_transport::ImageTransport it_priv(nh_priv_);
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>>(nh_priv_); map_markers_sub_ = nh_.subscribe("map_markers", 1, &ArucoDetect::mapMarkersCallback, this);
dyn_srv_->setCallback(std::bind(&ArucoDetect::paramCallback, this, std::placeholders::_1, std::placeholders::_2));
debug_pub_ = it_priv.advertise("debug", 1); debug_pub_ = it_priv.advertise("debug", 1);
markers_pub_ = nh_priv_.advertise<aruco_pose::MarkerArray>("markers", 1); markers_pub_ = nh_priv_.advertise<aruco_pose::MarkerArray>("markers", 1);
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1); vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1);
img_sub_ = it.subscribeCamera("image_raw", 1, &ArucoDetect::imageCallback, this); img_sub_ = it.subscribeCamera("image_raw", 1, &ArucoDetect::imageCallback, this);
map_markers_sub_ = nh_.subscribe("map_markers", 1, &ArucoDetect::mapMarkersCallback, this);
dyn_srv_ = std::make_shared<dynamic_reconfigure::Server<aruco_pose::DetectorConfig>>(nh_priv_);
dynamic_reconfigure::Server<aruco_pose::DetectorConfig>::CallbackType cb;
cb = std::bind(&ArucoDetect::paramCallback, this, std::placeholders::_1, std::placeholders::_2);
dyn_srv_->setCallback(cb);
NODELET_INFO("ready"); NODELET_INFO("ready");
} }

View File

@@ -124,11 +124,6 @@ public:
vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1, true); vis_markers_pub_ = nh_priv_.advertise<visualization_msgs::MarkerArray>("visualization", 1, true);
debug_pub_ = it_priv.advertise("debug", 1); debug_pub_ = it_priv.advertise("debug", 1);
publishMarkersFrames();
publishMarkers();
publishMapImage();
vis_markers_pub_.publish(vis_array_);
image_sub_.subscribe(nh_, "image_raw", 1); image_sub_.subscribe(nh_, "image_raw", 1);
info_sub_.subscribe(nh_, "camera_info", 1); info_sub_.subscribe(nh_, "camera_info", 1);
markers_sub_.subscribe(nh_, "markers", 1); markers_sub_.subscribe(nh_, "markers", 1);
@@ -136,6 +131,11 @@ public:
sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_)); sync_.reset(new message_filters::Synchronizer<SyncPolicy>(SyncPolicy(10), image_sub_, info_sub_, markers_sub_));
sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3)); sync_->registerCallback(boost::bind(&ArucoMap::callback, this, _1, _2, _3));
publishMarkersFrames();
publishMarkers();
publishMapImage();
vis_markers_pub_.publish(vis_array_);
NODELET_INFO("ready"); NODELET_INFO("ready");
} }

View File

@@ -3,11 +3,26 @@
#include "draw.h" #include "draw.h"
#include <math.h> #include <math.h>
#include <vector>
using namespace cv; using namespace cv;
using namespace cv::aruco; 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, void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginSize,
int borderBits, bool drawAxis) { int borderBits, bool drawAxis) {
@@ -127,194 +142,35 @@ void _drawPlanarBoard(Board *_board, Size outSize, OutputArray _img, int marginS
} }
} }
/** /* Draw a (potentially partially visible) line. */
* @brief Convert point coordinates from world space to camera space. static void linePartial(InputOutputArray image, Point3f p1, Point3f p2, const Scalar& color,
* int thickness = 1, int lineType = LINE_8, int shift = 0)
* @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)
{ {
// We operate with CV_64F matrices internally to avoid precision loss // If both points are behind the screen, don't draw anything
cv::Mat rvec_64f; if (p1.z <= 0 && p2.z <= 0) {
cv::Mat tvec_64f; return;
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);
} }
else Point2f p1p{p1.x, p1.y};
{ Point2f p2p{p2.x, p2.y};
rmat = rvec_64f.clone(); // 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;
}
} }
// Make sure tvec has a size of (3, 1) line(image, p1p, p2p, color, thickness, lineType, shift);
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, void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _distCoeffs,
@@ -330,23 +186,647 @@ void _drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _di
axisPoints.push_back(Point3f(length, 0, 0)); axisPoints.push_back(Point3f(length, 0, 0));
axisPoints.push_back(Point3f(0, length, 0)); axisPoints.push_back(Point3f(0, length, 0));
axisPoints.push_back(Point3f(0, 0, length)); axisPoints.push_back(Point3f(0, 0, length));
auto camAxisPoints = worldToCamera(axisPoints, _rvec.getMat(), _tvec.getMat()); std::vector<Point3f> imagePointsZ;
auto axisX = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[1]), _cameraMatrix.getMat(), _distCoeffs.getMat()); _projectPoints(axisPoints, _rvec, _tvec, _cameraMatrix, _distCoeffs, imagePointsZ);
auto axisY = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[2]), _cameraMatrix.getMat(), _distCoeffs.getMat());
auto axisZ = cameraToScreen(lineClip(camAxisPoints[0], camAxisPoints[3]), _cameraMatrix.getMat(), _distCoeffs.getMat()); // draw axis lines
if (axisX.size() > 0) linePartial(_image, imagePointsZ[0], imagePointsZ[1], Scalar(0, 0, 255), 3);
{ linePartial(_image, imagePointsZ[0], imagePointsZ[2], Scalar(0, 255, 0), 3);
line(_image, Point2f{axisX[0].x, axisX[0].y}, Point2f{axisX[1].x, axisX[1].y}, linePartial(_image, imagePointsZ[0], imagePointsZ[3], Scalar(255, 0, 0), 3);
Scalar(0, 0, 255), 3); }
}
if (axisY.size() > 0) static CvMat _cvMat(const cv::Mat& m)
{ {
line(_image, Point2f{axisY[0].x, axisY[0].y}, Point2f{axisY[1].x, axisY[1].y}, CvMat self;
Scalar(0, 255, 0), 3); CV_DbgAssert(m.dims <= 2);
} self = cvMat(m.rows, m.dims == 1 ? 1 : m.cols, m.type(), m.data);
if (axisZ.size() > 0) self.step = (int)m.step[0];
{ self.type = (self.type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG);
line(_image, Point2f{axisZ[0].x, axisZ[0].y}, Point2f{axisZ[1].x, axisZ[1].y}, return self;
Scalar(255, 0, 0), 3); }
}
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())
{
_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)));
}
_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 );
} }

View File

@@ -13,7 +13,7 @@
Generate map file for aruco_map nodelet. Generate map file for aruco_map nodelet.
Usage: Usage:
genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left] [-o <filename>] genmap.py <length> <x> <y> <dist_x> <dist_y> [<first>] [<x0>] [<y0>] [--top-left | --bottom-left]
genmap.py (-h | --help) genmap.py (-h | --help)
Options: Options:
@@ -27,7 +27,6 @@ Options:
<y0> Y coordinate for the first marker [default: 0] <y0> Y coordinate for the first marker [default: 0]
--top-left First marker is on top-left (default) --top-left First marker is on top-left (default)
--bottom-left First marker is on bottom-left --bottom-left First marker is on bottom-left
-o <filename> Output map file name in the 'map' subdirectory of aruco_pose package
Example: Example:
rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > $(catkin_find aruco_pose map)/test_map.txt rosrun aruco_pose genmap.py 0.33 2 4 1 1 0 > $(catkin_find aruco_pose map)/test_map.txt
@@ -35,8 +34,6 @@ Example:
from __future__ import print_function from __future__ import print_function
import sys
from os import path
from docopt import docopt from docopt import docopt
@@ -52,19 +49,14 @@ dist_x = float(arguments['<dist_x>'])
dist_y = float(arguments['<dist_y>']) dist_y = float(arguments['<dist_y>'])
bottom_left = arguments['--bottom-left'] bottom_left = arguments['--bottom-left']
if arguments['-o'] is None:
output = sys.stdout
else:
output = open(path.join(path.dirname(__file__), '..', 'map', arguments['-o']), 'w')
max_y = y0 + (markers_y - 1) * dist_y max_y = y0 + (markers_y - 1) * dist_y
output.write('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x\n') print('# id\tlength\tx\ty\tz\trot_z\trot_y\trot_x')
for y in range(markers_y): for y in range(markers_y):
for x in range(markers_x): for x in range(markers_x):
pos_x = x0 + x * dist_x pos_x = x0 + x * dist_x
pos_y = y0 + y * dist_y pos_y = y0 + y * dist_y
if not bottom_left: if not bottom_left:
pos_y = max_y - pos_y pos_y = max_y - pos_y
output.write('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n'.format(first, length, pos_x, pos_y, 0, 0, 0, 0)) print('{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}'.format(first, length, pos_x, pos_y, 0, 0, 0, 0))
first += 1 first += 1

View File

@@ -7,7 +7,6 @@ endif()
message(STATUS "Adding vendored aruco_pose OpenCV module") message(STATUS "Adding vendored aruco_pose OpenCV module")
add_library(_opencv_aruco STATIC add_library(_opencv_aruco STATIC
vendor/aruco/src/apriltag_quad_thresh.cpp
vendor/aruco/src/aruco.cpp vendor/aruco/src/aruco.cpp
vendor/aruco/src/charuco.cpp vendor/aruco/src/charuco.cpp
vendor/aruco/src/dictionary.cpp vendor/aruco/src/dictionary.cpp
@@ -24,7 +23,7 @@ target_compile_definitions(_opencv_aruco PRIVATE
CV_OVERRIDE=override CV_OVERRIDE=override
) )
target_compile_options(_opencv_aruco PRIVATE target_compile_options(_opencv_aruco PRIVATE
-fpic -fPIC -fvisibility=hidden -fpic -fPIC
) )
target_include_directories(_opencv_aruco PUBLIC target_include_directories(_opencv_aruco PUBLIC

View File

@@ -94,7 +94,7 @@ void ptsort_(struct pt *pts, int sz)
// Use stack storage if it's not too big. // Use stack storage if it's not too big.
cv::AutoBuffer<struct pt, 1024> _tmp_stack(sz); cv::AutoBuffer<struct pt, 1024> _tmp_stack(sz);
memcpy(_tmp_stack, pts, sizeof(struct pt) * sz); memcpy(_tmp_stack.data(), pts, sizeof(struct pt) * sz);
int asz = sz/2; int asz = sz/2;
int bsz = sz - asz; 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_pos = 0;
int rvalloc_size = 3*sz; int rvalloc_size = 3*sz;
cv::AutoBuffer<struct remove_vertex, 0> rvalloc_(std::max(1, rvalloc_size)); cv::AutoBuffer<struct remove_vertex, 0> rvalloc_(std::max(1, rvalloc_size));
memset(rvalloc_, 0, sizeof(rvalloc_[0]) * rvalloc_.size()); // TODO Add AutoBuffer zero fill memset(rvalloc_.data(), 0, sizeof(rvalloc_[0]) * rvalloc_.size()); // TODO Add AutoBuffer zero fill
struct remove_vertex *rvalloc = rvalloc_; struct remove_vertex *rvalloc = rvalloc_.data();
cv::AutoBuffer<struct segment, 0> segs_(std::max(1, sz)); // TODO Add AutoBuffer zero fill cv::AutoBuffer<struct segment, 0> segs_(std::max(1, sz)); // TODO Add AutoBuffer zero fill
memset(segs_, 0, sizeof(segs_[0]) * segs_.size()); memset(segs_.data(), 0, sizeof(segs_[0]) * segs_.size());
struct segment *segs = segs_; struct segment *segs = segs_.data();
// populate with initial entries // populate with initial entries
for (int i = 0; i < sz; i++) { 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. // efficiently computed for any contiguous range of indices.
cv::AutoBuffer<struct line_fit_pt, 64> lfps_(sz); cv::AutoBuffer<struct line_fit_pt, 64> lfps_(sz);
memset(lfps_, 0, sizeof(lfps_[0]) * lfps_.size()); // TODO Add AutoBuffer zero fill memset(lfps_.data(), 0, sizeof(lfps_[0]) * lfps_.size()); // TODO Add AutoBuffer zero fill
struct line_fit_pt *lfps = lfps_; struct line_fit_pt *lfps = lfps_.data();
for (int i = 0; i < sz; i++) { for (int i = 0; i < sz; i++) {
struct pt *p; struct pt *p;

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
from distutils.core import setup from distutils.core import setup

View File

@@ -7,25 +7,19 @@ rospy.init_node('leds')
set_effect = rospy.ServiceProxy('led/set_effect', SetLEDEffect) # define proxy to ROS-service 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 set_effect(r=255, g=0, b=0) # fill strip with red color
rospy.sleep(2) rospy.sleep(2)
print('Fill green')
set_effect(r=0, g=100, b=0) # fill strip with green color set_effect(r=0, g=100, b=0) # fill strip with green color
rospy.sleep(2) rospy.sleep(2)
print('Fade to blue')
set_effect(effect='fade', r=0, g=0, b=255) # fade to blue color set_effect(effect='fade', r=0, g=0, b=255) # fade to blue color
rospy.sleep(2) rospy.sleep(2)
print('Flash red')
set_effect(effect='flash', r=255, g=0, b=0) # flash twice with red color 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 set_effect(effect='blink', r=255, g=255, b=255) # blink with white color
rospy.sleep(5) rospy.sleep(5)
print('Rainbow')
set_effect(effect='rainbow') # show rainbow set_effect(effect='rainbow') # show rainbow

View File

@@ -1,18 +0,0 @@
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]

View File

@@ -3,7 +3,7 @@ Description=Launcher for the ROS master, parameter server and rosout logging nod
[Service] [Service]
User=pi User=pi
ExecStart=/bin/sh -c ". /opt/ros/noetic/setup.sh; ROS_HOSTNAME=`hostname`.local exec roscore" ExecStart=/bin/sh -c ". /opt/ros/melodic/setup.sh; ROS_HOSTNAME=`hostname`.local exec roscore"
Restart=on-failure Restart=on-failure
RestartSec=3 RestartSec=3

View File

@@ -15,7 +15,7 @@
set -e # Exit immidiately on non-zero result set -e # Exit immidiately on non-zero result
SOURCE_IMAGE="https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2020-08-24/2020-08-20-raspios-buster-arm64-lite.zip" SOURCE_IMAGE="https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2020-02-14/2020-02-13-raspbian-buster-lite.zip"
export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'} export DEBIAN_FRONTEND=${DEBIAN_FRONTEND:='noninteractive'}
export LANG=${LANG:='C.UTF-8'} export LANG=${LANG:='C.UTF-8'}
@@ -105,7 +105,7 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/monkey.
# software install # software install
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh' ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-software.sh'
# examples # 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 # network setup
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh' ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} exec ${SCRIPTS_DIR}'/image-network.sh'
# avahi setup # avahi setup
@@ -116,7 +116,7 @@ ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/avahi-s
# Clover # 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/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/roscore.service' '/lib/systemd/system/'
${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/melodic-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/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/pigpiod.service' '/lib/systemd/system/'
${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/' ${BUILDER_DIR}/image-chroot.sh ${IMAGE_PATH} copy ${SCRIPTS_DIR}'/assets/launch.nanorc' '/usr/share/nano/'

View File

@@ -55,4 +55,7 @@ echo_stamp "Set max space for syslogs"
# https://unix.stackexchange.com/questions/139513/how-to-clear-journalctl # https://unix.stackexchange.com/questions/139513/how-to-clear-journalctl
sed -i 's/#SystemMaxUse=/SystemMaxUse=200M/' /etc/systemd/journald.conf 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" echo_stamp "End of init image"

View File

@@ -21,9 +21,6 @@ INSTALL_ROS_PACK_SOURCES=$3
DISCOVER_ROS_PACK=$4 DISCOVER_ROS_PACK=$4
NUMBER_THREADS=$5 NUMBER_THREADS=$5
# Current ROS distribution
ROS_DISTRO=noetic
echo_stamp() { echo_stamp() {
# TEMPLATE: echo_stamp <TEXT> <TYPE> # TEMPLATE: echo_stamp <TEXT> <TYPE>
# TYPE: SUCCESS, ERROR, INFO # TYPE: SUCCESS, ERROR, INFO
@@ -71,8 +68,7 @@ my_travis_retry() {
# TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo? # TODO: 'kinetic-rosdep-clover.yaml' should add only if we use our repo?
echo_stamp "Init rosdep" echo_stamp "Init rosdep"
my_travis_retry rosdep init my_travis_retry rosdep init
# FIXME: Re-add this after missing packages are built echo "yaml file:///etc/ros/rosdep/melodic-rosdep-clover.yaml" >> /etc/ros/rosdep/sources.list.d/20-default.list
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 my_travis_retry rosdep update
echo_stamp "Populate rosdep for ROS user" echo_stamp "Populate rosdep for ROS user"
@@ -84,32 +80,16 @@ echo_stamp "Reconfiguring Clover repository for simplier unshallowing"
cd /home/pi/catkin_ws/src/clover cd /home/pi/catkin_ws/src/clover
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" 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" echo_stamp "Build and install Clover"
cd /home/pi/catkin_ws cd /home/pi/catkin_ws
# Don't try to install gazebo_ros # Don't try to install gazebo_ros
my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} --os=debian:buster \ my_travis_retry rosdep install -y --from-paths src --ignore-src --rosdistro melodic --os=debian:buster \
--skip-keys=gazebo_ros --skip-keys=gazebo_plugins --skip-keys=gazebo_ros --skip-keys=gazebo_plugins
my_travis_retry pip3 install wheel my_travis_retry pip install wheel
my_travis_retry pip3 install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt my_travis_retry pip install -r /home/pi/catkin_ws/src/clover/clover/requirements.txt
source /opt/ros/${ROS_DISTRO}/setup.bash source /opt/ros/melodic/setup.bash
# Don't build simulation plugins for actual drone # Don't build simulation plugins for actual drone
catkin_make -j2 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins catkin_make -j2 -DCMAKE_BUILD_TYPE=Release -DCATKIN_BLACKLIST_PACKAGES=clover_gazebo_plugins
echo_stamp "Install clever package (for backwards compatibility)" echo_stamp "Install clever package (for backwards compatibility)"
cd /home/pi/catkin_ws/src/clover/builder/assets/clever cd /home/pi/catkin_ws/src/clover/builder/assets/clever
@@ -125,18 +105,23 @@ touch node_modules/CATKIN_IGNORE docs/CATKIN_IGNORE _book/CATKIN_IGNORE clover/w
echo_stamp "Installing additional ROS packages" echo_stamp "Installing additional ROS packages"
my_travis_retry apt-get install -y --no-install-recommends \ my_travis_retry apt-get install -y --no-install-recommends \
ros-${ROS_DISTRO}-dynamic-reconfigure \ ros-melodic-dynamic-reconfigure \
ros-${ROS_DISTRO}-rosbridge-suite \ ros-melodic-compressed-image-transport \
ros-${ROS_DISTRO}-rosserial \ ros-melodic-rosbridge-suite \
ros-${ROS_DISTRO}-usb-cam \ ros-melodic-rosserial \
ros-${ROS_DISTRO}-vl53l1x \ ros-melodic-usb-cam \
ros-${ROS_DISTRO}-ws281x \ ros-melodic-vl53l1x \
ros-${ROS_DISTRO}-rosshow ros-melodic-ws281x \
ros-melodic-rosshow
# TODO move GeographicLib datasets to Mavros debian package # TODO move GeographicLib datasets to Mavros debian package
echo_stamp "Install GeographicLib datasets (needed for mavros)" \ echo_stamp "Install GeographicLib datasets (needed for mavros)" \
&& wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash && wget -qO- https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh | bash
# 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" echo_stamp "Running tests"
cd /home/pi/catkin_ws cd /home/pi/catkin_ws
# FIXME: Investigate failing tests # FIXME: Investigate failing tests
@@ -145,15 +130,12 @@ catkin_make run_tests #&& catkin_test_results
echo_stamp "Change permissions for catkin_ws" echo_stamp "Change permissions for catkin_ws"
chown -Rf pi:pi /home/pi/catkin_ws chown -Rf pi:pi /home/pi/catkin_ws
echo_stamp "Change permissions for examples"
chown -Rf pi:pi /home/pi/examples
echo_stamp "Setup ROS environment" echo_stamp "Setup ROS environment"
cat << EOF >> /home/pi/.bashrc cat << EOF >> /home/pi/.bashrc
LANG='C.UTF-8' LANG='C.UTF-8'
LC_ALL='C.UTF-8' LC_ALL='C.UTF-8'
export ROS_HOSTNAME=\`hostname\`.local export ROS_HOSTNAME=\`hostname\`.local
source /opt/ros/${ROS_DISTRO}/setup.bash source /opt/ros/melodic/setup.bash
source /home/pi/catkin_ws/devel/setup.bash source /home/pi/catkin_ws/devel/setup.bash
EOF EOF

View File

@@ -70,8 +70,8 @@ apt-get update \
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 && 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://packages.ros.org/ros/ubuntu buster main" > /etc/apt/sources.list.d/ros-latest.list
echo "deb http://deb.coex.tech/opencv4 buster main" > /etc/apt/sources.list.d/opencv3.list echo "deb http://deb.coex.tech/opencv3 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/rpi-ros-melodic 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 "deb http://deb.coex.tech/clover buster main" > /etc/apt/sources.list.d/clover.list
echo_stamp "Update apt cache" echo_stamp "Update apt cache"
@@ -96,19 +96,21 @@ dnsmasq \
tmux \ tmux \
vim \ vim \
cmake \ cmake \
libjpeg8 \
tcpdump \ tcpdump \
ltrace \
libpoco-dev \ libpoco-dev \
libzbar0 \ libzbar0 \
python3-rosdep \ python-rosdep \
python3-rosinstall-generator \ python-rosinstall-generator \
python3-wstool \ python-wstool \
python3-rosinstall \ python-rosinstall \
build-essential \ build-essential \
libffi-dev \ libffi-dev \
monkey \ monkey \
pigpio python-pigpio python3-pigpio \ pigpio python-pigpio python3-pigpio \
i2c-tools \ i2c-tools \
espeak espeak-data python-espeak python3-espeak \ espeak espeak-data python-espeak \
ntpdate \ ntpdate \
python-dev \ python-dev \
python3-dev \ python3-dev \
@@ -140,7 +142,7 @@ my_travis_retry pip3 install butterfly[systemd]
systemctl enable butterfly.socket systemctl enable butterfly.socket
echo_stamp "Install ws281x library" echo_stamp "Install ws281x library"
my_travis_retry pip3 install --prefer-binary rpi_ws281x my_travis_retry pip install --prefer-binary rpi_ws281x
echo_stamp "Setup Monkey" echo_stamp "Setup Monkey"
mv /etc/monkey/sites/default /etc/monkey/sites/default.orig mv /etc/monkey/sites/default /etc/monkey/sites/default.orig
@@ -149,11 +151,11 @@ systemctl enable monkey.service
echo_stamp "Install Node.js" echo_stamp "Install Node.js"
cd /home/pi cd /home/pi
wget https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-arm64.tar.gz wget https://nodejs.org/dist/v10.15.0/node-v10.15.0-linux-armv6l.tar.gz
tar -xzf node-v10.15.0-linux-arm64.tar.gz tar -xzf node-v10.15.0-linux-armv6l.tar.gz
cp -R node-v10.15.0-linux-arm64/* /usr/local/ cp -R node-v10.15.0-linux-armv6l/* /usr/local/
rm -rf node-v10.15.0-linux-arm64/ rm -rf node-v10.15.0-linux-armv6l/
rm node-v10.15.0-linux-arm64.tar.gz rm node-v10.15.0-linux-armv6l.tar.gz
echo_stamp "Installing ptvsd" echo_stamp "Installing ptvsd"
my_travis_retry pip install ptvsd my_travis_retry pip install ptvsd

View File

@@ -16,9 +16,9 @@ set -ex
echo "Run image tests" echo "Run image tests"
export ROS_DISTRO='noetic' export ROS_DISTRO='melodic'
export ROS_IP='127.0.0.1' export ROS_IP='127.0.0.1'
source /opt/ros/${ROS_DISTRO}/setup.bash source /opt/ros/melodic/setup.bash
source /home/pi/catkin_ws/devel/setup.bash source /home/pi/catkin_ws/devel/setup.bash
cd /home/pi/catkin_ws/src/clover/builder/test/ cd /home/pi/catkin_ws/src/clover/builder/test/
@@ -26,3 +26,6 @@ cd /home/pi/catkin_ws/src/clover/builder/test/
./tests.py ./tests.py
./tests_py3.py ./tests_py3.py
[[ $(./tests_clever.py) == "Warning: clever package is renamed to clover" ]] # test backwards compatibility [[ $(./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

View File

@@ -6,39 +6,20 @@ set -e
apt update apt update
apt install -y curl apt install -y curl
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
if [ "x${ROS_PYTHON_VERSION}" = "x3" ]; then python ./get-pip.py
PYTHON=python3
else
PYTHON=python
fi
${PYTHON} ./get-pip.py
# Step 1.5: Add deb.coex.tech to apt # Step 1.5: Add deb.coex.tech to apt
curl http://deb.coex.tech/aptly_repo_signing.key 2> /dev/null | apt-key add - 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 "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 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 cat <<EOF > /etc/ros/rosdep/coex.yaml
led_msgs: led_msgs:
ubuntu: ubuntu:
${CODENAME}: [ros-${ROS_DISTRO}-led-msgs] xenial: ros-kinetic-led-msgs
async_web_server_cpp: bionic: ros-melodic-led-msgs
ubuntu: debian:
${CODENAME}: [ros-${ROS_DISTRO}-async-web-server-cpp] stretch: ros-kinetic-led-msgs
ros_pytest: buster: ros-melodic-led-msgs
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 EOF
apt update apt update
rosdep update rosdep update
@@ -56,7 +37,7 @@ cd /root/catkin_ws
catkin_make catkin_make
# Step 4: Run tests # Step 4: Run tests
${PYTHON} -m pip install --upgrade pytest pip install --upgrade pytest
cd /root/catkin_ws cd /root/catkin_ws
source devel/setup.bash source devel/setup.bash
catkin_make run_tests && catkin_test_results catkin_make run_tests && catkin_test_results

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# validate all required modules installed # validate all required modules installed
@@ -28,4 +28,4 @@ import pigpio
from espeak import espeak from espeak import espeak
from pyzbar import pyzbar from pyzbar import pyzbar
print(cv2.getBuildInformation()) print cv2.getBuildInformation()

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# test backwards compatibility # test backwards compatibility

View File

@@ -30,15 +30,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(GeographicLib REQUIRED) find_package(GeographicLib REQUIRED)
# Workaround for OpenCV 3/4 support find_package(OpenCV 3 REQUIRED
set(_opencv_version 4)
find_package(OpenCV ${_opencv_version} QUIET COMPONENTS calib3d imgproc)
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 COMPONENTS
calib3d calib3d
imgproc imgproc
@@ -262,10 +254,6 @@ target_link_libraries(${PROJECT_NAME}
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} # 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 # Only install udev rules when building a Debian package
# FIXME: Other operating systems may have other prefixes # FIXME: Other operating systems may have other prefixes
string(FIND ${CMAKE_INSTALL_PREFIX} "/opt/ros" _PREFIX_INDEX) string(FIND ${CMAKE_INSTALL_PREFIX} "/opt/ros" _PREFIX_INDEX)

View File

@@ -4,7 +4,7 @@ A bundle for autonomous navigation and drone control.
## Manual installation ## Manual installation
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). 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).
Clone this repo to directory `~/catkin_ws/src/clover`: Clone this repo to directory `~/catkin_ws/src/clover`:

View File

@@ -2,37 +2,30 @@
<arg name="aruco_detect" default="true"/> <arg name="aruco_detect" default="true"/>
<arg name="aruco_map" default="false"/> <arg name="aruco_map" default="false"/>
<arg name="aruco_vpe" default="false"/> <arg name="aruco_vpe" default="false"/>
<arg name="placement" default="floor"/> <!-- markers placement: floor, ceiling, unknown -->
<arg name="length" default="0.33"/> <!-- not-in-map markers length, m -->
<arg name="map" default="map.txt"/> <!-- markers map file name -->
<!-- For additional help go to https://clover.coex.tech/aruco --> <!-- For additional help go to https://clover.coex.tech/aruco -->
<!-- aruco_detect: detect aruco markers, estimate poses --> <!-- aruco_detect: detect aruco markers, estimate poses -->
<node name="aruco_detect" pkg="nodelet" if="$(arg aruco_detect)" type="nodelet" args="load aruco_pose/aruco_detect nodelet_manager" output="screen" clear_params="true" respawn="true"> <node name="aruco_detect" pkg="nodelet" if="$(arg aruco_detect)" type="nodelet" args="load aruco_pose/aruco_detect nodelet_manager" output="screen" clear_params="true">
<remap from="image_raw" to="main_camera/image_raw"/> <remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/> <remap from="camera_info" to="main_camera/camera_info"/>
<remap from="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/> <remap from="map_markers" to="aruco_map/markers" if="$(arg aruco_map)"/>
<param name="estimate_poses" value="true"/> <param name="estimate_poses" value="true"/>
<param name="send_tf" value="true"/> <param name="send_tf" value="true"/>
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/> <param name="known_tilt" value="map"/>
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/> <param name="length" value="0.33"/>
<param name="length" value="$(arg length)"/>
<!-- aruco detector parameters --> <!-- aruco detector parameters -->
<param name="cornerRefinementMethod" value="2"/> <!-- contour refinement --> <param name="cornerRefinementMethod" value="2"/> <!-- contour refinement -->
<param name="minMarkerPerimeterRate" value="0.075"/> <!-- 0.075 for 320x240, 0.0375 for 640x480 --> <param name="minMarkerPerimeterRate" value="0.075"/> <!-- 0.075 for 320x240, 0.0375 for 640x480 -->
<!-- length override example: -->
<!-- <param name="length_override/3" value="0.1"/> -->
</node> </node>
<!-- aruco_map: estimate aruco map pose --> <!-- aruco_map: estimate aruco map pose -->
<node name="aruco_map" pkg="nodelet" type="nodelet" if="$(arg aruco_map)" args="load aruco_pose/aruco_map nodelet_manager" output="screen" clear_params="true" respawn="true"> <node name="aruco_map" pkg="nodelet" type="nodelet" if="$(arg aruco_map)" args="load aruco_pose/aruco_map nodelet_manager" output="screen" clear_params="true">
<remap from="image_raw" to="main_camera/image_raw"/> <remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/> <remap from="camera_info" to="main_camera/camera_info"/>
<remap from="markers" to="aruco_detect/markers"/> <remap from="markers" to="aruco_detect/markers"/>
<param name="map" value="$(find aruco_pose)/map/$(arg map)"/> <param name="map" value="$(find aruco_pose)/map/map.txt"/>
<param name="known_tilt" value="map" if="$(eval placement == 'floor')"/> <param name="known_tilt" value="map"/>
<param name="known_tilt" value="map_flipped" if="$(eval placement == 'ceiling')"/>
<param name="image_axis" value="true"/> <param name="image_axis" value="true"/>
<param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/> <param name="frame_id" value="aruco_map_detected" if="$(arg aruco_vpe)"/>
<param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/> <param name="frame_id" value="aruco_map" unless="$(arg aruco_vpe)"/>

View File

@@ -37,7 +37,7 @@
<include file="$(find clover)/launch/aruco.launch" if="$(arg aruco)"/> <include file="$(find clover)/launch/aruco.launch" if="$(arg aruco)"/>
<!-- optical flow --> <!-- optical flow -->
<node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen" respawn="true"> <node pkg="nodelet" type="nodelet" name="optical_flow" args="load clover/optical_flow nodelet_manager" if="$(arg optical_flow)" clear_params="true" output="screen">
<remap from="image_raw" to="main_camera/image_raw"/> <remap from="image_raw" to="main_camera/image_raw"/>
<remap from="camera_info" to="main_camera/camera_info"/> <remap from="camera_info" to="main_camera/camera_info"/>
<param name="calc_flow_gyro" value="true"/> <param name="calc_flow_gyro" value="true"/>
@@ -45,7 +45,7 @@
</node> </node>
<!-- main nodelet manager --> <!-- main nodelet manager -->
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" output="screen" clear_params="true" respawn="true"> <node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" output="screen" clear_params="true">
<param name="num_worker_threads" value="2"/> <param name="num_worker_threads" value="2"/>
</node> </node>

View File

@@ -2,17 +2,14 @@
<arg name="ws281x" default="true"/> <arg name="ws281x" default="true"/>
<arg name="led_effect" default="true"/> <arg name="led_effect" default="true"/>
<arg name="led_notify" default="true"/> <arg name="led_notify" default="true"/>
<arg name="led_count" default="72"/>
<arg name="gpio_pin" default="21"/>
<arg name="simulator" default="false"/> <arg name="simulator" default="false"/>
<!-- For additional help go to https://clover.coex.tech/led --> <!-- For additional help go to https://clover.coex.tech/led -->
<!-- ws281x led strip driver --> <!-- ws281x led strip driver -->
<node pkg="ws281x" name="led" type="ws281x_node" clear_params="true" output="screen" if="$(eval ws281x and not simulator)"> <node pkg="ws281x" name="led" type="ws281x_node" clear_params="true" output="screen" if="$(eval ws281x and not simulator)">
<param name="led_count" value="$(arg led_count)"/> <param name="led_count" value="58"/>
<param name="gpio_pin" value="$(arg gpio_pin)"/> <param name="gpio_pin" value="21"/>
<param name="brightness" value="64"/> <param name="brightness" value="64"/>
<param name="strip_type" value="WS2811_STRIP_GRB"/> <param name="strip_type" value="WS2811_STRIP_GRB"/>
<param name="target_frequency" value="800000"/> <param name="target_frequency" value="800000"/>
@@ -35,7 +32,7 @@
altctl: { r: 255, g: 255, b: 40 } altctl: { r: 255, g: 255, b: 40 }
posctl: { r: 50, g: 100, b: 220 } posctl: { r: 50, g: 100, b: 220 }
offboard: { r: 220, g: 20, b: 250 } offboard: { r: 220, g: 20, b: 250 }
low_battery: { threshold: 3.6, effect: blink_fast, r: 255, g: 0, b: 0 } low_battery: { threshold: 3.7, effect: blink_fast, r: 255, g: 0, b: 0 }
error: { effect: flash, r: 255, g: 0, b: 0 } error: { effect: flash, r: 255, g: 0, b: 0 }
</rosparam> </rosparam>
</node> </node>

View File

@@ -9,8 +9,6 @@
<node if="$(eval direction_z == 'down' and direction_y == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 1.5707963 0 3.1415926 base_link main_camera_optical"/> <node if="$(eval direction_z == 'down' and direction_y == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 1.5707963 0 3.1415926 base_link main_camera_optical"/>
<node if="$(eval direction_z == 'up' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 0.07 1.5707963 0 0 base_link main_camera_optical"/> <node if="$(eval direction_z == 'up' and direction_y == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 0.07 1.5707963 0 0 base_link main_camera_optical"/>
<node if="$(eval direction_z == 'up' and direction_y == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 0.07 -1.5707963 0 0 base_link main_camera_optical"/> <node if="$(eval direction_z == 'up' and direction_y == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 0.07 -1.5707963 0 0 base_link main_camera_optical"/>
<node if="$(eval direction_z == 'forward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.03 0 0.05 -1.5707963 0 -1.5707963 base_link main_camera_optical"/>
<node if="$(eval direction_z == 'backward')" pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="-0.03 0 0.05 1.5707963 0 -1.5707963 base_link main_camera_optical"/>
<!-- Template for custom camera orientation --> <!-- Template for custom camera orientation -->
<!-- Camera position and orientation are represented by base_link -> main_camera_optical transform --> <!-- Camera position and orientation are represented by base_link -> main_camera_optical transform -->
@@ -18,7 +16,7 @@
<!-- <node pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/> --> <!-- <node pkg="tf2_ros" type="static_transform_publisher" name="main_camera_frame" args="0.05 0 -0.07 -1.5707963 0 3.1415926 base_link main_camera_optical"/> -->
<!-- camera node --> <!-- camera node -->
<node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true" unless="$(arg simulator)" respawn="true"> <node pkg="nodelet" type="nodelet" name="main_camera" args="load cv_camera/CvCameraNodelet nodelet_manager" clear_params="true" unless="$(arg simulator)">
<param name="device_path" value="/dev/video0"/> <!-- v4l2 device --> <param name="device_path" value="/dev/video0"/> <!-- v4l2 device -->
<param name="frame_id" value="main_camera_optical"/> <param name="frame_id" value="main_camera_optical"/>
<param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/> <param name="camera_info_url" value="file://$(find clover)/camera_info/fisheye_cam.yaml"/>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<package format="3"> <package format="2">
<name>clover</name> <name>clover</name>
<version>0.0.1</version> <version>0.0.1</version>
<description>The Clover package</description> <description>The Clover package</description>
@@ -37,8 +37,7 @@
<depend>rosbridge_server</depend> <depend>rosbridge_server</depend>
<depend>web_video_server</depend> <depend>web_video_server</depend>
<depend>tf2_web_republisher</depend> <depend>tf2_web_republisher</depend>
<depend condition="$ROS_PYTHON_VERSION == 2">python-lxml</depend> <depend>python-lxml</depend>
<depend condition="$ROS_PYTHON_VERSION == 3">python3-lxml</depend>
<exec_depend>python-pymavlink</exec_depend> <exec_depend>python-pymavlink</exec_depend>
<!-- Use test_depend for packages you need only for testing: --> <!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> --> <!-- <test_depend>gtest</test_depend> -->

View File

@@ -70,6 +70,7 @@ private:
roi_rad_ = nh_priv.param("roi_rad", 0.0); roi_rad_ = nh_priv.param("roi_rad", 0.0);
calc_flow_gyro_ = nh_priv.param("calc_flow_gyro", false); calc_flow_gyro_ = nh_priv.param("calc_flow_gyro", false);
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
img_pub_ = it_priv.advertise("debug", 1); img_pub_ = it_priv.advertise("debug", 1);
flow_pub_ = nh.advertise<mavros_msgs::OpticalFlowRad>("mavros/px4flow/raw/send", 1); flow_pub_ = nh.advertise<mavros_msgs::OpticalFlowRad>("mavros/px4flow/raw/send", 1);
velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1); velo_pub_ = nh_priv.advertise<geometry_msgs::TwistStamped>("angular_velocity", 1);
@@ -82,8 +83,6 @@ private:
flow_.distance = -1; // no distance sensor available flow_.distance = -1; // no distance sensor available
flow_.temperature = 0; flow_.temperature = 0;
img_sub_ = it.subscribeCamera("image_raw", 1, &OpticalFlow::flow, this);
NODELET_INFO("Optical Flow initialized"); NODELET_INFO("Optical Flow initialized");
} }

View File

@@ -138,7 +138,7 @@ def mavlink_exec(cmd, timeout=3.0):
timeout=3, timeout=3,
baudrate=0, baudrate=0,
count=len(cmd), count=len(cmd),
data=[ord(c) for c in cmd.ljust(70, '\0')]) data=map(ord, cmd.ljust(70, '\0')))
msg.pack(link) msg.pack(link)
ros_msg = mavlink.convert_to_rosmsg(msg) ros_msg = mavlink.convert_to_rosmsg(msg)
mavlink_pub.publish(ros_msg) mavlink_pub.publish(ros_msg)
@@ -609,7 +609,7 @@ def check_rangefinder():
@check('Boot duration') @check('Boot duration')
def check_boot_duration(): def check_boot_duration():
output = subprocess.check_output('systemd-analyze').decode() output = subprocess.check_output('systemd-analyze')
r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE) r = re.compile(r'([\d\.]+)s\s*$', flags=re.MULTILINE)
duration = float(r.search(output).groups()[0]) duration = float(r.search(output).groups()[0])
if duration > 15: if duration > 15:
@@ -620,7 +620,7 @@ def check_boot_duration():
def check_cpu_usage(): def check_cpu_usage():
WHITELIST = 'nodelet', WHITELIST = 'nodelet',
CMD = "top -n 1 -b -i | tail -n +8 | awk '{ printf(\"%-8s\\t%-8s\\t%-8s\\n\", $1, $9, $12); }'" 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).decode() output = subprocess.check_output(CMD, shell=True)
processes = output.split('\n') processes = output.split('\n')
for process in processes: for process in processes:
if not process: if not process:
@@ -636,7 +636,7 @@ def check_cpu_usage():
def check_clover_service(): def check_clover_service():
try: try:
output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(), output = subprocess.check_output('systemctl show -p ActiveState --value clover.service'.split(),
stderr=subprocess.STDOUT).decode() stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
failure('systemctl returned %s: %s', e.returncode, e.output) failure('systemctl returned %s: %s', e.returncode, e.output)
return return
@@ -751,7 +751,7 @@ def check_rpi_health():
# <parameter>=<value> # <parameter>=<value>
# In case of `get_throttled`, <value> is a hexadecimal number # In case of `get_throttled`, <value> is a hexadecimal number
# with some of the FLAGs OR'ed together # with some of the FLAGs OR'ed together
output = subprocess.check_output(['vcgencmd', 'get_throttled']).decode() output = subprocess.check_output(['vcgencmd', 'get_throttled'])
except OSError: except OSError:
failure('could not call vcgencmd binary; not a Raspberry Pi?') failure('could not call vcgencmd binary; not a Raspberry Pi?')
return return

View File

@@ -36,7 +36,6 @@
#include <mavros_msgs/Thrust.h> #include <mavros_msgs/Thrust.h>
#include <mavros_msgs/State.h> #include <mavros_msgs/State.h>
#include <mavros_msgs/StatusText.h> #include <mavros_msgs/StatusText.h>
#include <mavros_msgs/ManualControl.h>
#include <clover/GetTelemetry.h> #include <clover/GetTelemetry.h>
#include <clover/Navigate.h> #include <clover/Navigate.h>
@@ -73,10 +72,9 @@ ros::Duration state_timeout;
ros::Duration velocity_timeout; ros::Duration velocity_timeout;
ros::Duration global_position_timeout; ros::Duration global_position_timeout;
ros::Duration battery_timeout; ros::Duration battery_timeout;
ros::Duration manual_control_timeout;
float default_speed; float default_speed;
bool auto_release; bool auto_release;
bool land_only_in_offboard, nav_from_sp, check_kill_switch; bool land_only_in_offboard, nav_from_sp;
std::map<string, string> reference_frames; std::map<string, string> reference_frames;
// Publishers // Publishers
@@ -124,7 +122,6 @@ enum { YAW, YAW_RATE, TOWARDS } setpoint_yaw_type;
// Last received telemetry messages // Last received telemetry messages
mavros_msgs::State state; mavros_msgs::State state;
mavros_msgs::StatusText statustext; mavros_msgs::StatusText statustext;
mavros_msgs::ManualControl manual_control;
PoseStamped local_position; PoseStamped local_position;
TwistStamped velocity; TwistStamped velocity;
NavSatFix global_position; NavSatFix global_position;
@@ -489,27 +486,6 @@ void publishSetpoint(const ros::TimerEvent& event)
publish(event.current_real); publish(event.current_real);
} }
inline void checkManualControl()
{
if (!manual_control_timeout.isZero() && TIMEOUT(manual_control, manual_control_timeout)) {
throw std::runtime_error("Manual control timeout, RC is switched off?");
}
if (check_kill_switch) {
// switch values: https://github.com/PX4/PX4-Autopilot/blob/c302514a0809b1765fafd13c014d705446ae1113/msg/manual_control_setpoint.msg#L3
const uint8_t SWITCH_POS_NONE = 0; // switch is not mapped
const uint8_t SWITCH_POS_ON = 1; // switch activated
const uint8_t SWITCH_POS_MIDDLE = 2; // middle position
const uint8_t SWITCH_POS_OFF = 3; // switch not activated
const int KILL_SWITCH_BIT = 12; // https://github.com/PX4/Firmware/blob/c302514a0809b1765fafd13c014d705446ae1113/src/modules/mavlink/mavlink_messages.cpp#L3975
uint8_t kill_switch = (manual_control.buttons & (0b11 << KILL_SWITCH_BIT)) >> KILL_SWITCH_BIT;
if (kill_switch == SWITCH_POS_ON)
throw std::runtime_error("Kill switch is on");
}
}
inline void checkState() inline void checkState()
{ {
if (TIMEOUT(state, state_timeout)) if (TIMEOUT(state, state_timeout))
@@ -537,10 +513,6 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl
// Checks // Checks
checkState(); checkState();
if (auto_arm) {
checkManualControl();
}
// default frame is local frame // default frame is local frame
if (frame_id.empty()) if (frame_id.empty())
frame_id = local_frame; frame_id = local_frame;
@@ -862,7 +834,6 @@ int main(int argc, char **argv)
nh_priv.param("auto_release", auto_release, true); nh_priv.param("auto_release", auto_release, true);
nh_priv.param("land_only_in_offboard", land_only_in_offboard, true); nh_priv.param("land_only_in_offboard", land_only_in_offboard, true);
nh_priv.param("nav_from_sp", nav_from_sp, true); nh_priv.param("nav_from_sp", nav_from_sp, true);
nh_priv.param("check_kill_switch", check_kill_switch, true);
nh_priv.param("default_speed", default_speed, 0.5f); nh_priv.param("default_speed", default_speed, 0.5f);
nh_priv.param<string>("body_frame", body.child_frame_id, "body"); nh_priv.param<string>("body_frame", body.child_frame_id, "body");
nh_priv.getParam("reference_frames", reference_frames); nh_priv.getParam("reference_frames", reference_frames);
@@ -872,7 +843,6 @@ int main(int argc, char **argv)
velocity_timeout = ros::Duration(nh_priv.param("velocity_timeout", 2.0)); velocity_timeout = ros::Duration(nh_priv.param("velocity_timeout", 2.0));
global_position_timeout = ros::Duration(nh_priv.param("global_position_timeout", 10.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)); 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)); transform_timeout = ros::Duration(nh_priv.param("transform_timeout", 0.5));
telemetry_transform_timeout = ros::Duration(nh_priv.param("telemetry_transform_timeout", 0.5)); telemetry_transform_timeout = ros::Duration(nh_priv.param("telemetry_transform_timeout", 0.5));
@@ -890,7 +860,6 @@ int main(int argc, char **argv)
auto global_position_sub = nh.subscribe("mavros/global_position/global", 1, &handleMessage<NavSatFix, global_position>); auto global_position_sub = nh.subscribe("mavros/global_position/global", 1, &handleMessage<NavSatFix, global_position>);
auto battery_sub = nh.subscribe("mavros/battery", 1, &handleMessage<BatteryState, battery>); auto battery_sub = nh.subscribe("mavros/battery", 1, &handleMessage<BatteryState, battery>);
auto statustext_sub = nh.subscribe("mavros/statustext/recv", 1, &handleMessage<mavros_msgs::StatusText, statustext>); auto statustext_sub = nh.subscribe("mavros/statustext/recv", 1, &handleMessage<mavros_msgs::StatusText, statustext>);
auto manual_control_sub = nh.subscribe("mavros/manual_control/control", 1, &handleMessage<mavros_msgs::ManualControl, manual_control>);
auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition); auto local_position_sub = nh.subscribe("mavros/local_position/pose", 1, &handleLocalPosition);
// Setpoint publishers // Setpoint publishers

View File

@@ -26,13 +26,8 @@ def test_simple_offboard_services_available():
rospy.wait_for_service('land', timeout=5) rospy.wait_for_service('land', timeout=5)
def test_web_video_server(node): def test_web_video_server(node):
try: import urllib2
# Python 2 urllib2.urlopen("http://localhost:8080").read()
import urllib2 as urllib
except ModuleNotFoundError:
# Python 3
import urllib.request as urllib
urllib.urlopen("http://localhost:8080").read()
def test_shell(node): def test_shell(node):
execute = rospy.ServiceProxy('exec', srv.Execute) execute = rospy.ServiceProxy('exec', srv.Execute)

View File

@@ -30,9 +30,7 @@ The frontend files are located in [`www`](./www/) subdirectory. The frontend app
Parameters read by frontend: Parameters read by frontend:
* `~navigate_tolerance` (*float*) distance tolerance in meters, used for navigate-like blocks (default: 0.2). * `~navigate_tolerance` (*float*) distance tolerance in meters, used for navigate-like blocks (default: 0.2).
* `~yaw_tolerance` (*float*) yaw angle tolerance in degrees, used in set_yaw block (default: 20).
* `~sleep_time` (*float*) duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2). * `~sleep_time` (*float*) duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2).
* `~confirm_run` (*bool*) enable confirmation to run the program (default: true).
These parameters also can be set as URL GET-parameters, for example: These parameters also can be set as URL GET-parameters, for example:

View File

@@ -29,7 +29,7 @@
</value> </value>
<value name="Z"> <value name="Z">
<shadow type="math_number" id="n0ULZn64%k.:,l(,D?TZ"> <shadow type="math_number" id="n0ULZn64%k.:,l(,D?TZ">
<field name="NUM">1</field> <field name="NUM">0</field>
</shadow> </shadow>
</value> </value>
<value name="ID"> <value name="ID">

View File

@@ -1,91 +1,106 @@
<xml xmlns="https://developers.google.com/blockly/xml"> <xml xmlns="https://developers.google.com/blockly/xml">
<block type="controls_whileUntil" id="U1it{GcGuSS:=[xiwZr1" x="113" y="113"> <variables>
<field name="MODE">WHILE</field> <variable id="_{V-S5HPBUl]CcJkL1Jw">led_count</variable>
<value name="BOOL"> </variables>
<block type="logic_boolean" id="]7ZDRwde}/RqjQCX}aVW"> <block type="variables_set" id="{)^J~:UMX%D;RWvztWLN" x="113" y="87">
<field name="BOOL">TRUE</field> <field name="VAR" id="_{V-S5HPBUl]CcJkL1Jw">led_count</field>
<value name="VALUE">
<block type="math_number" id="V_W$3,VFwZjcc|?|1o`l">
<field name="NUM">58</field>
</block> </block>
</value> </value>
<statement name="DO"> <next>
<block type="set_effect" id="WI0zqOz/z3].cR/6UWHn"> <block type="controls_whileUntil" id="U1it{GcGuSS:=[xiwZr1">
<field name="EFFECT">FILL</field> <field name="MODE">WHILE</field>
<value name="COLOR"> <value name="BOOL">
<shadow type="colour_picker" id="B`6;Xv{s2TFp8Yd=ZpSD"> <block type="logic_boolean" id="]7ZDRwde}/RqjQCX}aVW">
<field name="COLOUR">#000000</field> <field name="BOOL">TRUE</field>
</shadow> </block>
</value> </value>
<next> <statement name="DO">
<block type="set_led" id="^Vcs}ki?#ctf7rAchix$"> <block type="set_effect" id="WI0zqOz/z3].cR/6UWHn">
<value name="INDEX"> <field name="EFFECT">FILL</field>
<shadow type="math_number" id="U;VWW$[*LOF7Gf,~?YR7"> <value name="COLOR">
<field name="NUM">0</field> <shadow type="colour_picker" id="B`6;Xv{s2TFp8Yd=ZpSD">
<field name="COLOUR">#000000</field>
</shadow> </shadow>
<block type="math_arithmetic" id="AI6PZBd`]_Z%_~4c-%dB"> </value>
<field name="OP">MULTIPLY</field> <next>
<value name="A"> <block type="set_led" id="^Vcs}ki?#ctf7rAchix$">
<shadow type="math_number" id="|p}X]`SedK3).F/;}NlB"> <value name="INDEX">
<field name="NUM">1</field> <shadow type="math_number" id="U;VWW$[*LOF7Gf,~?YR7">
<field name="NUM">0</field>
</shadow> </shadow>
<block type="math_arithmetic" id="-haE#:,cg{-J=NZERA;F"> <block type="math_arithmetic" id="AI6PZBd`]_Z%_~4c-%dB">
<field name="OP">DIVIDE</field> <field name="OP">MULTIPLY</field>
<value name="A"> <value name="A">
<shadow type="math_number" id="::st;ot}[r]csqceURu*"> <shadow type="math_number" id="|p}X]`SedK3).F/;}NlB">
<field name="NUM">1</field> <field name="NUM">1</field>
</shadow> </shadow>
<block type="math_arithmetic" id="a%+LN)F~=Igg+,p02[qo"> <block type="math_arithmetic" id="-haE#:,cg{-J=NZERA;F">
<field name="OP">ADD</field> <field name="OP">DIVIDE</field>
<value name="A"> <value name="A">
<shadow type="math_number" id="*yIGN((y)/^z0:f5:VDw"> <shadow type="math_number" id="::st;ot}[r]csqceURu*">
<field name="NUM">1</field> <field name="NUM">1</field>
</shadow> </shadow>
<block type="get_yaw" id="mf%77q30bEqNfc/3`Mtb"> <block type="math_arithmetic" id="a%+LN)F~=Igg+,p02[qo">
<field name="FRAME_ID">MAP</field> <field name="OP">ADD</field>
<value name="ID"> <value name="A">
<shadow type="math_number" id="xb32G.N#ip`|^Xv*MOmY"> <shadow type="math_number" id="*yIGN((y)/^z0:f5:VDw">
<field name="NUM">0</field> <field name="NUM">1</field>
</shadow>
<block type="get_yaw" id="mf%77q30bEqNfc/3`Mtb">
<field name="FRAME_ID">MAP</field>
<value name="ID">
<shadow type="math_number" id="xb32G.N#ip`|^Xv*MOmY">
<field name="NUM">0</field>
</shadow>
</value>
</block>
</value>
<value name="B">
<shadow type="math_number" id="T/fTrm;j2Azgav;gI{ZW">
<field name="NUM">180</field>
</shadow> </shadow>
</value> </value>
</block> </block>
</value> </value>
<value name="B"> <value name="B">
<shadow type="math_number" id="T/fTrm;j2Azgav;gI{ZW"> <shadow type="math_number" id="Wo1/ZCeir,u6/.e1H+BB">
<field name="NUM">180</field> <field name="NUM">360</field>
</shadow> </shadow>
</value> </value>
</block> </block>
</value> </value>
<value name="B"> <value name="B">
<shadow type="math_number" id="Wo1/ZCeir,u6/.e1H+BB"> <shadow type="math_number" id="jENTcXz0C5/=)Xpd!}LL">
<field name="NUM">360</field> <field name="NUM">1</field>
</shadow> </shadow>
<block type="variables_get" id="Ko,`n=i88FY~`YbQLA?[">
<field name="VAR" id="_{V-S5HPBUl]CcJkL1Jw">led_count</field>
</block>
</value> </value>
</block> </block>
</value> </value>
<value name="B"> <value name="COLOR">
<shadow type="math_number" id="jENTcXz0C5/=)Xpd!}LL"> <shadow type="colour_picker" id="+vw3bff.5c[=_w,Xm^C(">
<field name="NUM">1</field> <field name="COLOUR">#3366ff</field>
</shadow>
<block type="led_count" id="vM@X8s!xa]v}AaK6PWF5"></block>
</value>
</block>
</value>
<value name="COLOR">
<shadow type="colour_picker" id="+vw3bff.5c[=_w,Xm^C(">
<field name="COLOUR">#3366ff</field>
</shadow>
</value>
<next>
<block type="wait" id="DT%f$bn1*1El5zsgUW8Y">
<value name="TIME">
<shadow type="math_number" id="~Y0hNY[_^#v@aZkE-TH[">
<field name="NUM">0.1</field>
</shadow> </shadow>
</value> </value>
<next>
<block type="wait" id="DT%f$bn1*1El5zsgUW8Y">
<value name="TIME">
<shadow type="math_number" id="~Y0hNY[_^#v@aZkE-TH[">
<field name="NUM">0.1</field>
</shadow>
</value>
</block>
</next>
</block> </block>
</next> </next>
</block> </block>
</next> </statement>
</block> </block>
</statement> </next>
</block> </block>
</xml> </xml>

View File

@@ -1,13 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# Copyright (C) 2020 Copter Express Technologies
#
# Author: Oleg Kalachev <okalachev@gmail.com>
#
# Distributed under MIT License (available at https://opensource.org/licenses/MIT).
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
from __future__ import print_function from __future__ import print_function
import rospy import rospy
@@ -111,7 +103,7 @@ def run(req):
'print': _print, 'print': _print,
'raw_input': _input} 'raw_input': _input}
try: try:
exec(req.code, g) exec req.code in g
except Stop: except Stop:
rospy.loginfo('Program forced to stop') rospy.loginfo('Program forced to stop')
except Exception as e: except Exception as e:

View File

@@ -1,13 +1,3 @@
/*
* Copyright (C) 2020 Copter Express Technologies
*
* Author: Oleg Kalachev <okalachev@gmail.com>
*
* Distributed under MIT License (available at https://opensource.org/licenses/MIT).
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
const COLOR_FLIGHT = 293; const COLOR_FLIGHT = 293;
const COLOR_STATE = 36; const COLOR_STATE = 36;
const COLOR_LED = 143; const COLOR_LED = 143;
@@ -353,17 +343,6 @@ Blockly.Blocks['set_effect'] = {
} }
}; };
Blockly.Blocks['led_count'] = {
init: function () {
this.appendDummyInput()
.appendField("LED count");
this.setOutput(true, "Number");
this.setColour(COLOR_LED);
this.setTooltip("Returns the number of LEDs (configured in led.launch).");
this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['take_off'] = { Blockly.Blocks['take_off'] = {
init: function () { init: function () {
this.appendValueInput("ALT") this.appendValueInput("ALT")
@@ -546,7 +525,7 @@ Blockly.Blocks['gpio_read'] = {
this.setOutput(true, "Boolean"); this.setOutput(true, "Boolean");
this.setColour(COLOR_GPIO); this.setColour(COLOR_GPIO);
this.setTooltip("Returns if there is voltage on a GPIO pin."); this.setTooltip("Returns if there is voltage on a GPIO pin.");
this.setHelpUrl(DOCS_URL + '#GPIO'); this.setHelpUrl(DOCS_URL + '#' + this.type);
} }
}; };
@@ -563,7 +542,7 @@ Blockly.Blocks['gpio_write'] = {
this.setPreviousStatement(true, null); this.setPreviousStatement(true, null);
this.setNextStatement(true, null); this.setNextStatement(true, null);
this.setTooltip("Set GPIO pin level."); this.setTooltip("Set GPIO pin level.");
this.setHelpUrl(DOCS_URL + '#GPIO'); this.setHelpUrl(DOCS_URL + '#' + this.type);
} }
}; };
@@ -579,24 +558,7 @@ Blockly.Blocks['set_servo'] = {
this.setColour(COLOR_GPIO); this.setColour(COLOR_GPIO);
this.setPreviousStatement(true, null); this.setPreviousStatement(true, null);
this.setNextStatement(true, null); this.setNextStatement(true, null);
this.setTooltip("Set PWM on a GPIO pin to control servo. PWM is specified in range of 5002500 μs."); this.setTooltip("Set PWM on a GPIO pin to control servo. PWM is specified in range of 5002500 ms.");
this.setHelpUrl(DOCS_URL + '#GPIO'); this.setHelpUrl(DOCS_URL + '#' + this.type);
}
};
Blockly.Blocks['set_duty_cycle'] = {
init: function () {
this.appendValueInput("PIN")
.setCheck("Number")
.appendField("set GPIO pin");
this.appendValueInput("DUTY_CYCLE")
.setCheck("Number")
.appendField("to duty cycle");
this.setInputsInline(true);
this.setColour(COLOR_GPIO);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip("Set PWM duty cycle on a GPIO pin (better to control LEDs, etc). Duty cycle is set in range of 01.");
this.setHelpUrl(DOCS_URL + '#GPIO');
} }
}; };

View File

@@ -106,7 +106,6 @@
<value name="INDEX"><shadow type="math_number"><field name="NUM">0</field></shadow></value> <value name="INDEX"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
<value name="COLOR"><shadow type="colour_picker"></shadow></value> <value name="COLOR"><shadow type="colour_picker"></shadow></value>
</block> </block>
<block type="led_count"></block>
</category> </category>
<category name="GPIO" colour="#5b97cc"> <category name="GPIO" colour="#5b97cc">
<block type="gpio_read"> <block type="gpio_read">
@@ -120,10 +119,6 @@
<value name="PIN"><shadow type="math_number"><field name="NUM">1</field></shadow></value> <value name="PIN"><shadow type="math_number"><field name="NUM">1</field></shadow></value>
<value name="PWM"><shadow type="math_number"><field name="NUM">1500</field></shadow></value> <value name="PWM"><shadow type="math_number"><field name="NUM">1500</field></shadow></value>
</block> </block>
<block type="set_duty_cycle">
<value name="PIN"><shadow type="math_number"><field name="NUM">1</field></shadow></value>
<value name="DUTY_CYCLE"><shadow type="math_number"><field name="NUM">0.5</field></shadow></value>
</block>
</category> </category>
<sep></sep> <sep></sep>
<category name="Logic" colour="#5b80a5"> <category name="Logic" colour="#5b80a5">

View File

@@ -1,13 +1,3 @@
/*
* Copyright (C) 2020 Copter Express Technologies
*
* Author: Oleg Kalachev <okalachev@gmail.com>
*
* Distributed under MIT License (available at https://opensource.org/licenses/MIT).
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
import * as ros from './ros.js'; import * as ros from './ros.js';
import './blocks.js'; import './blocks.js';
import {generateCode, generateUserCode} from './python.js'; import {generateCode, generateUserCode} from './python.js';
@@ -39,9 +29,7 @@ var workspace = Blockly.inject('blockly', {
function readParams() { function readParams() {
return Promise.all([ return Promise.all([
ros.readParam('navigate_tolerance', true, 0.2), ros.readParam('navigate_tolerance', true, 0.2),
ros.readParam('yaw_tolerance', true, 20), ros.readParam('sleep_time', true, 0.2)
ros.readParam('sleep_time', true, 0.2),
ros.readParam('confirm_run', true, true),
]); ]);
} }
@@ -109,7 +97,7 @@ new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'prompt', messageType: 'clover
name: ros.priv + 'input/' + msg.id, name: ros.priv + 'input/' + msg.id,
messageType: 'std_msgs/String', messageType: 'std_msgs/String',
latch: true latch: true
}).publish(new ROSLIB.Message({ data: response || '' })); }).publish(new ROSLIB.Message({ data: response }));
}); });
window.stopProgram = function() { window.stopProgram = function() {
@@ -125,7 +113,7 @@ ros.ros.on('close', update);
ready.then(() => runButton.disabled = false); ready.then(() => runButton.disabled = false);
window.runProgram = function() { window.runProgram = function() {
if (ros.params.confirm_run && !confirm('Run program?')) return; if (!confirm('Run program?')) return;
runRequest = true; runRequest = true;
update(); update();

View File

@@ -1,13 +1,3 @@
/*
* Copyright (C) 2020 Copter Express Technologies
*
* Author: Oleg Kalachev <okalachev@gmail.com>
*
* Distributed under MIT License (available at https://opensource.org/licenses/MIT).
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
import {params} from './ros.js'; import {params} from './ros.js';
// If any new block imports any library, add that library name here. // If any new block imports any library, add that library name here.
@@ -15,12 +5,17 @@ Blockly.Python.addReservedWords('_b,_print');
Blockly.Python.addReservedWords('rospy,srv,Trigger,get_telemetry,navigate,set_velocity,land'); Blockly.Python.addReservedWords('rospy,srv,Trigger,get_telemetry,navigate,set_velocity,land');
Blockly.Python.addReservedWords('navigate_wait,land_wait,wait_arrival,wait_yaw,get_distance'); Blockly.Python.addReservedWords('navigate_wait,land_wait,wait_arrival,wait_yaw,get_distance');
Blockly.Python.addReservedWords('pigpio,pi,Range'); Blockly.Python.addReservedWords('pigpio,pi,Range');
Blockly.Python.addReservedWords('SetLEDEffect,set_effect,led_count,get_led_count'); Blockly.Python.addReservedWords('SetLEDEffect,set_effect');
Blockly.Python.addReservedWords('SetLEDs,LEDState,set_leds'); Blockly.Python.addReservedWords('SetLEDs,LEDState,set_leds');
// TODO: parametrize
const navigate_tolerance = 0.2;
const sleep_time = 0.2;
const IMPORT_SRV = `from clover import srv const IMPORT_SRV = `from clover import srv
from std_srvs.srv import Trigger`; from std_srvs.srv import Trigger`;
// TODO: tolerance to parameters
const NAVIGATE_WAIT = () => `\ndef navigate_wait(x=0, y=0, z=0, speed=0.5, frame_id='body', auto_arm=False): const NAVIGATE_WAIT = () => `\ndef navigate_wait(x=0, y=0, z=0, speed=0.5, frame_id='body', auto_arm=False):
res = navigate(x=x, y=y, z=z, yaw=float('nan'), speed=speed, frame_id=frame_id, auto_arm=auto_arm) res = navigate(x=x, y=y, z=z, yaw=float('nan'), speed=speed, frame_id=frame_id, auto_arm=auto_arm)
@@ -38,13 +33,15 @@ const LAND_WAIT = () => `\ndef land_wait():
while get_telemetry().armed: while get_telemetry().armed:
rospy.sleep(${params.sleep_time})\n`; rospy.sleep(${params.sleep_time})\n`;
// TODO: tolerance to parameters
const WAIT_YAW = () => `\ndef wait_yaw(): const WAIT_YAW = () => `\ndef wait_yaw():
while not rospy.is_shutdown(): while not rospy.is_shutdown():
telem = get_telemetry(frame_id='navigate_target') telem = get_telemetry(frame_id='navigate_target')
if abs(telem.yaw) < math.radians(${params.yaw_tolerance}): if abs(telem.yaw) < math.radians(20):
return return
rospy.sleep(${params.sleep_time})\n`; rospy.sleep(${params.sleep_time})\n`;
// TODO: tolerance to parameters
const WAIT_ARRIVAL = () => `\ndef wait_arrival(): const WAIT_ARRIVAL = () => `\ndef wait_arrival():
while not rospy.is_shutdown(): while not rospy.is_shutdown():
telem = get_telemetry(frame_id='navigate_target') telem = get_telemetry(frame_id='navigate_target')
@@ -52,6 +49,7 @@ const WAIT_ARRIVAL = () => `\ndef wait_arrival():
return return
rospy.sleep(${params.sleep_time})\n`; rospy.sleep(${params.sleep_time})\n`;
// TODO: tolerance to parameters
const ARRIVED = () => `\ndef arrived(): const ARRIVED = () => `\ndef arrived():
telem = get_telemetry(frame_id='navigate_target') telem = get_telemetry(frame_id='navigate_target')
return math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_tolerance}\n` return math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_tolerance}\n`
@@ -87,9 +85,6 @@ function generateROSDefinitions() {
Blockly.Python.definitions_['import_set_led'] = 'from led_msgs.srv import SetLEDs\nfrom led_msgs.msg import LEDState'; Blockly.Python.definitions_['import_set_led'] = 'from led_msgs.srv import SetLEDs\nfrom led_msgs.msg import LEDState';
code += `set_leds = rospy.ServiceProxy('led/set_leds', SetLEDs, persistent=True)\n`; code += `set_leds = rospy.ServiceProxy('led/set_leds', SetLEDs, persistent=True)\n`;
} }
if (rosDefinitions.ledStateArray) {
Blockly.Python.definitions_['import_led_state_array'] = 'from led_msgs.msg import LEDStateArray';
}
if (rosDefinitions.navigateWait) { if (rosDefinitions.navigateWait) {
Blockly.Python.definitions_['import_math'] = 'import math'; Blockly.Python.definitions_['import_math'] = 'import math';
code += NAVIGATE_WAIT(); code += NAVIGATE_WAIT();
@@ -391,48 +386,29 @@ Blockly.Python.set_led = function(block) {
if (/^'(.*)'$/.test(colorCode)) { // is simple string if (/^'(.*)'$/.test(colorCode)) { // is simple string
let color = parseColor(colorCode); let color = parseColor(colorCode);
return `set_leds([LEDState(index=int(${index}), r=${color.r}, g=${color.g}, b=${color.b})])\n`; // TODO: check for simple int return `set_leds([LEDState(index=${index}, r=${color.r}, g=${color.g}, b=${color.b})])\n`;
} else { } else {
let parseColor = Blockly.Python.provideFunction_('parse_color', [PARSE_COLOR]); 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`;
} }
} }
const GET_LED_COUNT = `led_count = None
def get_led_count():
global led_count
if led_count is None:
led_count = len(rospy.wait_for_message('led/state', LEDStateArray, timeout=10).leds)
return led_count\n`;
Blockly.Python.led_count = function(block) {
rosDefinitions.ledStateArray = true;
initNode();
Blockly.Python.definitions_['get_led_count'] = GET_LED_COUNT;
return [`get_led_count()`, Blockly.Python.ORDER_FUNCTION_CALL]
}
function pigpio() { function pigpio() {
Blockly.Python.definitions_['import_pigpio'] = 'import pigpio'; Blockly.Python.definitions_['import_pigpio'] = 'import pigpio';
Blockly.Python.definitions_['init_pigpio'] = 'pi = pigpio.pi()'; Blockly.Python.definitions_['init_pigpio'] = 'pi = pigpio.pi()';
} }
const GPIO_READ = `\ndef gpio_read(pin): const GPIO_READ = `\ndef gpio_read(pin):
pi.set_mode(pin, pigpio.INPUT) pi.set_mode(pin, pigpio.INPUT)
return pi.read(pin)\n`; return pi.read(pin)\n`;
const GPIO_WRITE = `\ndef gpio_write(pin, level): const GPIO_WRITE = `\ndef gpio_write(pin, level):
pi.set_mode(pin, pigpio.OUTPUT) pi.set_mode(pin, pigpio.OUTPUT)
pi.write(pin, level)\n`; pi.write(pin, level)\n`;
const SET_SERVO = `\ndef set_servo(pin, pwm): const SET_SERVO = `\ndef set_servo(pin, pwm):
pi.set_mode(pin, pigpio.OUTPUT) pi.set_mode(pin, pigpio.OUTPUT)
pi.set_servo_pulsewidth(pin, pwm)\n`; pi.set_servo_pulsewidth(pin, pwm)\n`;
const SET_DUTY_CYCLE = `\ndef set_duty_cycle(pin, duty_cycle):
pi.set_mode(pin, pigpio.OUTPUT)
pi.set_PWM_dutycycle(pin, duty_cycle * 255)\n`;
Blockly.Python.gpio_read = function(block) { Blockly.Python.gpio_read = function(block) {
pigpio(); pigpio();
@@ -456,11 +432,3 @@ Blockly.Python.set_servo = function(block) {
var pwm = Blockly.Python.valueToCode(block, 'PWM', Blockly.Python.ORDER_NONE); var pwm = Blockly.Python.valueToCode(block, 'PWM', Blockly.Python.ORDER_NONE);
return `set_servo(${pin}, ${pwm})\n`; return `set_servo(${pin}, ${pwm})\n`;
} }
Blockly.Python.set_duty_cycle = function(block) {
pigpio();
Blockly.Python.definitions_['set_duty_cycle'] = SET_DUTY_CYCLE;
var pin = Blockly.Python.valueToCode(block, 'PIN', Blockly.Python.ORDER_NONE);
var dutyCycle = Blockly.Python.valueToCode(block, 'DUTY_CYCLE', Blockly.Python.ORDER_NONE);
return `set_duty_cycle(${pin}, ${dutyCycle})\n`;
}

View File

@@ -1,13 +1,3 @@
/*
* Copyright (C) 2020 Copter Express Technologies
*
* Author: Oleg Kalachev <okalachev@gmail.com>
*
* Distributed under MIT License (available at https://opensource.org/licenses/MIT).
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
var url = 'ws://' + location.hostname + ':9090'; var url = 'ws://' + location.hostname + ':9090';
export var ros = new ROSLIB.Ros({ url }); export var ros = new ROSLIB.Ros({ url });

View File

@@ -88,6 +88,6 @@ def aruco_gen():
off_x + marker.x, off_y + marker.y, off_z + marker.z, off_x + marker.x, off_y + marker.y, off_z + marker.z,
marker.roll, marker.pitch, marker.yaw) marker.roll, marker.pitch, marker.yaw)
output = open(source_world, 'wb') if inplace else stdout output = open(source_world, 'w') if inplace else stdout
save_world(world_tree, output) save_world(world_tree, output)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 715 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

Some files were not shown because too many files have changed in this diff Show More